blob: 503a5a0212649a329641387eab4774e1e4cdb3cf [file] [log] [blame]
Bill Coxca02d872010-11-02 15:10:52 -04001/* Sonic library
2 Copyright 2010
3 Bill Cox
4 This file is part of the Sonic Library.
5
Bill Coxa9999872010-11-11 14:36:59 -05006 The Sonic Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
Bill Coxca02d872010-11-02 15:10:52 -040010
Bill Coxa9999872010-11-11 14:36:59 -050011 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
Bill Coxca02d872010-11-02 15:10:52 -040015
Bill Coxa9999872010-11-11 14:36:59 -050016 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
Bill Cox882fb1d2010-11-02 16:27:20 -040020#include <stdio.h>
Bill Coxca02d872010-11-02 15:10:52 -040021#include <stdlib.h>
22#include <string.h>
Bill Coxa33e3bd2010-11-16 19:57:43 -050023#include <stdarg.h>
Bill Cox4e234d72010-12-17 05:44:02 -050024#ifdef SONIC_USE_SIN
25#include <math.h>
26#ifndef M_PI
27#define M_PI 3.14159265358979323846
28#endif
29#endif
Bill Coxca02d872010-11-02 15:10:52 -040030#include "sonic.h"
31
32struct sonicStreamStruct {
Bill Cox6a1bbb12010-11-19 11:14:28 -050033 short *inputBuffer;
34 short *outputBuffer;
Bill Coxd544fdb2010-11-23 14:13:46 -050035 short *pitchBuffer;
36 short *downSampleBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -050037 float speed;
Bill Coxd544fdb2010-11-23 14:13:46 -050038 float volume;
39 float pitch;
Bill Coxc978c392010-12-17 05:04:06 -050040 int quality;
Bill Cox1a299bb2010-11-19 15:07:17 -050041 int numChannels;
Bill Coxca02d872010-11-02 15:10:52 -040042 int inputBufferSize;
Bill Coxd544fdb2010-11-23 14:13:46 -050043 int pitchBufferSize;
Bill Coxca02d872010-11-02 15:10:52 -040044 int outputBufferSize;
45 int numInputSamples;
46 int numOutputSamples;
Bill Coxd544fdb2010-11-23 14:13:46 -050047 int numPitchSamples;
Bill Coxca02d872010-11-02 15:10:52 -040048 int minPeriod;
49 int maxPeriod;
50 int maxRequired;
51 int remainingInputToCopy;
52 int sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -050053 int prevPeriod;
54 int prevMaxDiff;
55 int prevMinDiff;
Bill Coxca02d872010-11-02 15:10:52 -040056};
57
Bill Cox1a299bb2010-11-19 15:07:17 -050058/* Just used for debugging */
Bill Coxa33e3bd2010-11-16 19:57:43 -050059void sonicMSG(char *format, ...)
60{
61 char buffer[4096];
62 va_list ap;
63 FILE *file;
64
65 va_start(ap, format);
66 vsprintf((char *)buffer, (char *)format, ap);
67 va_end(ap);
68 file=fopen("/tmp/sonic.log", "a");
69 fprintf(file, "%s", buffer);
70 fclose(file);
71}
72
Bill Coxd544fdb2010-11-23 14:13:46 -050073/* Scale the samples by the factor. */
74static void scaleSamples(
75 short *samples,
76 int numSamples,
77 float volume)
78{
79 int fixedPointVolume = volume*4096.0f;
80 int value;
81
82 while(numSamples--) {
83 value = (*samples*fixedPointVolume) >> 12;
84 if(value > 32767) {
85 value = 32767;
86 } else if(value < -32767) {
87 value = -32767;
88 }
89 *samples++ = value;
90 }
91}
92
Bill Coxaf9a6242010-11-08 09:32:27 -050093/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -050094float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -050095 sonicStream stream)
96{
97 return stream->speed;
98}
99
Bill Coxd544fdb2010-11-23 14:13:46 -0500100/* Set the speed of the stream. */
101void sonicSetSpeed(
102 sonicStream stream,
103 float speed)
104{
105 stream->speed = speed;
106}
107
108/* Get the pitch of the stream. */
109float sonicGetPitch(
110 sonicStream stream)
111{
112 return stream->pitch;
113}
114
115/* Set the pitch of the stream. */
116void sonicSetPitch(
117 sonicStream stream,
118 float pitch)
119{
120 stream->pitch = pitch;
121}
122
Bill Coxc978c392010-12-17 05:04:06 -0500123/* Get the quality setting. */
124int sonicGetQuality(
125 sonicStream stream)
126{
127 return stream->quality;
128}
129
130/* Set the "quality". Default 0 is virtually as good as 1, but very much faster. */
131void sonicSetQuality(
132 sonicStream stream,
133 int quality)
134{
135 stream->quality = quality;
136}
137
Bill Coxd544fdb2010-11-23 14:13:46 -0500138/* Get the scaling factor of the stream. */
139float sonicGetVolume(
140 sonicStream stream)
141{
142 return stream->volume;
143}
144
145/* Set the scaling factor of the stream. */
146void sonicSetVolume(
147 sonicStream stream,
148 float volume)
149{
150 stream->volume = volume;
151}
152
Bill Coxaf9a6242010-11-08 09:32:27 -0500153/* Get the sample rate of the stream. */
154int sonicGetSampleRate(
155 sonicStream stream)
156{
157 return stream->sampleRate;
158}
159
Bill Cox527b4e82010-11-24 17:42:58 -0500160/* Get the number of channels. */
161int sonicGetNumChannels(
162 sonicStream stream)
163{
164 return stream->numChannels;
165}
166
Bill Coxca02d872010-11-02 15:10:52 -0400167/* Destroy the sonic stream. */
168void sonicDestroyStream(
169 sonicStream stream)
170{
171 if(stream->inputBuffer != NULL) {
172 free(stream->inputBuffer);
173 }
174 if(stream->outputBuffer != NULL) {
175 free(stream->outputBuffer);
176 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500177 if(stream->pitchBuffer != NULL) {
178 free(stream->pitchBuffer);
179 }
180 if(stream->downSampleBuffer != NULL) {
181 free(stream->downSampleBuffer);
182 }
Bill Coxca02d872010-11-02 15:10:52 -0400183 free(stream);
184}
185
186/* Create a sonic stream. Return NULL only if we are out of memory and cannot
187 allocate the stream. */
188sonicStream sonicCreateStream(
Bill Cox1a299bb2010-11-19 15:07:17 -0500189 int sampleRate,
190 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400191{
192 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
193 int minPeriod = sampleRate/SONIC_MAX_PITCH;
194 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
195 int maxRequired = 2*maxPeriod;
196
197 if(stream == NULL) {
198 return NULL;
199 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500200 stream->inputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500201 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400202 if(stream->inputBuffer == NULL) {
203 sonicDestroyStream(stream);
204 return NULL;
205 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500206 stream->outputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500207 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400208 if(stream->outputBuffer == NULL) {
209 sonicDestroyStream(stream);
210 return NULL;
211 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500212 stream->pitchBufferSize = maxRequired;
213 stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
214 if(stream->pitchBuffer == NULL) {
215 sonicDestroyStream(stream);
216 return NULL;
217 }
218 stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
219 stream->speed = 1.0f;
220 stream->pitch = 1.0f;
221 stream->volume = 1.0f;
Bill Coxc978c392010-12-17 05:04:06 -0500222 stream->quality = 0;
Bill Coxca02d872010-11-02 15:10:52 -0400223 stream->sampleRate = sampleRate;
Bill Cox1a299bb2010-11-19 15:07:17 -0500224 stream->numChannels = numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400225 stream->minPeriod = minPeriod;
226 stream->maxPeriod = maxPeriod;
227 stream->maxRequired = maxRequired;
Bill Coxca02d872010-11-02 15:10:52 -0400228 return stream;
229}
230
Bill Coxca02d872010-11-02 15:10:52 -0400231/* Enlarge the output buffer if needed. */
232static int enlargeOutputBufferIfNeeded(
233 sonicStream stream,
234 int numSamples)
235{
236 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
237 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500238 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500239 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400240 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400241 return 0;
242 }
243 }
244 return 1;
245}
246
Bill Coxca02d872010-11-02 15:10:52 -0400247/* Enlarge the input buffer if needed. */
248static int enlargeInputBufferIfNeeded(
249 sonicStream stream,
250 int numSamples)
251{
252 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
253 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500254 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500255 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400256 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400257 return 0;
258 }
259 }
260 return 1;
261}
262
263/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500264static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400265 sonicStream stream,
266 float *samples,
267 int numSamples)
268{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500269 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500270 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500271
Bill Coxca02d872010-11-02 15:10:52 -0400272 if(numSamples == 0) {
273 return 1;
274 }
275 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
276 return 0;
277 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500278 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500279 while(count--) {
280 *buffer++ = (*samples++)*32767.0f;
281 }
Bill Cox14efa442010-11-02 15:43:58 -0400282 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400283 return 1;
284}
285
Bill Cox0c4c0602010-11-08 11:46:30 -0500286/* Add the input samples to the input buffer. */
287static int addShortSamplesToInputBuffer(
288 sonicStream stream,
289 short *samples,
290 int numSamples)
291{
Bill Cox0c4c0602010-11-08 11:46:30 -0500292 if(numSamples == 0) {
293 return 1;
294 }
295 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
296 return 0;
297 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500298 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
299 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500300 stream->numInputSamples += numSamples;
301 return 1;
302}
303
Bill Cox8a23d2f2010-11-16 18:49:36 -0500304/* Add the input samples to the input buffer. */
305static int addUnsignedCharSamplesToInputBuffer(
306 sonicStream stream,
307 unsigned char *samples,
308 int numSamples)
309{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500310 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500311 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500312
313 if(numSamples == 0) {
314 return 1;
315 }
316 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
317 return 0;
318 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500319 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500320 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500321 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400322 }
323 stream->numInputSamples += numSamples;
324 return 1;
325}
326
327/* Remove input samples that we have already processed. */
328static void removeInputSamples(
329 sonicStream stream,
330 int position)
331{
332 int remainingSamples = stream->numInputSamples - position;
333
334 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500335 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
336 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400337 }
338 stream->numInputSamples = remainingSamples;
339}
340
Bill Cox59e65122010-11-03 10:06:29 -0400341/* Just copy from the array to the output buffer */
Bill Cox68e2aee2010-11-23 19:24:41 -0500342static int copyToOutput(
Bill Cox0c4c0602010-11-08 11:46:30 -0500343 sonicStream stream,
344 short *samples,
345 int numSamples)
346{
Bill Cox0c4c0602010-11-08 11:46:30 -0500347 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
348 return 0;
349 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500350 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
351 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500352 stream->numOutputSamples += numSamples;
353 return numSamples;
354}
355
Bill Cox882fb1d2010-11-02 16:27:20 -0400356/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
357 resize the output buffer. Otherwise, return numSamples */
358static int copyInputToOutput(
359 sonicStream stream,
360 int position)
361{
362 int numSamples = stream->remainingInputToCopy;
363
364 if(numSamples > stream->maxRequired) {
365 numSamples = stream->maxRequired;
366 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500367 if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
Bill Cox1a299bb2010-11-19 15:07:17 -0500368 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400369 return 0;
370 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400371 stream->remainingInputToCopy -= numSamples;
372 return numSamples;
373}
374
Bill Coxca02d872010-11-02 15:10:52 -0400375/* Read data out of the stream. Sometimes no data will be available, and zero
376 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500377int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400378 sonicStream stream,
379 float *samples,
380 int maxSamples)
381{
382 int numSamples = stream->numOutputSamples;
383 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500384 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500385 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400386
387 if(numSamples == 0) {
388 return 0;
389 }
390 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400391 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400392 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400393 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500394 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500395 count = numSamples*stream->numChannels;
396 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500397 *samples++ = (*buffer++)/32767.0f;
398 }
Bill Coxca02d872010-11-02 15:10:52 -0400399 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500400 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
401 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400402 }
403 stream->numOutputSamples = remainingSamples;
404 return numSamples;
405}
406
Bill Cox0c4c0602010-11-08 11:46:30 -0500407/* Read short data out of the stream. Sometimes no data will be available, and zero
408 is returned, which is not an error condition. */
409int sonicReadShortFromStream(
410 sonicStream stream,
411 short *samples,
412 int maxSamples)
413{
414 int numSamples = stream->numOutputSamples;
415 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500416
417 if(numSamples == 0) {
418 return 0;
419 }
420 if(numSamples > maxSamples) {
421 remainingSamples = numSamples - maxSamples;
422 numSamples = maxSamples;
423 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500424 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500425 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500426 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
427 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500428 }
429 stream->numOutputSamples = remainingSamples;
430 return numSamples;
431}
432
Bill Cox8a23d2f2010-11-16 18:49:36 -0500433/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
434 is returned, which is not an error condition. */
435int sonicReadUnsignedCharFromStream(
436 sonicStream stream,
437 unsigned char *samples,
438 int maxSamples)
439{
440 int numSamples = stream->numOutputSamples;
441 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500442 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500443 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500444
445 if(numSamples == 0) {
446 return 0;
447 }
448 if(numSamples > maxSamples) {
449 remainingSamples = numSamples - maxSamples;
450 numSamples = maxSamples;
451 }
452 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500453 count = numSamples*stream->numChannels;
454 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500455 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400456 }
457 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500458 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
459 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400460 }
461 stream->numOutputSamples = remainingSamples;
462 return numSamples;
463}
464
465/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500466 has. No extra delay will be added to the output, but flushing in the middle of
467 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400468int sonicFlushStream(
469 sonicStream stream)
470{
471 int maxRequired = stream->maxRequired;
Bill Cox0283c342010-11-30 11:47:50 -0500472 int remainingSamples = stream->numInputSamples;
473 float speed = stream->speed/stream->pitch;
474 int expectedOutputSamples = stream->numOutputSamples +
475 (int)(remainingSamples/speed + stream->numPitchSamples/stream->pitch + 0.5f);
Bill Coxca02d872010-11-02 15:10:52 -0400476
Bill Cox0283c342010-11-30 11:47:50 -0500477 /* Add enough silence to flush both input and pitch buffers. */
478 if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
479 return 0;
Bill Coxca02d872010-11-02 15:10:52 -0400480 }
Bill Cox0283c342010-11-30 11:47:50 -0500481 memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
482 2*maxRequired*sizeof(short)*stream->numChannels);
483 stream->numInputSamples += 2*maxRequired;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500484 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500485 return 0;
486 }
487 /* Throw away any extra samples we generated due to the silence we added */
Bill Cox0283c342010-11-30 11:47:50 -0500488 if(stream->numOutputSamples > expectedOutputSamples) {
489 stream->numOutputSamples = expectedOutputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500490 }
Bill Cox0283c342010-11-30 11:47:50 -0500491 /* Empty input and pitch buffers */
492 stream->numInputSamples = 0;
493 stream->remainingInputToCopy = 0;
494 stream->numPitchSamples = 0;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500495 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400496}
497
498/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400499int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400500 sonicStream stream)
501{
502 return stream->numOutputSamples;
503}
Bill Cox9bf11b52010-11-03 05:33:09 -0400504
Bill Coxd544fdb2010-11-23 14:13:46 -0500505/* If skip is greater than one, average skip samples togther and write them to
506 the down-sample buffer. If numChannels is greater than one, mix the channels
507 together as we down sample. */
508static void downSampleInput(
509 sonicStream stream,
510 short *samples,
511 int skip)
512{
513 int numSamples = stream->maxRequired/skip;
514 int samplesPerValue = stream->numChannels*skip;
515 int i, j;
516 int value;
517 short *downSamples = stream->downSampleBuffer;
518
519 for(i = 0; i < numSamples; i++) {
520 value = 0;
521 for(j = 0; j < samplesPerValue; j++) {
522 value += *samples++;
523 }
524 value /= samplesPerValue;
525 *downSamples++ = value;
526 }
527}
528
Bill Cox1a299bb2010-11-19 15:07:17 -0500529/* Find the best frequency match in the range, and given a sample skip multiple.
530 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400531static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500532 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400533 int minPeriod,
Bill Coxc17208e2010-11-26 11:09:15 -0500534 int maxPeriod,
535 int *retMinDiff,
536 int *retMaxDiff)
Bill Cox0cd49c82010-11-03 10:46:22 -0400537{
Bill Cox0283c342010-11-30 11:47:50 -0500538 int period, bestPeriod = 0, worstPeriod = 255;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500539 short *s, *p, sVal, pVal;
Bill Coxc17208e2010-11-26 11:09:15 -0500540 unsigned long diff, minDiff = 1, maxDiff = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500541 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400542
Bill Coxd544fdb2010-11-23 14:13:46 -0500543 for(period = minPeriod; period <= maxPeriod; period++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500544 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400545 s = samples;
Bill Coxd544fdb2010-11-23 14:13:46 -0500546 p = samples + period;
547 for(i = 0; i < period; i++) {
548 sVal = *s++;
549 pVal = *p++;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500550 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
551 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400552 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500553 /* Note that the highest number of samples we add into diff will be less
554 than 256, since we skip samples. Thus, diff is a 24 bit number, and
555 we can safely multiply by numSamples without overflow */
Bill Coxc17208e2010-11-26 11:09:15 -0500556 if(diff*bestPeriod < minDiff*period) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400557 minDiff = diff;
558 bestPeriod = period;
559 }
Bill Cox0283c342010-11-30 11:47:50 -0500560 if(diff*worstPeriod > maxDiff*period) {
Bill Coxc17208e2010-11-26 11:09:15 -0500561 maxDiff = diff;
Bill Cox0283c342010-11-30 11:47:50 -0500562 worstPeriod = period;
Bill Coxc17208e2010-11-26 11:09:15 -0500563 }
Bill Cox0cd49c82010-11-03 10:46:22 -0400564 }
Bill Cox0283c342010-11-30 11:47:50 -0500565 *retMinDiff = minDiff/bestPeriod;
566 *retMaxDiff = maxDiff/worstPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400567 return bestPeriod;
568}
569
Bill Coxc17208e2010-11-26 11:09:15 -0500570/* At abrupt ends of voiced words, we can have pitch periods that are better
571 aproximated by the previous pitch period estimate. Try to detect this case. */
572static int prevPeriodBetter(
573 sonicStream stream,
574 int period,
575 int minDiff,
Bill Cox0283c342010-11-30 11:47:50 -0500576 int maxDiff,
577 int preferNewPeriod)
Bill Coxc17208e2010-11-26 11:09:15 -0500578{
Bill Cox0283c342010-11-30 11:47:50 -0500579 if(minDiff == 0) {
580 return 0;
Bill Coxc17208e2010-11-26 11:09:15 -0500581 }
Bill Cox0283c342010-11-30 11:47:50 -0500582 if(preferNewPeriod) {
583 if(maxDiff > minDiff*3) {
584 /* Got a reasonable match this period */
585 return 0;
586 }
587 if(minDiff*2 <= stream->prevMinDiff*3) {
588 /* Mismatch is not that much greater this period */
589 return 0;
590 }
591 } else {
592 if(minDiff <= stream->prevMinDiff) {
593 return 0;
594 }
595 }
596 return 1;
Bill Coxc17208e2010-11-26 11:09:15 -0500597}
598
Bill Cox9bf11b52010-11-03 05:33:09 -0400599/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400600 multiple ways to get a good answer. This version uses AMDF. To improve
601 speed, we down sample by an integer factor get in the 11KHz range, and then
602 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400603static int findPitchPeriod(
604 sonicStream stream,
Bill Cox0283c342010-11-30 11:47:50 -0500605 short *samples,
606 int preferNewPeriod)
Bill Cox9bf11b52010-11-03 05:33:09 -0400607{
608 int minPeriod = stream->minPeriod;
609 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400610 int sampleRate = stream->sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -0500611 int minDiff, maxDiff, retPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400612 int skip = 1;
613 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400614
Bill Cox4e234d72010-12-17 05:44:02 -0500615 if(sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400616 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400617 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500618 if(stream->numChannels == 1 && skip == 1) {
Bill Coxc17208e2010-11-26 11:09:15 -0500619 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
620 } else {
621 downSampleInput(stream, samples, skip);
622 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
623 maxPeriod/skip, &minDiff, &maxDiff);
624 if(skip != 1) {
625 period *= skip;
626 minPeriod = period - (skip << 2);
627 maxPeriod = period + (skip << 2);
628 if(minPeriod < stream->minPeriod) {
629 minPeriod = stream->minPeriod;
630 }
631 if(maxPeriod > stream->maxPeriod) {
632 maxPeriod = stream->maxPeriod;
633 }
634 if(stream->numChannels == 1) {
635 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
636 &minDiff, &maxDiff);
637 } else {
638 downSampleInput(stream, samples, 1);
639 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
640 maxPeriod, &minDiff, &maxDiff);
641 }
642 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500643 }
Bill Cox0283c342010-11-30 11:47:50 -0500644 if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
Bill Coxc17208e2010-11-26 11:09:15 -0500645 retPeriod = stream->prevPeriod;
646 } else {
647 retPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500648 }
Bill Coxc17208e2010-11-26 11:09:15 -0500649 stream->prevMinDiff = minDiff;
650 stream->prevMaxDiff = maxDiff;
651 stream->prevPeriod = period;
652 return retPeriod;
Bill Coxd544fdb2010-11-23 14:13:46 -0500653}
654
Bill Cox68e2aee2010-11-23 19:24:41 -0500655/* Overlap two sound segments, ramp the volume of one down, while ramping the
656 other one from zero up, and add them, storing the result at the output. */
657static void overlapAdd(
658 int numSamples,
659 int numChannels,
660 short *out,
661 short *rampDown,
662 short *rampUp)
663{
Bill Coxd76d2222010-11-24 11:42:29 -0500664 short *o, *u, *d;
Bill Cox68e2aee2010-11-23 19:24:41 -0500665 int i, t;
666
667 for(i = 0; i < numChannels; i++) {
668 o = out + i;
Bill Coxd76d2222010-11-24 11:42:29 -0500669 u = rampUp + i;
670 d = rampDown + i;
Bill Cox68e2aee2010-11-23 19:24:41 -0500671 for(t = 0; t < numSamples; t++) {
Bill Cox4e234d72010-12-17 05:44:02 -0500672#ifdef SONIC_USE_SIN
673 float ratio = sin(t*M_PI/(2*numSamples));
674 *o = *d*(1.0f - ratio) + *u*ratio;
675#else
Bill Coxd76d2222010-11-24 11:42:29 -0500676 *o = (*d*(numSamples - t) + *u*t)/numSamples;
Bill Cox4e234d72010-12-17 05:44:02 -0500677#endif
Bill Cox68e2aee2010-11-23 19:24:41 -0500678 o += numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500679 d += numChannels;
680 u += numChannels;
681 }
682 }
683}
684
685/* Overlap two sound segments, ramp the volume of one down, while ramping the
686 other one from zero up, and add them, storing the result at the output. */
687static void overlapAddWithSeparation(
688 int numSamples,
689 int numChannels,
690 int separation,
691 short *out,
692 short *rampDown,
693 short *rampUp)
694{
695 short *o, *u, *d;
696 int i, t;
697
698 for(i = 0; i < numChannels; i++) {
699 o = out + i;
700 u = rampUp + i;
701 d = rampDown + i;
702 for(t = 0; t < numSamples + separation; t++) {
703 if(t < separation) {
704 *o = *d*(numSamples - t)/numSamples;
705 d += numChannels;
706 } else if(t < numSamples) {
707 *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
708 d += numChannels;
709 u += numChannels;
710 } else {
711 *o = *u*(t - separation)/numSamples;
712 u += numChannels;
713 }
714 o += numChannels;
Bill Cox68e2aee2010-11-23 19:24:41 -0500715 }
716 }
717}
718
719/* Just move the new samples in the output buffer to the pitch bufer */
720static int moveNewSamplesToPitchBuffer(
Bill Coxd544fdb2010-11-23 14:13:46 -0500721 sonicStream stream,
722 int originalNumOutputSamples)
723{
Bill Cox68e2aee2010-11-23 19:24:41 -0500724 int numSamples = stream->numOutputSamples - originalNumOutputSamples;
725 int numChannels = stream->numChannels;
726
727 if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
728 stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
729 stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
730 stream->pitchBufferSize*sizeof(short)*numChannels);
731 if(stream->pitchBuffer == NULL) {
732 return 0;
733 }
734 }
735 memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
736 stream->outputBuffer + originalNumOutputSamples*numChannels,
737 numSamples*sizeof(short)*numChannels);
738 stream->numOutputSamples = originalNumOutputSamples;
739 stream->numPitchSamples += numSamples;
740 return 1;
741}
742
743/* Remove processed samples from the pitch buffer. */
744static void removePitchSamples(
745 sonicStream stream,
746 int numSamples)
747{
748 int numChannels = stream->numChannels;
749 short *source = stream->pitchBuffer + numSamples*numChannels;
750
751 if(numSamples == 0) {
752 return;
753 }
754 if(numSamples != stream->numPitchSamples) {
755 memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
756 numSamples)*sizeof(short)*numChannels);
757 }
758 stream->numPitchSamples -= numSamples;
759}
760
761/* Change the pitch. The latency this introduces could be reduced by looking at
762 past samples to determine pitch, rather than future. */
763static int adjustPitch(
764 sonicStream stream,
765 int originalNumOutputSamples)
766{
767 float pitch = stream->pitch;
768 int numChannels = stream->numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500769 int period, newPeriod, separation;
Bill Cox68e2aee2010-11-23 19:24:41 -0500770 int position = 0;
771 short *out, *rampDown, *rampUp;
772
773 if(stream->numOutputSamples == originalNumOutputSamples) {
774 return 1;
775 }
776 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
777 return 0;
778 }
779 while(stream->numPitchSamples - position >= stream->maxRequired) {
Bill Cox0283c342010-11-30 11:47:50 -0500780 period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
Bill Cox68e2aee2010-11-23 19:24:41 -0500781 newPeriod = period/pitch;
Bill Coxd76d2222010-11-24 11:42:29 -0500782 if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500783 return 0;
784 }
Bill Coxd76d2222010-11-24 11:42:29 -0500785 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
786 if(pitch >= 1.0f) {
787 rampDown = stream->pitchBuffer + position*numChannels;
788 rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
789 overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
790 } else {
791 rampDown = stream->pitchBuffer + position*numChannels;
792 rampUp = stream->pitchBuffer + position*numChannels;
793 separation = newPeriod - period;
794 overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
Bill Cox68e2aee2010-11-23 19:24:41 -0500795 }
Bill Coxd76d2222010-11-24 11:42:29 -0500796 stream->numOutputSamples += newPeriod;
Bill Cox68e2aee2010-11-23 19:24:41 -0500797 position += period;
798 }
799 removePitchSamples(stream, position);
800 return 1;
Bill Cox9bf11b52010-11-03 05:33:09 -0400801}
802
Bill Cox59e65122010-11-03 10:06:29 -0400803/* Skip over a pitch period, and copy period/speed samples to the output */
804static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400805 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500806 short *samples,
807 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400808 int period)
809{
Bill Cox68e2aee2010-11-23 19:24:41 -0500810 long newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500811 int numChannels = stream->numChannels;
Bill Cox9bf11b52010-11-03 05:33:09 -0400812
Bill Cox6a1bbb12010-11-19 11:14:28 -0500813 if(speed >= 2.0f) {
814 newSamples = period/(speed - 1.0f);
815 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400816 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500817 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400818 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400819 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
820 return 0;
821 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500822 overlapAdd(newSamples, numChannels, stream->outputBuffer +
823 stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
Bill Cox9bf11b52010-11-03 05:33:09 -0400824 stream->numOutputSamples += newSamples;
825 return newSamples;
826}
827
Bill Cox59e65122010-11-03 10:06:29 -0400828/* Insert a pitch period, and determine how much input to copy directly. */
829static int insertPitchPeriod(
830 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500831 short *samples,
832 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400833 int period)
834{
Bill Cox68e2aee2010-11-23 19:24:41 -0500835 long newSamples;
836 short *out;
Bill Cox1a299bb2010-11-19 15:07:17 -0500837 int numChannels = stream->numChannels;
Bill Cox59e65122010-11-03 10:06:29 -0400838
Bill Cox6a1bbb12010-11-19 11:14:28 -0500839 if(speed < 0.5f) {
840 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400841 } else {
842 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500843 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400844 }
845 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
846 return 0;
847 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500848 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
849 memcpy(out, samples, period*sizeof(short)*numChannels);
Bill Cox68e2aee2010-11-23 19:24:41 -0500850 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
851 overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
Bill Cox59e65122010-11-03 10:06:29 -0400852 stream->numOutputSamples += period + newSamples;
853 return newSamples;
854}
855
Bill Cox9bf11b52010-11-03 05:33:09 -0400856/* Resample as many pitch periods as we have buffered on the input. Return 0 if
Bill Coxd544fdb2010-11-23 14:13:46 -0500857 we fail to resize an input or output buffer. Also scale the output by the volume. */
858static int changeSpeed(
859 sonicStream stream,
860 float speed)
Bill Cox9bf11b52010-11-03 05:33:09 -0400861{
Bill Cox1a299bb2010-11-19 15:07:17 -0500862 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -0500863 int numSamples = stream->numInputSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400864 int position = 0, period, newSamples;
865 int maxRequired = stream->maxRequired;
866
Bill Cox9bf11b52010-11-03 05:33:09 -0400867 if(stream->numInputSamples < maxRequired) {
868 return 1;
869 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400870 do {
871 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400872 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400873 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400874 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500875 samples = stream->inputBuffer + position*stream->numChannels;
Bill Cox0283c342010-11-30 11:47:50 -0500876 period = findPitchPeriod(stream, samples, 1);
Bill Cox59e65122010-11-03 10:06:29 -0400877 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500878 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400879 position += period + newSamples;
880 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500881 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400882 position += newSamples;
883 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400884 }
885 if(newSamples == 0) {
886 return 0; /* Failed to resize output buffer */
887 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400888 } while(position + maxRequired <= numSamples);
889 removeInputSamples(stream, position);
890 return 1;
891}
Bill Cox0c4c0602010-11-08 11:46:30 -0500892
Bill Coxd544fdb2010-11-23 14:13:46 -0500893/* Resample as many pitch periods as we have buffered on the input. Return 0 if
894 we fail to resize an input or output buffer. Also scale the output by the volume. */
895static int processStreamInput(
896 sonicStream stream)
897{
898 int originalNumOutputSamples = stream->numOutputSamples;
899 float speed = stream->speed/stream->pitch;
900
901 if(speed > 1.00001 || speed < 0.99999) {
902 changeSpeed(stream, speed);
903 } else {
Bill Cox68e2aee2010-11-23 19:24:41 -0500904 if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
Bill Coxd544fdb2010-11-23 14:13:46 -0500905 return 0;
906 }
907 stream->numInputSamples = 0;
908 }
909 if(stream->pitch != 1.0f) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500910 if(!adjustPitch(stream, originalNumOutputSamples)) {
911 return 0;
912 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500913 }
914 if(stream->volume != 1.0f) {
915 /* Adjust output volume. */
916 scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
917 (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
918 stream->volume);
919 }
920 return 1;
921}
922
Bill Cox0c4c0602010-11-08 11:46:30 -0500923/* Write floating point data to the input buffer and process it. */
924int sonicWriteFloatToStream(
925 sonicStream stream,
926 float *samples,
927 int numSamples)
928{
Bill Cox0c4c0602010-11-08 11:46:30 -0500929 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
930 return 0;
931 }
932 return processStreamInput(stream);
933}
934
935/* Simple wrapper around sonicWriteFloatToStream that does the short to float
936 conversion for you. */
937int sonicWriteShortToStream(
938 sonicStream stream,
939 short *samples,
940 int numSamples)
941{
Bill Cox0c4c0602010-11-08 11:46:30 -0500942 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
943 return 0;
944 }
945 return processStreamInput(stream);
946}
947
Bill Cox8a23d2f2010-11-16 18:49:36 -0500948/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
949 conversion for you. */
950int sonicWriteUnsignedCharToStream(
951 sonicStream stream,
952 unsigned char *samples,
953 int numSamples)
954{
Bill Cox8a23d2f2010-11-16 18:49:36 -0500955 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
956 return 0;
957 }
958 return processStreamInput(stream);
959}
960
Bill Cox036d7322010-11-09 09:29:24 -0500961/* This is a non-stream oriented interface to just change the speed of a sound sample */
962int sonicChangeFloatSpeed(
963 float *samples,
964 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500965 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500966 float pitch,
967 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500968 int sampleRate,
969 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500970{
Bill Coxd544fdb2010-11-23 14:13:46 -0500971 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500972
Bill Coxd544fdb2010-11-23 14:13:46 -0500973 sonicSetSpeed(stream, speed);
974 sonicSetPitch(stream, pitch);
975 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500976 sonicWriteFloatToStream(stream, samples, numSamples);
977 sonicFlushStream(stream);
978 numSamples = sonicSamplesAvailable(stream);
979 sonicReadFloatFromStream(stream, samples, numSamples);
980 sonicDestroyStream(stream);
981 return numSamples;
982}
983
984/* This is a non-stream oriented interface to just change the speed of a sound sample */
985int sonicChangeShortSpeed(
986 short *samples,
987 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500988 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500989 float pitch,
990 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500991 int sampleRate,
992 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500993{
Bill Coxd544fdb2010-11-23 14:13:46 -0500994 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500995
Bill Coxd544fdb2010-11-23 14:13:46 -0500996 sonicSetSpeed(stream, speed);
997 sonicSetPitch(stream, pitch);
998 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500999 sonicWriteShortToStream(stream, samples, numSamples);
1000 sonicFlushStream(stream);
1001 numSamples = sonicSamplesAvailable(stream);
1002 sonicReadShortFromStream(stream, samples, numSamples);
1003 sonicDestroyStream(stream);
1004 return numSamples;
1005}