1 /* Sonic library
2    Copyright 2010
3    Bill Cox
4    This file is part of the Sonic Library.
5 
6    This file is licensed under the Apache 2.0 license.
7 */
8 
9 #include "sonic.h"
10 
11 #include <limits.h>
12 #include <math.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 /*
19     The following code was used to generate the following sinc lookup table.
20 
21     #include <limits.h>
22     #include <math.h>
23     #include <stdio.h>
24 
25     double findHannWeight(int N, double x) {
26         return 0.5*(1.0 - cos(2*M_PI*x/N));
27     }
28 
29     double findSincCoefficient(int N, double x) {
30         double hannWindowWeight = findHannWeight(N, x);
31         double sincWeight;
32 
33         x -= N/2.0;
34         if (x > 1e-9 || x < -1e-9) {
35             sincWeight = sin(M_PI*x)/(M_PI*x);
36         } else {
37             sincWeight = 1.0;
38         }
39         return hannWindowWeight*sincWeight;
40     }
41 
42     int main() {
43         double x;
44         int i;
45         int N = 12;
46 
47         for (i = 0, x = 0.0; x <= N; x += 0.02, i++) {
48             printf("%u %d\n", i, (int)(SHRT_MAX*findSincCoefficient(N, x)));
49         }
50         return 0;
51     }
52 */
53 
54 /* The number of points to use in the sinc FIR filter for resampling. */
55 #define SINC_FILTER_POINTS \
56   12 /* I am not able to hear improvement with higher N. */
57 #define SINC_TABLE_SIZE 601
58 
59 /* Lookup table for windowed sinc function of SINC_FILTER_POINTS points. */
60 static short sincTable[SINC_TABLE_SIZE] = {
61     0,     0,     0,     0,     0,     0,     0,     -1,    -1,    -2,    -2,
62     -3,    -4,    -6,    -7,    -9,    -10,   -12,   -14,   -17,   -19,   -21,
63     -24,   -26,   -29,   -32,   -34,   -37,   -40,   -42,   -44,   -47,   -48,
64     -50,   -51,   -52,   -53,   -53,   -53,   -52,   -50,   -48,   -46,   -43,
65     -39,   -34,   -29,   -22,   -16,   -8,    0,     9,     19,    29,    41,
66     53,    65,    79,    92,    107,   121,   137,   152,   168,   184,   200,
67     215,   231,   247,   262,   276,   291,   304,   317,   328,   339,   348,
68     357,   363,   369,   372,   374,   375,   373,   369,   363,   355,   345,
69     332,   318,   300,   281,   259,   234,   208,   178,   147,   113,   77,
70     39,    0,     -41,   -85,   -130,  -177,  -225,  -274,  -324,  -375,  -426,
71     -478,  -530,  -581,  -632,  -682,  -731,  -779,  -825,  -870,  -912,  -951,
72     -989,  -1023, -1053, -1080, -1104, -1123, -1138, -1149, -1154, -1155, -1151,
73     -1141, -1125, -1105, -1078, -1046, -1007, -963,  -913,  -857,  -796,  -728,
74     -655,  -576,  -492,  -403,  -309,  -210,  -107,  0,     111,   225,   342,
75     462,   584,   708,   833,   958,   1084,  1209,  1333,  1455,  1575,  1693,
76     1807,  1916,  2022,  2122,  2216,  2304,  2384,  2457,  2522,  2579,  2625,
77     2663,  2689,  2706,  2711,  2705,  2687,  2657,  2614,  2559,  2491,  2411,
78     2317,  2211,  2092,  1960,  1815,  1658,  1489,  1308,  1115,  912,   698,
79     474,   241,   0,     -249,  -506,  -769,  -1037, -1310, -1586, -1864, -2144,
80     -2424, -2703, -2980, -3254, -3523, -3787, -4043, -4291, -4529, -4757, -4972,
81     -5174, -5360, -5531, -5685, -5819, -5935, -6029, -6101, -6150, -6175, -6175,
82     -6149, -6096, -6015, -5905, -5767, -5599, -5401, -5172, -4912, -4621, -4298,
83     -3944, -3558, -3141, -2693, -2214, -1705, -1166, -597,  0,     625,   1277,
84     1955,  2658,  3386,  4135,  4906,  5697,  6506,  7332,  8173,  9027,  9893,
85     10769, 11654, 12544, 13439, 14335, 15232, 16128, 17019, 17904, 18782, 19649,
86     20504, 21345, 22170, 22977, 23763, 24527, 25268, 25982, 26669, 27327, 27953,
87     28547, 29107, 29632, 30119, 30569, 30979, 31349, 31678, 31964, 32208, 32408,
88     32565, 32677, 32744, 32767, 32744, 32677, 32565, 32408, 32208, 31964, 31678,
89     31349, 30979, 30569, 30119, 29632, 29107, 28547, 27953, 27327, 26669, 25982,
90     25268, 24527, 23763, 22977, 22170, 21345, 20504, 19649, 18782, 17904, 17019,
91     16128, 15232, 14335, 13439, 12544, 11654, 10769, 9893,  9027,  8173,  7332,
92     6506,  5697,  4906,  4135,  3386,  2658,  1955,  1277,  625,   0,     -597,
93     -1166, -1705, -2214, -2693, -3141, -3558, -3944, -4298, -4621, -4912, -5172,
94     -5401, -5599, -5767, -5905, -6015, -6096, -6149, -6175, -6175, -6150, -6101,
95     -6029, -5935, -5819, -5685, -5531, -5360, -5174, -4972, -4757, -4529, -4291,
96     -4043, -3787, -3523, -3254, -2980, -2703, -2424, -2144, -1864, -1586, -1310,
97     -1037, -769,  -506,  -249,  0,     241,   474,   698,   912,   1115,  1308,
98     1489,  1658,  1815,  1960,  2092,  2211,  2317,  2411,  2491,  2559,  2614,
99     2657,  2687,  2705,  2711,  2706,  2689,  2663,  2625,  2579,  2522,  2457,
100     2384,  2304,  2216,  2122,  2022,  1916,  1807,  1693,  1575,  1455,  1333,
101     1209,  1084,  958,   833,   708,   584,   462,   342,   225,   111,   0,
102     -107,  -210,  -309,  -403,  -492,  -576,  -655,  -728,  -796,  -857,  -913,
103     -963,  -1007, -1046, -1078, -1105, -1125, -1141, -1151, -1155, -1154, -1149,
104     -1138, -1123, -1104, -1080, -1053, -1023, -989,  -951,  -912,  -870,  -825,
105     -779,  -731,  -682,  -632,  -581,  -530,  -478,  -426,  -375,  -324,  -274,
106     -225,  -177,  -130,  -85,   -41,   0,     39,    77,    113,   147,   178,
107     208,   234,   259,   281,   300,   318,   332,   345,   355,   363,   369,
108     373,   375,   374,   372,   369,   363,   357,   348,   339,   328,   317,
109     304,   291,   276,   262,   247,   231,   215,   200,   184,   168,   152,
110     137,   121,   107,   92,    79,    65,    53,    41,    29,    19,    9,
111     0,     -8,    -16,   -22,   -29,   -34,   -39,   -43,   -46,   -48,   -50,
112     -52,   -53,   -53,   -53,   -52,   -51,   -50,   -48,   -47,   -44,   -42,
113     -40,   -37,   -34,   -32,   -29,   -26,   -24,   -21,   -19,   -17,   -14,
114     -12,   -10,   -9,    -7,    -6,    -4,    -3,    -2,    -2,    -1,    -1,
115     0,     0,     0,     0,     0,     0,     0};
116 
117 struct sonicStreamStruct {
118 #ifdef SONIC_SPECTROGRAM
119   sonicSpectrogram spectrogram;
120 #endif  /* SONIC_SPECTROGRAM */
121   short* inputBuffer;
122   short* outputBuffer;
123   short* pitchBuffer;
124   short* downSampleBuffer;
125   float speed;
126   float volume;
127   float pitch;
128   float rate;
129   int oldRatePosition;
130   int newRatePosition;
131   int useChordPitch;
132   int quality;
133   int numChannels;
134   int inputBufferSize;
135   int pitchBufferSize;
136   int outputBufferSize;
137   int numInputSamples;
138   int numOutputSamples;
139   int numPitchSamples;
140   int minPeriod;
141   int maxPeriod;
142   int maxRequired;
143   int remainingInputToCopy;
144   int sampleRate;
145   int prevPeriod;
146   int prevMinDiff;
147   float avePower;
148 };
149 
150 #ifdef SONIC_SPECTROGRAM
151 
152 /* Compute a spectrogram on the fly. */
sonicComputeSpectrogram(sonicStream stream)153 void sonicComputeSpectrogram(sonicStream stream) {
154   stream->spectrogram = sonicCreateSpectrogram(stream->sampleRate);
155   /* Force changeSpeed to be called to compute the spectrogram. */
156   sonicSetSpeed(stream, 2.0);
157 }
158 
159 /* Get the spectrogram. */
sonicGetSpectrogram(sonicStream stream)160 sonicSpectrogram sonicGetSpectrogram(sonicStream stream) {
161   return stream->spectrogram;
162 }
163 
164 #endif
165 
166 /* Scale the samples by the factor. */
scaleSamples(short * samples,int numSamples,float volume)167 static void scaleSamples(short* samples, int numSamples, float volume) {
168   int fixedPointVolume = volume * 4096.0f;
169   int value;
170 
171   while (numSamples--) {
172     value = (*samples * fixedPointVolume) >> 12;
173     if (value > 32767) {
174       value = 32767;
175     } else if (value < -32767) {
176       value = -32767;
177     }
178     *samples++ = value;
179   }
180 }
181 
182 /* Get the speed of the stream. */
sonicGetSpeed(sonicStream stream)183 float sonicGetSpeed(sonicStream stream) { return stream->speed; }
184 
185 /* Set the speed of the stream. */
sonicSetSpeed(sonicStream stream,float speed)186 void sonicSetSpeed(sonicStream stream, float speed) { stream->speed = speed; }
187 
188 /* Get the pitch of the stream. */
sonicGetPitch(sonicStream stream)189 float sonicGetPitch(sonicStream stream) { return stream->pitch; }
190 
191 /* Set the pitch of the stream. */
sonicSetPitch(sonicStream stream,float pitch)192 void sonicSetPitch(sonicStream stream, float pitch) { stream->pitch = pitch; }
193 
194 /* Get the rate of the stream. */
sonicGetRate(sonicStream stream)195 float sonicGetRate(sonicStream stream) { return stream->rate; }
196 
197 /* Set the playback rate of the stream. This scales pitch and speed at the same
198    time. */
sonicSetRate(sonicStream stream,float rate)199 void sonicSetRate(sonicStream stream, float rate) {
200   stream->rate = rate;
201 
202   stream->oldRatePosition = 0;
203   stream->newRatePosition = 0;
204 }
205 
206 /* Get the vocal chord pitch setting. */
sonicGetChordPitch(sonicStream stream)207 int sonicGetChordPitch(sonicStream stream) { return stream->useChordPitch; }
208 
209 /* Set the vocal chord mode for pitch computation.  Default is off. */
sonicSetChordPitch(sonicStream stream,int useChordPitch)210 void sonicSetChordPitch(sonicStream stream, int useChordPitch) {
211   stream->useChordPitch = useChordPitch;
212 }
213 
214 /* Get the quality setting. */
sonicGetQuality(sonicStream stream)215 int sonicGetQuality(sonicStream stream) { return stream->quality; }
216 
217 /* Set the "quality".  Default 0 is virtually as good as 1, but very much
218    faster. */
sonicSetQuality(sonicStream stream,int quality)219 void sonicSetQuality(sonicStream stream, int quality) {
220   stream->quality = quality;
221 }
222 
223 /* Get the scaling factor of the stream. */
sonicGetVolume(sonicStream stream)224 float sonicGetVolume(sonicStream stream) { return stream->volume; }
225 
226 /* Set the scaling factor of the stream. */
sonicSetVolume(sonicStream stream,float volume)227 void sonicSetVolume(sonicStream stream, float volume) {
228   stream->volume = volume;
229 }
230 
231 /* Free stream buffers. */
freeStreamBuffers(sonicStream stream)232 static void freeStreamBuffers(sonicStream stream) {
233   if (stream->inputBuffer != NULL) {
234     free(stream->inputBuffer);
235   }
236   if (stream->outputBuffer != NULL) {
237     free(stream->outputBuffer);
238   }
239   if (stream->pitchBuffer != NULL) {
240     free(stream->pitchBuffer);
241   }
242   if (stream->downSampleBuffer != NULL) {
243     free(stream->downSampleBuffer);
244   }
245 }
246 
247 /* Destroy the sonic stream. */
sonicDestroyStream(sonicStream stream)248 void sonicDestroyStream(sonicStream stream) {
249 #ifdef SONIC_SPECTROGRAM
250   if (stream->spectrogram != NULL) {
251     sonicDestroySpectrogram(stream->spectrogram);
252   }
253 #endif  /* SONIC_SPECTROGRAM */
254   freeStreamBuffers(stream);
255   free(stream);
256 }
257 
258 /* Allocate stream buffers. */
allocateStreamBuffers(sonicStream stream,int sampleRate,int numChannels)259 static int allocateStreamBuffers(sonicStream stream, int sampleRate,
260                                  int numChannels) {
261   int minPeriod = sampleRate / SONIC_MAX_PITCH;
262   int maxPeriod = sampleRate / SONIC_MIN_PITCH;
263   int maxRequired = 2 * maxPeriod;
264 
265   stream->inputBufferSize = maxRequired;
266   stream->inputBuffer =
267       (short*)calloc(maxRequired, sizeof(short) * numChannels);
268   if (stream->inputBuffer == NULL) {
269     sonicDestroyStream(stream);
270     return 0;
271   }
272   stream->outputBufferSize = maxRequired;
273   stream->outputBuffer =
274       (short*)calloc(maxRequired, sizeof(short) * numChannels);
275   if (stream->outputBuffer == NULL) {
276     sonicDestroyStream(stream);
277     return 0;
278   }
279   stream->pitchBufferSize = maxRequired;
280   stream->pitchBuffer =
281       (short*)calloc(maxRequired, sizeof(short) * numChannels);
282   if (stream->pitchBuffer == NULL) {
283     sonicDestroyStream(stream);
284     return 0;
285   }
286   stream->downSampleBuffer = (short*)calloc(maxRequired, sizeof(short));
287   if (stream->downSampleBuffer == NULL) {
288     sonicDestroyStream(stream);
289     return 0;
290   }
291   stream->sampleRate = sampleRate;
292   stream->numChannels = numChannels;
293   stream->oldRatePosition = 0;
294   stream->newRatePosition = 0;
295   stream->minPeriod = minPeriod;
296   stream->maxPeriod = maxPeriod;
297   stream->maxRequired = maxRequired;
298   stream->prevPeriod = 0;
299   return 1;
300 }
301 
302 /* Create a sonic stream.  Return NULL only if we are out of memory and cannot
303    allocate the stream. */
sonicCreateStream(int sampleRate,int numChannels)304 sonicStream sonicCreateStream(int sampleRate, int numChannels) {
305   sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
306 
307   if (stream == NULL) {
308     return NULL;
309   }
310   if (!allocateStreamBuffers(stream, sampleRate, numChannels)) {
311     return NULL;
312   }
313   stream->speed = 1.0f;
314   stream->pitch = 1.0f;
315   stream->volume = 1.0f;
316   stream->rate = 1.0f;
317   stream->oldRatePosition = 0;
318   stream->newRatePosition = 0;
319   stream->useChordPitch = 0;
320   stream->quality = 0;
321   stream->avePower = 50.0f;
322   return stream;
323 }
324 
325 /* Get the sample rate of the stream. */
sonicGetSampleRate(sonicStream stream)326 int sonicGetSampleRate(sonicStream stream) { return stream->sampleRate; }
327 
328 /* Set the sample rate of the stream.  This will cause samples buffered in the
329    stream to be lost. */
sonicSetSampleRate(sonicStream stream,int sampleRate)330 void sonicSetSampleRate(sonicStream stream, int sampleRate) {
331   freeStreamBuffers(stream);
332   allocateStreamBuffers(stream, sampleRate, stream->numChannels);
333 }
334 
335 /* Get the number of channels. */
sonicGetNumChannels(sonicStream stream)336 int sonicGetNumChannels(sonicStream stream) { return stream->numChannels; }
337 
338 /* Set the num channels of the stream.  This will cause samples buffered in the
339    stream to be lost. */
sonicSetNumChannels(sonicStream stream,int numChannels)340 void sonicSetNumChannels(sonicStream stream, int numChannels) {
341   freeStreamBuffers(stream);
342   allocateStreamBuffers(stream, stream->sampleRate, numChannels);
343 }
344 
345 /* Enlarge the output buffer if needed. */
enlargeOutputBufferIfNeeded(sonicStream stream,int numSamples)346 static int enlargeOutputBufferIfNeeded(sonicStream stream, int numSamples) {
347   if (stream->numOutputSamples + numSamples > stream->outputBufferSize) {
348     stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
349     stream->outputBuffer = (short*)realloc(
350         stream->outputBuffer,
351         stream->outputBufferSize * sizeof(short) * stream->numChannels);
352     if (stream->outputBuffer == NULL) {
353       return 0;
354     }
355   }
356   return 1;
357 }
358 
359 /* Enlarge the input buffer if needed. */
enlargeInputBufferIfNeeded(sonicStream stream,int numSamples)360 static int enlargeInputBufferIfNeeded(sonicStream stream, int numSamples) {
361   if (stream->numInputSamples + numSamples > stream->inputBufferSize) {
362     stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
363     stream->inputBuffer = (short*)realloc(
364         stream->inputBuffer,
365         stream->inputBufferSize * sizeof(short) * stream->numChannels);
366     if (stream->inputBuffer == NULL) {
367       return 0;
368     }
369   }
370   return 1;
371 }
372 
373 /* Add the input samples to the input buffer. */
addFloatSamplesToInputBuffer(sonicStream stream,float * samples,int numSamples)374 static int addFloatSamplesToInputBuffer(sonicStream stream, float* samples,
375                                         int numSamples) {
376   short* buffer;
377   int count = numSamples * stream->numChannels;
378 
379   if (numSamples == 0) {
380     return 1;
381   }
382   if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
383     return 0;
384   }
385   buffer = stream->inputBuffer + stream->numInputSamples * stream->numChannels;
386   while (count--) {
387     *buffer++ = (*samples++) * 32767.0f;
388   }
389   stream->numInputSamples += numSamples;
390   return 1;
391 }
392 
393 /* Add the input samples to the input buffer. */
addShortSamplesToInputBuffer(sonicStream stream,short * samples,int numSamples)394 static int addShortSamplesToInputBuffer(sonicStream stream, short* samples,
395                                         int numSamples) {
396   if (numSamples == 0) {
397     return 1;
398   }
399   if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
400     return 0;
401   }
402   memcpy(stream->inputBuffer + stream->numInputSamples * stream->numChannels,
403          samples, numSamples * sizeof(short) * stream->numChannels);
404   stream->numInputSamples += numSamples;
405   return 1;
406 }
407 
408 /* Add the input samples to the input buffer. */
addUnsignedCharSamplesToInputBuffer(sonicStream stream,unsigned char * samples,int numSamples)409 static int addUnsignedCharSamplesToInputBuffer(sonicStream stream,
410                                                unsigned char* samples,
411                                                int numSamples) {
412   short* buffer;
413   int count = numSamples * stream->numChannels;
414 
415   if (numSamples == 0) {
416     return 1;
417   }
418   if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
419     return 0;
420   }
421   buffer = stream->inputBuffer + stream->numInputSamples * stream->numChannels;
422   while (count--) {
423     *buffer++ = (*samples++ - 128) << 8;
424   }
425   stream->numInputSamples += numSamples;
426   return 1;
427 }
428 
429 /* Remove input samples that we have already processed. */
removeInputSamples(sonicStream stream,int position)430 static void removeInputSamples(sonicStream stream, int position) {
431   int remainingSamples = stream->numInputSamples - position;
432 
433   if (remainingSamples > 0) {
434     memmove(stream->inputBuffer,
435             stream->inputBuffer + position * stream->numChannels,
436             remainingSamples * sizeof(short) * stream->numChannels);
437   }
438   stream->numInputSamples = remainingSamples;
439 }
440 
441 /* Just copy from the array to the output buffer */
copyToOutput(sonicStream stream,short * samples,int numSamples)442 static int copyToOutput(sonicStream stream, short* samples, int numSamples) {
443   if (!enlargeOutputBufferIfNeeded(stream, numSamples)) {
444     return 0;
445   }
446   memcpy(stream->outputBuffer + stream->numOutputSamples * stream->numChannels,
447          samples, numSamples * sizeof(short) * stream->numChannels);
448   stream->numOutputSamples += numSamples;
449   return 1;
450 }
451 
452 /* Just copy from the input buffer to the output buffer.  Return 0 if we fail to
453    resize the output buffer.  Otherwise, return numSamples */
copyInputToOutput(sonicStream stream,int position)454 static int copyInputToOutput(sonicStream stream, int position) {
455   int numSamples = stream->remainingInputToCopy;
456 
457   if (numSamples > stream->maxRequired) {
458     numSamples = stream->maxRequired;
459   }
460   if (!copyToOutput(stream,
461                     stream->inputBuffer + position * stream->numChannels,
462                     numSamples)) {
463     return 0;
464   }
465   stream->remainingInputToCopy -= numSamples;
466   return numSamples;
467 }
468 
469 /* Read data out of the stream.  Sometimes no data will be available, and zero
470    is returned, which is not an error condition. */
sonicReadFloatFromStream(sonicStream stream,float * samples,int maxSamples)471 int sonicReadFloatFromStream(sonicStream stream, float* samples,
472                              int maxSamples) {
473   int numSamples = stream->numOutputSamples;
474   int remainingSamples = 0;
475   short* buffer;
476   int count;
477 
478   if (numSamples == 0) {
479     return 0;
480   }
481   if (numSamples > maxSamples) {
482     remainingSamples = numSamples - maxSamples;
483     numSamples = maxSamples;
484   }
485   buffer = stream->outputBuffer;
486   count = numSamples * stream->numChannels;
487   while (count--) {
488     *samples++ = (*buffer++) / 32767.0f;
489   }
490   if (remainingSamples > 0) {
491     memmove(stream->outputBuffer,
492             stream->outputBuffer + numSamples * stream->numChannels,
493             remainingSamples * sizeof(short) * stream->numChannels);
494   }
495   stream->numOutputSamples = remainingSamples;
496   return numSamples;
497 }
498 
499 /* Read short data out of the stream.  Sometimes no data will be available, and
500    zero is returned, which is not an error condition. */
sonicReadShortFromStream(sonicStream stream,short * samples,int maxSamples)501 int sonicReadShortFromStream(sonicStream stream, short* samples,
502                              int maxSamples) {
503   int numSamples = stream->numOutputSamples;
504   int remainingSamples = 0;
505 
506   if (numSamples == 0) {
507     return 0;
508   }
509   if (numSamples > maxSamples) {
510     remainingSamples = numSamples - maxSamples;
511     numSamples = maxSamples;
512   }
513   memcpy(samples, stream->outputBuffer,
514          numSamples * sizeof(short) * stream->numChannels);
515   if (remainingSamples > 0) {
516     memmove(stream->outputBuffer,
517             stream->outputBuffer + numSamples * stream->numChannels,
518             remainingSamples * sizeof(short) * stream->numChannels);
519   }
520   stream->numOutputSamples = remainingSamples;
521   return numSamples;
522 }
523 
524 /* Read unsigned char data out of the stream.  Sometimes no data will be
525    available, and zero is returned, which is not an error condition. */
sonicReadUnsignedCharFromStream(sonicStream stream,unsigned char * samples,int maxSamples)526 int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char* samples,
527                                     int maxSamples) {
528   int numSamples = stream->numOutputSamples;
529   int remainingSamples = 0;
530   short* buffer;
531   int count;
532 
533   if (numSamples == 0) {
534     return 0;
535   }
536   if (numSamples > maxSamples) {
537     remainingSamples = numSamples - maxSamples;
538     numSamples = maxSamples;
539   }
540   buffer = stream->outputBuffer;
541   count = numSamples * stream->numChannels;
542   while (count--) {
543     *samples++ = (char)((*buffer++) >> 8) + 128;
544   }
545   if (remainingSamples > 0) {
546     memmove(stream->outputBuffer,
547             stream->outputBuffer + numSamples * stream->numChannels,
548             remainingSamples * sizeof(short) * stream->numChannels);
549   }
550   stream->numOutputSamples = remainingSamples;
551   return numSamples;
552 }
553 
554 /* Force the sonic stream to generate output using whatever data it currently
555    has.  No extra delay will be added to the output, but flushing in the middle
556    of words could introduce distortion. */
sonicFlushStream(sonicStream stream)557 int sonicFlushStream(sonicStream stream) {
558   int maxRequired = stream->maxRequired;
559   int remainingSamples = stream->numInputSamples;
560   float speed = stream->speed / stream->pitch;
561   float rate = stream->rate * stream->pitch;
562   int expectedOutputSamples =
563       stream->numOutputSamples +
564       (int)((remainingSamples / speed + stream->numPitchSamples) / rate + 0.5f);
565 
566   /* Add enough silence to flush both input and pitch buffers. */
567   if (!enlargeInputBufferIfNeeded(stream, remainingSamples + 2 * maxRequired)) {
568     return 0;
569   }
570   memset(stream->inputBuffer + remainingSamples * stream->numChannels, 0,
571          2 * maxRequired * sizeof(short) * stream->numChannels);
572   stream->numInputSamples += 2 * maxRequired;
573   if (!sonicWriteShortToStream(stream, NULL, 0)) {
574     return 0;
575   }
576   /* Throw away any extra samples we generated due to the silence we added */
577   if (stream->numOutputSamples > expectedOutputSamples) {
578     stream->numOutputSamples = expectedOutputSamples;
579   }
580   /* Empty input and pitch buffers */
581   stream->numInputSamples = 0;
582   stream->remainingInputToCopy = 0;
583   stream->numPitchSamples = 0;
584   return 1;
585 }
586 
587 /* Return the number of samples in the output buffer */
sonicSamplesAvailable(sonicStream stream)588 int sonicSamplesAvailable(sonicStream stream) {
589   return stream->numOutputSamples;
590 }
591 
592 /* If skip is greater than one, average skip samples together and write them to
593    the down-sample buffer.  If numChannels is greater than one, mix the channels
594    together as we down sample. */
downSampleInput(sonicStream stream,short * samples,int skip)595 static void downSampleInput(sonicStream stream, short* samples, int skip) {
596   int numSamples = stream->maxRequired / skip;
597   int samplesPerValue = stream->numChannels * skip;
598   int i, j;
599   int value;
600   short* downSamples = stream->downSampleBuffer;
601 
602   for (i = 0; i < numSamples; i++) {
603     value = 0;
604     for (j = 0; j < samplesPerValue; j++) {
605       value += *samples++;
606     }
607     value /= samplesPerValue;
608     *downSamples++ = value;
609   }
610 }
611 
612 /* Find the best frequency match in the range, and given a sample skip multiple.
613    For now, just find the pitch of the first channel. */
findPitchPeriodInRange(short * samples,int minPeriod,int maxPeriod,int * retMinDiff,int * retMaxDiff)614 static int findPitchPeriodInRange(short* samples, int minPeriod, int maxPeriod,
615                                   int* retMinDiff, int* retMaxDiff) {
616   int period, bestPeriod = 0, worstPeriod = 255;
617   short* s;
618   short* p;
619   short sVal, pVal;
620   unsigned long diff, minDiff = 1, maxDiff = 0;
621   int i;
622 
623   for (period = minPeriod; period <= maxPeriod; period++) {
624     diff = 0;
625     s = samples;
626     p = samples + period;
627     for (i = 0; i < period; i++) {
628       sVal = *s++;
629       pVal = *p++;
630       diff += sVal >= pVal ? (unsigned short)(sVal - pVal)
631                            : (unsigned short)(pVal - sVal);
632     }
633     /* Note that the highest number of samples we add into diff will be less
634        than 256, since we skip samples.  Thus, diff is a 24 bit number, and
635        we can safely multiply by numSamples without overflow */
636     /* if (bestPeriod == 0 || (bestPeriod*3/2 > period && diff*bestPeriod <
637        minDiff*period) || diff*bestPeriod < (minDiff >> 1)*period) {*/
638     if (bestPeriod == 0 || diff * bestPeriod < minDiff * period) {
639       minDiff = diff;
640       bestPeriod = period;
641     }
642     if (diff * worstPeriod > maxDiff * period) {
643       maxDiff = diff;
644       worstPeriod = period;
645     }
646   }
647   *retMinDiff = minDiff / bestPeriod;
648   *retMaxDiff = maxDiff / worstPeriod;
649   return bestPeriod;
650 }
651 
652 /* At abrupt ends of voiced words, we can have pitch periods that are better
653    approximated by the previous pitch period estimate.  Try to detect this case.
654  */
prevPeriodBetter(sonicStream stream,int minDiff,int maxDiff,int preferNewPeriod)655 static int prevPeriodBetter(sonicStream stream, int minDiff,
656                             int maxDiff, int preferNewPeriod) {
657   if (minDiff == 0 || stream->prevPeriod == 0) {
658     return 0;
659   }
660   if (preferNewPeriod) {
661     if (maxDiff > minDiff * 3) {
662       /* Got a reasonable match this period */
663       return 0;
664     }
665     if (minDiff * 2 <= stream->prevMinDiff * 3) {
666       /* Mismatch is not that much greater this period */
667       return 0;
668     }
669   } else {
670     if (minDiff <= stream->prevMinDiff) {
671       return 0;
672     }
673   }
674   return 1;
675 }
676 
677 /* Find the pitch period.  This is a critical step, and we may have to try
678    multiple ways to get a good answer.  This version uses Average Magnitude
679    Difference Function (AMDF).  To improve speed, we down sample by an integer
680    factor get in the 11KHz range, and then do it again with a narrower
681    frequency range without down sampling */
findPitchPeriod(sonicStream stream,short * samples,int preferNewPeriod)682 static int findPitchPeriod(sonicStream stream, short* samples,
683                            int preferNewPeriod) {
684   int minPeriod = stream->minPeriod;
685   int maxPeriod = stream->maxPeriod;
686   int sampleRate = stream->sampleRate;
687   int minDiff, maxDiff, retPeriod;
688   int skip = 1;
689   int period;
690 
691   if (sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
692     skip = sampleRate / SONIC_AMDF_FREQ;
693   }
694   if (stream->numChannels == 1 && skip == 1) {
695     period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff,
696                                     &maxDiff);
697   } else {
698     downSampleInput(stream, samples, skip);
699     period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod / skip,
700                                     maxPeriod / skip, &minDiff, &maxDiff);
701     if (skip != 1) {
702       period *= skip;
703       minPeriod = period - (skip << 2);
704       maxPeriod = period + (skip << 2);
705       if (minPeriod < stream->minPeriod) {
706         minPeriod = stream->minPeriod;
707       }
708       if (maxPeriod > stream->maxPeriod) {
709         maxPeriod = stream->maxPeriod;
710       }
711       if (stream->numChannels == 1) {
712         period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff,
713                                         &maxDiff);
714       } else {
715         downSampleInput(stream, samples, 1);
716         period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
717                                         maxPeriod, &minDiff, &maxDiff);
718       }
719     }
720   }
721   if (prevPeriodBetter(stream, minDiff, maxDiff, preferNewPeriod)) {
722     retPeriod = stream->prevPeriod;
723   } else {
724     retPeriod = period;
725   }
726   stream->prevMinDiff = minDiff;
727   stream->prevPeriod = period;
728   return retPeriod;
729 }
730 
731 /* Overlap two sound segments, ramp the volume of one down, while ramping the
732    other one from zero up, and add them, storing the result at the output. */
overlapAdd(int numSamples,int numChannels,short * out,short * rampDown,short * rampUp)733 static void overlapAdd(int numSamples, int numChannels, short* out,
734                        short* rampDown, short* rampUp) {
735   short* o;
736   short* u;
737   short* d;
738   int i, t;
739 
740   for (i = 0; i < numChannels; i++) {
741     o = out + i;
742     u = rampUp + i;
743     d = rampDown + i;
744     for (t = 0; t < numSamples; t++) {
745 #ifdef SONIC_USE_SIN
746       float ratio = sin(t * M_PI / (2 * numSamples));
747       *o = *d * (1.0f - ratio) + *u * ratio;
748 #else
749       *o = (*d * (numSamples - t) + *u * t) / numSamples;
750 #endif
751       o += numChannels;
752       d += numChannels;
753       u += numChannels;
754     }
755   }
756 }
757 
758 /* Overlap two sound segments, ramp the volume of one down, while ramping the
759    other one from zero up, and add them, storing the result at the output. */
overlapAddWithSeparation(int numSamples,int numChannels,int separation,short * out,short * rampDown,short * rampUp)760 static void overlapAddWithSeparation(int numSamples, int numChannels,
761                                      int separation, short* out,
762                                      short* rampDown, short* rampUp) {
763   short *o, *u, *d;
764   int i, t;
765 
766   for (i = 0; i < numChannels; i++) {
767     o = out + i;
768     u = rampUp + i;
769     d = rampDown + i;
770     for (t = 0; t < numSamples + separation; t++) {
771       if (t < separation) {
772         *o = *d * (numSamples - t) / numSamples;
773         d += numChannels;
774       } else if (t < numSamples) {
775         *o = (*d * (numSamples - t) + *u * (t - separation)) / numSamples;
776         d += numChannels;
777         u += numChannels;
778       } else {
779         *o = *u * (t - separation) / numSamples;
780         u += numChannels;
781       }
782       o += numChannels;
783     }
784   }
785 }
786 
787 /* Just move the new samples in the output buffer to the pitch buffer */
moveNewSamplesToPitchBuffer(sonicStream stream,int originalNumOutputSamples)788 static int moveNewSamplesToPitchBuffer(sonicStream stream,
789                                        int originalNumOutputSamples) {
790   int numSamples = stream->numOutputSamples - originalNumOutputSamples;
791   int numChannels = stream->numChannels;
792 
793   if (stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
794     stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
795     stream->pitchBuffer =
796         (short*)realloc(stream->pitchBuffer,
797                         stream->pitchBufferSize * sizeof(short) * numChannels);
798     if (stream->pitchBuffer == NULL) {
799       return 0;
800     }
801   }
802   memcpy(stream->pitchBuffer + stream->numPitchSamples * numChannels,
803          stream->outputBuffer + originalNumOutputSamples * numChannels,
804          numSamples * sizeof(short) * numChannels);
805   stream->numOutputSamples = originalNumOutputSamples;
806   stream->numPitchSamples += numSamples;
807   return 1;
808 }
809 
810 /* Remove processed samples from the pitch buffer. */
removePitchSamples(sonicStream stream,int numSamples)811 static void removePitchSamples(sonicStream stream, int numSamples) {
812   int numChannels = stream->numChannels;
813   short* source = stream->pitchBuffer + numSamples * numChannels;
814 
815   if (numSamples == 0) {
816     return;
817   }
818   if (numSamples != stream->numPitchSamples) {
819     memmove(
820         stream->pitchBuffer, source,
821         (stream->numPitchSamples - numSamples) * sizeof(short) * numChannels);
822   }
823   stream->numPitchSamples -= numSamples;
824 }
825 
826 /* Change the pitch.  The latency this introduces could be reduced by looking at
827    past samples to determine pitch, rather than future. */
adjustPitch(sonicStream stream,int originalNumOutputSamples)828 static int adjustPitch(sonicStream stream, int originalNumOutputSamples) {
829   float pitch = stream->pitch;
830   int numChannels = stream->numChannels;
831   int period, newPeriod, separation;
832   int position = 0;
833   short* out;
834   short* rampDown;
835   short* rampUp;
836 
837   if (stream->numOutputSamples == originalNumOutputSamples) {
838     return 1;
839   }
840   if (!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
841     return 0;
842   }
843   while (stream->numPitchSamples - position >= stream->maxRequired) {
844     period = findPitchPeriod(stream,
845                              stream->pitchBuffer + position * numChannels, 0);
846     newPeriod = period / pitch;
847     if (!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
848       return 0;
849     }
850     out = stream->outputBuffer + stream->numOutputSamples * numChannels;
851     if (pitch >= 1.0f) {
852       rampDown = stream->pitchBuffer + position * numChannels;
853       rampUp =
854           stream->pitchBuffer + (position + period - newPeriod) * numChannels;
855       overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
856     } else {
857       rampDown = stream->pitchBuffer + position * numChannels;
858       rampUp = stream->pitchBuffer + position * numChannels;
859       separation = newPeriod - period;
860       overlapAddWithSeparation(period, numChannels, separation, out, rampDown,
861                                rampUp);
862     }
863     stream->numOutputSamples += newPeriod;
864     position += period;
865   }
866   removePitchSamples(stream, position);
867   return 1;
868 }
869 
870 /* Aproximate the sinc function times a Hann window from the sinc table. */
findSincCoefficient(int i,int ratio,int width)871 static int findSincCoefficient(int i, int ratio, int width) {
872   int lobePoints = (SINC_TABLE_SIZE - 1) / SINC_FILTER_POINTS;
873   int left = i * lobePoints + (ratio * lobePoints) / width;
874   int right = left + 1;
875   int position = i * lobePoints * width + ratio * lobePoints - left * width;
876   int leftVal = sincTable[left];
877   int rightVal = sincTable[right];
878 
879   return ((leftVal * (width - position) + rightVal * position) << 1) / width;
880 }
881 
882 /* Return 1 if value >= 0, else -1.  This represents the sign of value. */
getSign(int value)883 static int getSign(int value) { return value >= 0 ? 1 : -1; }
884 
885 /* Interpolate the new output sample. */
interpolate(sonicStream stream,short * in,int oldSampleRate,int newSampleRate)886 static short interpolate(sonicStream stream, short* in, int oldSampleRate,
887                          int newSampleRate) {
888   /* Compute N-point sinc FIR-filter here.  Clip rather than overflow. */
889   int i;
890   int total = 0;
891   int position = stream->newRatePosition * oldSampleRate;
892   int leftPosition = stream->oldRatePosition * newSampleRate;
893   int rightPosition = (stream->oldRatePosition + 1) * newSampleRate;
894   int ratio = rightPosition - position - 1;
895   int width = rightPosition - leftPosition;
896   int weight, value;
897   int oldSign;
898   int overflowCount = 0;
899 
900   for (i = 0; i < SINC_FILTER_POINTS; i++) {
901     weight = findSincCoefficient(i, ratio, width);
902     /* printf("%u %f\n", i, weight); */
903     value = in[i * stream->numChannels] * weight;
904     oldSign = getSign(total);
905     total += value;
906     if (oldSign != getSign(total) && getSign(value) == oldSign) {
907       /* We must have overflowed.  This can happen with a sinc filter. */
908       overflowCount += oldSign;
909     }
910   }
911   /* It is better to clip than to wrap if there was a overflow. */
912   if (overflowCount > 0) {
913     return SHRT_MAX;
914   } else if (overflowCount < 0) {
915     return SHRT_MIN;
916   }
917   return total >> 16;
918 }
919 
920 /* Change the rate.  Interpolate with a sinc FIR filter using a Hann window. */
adjustRate(sonicStream stream,float rate,int originalNumOutputSamples)921 static int adjustRate(sonicStream stream, float rate,
922                       int originalNumOutputSamples) {
923   int newSampleRate = stream->sampleRate / rate;
924   int oldSampleRate = stream->sampleRate;
925   int numChannels = stream->numChannels;
926   int position;
927   short *in, *out;
928   int i;
929   int N = SINC_FILTER_POINTS;
930 
931   /* Set these values to help with the integer math */
932   while (newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
933     newSampleRate >>= 1;
934     oldSampleRate >>= 1;
935   }
936   if (stream->numOutputSamples == originalNumOutputSamples) {
937     return 1;
938   }
939   if (!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
940     return 0;
941   }
942   /* Leave at least N pitch sample in the buffer */
943   for (position = 0; position < stream->numPitchSamples - N; position++) {
944     while ((stream->oldRatePosition + 1) * newSampleRate >
945            stream->newRatePosition * oldSampleRate) {
946       if (!enlargeOutputBufferIfNeeded(stream, 1)) {
947         return 0;
948       }
949       out = stream->outputBuffer + stream->numOutputSamples * numChannels;
950       in = stream->pitchBuffer + position * numChannels;
951       for (i = 0; i < numChannels; i++) {
952         *out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
953         in++;
954       }
955       stream->newRatePosition++;
956       stream->numOutputSamples++;
957     }
958     stream->oldRatePosition++;
959     if (stream->oldRatePosition == oldSampleRate) {
960       stream->oldRatePosition = 0;
961       if (stream->newRatePosition != newSampleRate) {
962         fprintf(stderr,
963                 "Assertion failed: stream->newRatePosition != newSampleRate\n");
964         exit(1);
965       }
966       stream->newRatePosition = 0;
967     }
968   }
969   removePitchSamples(stream, position);
970   return 1;
971 }
972 
973 /* Skip over a pitch period, and copy period/speed samples to the output */
skipPitchPeriod(sonicStream stream,short * samples,float speed,int period)974 static int skipPitchPeriod(sonicStream stream, short* samples, float speed,
975                            int period) {
976   long newSamples;
977   int numChannels = stream->numChannels;
978 
979   if (speed >= 2.0f) {
980     newSamples = period / (speed - 1.0f);
981   } else {
982     newSamples = period;
983     stream->remainingInputToCopy = period * (2.0f - speed) / (speed - 1.0f);
984   }
985   if (!enlargeOutputBufferIfNeeded(stream, newSamples)) {
986     return 0;
987   }
988   overlapAdd(newSamples, numChannels,
989              stream->outputBuffer + stream->numOutputSamples * numChannels,
990              samples, samples + period * numChannels);
991   stream->numOutputSamples += newSamples;
992   return newSamples;
993 }
994 
995 /* Insert a pitch period, and determine how much input to copy directly. */
insertPitchPeriod(sonicStream stream,short * samples,float speed,int period)996 static int insertPitchPeriod(sonicStream stream, short* samples, float speed,
997                              int period) {
998   long newSamples;
999   short* out;
1000   int numChannels = stream->numChannels;
1001 
1002   if (speed < 0.5f) {
1003     newSamples = period * speed / (1.0f - speed);
1004   } else {
1005     newSamples = period;
1006     stream->remainingInputToCopy =
1007         period * (2.0f * speed - 1.0f) / (1.0f - speed);
1008   }
1009   if (!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
1010     return 0;
1011   }
1012   out = stream->outputBuffer + stream->numOutputSamples * numChannels;
1013   memcpy(out, samples, period * sizeof(short) * numChannels);
1014   out =
1015       stream->outputBuffer + (stream->numOutputSamples + period) * numChannels;
1016   overlapAdd(newSamples, numChannels, out, samples + period * numChannels,
1017              samples);
1018   stream->numOutputSamples += period + newSamples;
1019   return newSamples;
1020 }
1021 
1022 /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1023    we fail to resize an input or output buffer. */
changeSpeed(sonicStream stream,float speed)1024 static int changeSpeed(sonicStream stream, float speed) {
1025   short* samples;
1026   int numSamples = stream->numInputSamples;
1027   int position = 0, period, newSamples;
1028   int maxRequired = stream->maxRequired;
1029 
1030   /* printf("Changing speed to %f\n", speed); */
1031   if (stream->numInputSamples < maxRequired) {
1032     return 1;
1033   }
1034   do {
1035     if (stream->remainingInputToCopy > 0) {
1036       newSamples = copyInputToOutput(stream, position);
1037       position += newSamples;
1038     } else {
1039       samples = stream->inputBuffer + position * stream->numChannels;
1040       period = findPitchPeriod(stream, samples, 1);
1041 #ifdef SONIC_SPECTROGRAM
1042       if (stream->spectrogram != NULL) {
1043         sonicAddPitchPeriodToSpectrogram(stream->spectrogram, samples, period,
1044                                          stream->numChannels);
1045         newSamples = period;
1046         position += period;
1047       } else
1048 #endif  /* SONIC_SPECTROGRAM */
1049           if (speed > 1.0) {
1050         newSamples = skipPitchPeriod(stream, samples, speed, period);
1051         position += period + newSamples;
1052       } else {
1053         newSamples = insertPitchPeriod(stream, samples, speed, period);
1054         position += newSamples;
1055       }
1056     }
1057     if (newSamples == 0) {
1058       return 0; /* Failed to resize output buffer */
1059     }
1060   } while (position + maxRequired <= numSamples);
1061   removeInputSamples(stream, position);
1062   return 1;
1063 }
1064 
1065 /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1066    we fail to resize an input or output buffer.  Also scale the output by the
1067    volume. */
processStreamInput(sonicStream stream)1068 static int processStreamInput(sonicStream stream) {
1069   int originalNumOutputSamples = stream->numOutputSamples;
1070   float speed = stream->speed / stream->pitch;
1071   float rate = stream->rate;
1072 
1073   if (!stream->useChordPitch) {
1074     rate *= stream->pitch;
1075   }
1076   if (speed > 1.00001 || speed < 0.99999) {
1077     changeSpeed(stream, speed);
1078   } else {
1079     if (!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
1080       return 0;
1081     }
1082     stream->numInputSamples = 0;
1083   }
1084   if (stream->useChordPitch) {
1085     if (stream->pitch != 1.0f) {
1086       if (!adjustPitch(stream, originalNumOutputSamples)) {
1087         return 0;
1088       }
1089     }
1090   } else if (rate != 1.0f) {
1091     if (!adjustRate(stream, rate, originalNumOutputSamples)) {
1092       return 0;
1093     }
1094   }
1095   if (stream->volume != 1.0f) {
1096     /* Adjust output volume. */
1097     scaleSamples(
1098         stream->outputBuffer + originalNumOutputSamples * stream->numChannels,
1099         (stream->numOutputSamples - originalNumOutputSamples) *
1100             stream->numChannels,
1101         stream->volume);
1102   }
1103   return 1;
1104 }
1105 
1106 /* Write floating point data to the input buffer and process it. */
sonicWriteFloatToStream(sonicStream stream,float * samples,int numSamples)1107 int sonicWriteFloatToStream(sonicStream stream, float* samples,
1108                             int numSamples) {
1109   if (!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
1110     return 0;
1111   }
1112   return processStreamInput(stream);
1113 }
1114 
1115 /* Simple wrapper around sonicWriteFloatToStream that does the short to float
1116    conversion for you. */
sonicWriteShortToStream(sonicStream stream,short * samples,int numSamples)1117 int sonicWriteShortToStream(sonicStream stream, short* samples,
1118                             int numSamples) {
1119   if (!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
1120     return 0;
1121   }
1122   return processStreamInput(stream);
1123 }
1124 
1125 /* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to
1126    float conversion for you. */
sonicWriteUnsignedCharToStream(sonicStream stream,unsigned char * samples,int numSamples)1127 int sonicWriteUnsignedCharToStream(sonicStream stream, unsigned char* samples,
1128                                    int numSamples) {
1129   if (!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
1130     return 0;
1131   }
1132   return processStreamInput(stream);
1133 }
1134 
1135 /* This is a non-stream oriented interface to just change the speed of a sound
1136  * sample */
sonicChangeFloatSpeed(float * samples,int numSamples,float speed,float pitch,float rate,float volume,int useChordPitch,int sampleRate,int numChannels)1137 int sonicChangeFloatSpeed(float* samples, int numSamples, float speed,
1138                           float pitch, float rate, float volume,
1139                           int useChordPitch, int sampleRate, int numChannels) {
1140   sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1141 
1142   sonicSetSpeed(stream, speed);
1143   sonicSetPitch(stream, pitch);
1144   sonicSetRate(stream, rate);
1145   sonicSetVolume(stream, volume);
1146   sonicSetChordPitch(stream, useChordPitch);
1147   sonicWriteFloatToStream(stream, samples, numSamples);
1148   sonicFlushStream(stream);
1149   numSamples = sonicSamplesAvailable(stream);
1150   sonicReadFloatFromStream(stream, samples, numSamples);
1151   sonicDestroyStream(stream);
1152   return numSamples;
1153 }
1154 
1155 /* This is a non-stream oriented interface to just change the speed of a sound
1156  * sample */
sonicChangeShortSpeed(short * samples,int numSamples,float speed,float pitch,float rate,float volume,int useChordPitch,int sampleRate,int numChannels)1157 int sonicChangeShortSpeed(short* samples, int numSamples, float speed,
1158                           float pitch, float rate, float volume,
1159                           int useChordPitch, int sampleRate, int numChannels) {
1160   sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1161 
1162   sonicSetSpeed(stream, speed);
1163   sonicSetPitch(stream, pitch);
1164   sonicSetRate(stream, rate);
1165   sonicSetVolume(stream, volume);
1166   sonicSetChordPitch(stream, useChordPitch);
1167   sonicWriteShortToStream(stream, samples, numSamples);
1168   sonicFlushStream(stream);
1169   numSamples = sonicSamplesAvailable(stream);
1170   sonicReadShortFromStream(stream, samples, numSamples);
1171   sonicDestroyStream(stream);
1172   return numSamples;
1173 }
1174