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