#include #include #include #include #include #include #include #include #include // global values static short * g_overlap_buffer; static short * g_overlap_heuristic; static size_t g_overlap; static size_t g_input_length; // Minimal length static size_t g_output_length; static size_t g_corr_length; static size_t g_skip; // Per frame skip static size_t g_offset; // Offset into stream, lower bits static size_t g_firstframe; // If this is set, the caller should provide initial data in g_overlap_buffer /* some good default for mixing voice, values in micro seconds */ #define OVERLAP 10 // overlapping length (music default = 12 ms) #define CORR_WINDOW 15 // overlapping correlation window length (music default = 28 ms) #define OUTPUT_LEN 30 // one processing sequence length in milliseconds (music default = 82 ms) #define sampcpy(A,B,C) memcpy((A),(B),(C)*sizeof(short)) // Returns the length of one output frame static size_t calc_convert_values( int sample_rate, float tempo ) { unsigned int i; g_overlap = ( sample_rate * OVERLAP ) / 1000; free( g_overlap_buffer ); g_overlap_buffer = malloc( sizeof(short) * g_overlap ); g_firstframe = 1; g_output_length = (sample_rate * OUTPUT_LEN ) / 1000; if ( g_output_length < g_overlap) g_output_length = g_overlap; g_corr_length = ( sample_rate * CORR_WINDOW ) / 1000; free( g_overlap_heuristic ); g_overlap_heuristic = malloc( sizeof(short) * g_corr_length ); // boost middle of sequence by top of a flat parabola // slope stolen from soundtouch, precalc table for( i = 0; i < g_corr_length; ++i ) g_overlap_heuristic[i] = (short)16384.0*(-(double)i*(double)i/((double)g_corr_length*(double)g_corr_length)+(double)i/(double)g_corr_length+0.75f); g_skip = (size_t)( tempo * (float)g_output_length * 65536.0 ); g_input_length = g_corr_length + g_output_length + g_overlap; if( g_skip / 65536 > g_input_length ) g_input_length = g_skip / 65536; return g_output_length; } /* Example: tempo 1.5 with 30/15/10 => skip == 45, out = 30 we found an offset of 5 msec [#####OOOOOOOOOOVVVVVVVVVVVVVVVVVVVVmmmmmmmmmm?????????????????????????] ^--skip [###############OOOOOOOOOOVVVVVVVVVVVVVVVVVVVVmmmmmmmmmm???????????????] [ #####OOOOOOOOOOVVVVVVVVVVVVVVVVVVVVmmmmmmmmmm?????????????????????????] */ static unsigned int find_corr_max(const short *input, const short *mixbuf) { unsigned int i, j, offs = 0; int64_t acc, corr_max = 0; // Scans for the best correlation value by testing each possible position for (i = 0; i < g_corr_length; ++i) { for(j = 0, acc = 0; j < g_overlap; ++j ) acc += input[i+j] * mixbuf[j]; acc *= g_overlap_heuristic[i]; if ( corr_max < acc ) { offs = i; corr_max = acc; } } printf( "%03d %016llX\n", offs, corr_max ); return offs; } // Returns the amount of samples that can be discarded from begin of the input buffer size_t process_frame( short *input, short *output, short *overlap ) { int i, i_ = (int)g_overlap; unsigned int offset = 0; // The first frame needs to be copied verbatim, // we do not have anything to mix, yet. if( g_firstframe ) { sampcpy( output, input, g_output_length ); g_firstframe = 0; } else { offset = find_corr_max( input, overlap ); // Mix end of last frame with begin of this frame for (i = 0; i < (int)g_overlap ; ++i, --i_ ) output[i] = ( i_ * overlap[i] + i * input[i+offset] ) / (int)g_overlap; // Copy rest of the input verbatim sampcpy( output + g_overlap, input + offset + g_overlap, g_output_length - g_overlap ); } // Remember end of this frame for next frame sampcpy( overlap, input + offset + g_output_length, g_overlap ); // Remove the processed samples from the input buffer. g_offset &= 0xffff; g_offset += g_skip; return g_offset / 65536; } int main( int args, char **argv ) { size_t out_chunk_size = calc_convert_values( 8000, 1.25f ); size_t in_fill = 0; short outbuf[ g_output_length ]; short inbuf [ g_input_length ]; 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 ); int fd_in = open( "in.raw", O_RDONLY ); int fd_out = open( "out.raw", O_CREAT | O_WRONLY | O_TRUNC ); (void)args; (void)argv; (void)out_chunk_size; while( 1 ) { size_t processed; size_t missing = g_input_length - in_fill; size_t fromfd = read( fd_in, inbuf + in_fill, missing * sizeof(short) ); if( fromfd > 0 ) in_fill += fromfd / sizeof(short); if( fromfd != missing * sizeof(short) ) { write( fd_out, inbuf, in_fill * sizeof(short) ); close( fd_in ); close( fd_out ); exit(0); } // Do one cycle of processing and outputting processed = process_frame( inbuf, outbuf, g_overlap_buffer ); write( fd_out, outbuf, g_output_length * sizeof(short) ); memmove( inbuf, inbuf + processed, ( in_fill - processed ) * sizeof(short) ); in_fill -= processed; } return 0; }