blob: 651c2ec6d295b7eb4bffafc9fb7428fb140a59ac [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 Coxca02d872010-11-02 15:10:52 -040024#include "sonic.h"
25
26struct sonicStreamStruct {
Bill Cox6a1bbb12010-11-19 11:14:28 -050027 short *inputBuffer;
28 short *outputBuffer;
Bill Coxd544fdb2010-11-23 14:13:46 -050029 short *pitchBuffer;
30 short *downSampleBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -050031 float speed;
Bill Coxd544fdb2010-11-23 14:13:46 -050032 float volume;
33 float pitch;
Bill Cox1a299bb2010-11-19 15:07:17 -050034 int numChannels;
Bill Coxca02d872010-11-02 15:10:52 -040035 int inputBufferSize;
Bill Coxd544fdb2010-11-23 14:13:46 -050036 int pitchBufferSize;
Bill Coxca02d872010-11-02 15:10:52 -040037 int outputBufferSize;
38 int numInputSamples;
39 int numOutputSamples;
Bill Coxd544fdb2010-11-23 14:13:46 -050040 int numPitchSamples;
Bill Coxca02d872010-11-02 15:10:52 -040041 int minPeriod;
42 int maxPeriod;
43 int maxRequired;
44 int remainingInputToCopy;
45 int sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -050046 int prevPeriod;
47 int prevMaxDiff;
48 int prevMinDiff;
Bill Coxca02d872010-11-02 15:10:52 -040049};
50
Bill Cox1a299bb2010-11-19 15:07:17 -050051/* Just used for debugging */
Bill Coxa33e3bd2010-11-16 19:57:43 -050052void sonicMSG(char *format, ...)
53{
54 char buffer[4096];
55 va_list ap;
56 FILE *file;
57
58 va_start(ap, format);
59 vsprintf((char *)buffer, (char *)format, ap);
60 va_end(ap);
61 file=fopen("/tmp/sonic.log", "a");
62 fprintf(file, "%s", buffer);
63 fclose(file);
64}
65
Bill Coxd544fdb2010-11-23 14:13:46 -050066/* Scale the samples by the factor. */
67static void scaleSamples(
68 short *samples,
69 int numSamples,
70 float volume)
71{
72 int fixedPointVolume = volume*4096.0f;
73 int value;
74
75 while(numSamples--) {
76 value = (*samples*fixedPointVolume) >> 12;
77 if(value > 32767) {
78 value = 32767;
79 } else if(value < -32767) {
80 value = -32767;
81 }
82 *samples++ = value;
83 }
84}
85
Bill Coxaf9a6242010-11-08 09:32:27 -050086/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -050087float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -050088 sonicStream stream)
89{
90 return stream->speed;
91}
92
Bill Coxd544fdb2010-11-23 14:13:46 -050093/* Set the speed of the stream. */
94void sonicSetSpeed(
95 sonicStream stream,
96 float speed)
97{
98 stream->speed = speed;
99}
100
101/* Get the pitch of the stream. */
102float sonicGetPitch(
103 sonicStream stream)
104{
105 return stream->pitch;
106}
107
108/* Set the pitch of the stream. */
109void sonicSetPitch(
110 sonicStream stream,
111 float pitch)
112{
113 stream->pitch = pitch;
114}
115
116/* Get the scaling factor of the stream. */
117float sonicGetVolume(
118 sonicStream stream)
119{
120 return stream->volume;
121}
122
123/* Set the scaling factor of the stream. */
124void sonicSetVolume(
125 sonicStream stream,
126 float volume)
127{
128 stream->volume = volume;
129}
130
Bill Coxaf9a6242010-11-08 09:32:27 -0500131/* Get the sample rate of the stream. */
132int sonicGetSampleRate(
133 sonicStream stream)
134{
135 return stream->sampleRate;
136}
137
Bill Cox527b4e82010-11-24 17:42:58 -0500138/* Get the number of channels. */
139int sonicGetNumChannels(
140 sonicStream stream)
141{
142 return stream->numChannels;
143}
144
Bill Coxca02d872010-11-02 15:10:52 -0400145/* Destroy the sonic stream. */
146void sonicDestroyStream(
147 sonicStream stream)
148{
149 if(stream->inputBuffer != NULL) {
150 free(stream->inputBuffer);
151 }
152 if(stream->outputBuffer != NULL) {
153 free(stream->outputBuffer);
154 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500155 if(stream->pitchBuffer != NULL) {
156 free(stream->pitchBuffer);
157 }
158 if(stream->downSampleBuffer != NULL) {
159 free(stream->downSampleBuffer);
160 }
Bill Coxca02d872010-11-02 15:10:52 -0400161 free(stream);
162}
163
164/* Create a sonic stream. Return NULL only if we are out of memory and cannot
165 allocate the stream. */
166sonicStream sonicCreateStream(
Bill Cox1a299bb2010-11-19 15:07:17 -0500167 int sampleRate,
168 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400169{
170 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
171 int minPeriod = sampleRate/SONIC_MAX_PITCH;
172 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
173 int maxRequired = 2*maxPeriod;
174
175 if(stream == NULL) {
176 return NULL;
177 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500178 stream->inputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500179 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400180 if(stream->inputBuffer == NULL) {
181 sonicDestroyStream(stream);
182 return NULL;
183 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500184 stream->outputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500185 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400186 if(stream->outputBuffer == NULL) {
187 sonicDestroyStream(stream);
188 return NULL;
189 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500190 stream->pitchBufferSize = maxRequired;
191 stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
192 if(stream->pitchBuffer == NULL) {
193 sonicDestroyStream(stream);
194 return NULL;
195 }
196 stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
197 stream->speed = 1.0f;
198 stream->pitch = 1.0f;
199 stream->volume = 1.0f;
Bill Coxca02d872010-11-02 15:10:52 -0400200 stream->sampleRate = sampleRate;
Bill Cox1a299bb2010-11-19 15:07:17 -0500201 stream->numChannels = numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400202 stream->minPeriod = minPeriod;
203 stream->maxPeriod = maxPeriod;
204 stream->maxRequired = maxRequired;
Bill Coxca02d872010-11-02 15:10:52 -0400205 return stream;
206}
207
Bill Coxca02d872010-11-02 15:10:52 -0400208/* Enlarge the output buffer if needed. */
209static int enlargeOutputBufferIfNeeded(
210 sonicStream stream,
211 int numSamples)
212{
213 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
214 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500215 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500216 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400217 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400218 return 0;
219 }
220 }
221 return 1;
222}
223
Bill Coxca02d872010-11-02 15:10:52 -0400224/* Enlarge the input buffer if needed. */
225static int enlargeInputBufferIfNeeded(
226 sonicStream stream,
227 int numSamples)
228{
229 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
230 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500231 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500232 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400233 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400234 return 0;
235 }
236 }
237 return 1;
238}
239
240/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500241static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400242 sonicStream stream,
243 float *samples,
244 int numSamples)
245{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500246 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500247 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500248
Bill Coxca02d872010-11-02 15:10:52 -0400249 if(numSamples == 0) {
250 return 1;
251 }
252 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
253 return 0;
254 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500255 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500256 while(count--) {
257 *buffer++ = (*samples++)*32767.0f;
258 }
Bill Cox14efa442010-11-02 15:43:58 -0400259 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400260 return 1;
261}
262
Bill Cox0c4c0602010-11-08 11:46:30 -0500263/* Add the input samples to the input buffer. */
264static int addShortSamplesToInputBuffer(
265 sonicStream stream,
266 short *samples,
267 int numSamples)
268{
Bill Cox0c4c0602010-11-08 11:46:30 -0500269 if(numSamples == 0) {
270 return 1;
271 }
272 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
273 return 0;
274 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500275 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
276 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500277 stream->numInputSamples += numSamples;
278 return 1;
279}
280
Bill Cox8a23d2f2010-11-16 18:49:36 -0500281/* Add the input samples to the input buffer. */
282static int addUnsignedCharSamplesToInputBuffer(
283 sonicStream stream,
284 unsigned char *samples,
285 int numSamples)
286{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500287 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500288 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500289
290 if(numSamples == 0) {
291 return 1;
292 }
293 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
294 return 0;
295 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500296 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500297 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500298 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400299 }
300 stream->numInputSamples += numSamples;
301 return 1;
302}
303
304/* Remove input samples that we have already processed. */
305static void removeInputSamples(
306 sonicStream stream,
307 int position)
308{
309 int remainingSamples = stream->numInputSamples - position;
310
311 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500312 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
313 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400314 }
315 stream->numInputSamples = remainingSamples;
316}
317
Bill Cox59e65122010-11-03 10:06:29 -0400318/* Just copy from the array to the output buffer */
Bill Cox68e2aee2010-11-23 19:24:41 -0500319static int copyToOutput(
Bill Cox0c4c0602010-11-08 11:46:30 -0500320 sonicStream stream,
321 short *samples,
322 int numSamples)
323{
Bill Cox0c4c0602010-11-08 11:46:30 -0500324 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
325 return 0;
326 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500327 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
328 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500329 stream->numOutputSamples += numSamples;
330 return numSamples;
331}
332
Bill Cox882fb1d2010-11-02 16:27:20 -0400333/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
334 resize the output buffer. Otherwise, return numSamples */
335static int copyInputToOutput(
336 sonicStream stream,
337 int position)
338{
339 int numSamples = stream->remainingInputToCopy;
340
341 if(numSamples > stream->maxRequired) {
342 numSamples = stream->maxRequired;
343 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500344 if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
Bill Cox1a299bb2010-11-19 15:07:17 -0500345 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400346 return 0;
347 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400348 stream->remainingInputToCopy -= numSamples;
349 return numSamples;
350}
351
Bill Coxca02d872010-11-02 15:10:52 -0400352/* Read data out of the stream. Sometimes no data will be available, and zero
353 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500354int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400355 sonicStream stream,
356 float *samples,
357 int maxSamples)
358{
359 int numSamples = stream->numOutputSamples;
360 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500361 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500362 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400363
364 if(numSamples == 0) {
365 return 0;
366 }
367 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400368 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400369 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400370 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500371 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500372 count = numSamples*stream->numChannels;
373 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500374 *samples++ = (*buffer++)/32767.0f;
375 }
Bill Coxca02d872010-11-02 15:10:52 -0400376 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500377 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
378 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400379 }
380 stream->numOutputSamples = remainingSamples;
381 return numSamples;
382}
383
Bill Cox0c4c0602010-11-08 11:46:30 -0500384/* Read short data out of the stream. Sometimes no data will be available, and zero
385 is returned, which is not an error condition. */
386int sonicReadShortFromStream(
387 sonicStream stream,
388 short *samples,
389 int maxSamples)
390{
391 int numSamples = stream->numOutputSamples;
392 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500393
394 if(numSamples == 0) {
395 return 0;
396 }
397 if(numSamples > maxSamples) {
398 remainingSamples = numSamples - maxSamples;
399 numSamples = maxSamples;
400 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500401 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500402 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500403 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
404 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500405 }
406 stream->numOutputSamples = remainingSamples;
407 return numSamples;
408}
409
Bill Cox8a23d2f2010-11-16 18:49:36 -0500410/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
411 is returned, which is not an error condition. */
412int sonicReadUnsignedCharFromStream(
413 sonicStream stream,
414 unsigned char *samples,
415 int maxSamples)
416{
417 int numSamples = stream->numOutputSamples;
418 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500419 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500420 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500421
422 if(numSamples == 0) {
423 return 0;
424 }
425 if(numSamples > maxSamples) {
426 remainingSamples = numSamples - maxSamples;
427 numSamples = maxSamples;
428 }
429 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500430 count = numSamples*stream->numChannels;
431 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500432 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400433 }
434 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500435 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
436 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400437 }
438 stream->numOutputSamples = remainingSamples;
439 return numSamples;
440}
441
442/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500443 has. No extra delay will be added to the output, but flushing in the middle of
444 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400445int sonicFlushStream(
446 sonicStream stream)
447{
448 int maxRequired = stream->maxRequired;
Bill Cox0283c342010-11-30 11:47:50 -0500449 int remainingSamples = stream->numInputSamples;
450 float speed = stream->speed/stream->pitch;
451 int expectedOutputSamples = stream->numOutputSamples +
452 (int)(remainingSamples/speed + stream->numPitchSamples/stream->pitch + 0.5f);
Bill Coxca02d872010-11-02 15:10:52 -0400453
Bill Cox0283c342010-11-30 11:47:50 -0500454 /* Add enough silence to flush both input and pitch buffers. */
455 if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
456 return 0;
Bill Coxca02d872010-11-02 15:10:52 -0400457 }
Bill Cox0283c342010-11-30 11:47:50 -0500458 memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
459 2*maxRequired*sizeof(short)*stream->numChannels);
460 stream->numInputSamples += 2*maxRequired;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500461 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500462 return 0;
463 }
464 /* Throw away any extra samples we generated due to the silence we added */
Bill Cox0283c342010-11-30 11:47:50 -0500465 if(stream->numOutputSamples > expectedOutputSamples) {
466 stream->numOutputSamples = expectedOutputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500467 }
Bill Cox0283c342010-11-30 11:47:50 -0500468 /* Empty input and pitch buffers */
469 stream->numInputSamples = 0;
470 stream->remainingInputToCopy = 0;
471 stream->numPitchSamples = 0;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500472 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400473}
474
475/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400476int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400477 sonicStream stream)
478{
479 return stream->numOutputSamples;
480}
Bill Cox9bf11b52010-11-03 05:33:09 -0400481
Bill Coxd544fdb2010-11-23 14:13:46 -0500482/* If skip is greater than one, average skip samples togther and write them to
483 the down-sample buffer. If numChannels is greater than one, mix the channels
484 together as we down sample. */
485static void downSampleInput(
486 sonicStream stream,
487 short *samples,
488 int skip)
489{
490 int numSamples = stream->maxRequired/skip;
491 int samplesPerValue = stream->numChannels*skip;
492 int i, j;
493 int value;
494 short *downSamples = stream->downSampleBuffer;
495
496 for(i = 0; i < numSamples; i++) {
497 value = 0;
498 for(j = 0; j < samplesPerValue; j++) {
499 value += *samples++;
500 }
501 value /= samplesPerValue;
502 *downSamples++ = value;
503 }
504}
505
Bill Cox1a299bb2010-11-19 15:07:17 -0500506/* Find the best frequency match in the range, and given a sample skip multiple.
507 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400508static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500509 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400510 int minPeriod,
Bill Coxc17208e2010-11-26 11:09:15 -0500511 int maxPeriod,
512 int *retMinDiff,
513 int *retMaxDiff)
Bill Cox0cd49c82010-11-03 10:46:22 -0400514{
Bill Cox0283c342010-11-30 11:47:50 -0500515 int period, bestPeriod = 0, worstPeriod = 255;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500516 short *s, *p, sVal, pVal;
Bill Coxc17208e2010-11-26 11:09:15 -0500517 unsigned long diff, minDiff = 1, maxDiff = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500518 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400519
Bill Coxd544fdb2010-11-23 14:13:46 -0500520 for(period = minPeriod; period <= maxPeriod; period++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500521 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400522 s = samples;
Bill Coxd544fdb2010-11-23 14:13:46 -0500523 p = samples + period;
524 for(i = 0; i < period; i++) {
525 sVal = *s++;
526 pVal = *p++;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500527 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
528 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400529 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500530 /* Note that the highest number of samples we add into diff will be less
531 than 256, since we skip samples. Thus, diff is a 24 bit number, and
532 we can safely multiply by numSamples without overflow */
Bill Coxc17208e2010-11-26 11:09:15 -0500533 if(diff*bestPeriod < minDiff*period) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400534 minDiff = diff;
535 bestPeriod = period;
536 }
Bill Cox0283c342010-11-30 11:47:50 -0500537 if(diff*worstPeriod > maxDiff*period) {
Bill Coxc17208e2010-11-26 11:09:15 -0500538 maxDiff = diff;
Bill Cox0283c342010-11-30 11:47:50 -0500539 worstPeriod = period;
Bill Coxc17208e2010-11-26 11:09:15 -0500540 }
Bill Cox0cd49c82010-11-03 10:46:22 -0400541 }
Bill Cox0283c342010-11-30 11:47:50 -0500542 *retMinDiff = minDiff/bestPeriod;
543 *retMaxDiff = maxDiff/worstPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400544 return bestPeriod;
545}
546
Bill Coxc17208e2010-11-26 11:09:15 -0500547/* At abrupt ends of voiced words, we can have pitch periods that are better
548 aproximated by the previous pitch period estimate. Try to detect this case. */
549static int prevPeriodBetter(
550 sonicStream stream,
551 int period,
552 int minDiff,
Bill Cox0283c342010-11-30 11:47:50 -0500553 int maxDiff,
554 int preferNewPeriod)
Bill Coxc17208e2010-11-26 11:09:15 -0500555{
Bill Cox0283c342010-11-30 11:47:50 -0500556 if(minDiff == 0) {
557 return 0;
Bill Coxc17208e2010-11-26 11:09:15 -0500558 }
Bill Cox0283c342010-11-30 11:47:50 -0500559 if(preferNewPeriod) {
560 if(maxDiff > minDiff*3) {
561 /* Got a reasonable match this period */
562 return 0;
563 }
564 if(minDiff*2 <= stream->prevMinDiff*3) {
565 /* Mismatch is not that much greater this period */
566 return 0;
567 }
568 } else {
569 if(minDiff <= stream->prevMinDiff) {
570 return 0;
571 }
572 }
573 return 1;
Bill Coxc17208e2010-11-26 11:09:15 -0500574}
575
Bill Cox9bf11b52010-11-03 05:33:09 -0400576/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400577 multiple ways to get a good answer. This version uses AMDF. To improve
578 speed, we down sample by an integer factor get in the 11KHz range, and then
579 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400580static int findPitchPeriod(
581 sonicStream stream,
Bill Cox0283c342010-11-30 11:47:50 -0500582 short *samples,
583 int preferNewPeriod)
Bill Cox9bf11b52010-11-03 05:33:09 -0400584{
585 int minPeriod = stream->minPeriod;
586 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400587 int sampleRate = stream->sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -0500588 int minDiff, maxDiff, retPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400589 int skip = 1;
590 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400591
Bill Cox0cd49c82010-11-03 10:46:22 -0400592 if(sampleRate > SONIC_AMDF_FREQ) {
593 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400594 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500595 if(stream->numChannels == 1 && skip == 1) {
Bill Coxc17208e2010-11-26 11:09:15 -0500596 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
597 } else {
598 downSampleInput(stream, samples, skip);
599 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
600 maxPeriod/skip, &minDiff, &maxDiff);
601 if(skip != 1) {
602 period *= skip;
603 minPeriod = period - (skip << 2);
604 maxPeriod = period + (skip << 2);
605 if(minPeriod < stream->minPeriod) {
606 minPeriod = stream->minPeriod;
607 }
608 if(maxPeriod > stream->maxPeriod) {
609 maxPeriod = stream->maxPeriod;
610 }
611 if(stream->numChannels == 1) {
612 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
613 &minDiff, &maxDiff);
614 } else {
615 downSampleInput(stream, samples, 1);
616 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
617 maxPeriod, &minDiff, &maxDiff);
618 }
619 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500620 }
Bill Cox0283c342010-11-30 11:47:50 -0500621 if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
Bill Coxc17208e2010-11-26 11:09:15 -0500622 retPeriod = stream->prevPeriod;
623 } else {
624 retPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500625 }
Bill Coxc17208e2010-11-26 11:09:15 -0500626 stream->prevMinDiff = minDiff;
627 stream->prevMaxDiff = maxDiff;
628 stream->prevPeriod = period;
629 return retPeriod;
Bill Coxd544fdb2010-11-23 14:13:46 -0500630}
631
Bill Cox68e2aee2010-11-23 19:24:41 -0500632/* Overlap two sound segments, ramp the volume of one down, while ramping the
633 other one from zero up, and add them, storing the result at the output. */
634static void overlapAdd(
635 int numSamples,
636 int numChannels,
637 short *out,
638 short *rampDown,
639 short *rampUp)
640{
Bill Coxd76d2222010-11-24 11:42:29 -0500641 short *o, *u, *d;
Bill Cox68e2aee2010-11-23 19:24:41 -0500642 int i, t;
643
644 for(i = 0; i < numChannels; i++) {
645 o = out + i;
Bill Coxd76d2222010-11-24 11:42:29 -0500646 u = rampUp + i;
647 d = rampDown + i;
Bill Cox68e2aee2010-11-23 19:24:41 -0500648 for(t = 0; t < numSamples; t++) {
Bill Coxd76d2222010-11-24 11:42:29 -0500649 *o = (*d*(numSamples - t) + *u*t)/numSamples;
Bill Cox68e2aee2010-11-23 19:24:41 -0500650 o += numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500651 d += numChannels;
652 u += numChannels;
653 }
654 }
655}
656
657/* Overlap two sound segments, ramp the volume of one down, while ramping the
658 other one from zero up, and add them, storing the result at the output. */
659static void overlapAddWithSeparation(
660 int numSamples,
661 int numChannels,
662 int separation,
663 short *out,
664 short *rampDown,
665 short *rampUp)
666{
667 short *o, *u, *d;
668 int i, t;
669
670 for(i = 0; i < numChannels; i++) {
671 o = out + i;
672 u = rampUp + i;
673 d = rampDown + i;
674 for(t = 0; t < numSamples + separation; t++) {
675 if(t < separation) {
676 *o = *d*(numSamples - t)/numSamples;
677 d += numChannels;
678 } else if(t < numSamples) {
679 *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
680 d += numChannels;
681 u += numChannels;
682 } else {
683 *o = *u*(t - separation)/numSamples;
684 u += numChannels;
685 }
686 o += numChannels;
Bill Cox68e2aee2010-11-23 19:24:41 -0500687 }
688 }
689}
690
691/* Just move the new samples in the output buffer to the pitch bufer */
692static int moveNewSamplesToPitchBuffer(
Bill Coxd544fdb2010-11-23 14:13:46 -0500693 sonicStream stream,
694 int originalNumOutputSamples)
695{
Bill Cox68e2aee2010-11-23 19:24:41 -0500696 int numSamples = stream->numOutputSamples - originalNumOutputSamples;
697 int numChannels = stream->numChannels;
698
699 if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
700 stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
701 stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
702 stream->pitchBufferSize*sizeof(short)*numChannels);
703 if(stream->pitchBuffer == NULL) {
704 return 0;
705 }
706 }
707 memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
708 stream->outputBuffer + originalNumOutputSamples*numChannels,
709 numSamples*sizeof(short)*numChannels);
710 stream->numOutputSamples = originalNumOutputSamples;
711 stream->numPitchSamples += numSamples;
712 return 1;
713}
714
715/* Remove processed samples from the pitch buffer. */
716static void removePitchSamples(
717 sonicStream stream,
718 int numSamples)
719{
720 int numChannels = stream->numChannels;
721 short *source = stream->pitchBuffer + numSamples*numChannels;
722
723 if(numSamples == 0) {
724 return;
725 }
726 if(numSamples != stream->numPitchSamples) {
727 memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
728 numSamples)*sizeof(short)*numChannels);
729 }
730 stream->numPitchSamples -= numSamples;
731}
732
733/* Change the pitch. The latency this introduces could be reduced by looking at
734 past samples to determine pitch, rather than future. */
735static int adjustPitch(
736 sonicStream stream,
737 int originalNumOutputSamples)
738{
739 float pitch = stream->pitch;
740 int numChannels = stream->numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500741 int period, newPeriod, separation;
Bill Cox68e2aee2010-11-23 19:24:41 -0500742 int position = 0;
743 short *out, *rampDown, *rampUp;
744
745 if(stream->numOutputSamples == originalNumOutputSamples) {
746 return 1;
747 }
748 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
749 return 0;
750 }
751 while(stream->numPitchSamples - position >= stream->maxRequired) {
Bill Cox0283c342010-11-30 11:47:50 -0500752 period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
Bill Cox68e2aee2010-11-23 19:24:41 -0500753 newPeriod = period/pitch;
Bill Coxd76d2222010-11-24 11:42:29 -0500754 if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500755 return 0;
756 }
Bill Coxd76d2222010-11-24 11:42:29 -0500757 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
758 if(pitch >= 1.0f) {
759 rampDown = stream->pitchBuffer + position*numChannels;
760 rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
761 overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
762 } else {
763 rampDown = stream->pitchBuffer + position*numChannels;
764 rampUp = stream->pitchBuffer + position*numChannels;
765 separation = newPeriod - period;
766 overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
Bill Cox68e2aee2010-11-23 19:24:41 -0500767 }
Bill Coxd76d2222010-11-24 11:42:29 -0500768 stream->numOutputSamples += newPeriod;
Bill Cox68e2aee2010-11-23 19:24:41 -0500769 position += period;
770 }
771 removePitchSamples(stream, position);
772 return 1;
Bill Cox9bf11b52010-11-03 05:33:09 -0400773}
774
Bill Cox59e65122010-11-03 10:06:29 -0400775/* Skip over a pitch period, and copy period/speed samples to the output */
776static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400777 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500778 short *samples,
779 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400780 int period)
781{
Bill Cox68e2aee2010-11-23 19:24:41 -0500782 long newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500783 int numChannels = stream->numChannels;
Bill Cox9bf11b52010-11-03 05:33:09 -0400784
Bill Cox6a1bbb12010-11-19 11:14:28 -0500785 if(speed >= 2.0f) {
786 newSamples = period/(speed - 1.0f);
787 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400788 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500789 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400790 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400791 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
792 return 0;
793 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500794 overlapAdd(newSamples, numChannels, stream->outputBuffer +
795 stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
Bill Cox9bf11b52010-11-03 05:33:09 -0400796 stream->numOutputSamples += newSamples;
797 return newSamples;
798}
799
Bill Cox59e65122010-11-03 10:06:29 -0400800/* Insert a pitch period, and determine how much input to copy directly. */
801static int insertPitchPeriod(
802 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500803 short *samples,
804 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400805 int period)
806{
Bill Cox68e2aee2010-11-23 19:24:41 -0500807 long newSamples;
808 short *out;
Bill Cox1a299bb2010-11-19 15:07:17 -0500809 int numChannels = stream->numChannels;
Bill Cox59e65122010-11-03 10:06:29 -0400810
Bill Cox6a1bbb12010-11-19 11:14:28 -0500811 if(speed < 0.5f) {
812 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400813 } else {
814 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500815 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400816 }
817 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
818 return 0;
819 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500820 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
821 memcpy(out, samples, period*sizeof(short)*numChannels);
Bill Cox68e2aee2010-11-23 19:24:41 -0500822 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
823 overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
Bill Cox59e65122010-11-03 10:06:29 -0400824 stream->numOutputSamples += period + newSamples;
825 return newSamples;
826}
827
Bill Cox9bf11b52010-11-03 05:33:09 -0400828/* Resample as many pitch periods as we have buffered on the input. Return 0 if
Bill Coxd544fdb2010-11-23 14:13:46 -0500829 we fail to resize an input or output buffer. Also scale the output by the volume. */
830static int changeSpeed(
831 sonicStream stream,
832 float speed)
Bill Cox9bf11b52010-11-03 05:33:09 -0400833{
Bill Cox1a299bb2010-11-19 15:07:17 -0500834 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -0500835 int numSamples = stream->numInputSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400836 int position = 0, period, newSamples;
837 int maxRequired = stream->maxRequired;
838
Bill Cox9bf11b52010-11-03 05:33:09 -0400839 if(stream->numInputSamples < maxRequired) {
840 return 1;
841 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400842 do {
843 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400844 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400845 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400846 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500847 samples = stream->inputBuffer + position*stream->numChannels;
Bill Cox0283c342010-11-30 11:47:50 -0500848 period = findPitchPeriod(stream, samples, 1);
Bill Cox59e65122010-11-03 10:06:29 -0400849 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500850 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400851 position += period + newSamples;
852 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500853 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400854 position += newSamples;
855 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400856 }
857 if(newSamples == 0) {
858 return 0; /* Failed to resize output buffer */
859 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400860 } while(position + maxRequired <= numSamples);
861 removeInputSamples(stream, position);
862 return 1;
863}
Bill Cox0c4c0602010-11-08 11:46:30 -0500864
Bill Coxd544fdb2010-11-23 14:13:46 -0500865/* Resample as many pitch periods as we have buffered on the input. Return 0 if
866 we fail to resize an input or output buffer. Also scale the output by the volume. */
867static int processStreamInput(
868 sonicStream stream)
869{
870 int originalNumOutputSamples = stream->numOutputSamples;
871 float speed = stream->speed/stream->pitch;
872
873 if(speed > 1.00001 || speed < 0.99999) {
874 changeSpeed(stream, speed);
875 } else {
Bill Cox68e2aee2010-11-23 19:24:41 -0500876 if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
Bill Coxd544fdb2010-11-23 14:13:46 -0500877 return 0;
878 }
879 stream->numInputSamples = 0;
880 }
881 if(stream->pitch != 1.0f) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500882 if(!adjustPitch(stream, originalNumOutputSamples)) {
883 return 0;
884 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500885 }
886 if(stream->volume != 1.0f) {
887 /* Adjust output volume. */
888 scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
889 (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
890 stream->volume);
891 }
892 return 1;
893}
894
Bill Cox0c4c0602010-11-08 11:46:30 -0500895/* Write floating point data to the input buffer and process it. */
896int sonicWriteFloatToStream(
897 sonicStream stream,
898 float *samples,
899 int numSamples)
900{
Bill Cox0c4c0602010-11-08 11:46:30 -0500901 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
902 return 0;
903 }
904 return processStreamInput(stream);
905}
906
907/* Simple wrapper around sonicWriteFloatToStream that does the short to float
908 conversion for you. */
909int sonicWriteShortToStream(
910 sonicStream stream,
911 short *samples,
912 int numSamples)
913{
Bill Cox0c4c0602010-11-08 11:46:30 -0500914 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
915 return 0;
916 }
917 return processStreamInput(stream);
918}
919
Bill Cox8a23d2f2010-11-16 18:49:36 -0500920/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
921 conversion for you. */
922int sonicWriteUnsignedCharToStream(
923 sonicStream stream,
924 unsigned char *samples,
925 int numSamples)
926{
Bill Cox8a23d2f2010-11-16 18:49:36 -0500927 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
928 return 0;
929 }
930 return processStreamInput(stream);
931}
932
Bill Cox036d7322010-11-09 09:29:24 -0500933/* This is a non-stream oriented interface to just change the speed of a sound sample */
934int sonicChangeFloatSpeed(
935 float *samples,
936 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500937 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500938 float pitch,
939 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500940 int sampleRate,
941 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500942{
Bill Coxd544fdb2010-11-23 14:13:46 -0500943 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500944
Bill Coxd544fdb2010-11-23 14:13:46 -0500945 sonicSetSpeed(stream, speed);
946 sonicSetPitch(stream, pitch);
947 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500948 sonicWriteFloatToStream(stream, samples, numSamples);
949 sonicFlushStream(stream);
950 numSamples = sonicSamplesAvailable(stream);
951 sonicReadFloatFromStream(stream, samples, numSamples);
952 sonicDestroyStream(stream);
953 return numSamples;
954}
955
956/* This is a non-stream oriented interface to just change the speed of a sound sample */
957int sonicChangeShortSpeed(
958 short *samples,
959 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500960 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500961 float pitch,
962 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500963 int sampleRate,
964 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500965{
Bill Coxd544fdb2010-11-23 14:13:46 -0500966 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500967
Bill Coxd544fdb2010-11-23 14:13:46 -0500968 sonicSetSpeed(stream, speed);
969 sonicSetPitch(stream, pitch);
970 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500971 sonicWriteShortToStream(stream, samples, numSamples);
972 sonicFlushStream(stream);
973 numSamples = sonicSamplesAvailable(stream);
974 sonicReadShortFromStream(stream, samples, numSamples);
975 sonicDestroyStream(stream);
976 return numSamples;
977}