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