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