xref: /aosp_15_r20/external/sonic/sonic.c (revision b290403dc9d28f89f133eb7e190ea8185d440ecd)
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(maxRequired, 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 
891   if (stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
892     int pitchBufferSize = stream->pitchBufferSize;
893     stream->pitchBufferSize += (pitchBufferSize >> 1) + numSamples;
894     stream->pitchBuffer = (short*)sonicRealloc(
895         stream->pitchBuffer,
896         pitchBufferSize,
897         stream->pitchBufferSize,
898         sizeof(short) * numChannels);
899   }
900   memcpy(stream->pitchBuffer + stream->numPitchSamples * numChannels,
901          stream->outputBuffer + originalNumOutputSamples * numChannels,
902          numSamples * sizeof(short) * numChannels);
903   stream->numOutputSamples = originalNumOutputSamples;
904   stream->numPitchSamples += numSamples;
905   return 1;
906 }
907 
908 /* Remove processed samples from the pitch buffer. */
removePitchSamples(sonicStream stream,int numSamples)909 static void removePitchSamples(sonicStream stream, int numSamples) {
910   int numChannels = stream->numChannels;
911   short* source = stream->pitchBuffer + numSamples * numChannels;
912 
913   if (numSamples == 0) {
914     return;
915   }
916   if (numSamples != stream->numPitchSamples) {
917     memmove(
918         stream->pitchBuffer, source,
919         (stream->numPitchSamples - numSamples) * sizeof(short) * numChannels);
920   }
921   stream->numPitchSamples -= numSamples;
922 }
923 
924 /* Approximate the sinc function times a Hann window from the sinc table. */
findSincCoefficient(int i,int ratio,int width)925 static int findSincCoefficient(int i, int ratio, int width) {
926   int lobePoints = (SINC_TABLE_SIZE - 1) / SINC_FILTER_POINTS;
927   int left = i * lobePoints + (ratio * lobePoints) / width;
928   int right = left + 1;
929   int position = i * lobePoints * width + ratio * lobePoints - left * width;
930   int leftVal = sincTable[left];
931   int rightVal = sincTable[right];
932 
933   return ((leftVal * (width - position) + rightVal * position) << 1) / width;
934 }
935 
936 /* Return 1 if value >= 0, else -1.  This represents the sign of value. */
getSign(int value)937 static int getSign(int value) { return value >= 0 ? 1 : -1; }
938 
939 /* Interpolate the new output sample. */
interpolate(sonicStream stream,short * in,int oldSampleRate,int newSampleRate)940 static short interpolate(sonicStream stream, short* in, int oldSampleRate,
941                          int newSampleRate) {
942   /* Compute N-point sinc FIR-filter here.  Clip rather than overflow. */
943   int i;
944   int total = 0;
945   int position = stream->newRatePosition * oldSampleRate;
946   int leftPosition = stream->oldRatePosition * newSampleRate;
947   int rightPosition = (stream->oldRatePosition + 1) * newSampleRate;
948   int ratio = rightPosition - position - 1;
949   int width = rightPosition - leftPosition;
950   int weight, value;
951   int oldSign;
952   int overflowCount = 0;
953 
954   for (i = 0; i < SINC_FILTER_POINTS; i++) {
955     weight = findSincCoefficient(i, ratio, width);
956     value = in[i * stream->numChannels] * weight;
957     oldSign = getSign(total);
958     total += value;
959     if (oldSign != getSign(total) && getSign(value) == oldSign) {
960       /* We must have overflowed.  This can happen with a sinc filter. */
961       overflowCount += oldSign;
962     }
963   }
964   /* It is better to clip than to wrap if there was a overflow. */
965   if (overflowCount > 0) {
966     return SHRT_MAX;
967   } else if (overflowCount < 0) {
968     return SHRT_MIN;
969   }
970   return total >> 16;
971 }
972 
973 /* Change the rate.  Interpolate with a sinc FIR filter using a Hann window. */
adjustRate(sonicStream stream,float rate,int originalNumOutputSamples)974 static int adjustRate(sonicStream stream, float rate,
975                       int originalNumOutputSamples) {
976   int newSampleRate = stream->sampleRate / rate;
977   int oldSampleRate = stream->sampleRate;
978   int numChannels = stream->numChannels;
979   int position;
980   short *in, *out;
981   int i;
982   int N = SINC_FILTER_POINTS;
983 
984   /* Set these values to help with the integer math */
985   while (newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
986     newSampleRate >>= 1;
987     oldSampleRate >>= 1;
988   }
989   if (stream->numOutputSamples == originalNumOutputSamples) {
990     return 1;
991   }
992   if (!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
993     return 0;
994   }
995   /* Leave at least N pitch sample in the buffer */
996   for (position = 0; position < stream->numPitchSamples - N; position++) {
997     while ((stream->oldRatePosition + 1) * newSampleRate >
998            stream->newRatePosition * oldSampleRate) {
999       if (!enlargeOutputBufferIfNeeded(stream, 1)) {
1000         return 0;
1001       }
1002       out = stream->outputBuffer + stream->numOutputSamples * numChannels;
1003       in = stream->pitchBuffer + position * numChannels;
1004       for (i = 0; i < numChannels; i++) {
1005         *out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
1006         in++;
1007       }
1008       stream->newRatePosition++;
1009       stream->numOutputSamples++;
1010     }
1011     stream->oldRatePosition++;
1012     if (stream->oldRatePosition == oldSampleRate) {
1013       stream->oldRatePosition = 0;
1014       stream->newRatePosition = 0;
1015     }
1016   }
1017   removePitchSamples(stream, position);
1018   return 1;
1019 }
1020 
1021 /* Skip over a pitch period.  Return the number of output samples. */
skipPitchPeriod(sonicStream stream,short * samples,float speed,int period)1022 static int skipPitchPeriod(sonicStream stream, short* samples, float speed,
1023                            int period) {
1024   long newSamples;
1025   int numChannels = stream->numChannels;
1026 
1027   if (speed >= 2.0f) {
1028     /* For speeds >= 2.0, we skip over a portion of each pitch period rather
1029        than dropping whole pitch periods. */
1030     newSamples = period / (speed - 1.0f);
1031   } else {
1032     newSamples = period;
1033   }
1034   if (!enlargeOutputBufferIfNeeded(stream, newSamples)) {
1035     return 0;
1036   }
1037   overlapAdd(newSamples, numChannels,
1038              stream->outputBuffer + stream->numOutputSamples * numChannels,
1039              samples, samples + period * numChannels);
1040   stream->numOutputSamples += newSamples;
1041   return newSamples;
1042 }
1043 
1044 /* Insert a pitch period, and determine how much input to copy directly. */
insertPitchPeriod(sonicStream stream,short * samples,float speed,int period)1045 static int insertPitchPeriod(sonicStream stream, short* samples, float speed,
1046                              int period) {
1047   long newSamples;
1048   short* out;
1049   int numChannels = stream->numChannels;
1050 
1051   if (speed <= 0.5f) {
1052     newSamples = period * speed / (1.0f - speed);
1053   } else {
1054     newSamples = period;
1055   }
1056   if (!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
1057     return 0;
1058   }
1059   out = stream->outputBuffer + stream->numOutputSamples * numChannels;
1060   memcpy(out, samples, period * sizeof(short) * numChannels);
1061   out =
1062       stream->outputBuffer + (stream->numOutputSamples + period) * numChannels;
1063   overlapAdd(newSamples, numChannels, out, samples + period * numChannels,
1064              samples);
1065   stream->numOutputSamples += period + newSamples;
1066   return newSamples;
1067 }
1068 
1069 /* PICOLA copies input to output until the total output samples == consumed
1070    input samples * speed. */
copyUnmodifiedSamples(sonicStream stream,short * samples,float speed,int position,int * newSamples)1071 static int copyUnmodifiedSamples(sonicStream stream, short* samples,
1072                                  float speed, int position, int* newSamples) {
1073   int availableSamples = stream->numInputSamples - position;
1074   float inputToCopyFloat =
1075       1 - stream->timeError * speed / (stream->samplePeriod * (speed - 1.0));
1076 
1077   *newSamples = inputToCopyFloat > availableSamples ? availableSamples
1078                                                     : (int)inputToCopyFloat;
1079   if (!copyToOutput(stream, samples, *newSamples)) {
1080     return 0;
1081   }
1082   stream->timeError +=
1083       *newSamples * stream->samplePeriod * (speed - 1.0) / speed;
1084   return 1;
1085 }
1086 
1087 /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1088    we fail to resize an input or output buffer. */
changeSpeed(sonicStream stream,float speed)1089 static int changeSpeed(sonicStream stream, float speed) {
1090   short* samples;
1091   int numSamples = stream->numInputSamples;
1092   int position = 0, period, newSamples;
1093   int maxRequired = stream->maxRequired;
1094 
1095   if (stream->numInputSamples < maxRequired) {
1096     return 1;
1097   }
1098   do {
1099     samples = stream->inputBuffer + position * stream->numChannels;
1100     if ((speed > 1.0f && speed < 2.0f && stream->timeError < 0.0f) ||
1101         (speed < 1.0f && speed > 0.5f && stream->timeError > 0.0f)) {
1102       /* Deal with the case where PICOLA is still copying input samples to
1103          output unmodified, */
1104       if (!copyUnmodifiedSamples(stream, samples, speed, position,
1105                                  &newSamples)) {
1106         return 0;
1107       }
1108       position += newSamples;
1109     } else {
1110       /* We are in the remaining cases, either inserting/removing a pitch period
1111          for speed < 2.0X, or a portion of one for speed >= 2.0X. */
1112       period = findPitchPeriod(stream, samples, 1);
1113 #ifdef SONIC_SPECTROGRAM
1114       if (stream->spectrogram != NULL) {
1115         sonicAddPitchPeriodToSpectrogram(stream->spectrogram, samples, period,
1116                                          stream->numChannels);
1117         newSamples = period;
1118         position += period;
1119       } else
1120 #endif /* SONIC_SPECTROGRAM */
1121         if (speed > 1.0) {
1122           newSamples = skipPitchPeriod(stream, samples, speed, period);
1123           position += period + newSamples;
1124           if (speed < 2.0) {
1125             stream->timeError += newSamples * stream->samplePeriod -
1126                                  (period + newSamples) * stream->inputPlayTime /
1127                                      stream->numInputSamples;
1128           }
1129         } else {
1130           newSamples = insertPitchPeriod(stream, samples, speed, period);
1131           position += newSamples;
1132           if (speed > 0.5) {
1133             stream->timeError +=
1134                 (period + newSamples) * stream->samplePeriod -
1135                 newSamples * stream->inputPlayTime / stream->numInputSamples;
1136           }
1137         }
1138       if (newSamples == 0) {
1139         return 0; /* Failed to resize output buffer */
1140       }
1141     }
1142   } while (position + maxRequired <= numSamples);
1143   removeInputSamples(stream, position);
1144   return 1;
1145 }
1146 
1147 /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1148    we fail to resize an input or output buffer.  Also scale the output by the
1149    volume. */
processStreamInput(sonicStream stream)1150 static int processStreamInput(sonicStream stream) {
1151   int originalNumOutputSamples = stream->numOutputSamples;
1152   float rate = stream->rate * stream->pitch;
1153   float localSpeed;
1154 
1155   if (stream->numInputSamples == 0) {
1156     return 1;
1157   }
1158   localSpeed =
1159       stream->numInputSamples * stream->samplePeriod / stream->inputPlayTime;
1160   if (localSpeed > 1.00001 || localSpeed < 0.99999) {
1161     changeSpeed(stream, localSpeed);
1162   } else {
1163     if (!copyInputToOutput(stream, stream->numInputSamples)) {
1164       return 0;
1165     }
1166   }
1167   if (rate != 1.0f) {
1168     if (!adjustRate(stream, rate, originalNumOutputSamples)) {
1169       return 0;
1170     }
1171   }
1172   if (stream->volume != 1.0f) {
1173     /* Adjust output volume. */
1174     scaleSamples(
1175         stream->outputBuffer + originalNumOutputSamples * stream->numChannels,
1176         (stream->numOutputSamples - originalNumOutputSamples) *
1177             stream->numChannels,
1178         stream->volume);
1179   }
1180   return 1;
1181 }
1182 
1183 /* Write floating point data to the input buffer and process it. */
sonicWriteFloatToStream(sonicStream stream,const float * samples,int numSamples)1184 int sonicWriteFloatToStream(sonicStream stream, const float* samples,
1185                             int numSamples) {
1186   if (!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
1187     return 0;
1188   }
1189   return processStreamInput(stream);
1190 }
1191 
1192 /* Simple wrapper around sonicWriteFloatToStream that does the short to float
1193    conversion for you. */
sonicWriteShortToStream(sonicStream stream,const short * samples,int numSamples)1194 int sonicWriteShortToStream(sonicStream stream, const short* samples,
1195                             int numSamples) {
1196   if (!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
1197     return 0;
1198   }
1199   return processStreamInput(stream);
1200 }
1201 
1202 /* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to
1203    float conversion for you. */
sonicWriteUnsignedCharToStream(sonicStream stream,const unsigned char * samples,int numSamples)1204 int sonicWriteUnsignedCharToStream(sonicStream stream, const unsigned char* samples,
1205                                    int numSamples) {
1206   if (!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
1207     return 0;
1208   }
1209   return processStreamInput(stream);
1210 }
1211 
1212 /* This is a non-stream oriented interface to just change the speed of a sound
1213  * sample */
sonicChangeFloatSpeed(float * samples,int numSamples,float speed,float pitch,float rate,float volume,int useChordPitch,int sampleRate,int numChannels)1214 int sonicChangeFloatSpeed(float* samples, int numSamples, float speed,
1215                           float pitch, float rate, float volume,
1216                           int useChordPitch, int sampleRate, int numChannels) {
1217   sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1218 
1219   sonicSetSpeed(stream, speed);
1220   sonicSetPitch(stream, pitch);
1221   sonicSetRate(stream, rate);
1222   sonicSetVolume(stream, volume);
1223   sonicWriteFloatToStream(stream, samples, numSamples);
1224   sonicFlushStream(stream);
1225   numSamples = sonicSamplesAvailable(stream);
1226   sonicReadFloatFromStream(stream, samples, numSamples);
1227   sonicDestroyStream(stream);
1228   return numSamples;
1229 }
1230 
1231 /* This is a non-stream oriented interface to just change the speed of a sound
1232  * sample */
sonicChangeShortSpeed(short * samples,int numSamples,float speed,float pitch,float rate,float volume,int useChordPitch,int sampleRate,int numChannels)1233 int sonicChangeShortSpeed(short* samples, int numSamples, float speed,
1234                           float pitch, float rate, float volume,
1235                           int useChordPitch, int sampleRate, int numChannels) {
1236   sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1237 
1238   sonicSetSpeed(stream, speed);
1239   sonicSetPitch(stream, pitch);
1240   sonicSetRate(stream, rate);
1241   sonicSetVolume(stream, volume);
1242   sonicWriteShortToStream(stream, samples, numSamples);
1243   sonicFlushStream(stream);
1244   numSamples = sonicSamplesAvailable(stream);
1245   sonicReadShortFromStream(stream, samples, numSamples);
1246   sonicDestroyStream(stream);
1247   return numSamples;
1248 }
1249