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