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