summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--timestretch.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/timestretch.c b/timestretch.c
new file mode 100644
index 0000000..63ba059
--- /dev/null
+++ b/timestretch.c
@@ -0,0 +1,149 @@
1#include <stdlib.h>
2#include <stdio.h>
3#include <stdint.h>
4#include <string.h>
5#include <float.h>
6#include <math.h>
7#include <fcntl.h>
8#include <unistd.h>
9#include <inttypes.h>
10
11// global values
12static short * g_overlap_buffer;
13static size_t g_overlap;
14
15static size_t g_input_length; // Minimal length
16static size_t g_output_length;
17static size_t g_corr_length;
18
19static size_t g_skip; // Per frame skip
20static size_t g_offset; // Offset into stream, lower bits
21static size_t g_firstframe; // If this is set, the caller should provide initial data in g_overlap_buffer
22
23/* some good default for mixing voice, values in micro seconds */
24#define OVERLAP 10 // overlapping length (music default = 12 ms)
25#define CORR_WINDOW 15 // overlapping correlation window length (music default = 28 ms)
26#define OUTPUT_LEN 30 // one processing sequence length in milliseconds (music default = 82 ms)
27
28// Returns the length of one output frame
29static size_t calc_convert_values( int sample_rate, float tempo ) {
30 g_overlap = ( sample_rate * OVERLAP ) / 1000;
31
32 free( g_overlap_buffer );
33 g_overlap_buffer = malloc( sizeof(short) * g_overlap );
34 g_firstframe = 1;
35
36 g_output_length = (sample_rate * OUTPUT_LEN ) / 1000;
37 if ( g_output_length < g_overlap)
38 g_output_length = g_overlap;
39 g_corr_length = ( sample_rate * CORR_WINDOW ) / 1000;
40
41 g_skip = (size_t)( tempo * (float)g_output_length * 65536.0 );
42 g_input_length = g_corr_length + g_output_length + g_overlap;
43 if( g_skip / 65536 > g_input_length )
44 g_input_length = g_skip / 65536;
45
46 return g_output_length;
47}
48
49/*
50 Example: tempo 1.5 with 30/15/10 => skip == 45, out = 30
51we found an offset of 5 msec
52
53 [#####OOOOOOOOOOVVVVVVVVVVVVVVVVVVVVmmmmmmmmmm?????????????????????????]
54 ^--skip
55 [###############OOOOOOOOOOVVVVVVVVVVVVVVVVVVVVmmmmmmmmmm???????????????]
56 [ #####OOOOOOOOOOVVVVVVVVVVVVVVVVVVVVmmmmmmmmmm?????????????????????????]
57*/
58
59static unsigned int find_corr_max(const short *input, const short *mixbuf) {
60 unsigned int i, j, offs = 0;
61 double corr_max = FLT_MIN;
62
63 // Scans for the best correlation value by testing each possible position
64 for (i = 0; i < g_corr_length; ++i) {
65 double acc, i_f = (double)i, sl_f = (double)g_corr_length;
66 double heur = 0.75 - ( i_f * i_f ) / ( sl_f * sl_f ) + i_f / sl_f;
67 int64_t acc_i;
68
69 for(j = 0, acc_i = 0; j < g_overlap; ++j )
70 acc_i += input[i+j] * mixbuf[j];
71
72 // boost middle of sequence by top of a flat parabola
73 // slope stolen from soundtouch
74 acc = (double)acc_i * heur;
75 if ( corr_max < acc ) {
76 offs = i;
77 corr_max = acc;
78 }
79 }
80// printf( "%03d %014.0lf\n", best_offs, best_corr );
81 return offs;
82}
83
84// Returns the amount of samples that can be discarded from begin of the input buffer
85size_t process_frame( short *input, short *output, short *overlap ) {
86 int i, i_ = (int)g_overlap;
87 unsigned int offset = 0;
88
89 // The first frame needs to be copied verbatim,
90 // we do not have anything to mix, yet.
91 if( g_firstframe ) {
92 memcpy( output, input, g_output_length * sizeof(short) );
93 g_firstframe = 0;
94 } else {
95 offset = find_corr_max( input, overlap );
96
97 // Mix end of last frame with begin of this frame
98 for (i = 0; i < (int)g_overlap ; ++i, --i_ )
99 output[i] = ( i_ * overlap[i] + i * input[i+offset] ) / (int)g_overlap;
100
101 // Copy rest of the input verbatim
102 memcpy( output + g_overlap, input + offset + g_overlap, ( g_output_length - g_overlap ) * sizeof(short) );
103 }
104
105 // Remember end of this frame for next frame
106 memcpy( overlap, input + offset + g_output_length, g_overlap * sizeof(short) );
107
108 // Remove the processed samples from the input buffer.
109 g_offset &= 0xffff;
110 g_offset += g_skip;
111 return g_offset / 65536;
112}
113
114int main( int args, char **argv ) {
115 size_t out_chunk_size = calc_convert_values( 8000, 1.25f );
116 size_t in_fill = 0;
117 short outbuf[ g_output_length ];
118 short inbuf [ g_input_length ];
119
120 printf( "DEBUG: OL: %zd SWL: %zd SL: %zd SK: %zd ICM: %zd\n", g_overlap, g_output_length, g_corr_length, g_skip / 65536, g_input_length );
121
122 int fd_in = open( "in.raw", O_RDONLY );
123 int fd_out = open( "out.raw", O_CREAT | O_WRONLY | O_TRUNC );
124
125 (void)args; (void)argv; (void)out_chunk_size;
126
127 while( 1 ) {
128 size_t processed;
129 size_t missing = g_input_length - in_fill;
130 size_t fromfd = read( fd_in, inbuf + in_fill, missing * sizeof(short) );
131
132 if( fromfd > 0 )
133 in_fill += fromfd / sizeof(short);
134 if( fromfd != missing * sizeof(short) ) {
135 write( fd_out, inbuf, in_fill * sizeof(short) );
136 close( fd_in ); close( fd_out );
137 exit(0);
138 }
139
140 // Do one cycle of processing and outputting
141 processed = process_frame( inbuf, outbuf, g_overlap_buffer );
142 write( fd_out, outbuf, g_output_length * sizeof(short) );
143
144 memmove( inbuf, inbuf + processed, ( in_fill - processed ) * sizeof(short) );
145 in_fill -= processed;
146 }
147
148 return 0;
149}