blob: e019cbb2e19f699d430efaf75cfc1c422b6141cb [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 Cox59af4672010-12-17 16:57:41 -050059/*
Bill Coxa33e3bd2010-11-16 19:57:43 -050060void sonicMSG(char *format, ...)
61{
62 char buffer[4096];
63 va_list ap;
64 FILE *file;
65
66 va_start(ap, format);
67 vsprintf((char *)buffer, (char *)format, ap);
68 va_end(ap);
69 file=fopen("/tmp/sonic.log", "a");
70 fprintf(file, "%s", buffer);
71 fclose(file);
72}
Bill Cox59af4672010-12-17 16:57:41 -050073*/
Bill Coxa33e3bd2010-11-16 19:57:43 -050074
Bill Coxd544fdb2010-11-23 14:13:46 -050075/* Scale the samples by the factor. */
76static void scaleSamples(
77 short *samples,
78 int numSamples,
79 float volume)
80{
81 int fixedPointVolume = volume*4096.0f;
82 int value;
83
84 while(numSamples--) {
85 value = (*samples*fixedPointVolume) >> 12;
86 if(value > 32767) {
87 value = 32767;
88 } else if(value < -32767) {
89 value = -32767;
90 }
91 *samples++ = value;
92 }
93}
94
Bill Coxaf9a6242010-11-08 09:32:27 -050095/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -050096float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -050097 sonicStream stream)
98{
99 return stream->speed;
100}
101
Bill Coxd544fdb2010-11-23 14:13:46 -0500102/* Set the speed of the stream. */
103void sonicSetSpeed(
104 sonicStream stream,
105 float speed)
106{
107 stream->speed = speed;
108}
109
110/* Get the pitch of the stream. */
111float sonicGetPitch(
112 sonicStream stream)
113{
114 return stream->pitch;
115}
116
117/* Set the pitch of the stream. */
118void sonicSetPitch(
119 sonicStream stream,
120 float pitch)
121{
122 stream->pitch = pitch;
123}
124
Bill Coxc978c392010-12-17 05:04:06 -0500125/* Get the quality setting. */
126int sonicGetQuality(
127 sonicStream stream)
128{
129 return stream->quality;
130}
131
132/* Set the "quality". Default 0 is virtually as good as 1, but very much faster. */
133void sonicSetQuality(
134 sonicStream stream,
135 int quality)
136{
137 stream->quality = quality;
138}
139
Bill Coxd544fdb2010-11-23 14:13:46 -0500140/* Get the scaling factor of the stream. */
141float sonicGetVolume(
142 sonicStream stream)
143{
144 return stream->volume;
145}
146
147/* Set the scaling factor of the stream. */
148void sonicSetVolume(
149 sonicStream stream,
150 float volume)
151{
152 stream->volume = volume;
153}
154
Bill Coxaf9a6242010-11-08 09:32:27 -0500155/* Get the sample rate of the stream. */
156int sonicGetSampleRate(
157 sonicStream stream)
158{
159 return stream->sampleRate;
160}
161
Bill Cox527b4e82010-11-24 17:42:58 -0500162/* Get the number of channels. */
163int sonicGetNumChannels(
164 sonicStream stream)
165{
166 return stream->numChannels;
167}
168
Bill Coxca02d872010-11-02 15:10:52 -0400169/* Destroy the sonic stream. */
170void sonicDestroyStream(
171 sonicStream stream)
172{
173 if(stream->inputBuffer != NULL) {
174 free(stream->inputBuffer);
175 }
176 if(stream->outputBuffer != NULL) {
177 free(stream->outputBuffer);
178 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500179 if(stream->pitchBuffer != NULL) {
180 free(stream->pitchBuffer);
181 }
182 if(stream->downSampleBuffer != NULL) {
183 free(stream->downSampleBuffer);
184 }
Bill Coxca02d872010-11-02 15:10:52 -0400185 free(stream);
186}
187
188/* Create a sonic stream. Return NULL only if we are out of memory and cannot
189 allocate the stream. */
190sonicStream sonicCreateStream(
Bill Cox1a299bb2010-11-19 15:07:17 -0500191 int sampleRate,
192 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400193{
194 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
195 int minPeriod = sampleRate/SONIC_MAX_PITCH;
196 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
197 int maxRequired = 2*maxPeriod;
198
199 if(stream == NULL) {
200 return NULL;
201 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500202 stream->inputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500203 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400204 if(stream->inputBuffer == NULL) {
205 sonicDestroyStream(stream);
206 return NULL;
207 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500208 stream->outputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500209 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400210 if(stream->outputBuffer == NULL) {
211 sonicDestroyStream(stream);
212 return NULL;
213 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500214 stream->pitchBufferSize = maxRequired;
215 stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
216 if(stream->pitchBuffer == NULL) {
217 sonicDestroyStream(stream);
218 return NULL;
219 }
220 stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
221 stream->speed = 1.0f;
222 stream->pitch = 1.0f;
223 stream->volume = 1.0f;
Bill Coxc978c392010-12-17 05:04:06 -0500224 stream->quality = 0;
Bill Coxca02d872010-11-02 15:10:52 -0400225 stream->sampleRate = sampleRate;
Bill Cox1a299bb2010-11-19 15:07:17 -0500226 stream->numChannels = numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400227 stream->minPeriod = minPeriod;
228 stream->maxPeriod = maxPeriod;
229 stream->maxRequired = maxRequired;
Bill Coxca02d872010-11-02 15:10:52 -0400230 return stream;
231}
232
Bill Coxca02d872010-11-02 15:10:52 -0400233/* Enlarge the output buffer if needed. */
234static int enlargeOutputBufferIfNeeded(
235 sonicStream stream,
236 int numSamples)
237{
238 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
239 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500240 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500241 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400242 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400243 return 0;
244 }
245 }
246 return 1;
247}
248
Bill Coxca02d872010-11-02 15:10:52 -0400249/* Enlarge the input buffer if needed. */
250static int enlargeInputBufferIfNeeded(
251 sonicStream stream,
252 int numSamples)
253{
254 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
255 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500256 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500257 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400258 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400259 return 0;
260 }
261 }
262 return 1;
263}
264
265/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500266static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400267 sonicStream stream,
268 float *samples,
269 int numSamples)
270{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500271 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500272 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500273
Bill Coxca02d872010-11-02 15:10:52 -0400274 if(numSamples == 0) {
275 return 1;
276 }
277 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
278 return 0;
279 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500280 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500281 while(count--) {
282 *buffer++ = (*samples++)*32767.0f;
283 }
Bill Cox14efa442010-11-02 15:43:58 -0400284 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400285 return 1;
286}
287
Bill Cox0c4c0602010-11-08 11:46:30 -0500288/* Add the input samples to the input buffer. */
289static int addShortSamplesToInputBuffer(
290 sonicStream stream,
291 short *samples,
292 int numSamples)
293{
Bill Cox0c4c0602010-11-08 11:46:30 -0500294 if(numSamples == 0) {
295 return 1;
296 }
297 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
298 return 0;
299 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500300 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
301 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500302 stream->numInputSamples += numSamples;
303 return 1;
304}
305
Bill Cox8a23d2f2010-11-16 18:49:36 -0500306/* Add the input samples to the input buffer. */
307static int addUnsignedCharSamplesToInputBuffer(
308 sonicStream stream,
309 unsigned char *samples,
310 int numSamples)
311{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500312 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500313 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500314
315 if(numSamples == 0) {
316 return 1;
317 }
318 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
319 return 0;
320 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500321 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500322 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500323 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400324 }
325 stream->numInputSamples += numSamples;
326 return 1;
327}
328
329/* Remove input samples that we have already processed. */
330static void removeInputSamples(
331 sonicStream stream,
332 int position)
333{
334 int remainingSamples = stream->numInputSamples - position;
335
336 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500337 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
338 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400339 }
340 stream->numInputSamples = remainingSamples;
341}
342
Bill Cox59e65122010-11-03 10:06:29 -0400343/* Just copy from the array to the output buffer */
Bill Cox68e2aee2010-11-23 19:24:41 -0500344static int copyToOutput(
Bill Cox0c4c0602010-11-08 11:46:30 -0500345 sonicStream stream,
346 short *samples,
347 int numSamples)
348{
Bill Cox0c4c0602010-11-08 11:46:30 -0500349 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
350 return 0;
351 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500352 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
353 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500354 stream->numOutputSamples += numSamples;
355 return numSamples;
356}
357
Bill Cox882fb1d2010-11-02 16:27:20 -0400358/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
359 resize the output buffer. Otherwise, return numSamples */
360static int copyInputToOutput(
361 sonicStream stream,
362 int position)
363{
364 int numSamples = stream->remainingInputToCopy;
365
366 if(numSamples > stream->maxRequired) {
367 numSamples = stream->maxRequired;
368 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500369 if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
Bill Cox1a299bb2010-11-19 15:07:17 -0500370 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400371 return 0;
372 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400373 stream->remainingInputToCopy -= numSamples;
374 return numSamples;
375}
376
Bill Coxca02d872010-11-02 15:10:52 -0400377/* Read data out of the stream. Sometimes no data will be available, and zero
378 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500379int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400380 sonicStream stream,
381 float *samples,
382 int maxSamples)
383{
384 int numSamples = stream->numOutputSamples;
385 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500386 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500387 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400388
389 if(numSamples == 0) {
390 return 0;
391 }
392 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400393 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400394 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400395 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500396 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500397 count = numSamples*stream->numChannels;
398 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500399 *samples++ = (*buffer++)/32767.0f;
400 }
Bill Coxca02d872010-11-02 15:10:52 -0400401 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500402 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
403 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400404 }
405 stream->numOutputSamples = remainingSamples;
406 return numSamples;
407}
408
Bill Cox0c4c0602010-11-08 11:46:30 -0500409/* Read short data out of the stream. Sometimes no data will be available, and zero
410 is returned, which is not an error condition. */
411int sonicReadShortFromStream(
412 sonicStream stream,
413 short *samples,
414 int maxSamples)
415{
416 int numSamples = stream->numOutputSamples;
417 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500418
419 if(numSamples == 0) {
420 return 0;
421 }
422 if(numSamples > maxSamples) {
423 remainingSamples = numSamples - maxSamples;
424 numSamples = maxSamples;
425 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500426 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500427 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500428 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
429 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500430 }
431 stream->numOutputSamples = remainingSamples;
432 return numSamples;
433}
434
Bill Cox8a23d2f2010-11-16 18:49:36 -0500435/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
436 is returned, which is not an error condition. */
437int sonicReadUnsignedCharFromStream(
438 sonicStream stream,
439 unsigned char *samples,
440 int maxSamples)
441{
442 int numSamples = stream->numOutputSamples;
443 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500444 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500445 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500446
447 if(numSamples == 0) {
448 return 0;
449 }
450 if(numSamples > maxSamples) {
451 remainingSamples = numSamples - maxSamples;
452 numSamples = maxSamples;
453 }
454 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500455 count = numSamples*stream->numChannels;
456 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500457 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400458 }
459 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500460 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
461 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400462 }
463 stream->numOutputSamples = remainingSamples;
464 return numSamples;
465}
466
467/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500468 has. No extra delay will be added to the output, but flushing in the middle of
469 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400470int sonicFlushStream(
471 sonicStream stream)
472{
473 int maxRequired = stream->maxRequired;
Bill Cox0283c342010-11-30 11:47:50 -0500474 int remainingSamples = stream->numInputSamples;
475 float speed = stream->speed/stream->pitch;
476 int expectedOutputSamples = stream->numOutputSamples +
477 (int)(remainingSamples/speed + stream->numPitchSamples/stream->pitch + 0.5f);
Bill Coxca02d872010-11-02 15:10:52 -0400478
Bill Cox0283c342010-11-30 11:47:50 -0500479 /* Add enough silence to flush both input and pitch buffers. */
480 if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
481 return 0;
Bill Coxca02d872010-11-02 15:10:52 -0400482 }
Bill Cox0283c342010-11-30 11:47:50 -0500483 memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
484 2*maxRequired*sizeof(short)*stream->numChannels);
485 stream->numInputSamples += 2*maxRequired;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500486 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500487 return 0;
488 }
489 /* Throw away any extra samples we generated due to the silence we added */
Bill Cox0283c342010-11-30 11:47:50 -0500490 if(stream->numOutputSamples > expectedOutputSamples) {
491 stream->numOutputSamples = expectedOutputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500492 }
Bill Cox0283c342010-11-30 11:47:50 -0500493 /* Empty input and pitch buffers */
494 stream->numInputSamples = 0;
495 stream->remainingInputToCopy = 0;
496 stream->numPitchSamples = 0;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500497 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400498}
499
500/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400501int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400502 sonicStream stream)
503{
504 return stream->numOutputSamples;
505}
Bill Cox9bf11b52010-11-03 05:33:09 -0400506
Bill Coxd544fdb2010-11-23 14:13:46 -0500507/* If skip is greater than one, average skip samples togther and write them to
508 the down-sample buffer. If numChannels is greater than one, mix the channels
509 together as we down sample. */
510static void downSampleInput(
511 sonicStream stream,
512 short *samples,
513 int skip)
514{
515 int numSamples = stream->maxRequired/skip;
516 int samplesPerValue = stream->numChannels*skip;
517 int i, j;
518 int value;
519 short *downSamples = stream->downSampleBuffer;
520
521 for(i = 0; i < numSamples; i++) {
522 value = 0;
523 for(j = 0; j < samplesPerValue; j++) {
524 value += *samples++;
525 }
526 value /= samplesPerValue;
527 *downSamples++ = value;
528 }
529}
530
Bill Cox1a299bb2010-11-19 15:07:17 -0500531/* Find the best frequency match in the range, and given a sample skip multiple.
532 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400533static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500534 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400535 int minPeriod,
Bill Coxc17208e2010-11-26 11:09:15 -0500536 int maxPeriod,
537 int *retMinDiff,
538 int *retMaxDiff)
Bill Cox0cd49c82010-11-03 10:46:22 -0400539{
Bill Cox0283c342010-11-30 11:47:50 -0500540 int period, bestPeriod = 0, worstPeriod = 255;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500541 short *s, *p, sVal, pVal;
Bill Coxc17208e2010-11-26 11:09:15 -0500542 unsigned long diff, minDiff = 1, maxDiff = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500543 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400544
Bill Coxd544fdb2010-11-23 14:13:46 -0500545 for(period = minPeriod; period <= maxPeriod; period++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500546 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400547 s = samples;
Bill Coxd544fdb2010-11-23 14:13:46 -0500548 p = samples + period;
549 for(i = 0; i < period; i++) {
550 sVal = *s++;
551 pVal = *p++;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500552 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
553 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400554 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500555 /* Note that the highest number of samples we add into diff will be less
556 than 256, since we skip samples. Thus, diff is a 24 bit number, and
557 we can safely multiply by numSamples without overflow */
Bill Coxc17208e2010-11-26 11:09:15 -0500558 if(diff*bestPeriod < minDiff*period) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400559 minDiff = diff;
560 bestPeriod = period;
561 }
Bill Cox0283c342010-11-30 11:47:50 -0500562 if(diff*worstPeriod > maxDiff*period) {
Bill Coxc17208e2010-11-26 11:09:15 -0500563 maxDiff = diff;
Bill Cox0283c342010-11-30 11:47:50 -0500564 worstPeriod = period;
Bill Coxc17208e2010-11-26 11:09:15 -0500565 }
Bill Cox0cd49c82010-11-03 10:46:22 -0400566 }
Bill Cox0283c342010-11-30 11:47:50 -0500567 *retMinDiff = minDiff/bestPeriod;
568 *retMaxDiff = maxDiff/worstPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400569 return bestPeriod;
570}
571
Bill Coxc17208e2010-11-26 11:09:15 -0500572/* At abrupt ends of voiced words, we can have pitch periods that are better
573 aproximated by the previous pitch period estimate. Try to detect this case. */
574static int prevPeriodBetter(
575 sonicStream stream,
576 int period,
577 int minDiff,
Bill Cox0283c342010-11-30 11:47:50 -0500578 int maxDiff,
579 int preferNewPeriod)
Bill Coxc17208e2010-11-26 11:09:15 -0500580{
Bill Cox0283c342010-11-30 11:47:50 -0500581 if(minDiff == 0) {
582 return 0;
Bill Coxc17208e2010-11-26 11:09:15 -0500583 }
Bill Cox0283c342010-11-30 11:47:50 -0500584 if(preferNewPeriod) {
585 if(maxDiff > minDiff*3) {
586 /* Got a reasonable match this period */
587 return 0;
588 }
589 if(minDiff*2 <= stream->prevMinDiff*3) {
590 /* Mismatch is not that much greater this period */
591 return 0;
592 }
593 } else {
594 if(minDiff <= stream->prevMinDiff) {
595 return 0;
596 }
597 }
598 return 1;
Bill Coxc17208e2010-11-26 11:09:15 -0500599}
600
Bill Cox9bf11b52010-11-03 05:33:09 -0400601/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400602 multiple ways to get a good answer. This version uses AMDF. To improve
603 speed, we down sample by an integer factor get in the 11KHz range, and then
604 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400605static int findPitchPeriod(
606 sonicStream stream,
Bill Cox0283c342010-11-30 11:47:50 -0500607 short *samples,
608 int preferNewPeriod)
Bill Cox9bf11b52010-11-03 05:33:09 -0400609{
610 int minPeriod = stream->minPeriod;
611 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400612 int sampleRate = stream->sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -0500613 int minDiff, maxDiff, retPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400614 int skip = 1;
615 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400616
Bill Cox4e234d72010-12-17 05:44:02 -0500617 if(sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400618 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400619 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500620 if(stream->numChannels == 1 && skip == 1) {
Bill Coxc17208e2010-11-26 11:09:15 -0500621 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
622 } else {
623 downSampleInput(stream, samples, skip);
624 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
625 maxPeriod/skip, &minDiff, &maxDiff);
626 if(skip != 1) {
627 period *= skip;
628 minPeriod = period - (skip << 2);
629 maxPeriod = period + (skip << 2);
630 if(minPeriod < stream->minPeriod) {
631 minPeriod = stream->minPeriod;
632 }
633 if(maxPeriod > stream->maxPeriod) {
634 maxPeriod = stream->maxPeriod;
635 }
636 if(stream->numChannels == 1) {
637 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
638 &minDiff, &maxDiff);
639 } else {
640 downSampleInput(stream, samples, 1);
641 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
642 maxPeriod, &minDiff, &maxDiff);
643 }
644 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500645 }
Bill Cox0283c342010-11-30 11:47:50 -0500646 if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
Bill Coxc17208e2010-11-26 11:09:15 -0500647 retPeriod = stream->prevPeriod;
648 } else {
649 retPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500650 }
Bill Coxc17208e2010-11-26 11:09:15 -0500651 stream->prevMinDiff = minDiff;
652 stream->prevMaxDiff = maxDiff;
653 stream->prevPeriod = period;
654 return retPeriod;
Bill Coxd544fdb2010-11-23 14:13:46 -0500655}
656
Bill Cox68e2aee2010-11-23 19:24:41 -0500657/* 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 overlapAdd(
660 int numSamples,
661 int numChannels,
662 short *out,
663 short *rampDown,
664 short *rampUp)
665{
Bill Coxd76d2222010-11-24 11:42:29 -0500666 short *o, *u, *d;
Bill Cox68e2aee2010-11-23 19:24:41 -0500667 int i, t;
668
669 for(i = 0; i < numChannels; i++) {
670 o = out + i;
Bill Coxd76d2222010-11-24 11:42:29 -0500671 u = rampUp + i;
672 d = rampDown + i;
Bill Cox68e2aee2010-11-23 19:24:41 -0500673 for(t = 0; t < numSamples; t++) {
Bill Cox4e234d72010-12-17 05:44:02 -0500674#ifdef SONIC_USE_SIN
675 float ratio = sin(t*M_PI/(2*numSamples));
676 *o = *d*(1.0f - ratio) + *u*ratio;
677#else
Bill Coxd76d2222010-11-24 11:42:29 -0500678 *o = (*d*(numSamples - t) + *u*t)/numSamples;
Bill Cox4e234d72010-12-17 05:44:02 -0500679#endif
Bill Cox68e2aee2010-11-23 19:24:41 -0500680 o += numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500681 d += numChannels;
682 u += numChannels;
683 }
684 }
685}
686
687/* Overlap two sound segments, ramp the volume of one down, while ramping the
688 other one from zero up, and add them, storing the result at the output. */
689static void overlapAddWithSeparation(
690 int numSamples,
691 int numChannels,
692 int separation,
693 short *out,
694 short *rampDown,
695 short *rampUp)
696{
697 short *o, *u, *d;
698 int i, t;
699
700 for(i = 0; i < numChannels; i++) {
701 o = out + i;
702 u = rampUp + i;
703 d = rampDown + i;
704 for(t = 0; t < numSamples + separation; t++) {
705 if(t < separation) {
706 *o = *d*(numSamples - t)/numSamples;
707 d += numChannels;
708 } else if(t < numSamples) {
709 *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
710 d += numChannels;
711 u += numChannels;
712 } else {
713 *o = *u*(t - separation)/numSamples;
714 u += numChannels;
715 }
716 o += numChannels;
Bill Cox68e2aee2010-11-23 19:24:41 -0500717 }
718 }
719}
720
721/* Just move the new samples in the output buffer to the pitch bufer */
722static int moveNewSamplesToPitchBuffer(
Bill Coxd544fdb2010-11-23 14:13:46 -0500723 sonicStream stream,
724 int originalNumOutputSamples)
725{
Bill Cox68e2aee2010-11-23 19:24:41 -0500726 int numSamples = stream->numOutputSamples - originalNumOutputSamples;
727 int numChannels = stream->numChannels;
728
729 if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
730 stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
731 stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
732 stream->pitchBufferSize*sizeof(short)*numChannels);
733 if(stream->pitchBuffer == NULL) {
734 return 0;
735 }
736 }
737 memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
738 stream->outputBuffer + originalNumOutputSamples*numChannels,
739 numSamples*sizeof(short)*numChannels);
740 stream->numOutputSamples = originalNumOutputSamples;
741 stream->numPitchSamples += numSamples;
742 return 1;
743}
744
745/* Remove processed samples from the pitch buffer. */
746static void removePitchSamples(
747 sonicStream stream,
748 int numSamples)
749{
750 int numChannels = stream->numChannels;
751 short *source = stream->pitchBuffer + numSamples*numChannels;
752
753 if(numSamples == 0) {
754 return;
755 }
756 if(numSamples != stream->numPitchSamples) {
757 memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
758 numSamples)*sizeof(short)*numChannels);
759 }
760 stream->numPitchSamples -= numSamples;
761}
762
763/* Change the pitch. The latency this introduces could be reduced by looking at
764 past samples to determine pitch, rather than future. */
765static int adjustPitch(
766 sonicStream stream,
767 int originalNumOutputSamples)
768{
769 float pitch = stream->pitch;
770 int numChannels = stream->numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500771 int period, newPeriod, separation;
Bill Cox68e2aee2010-11-23 19:24:41 -0500772 int position = 0;
773 short *out, *rampDown, *rampUp;
774
775 if(stream->numOutputSamples == originalNumOutputSamples) {
776 return 1;
777 }
778 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
779 return 0;
780 }
781 while(stream->numPitchSamples - position >= stream->maxRequired) {
Bill Cox0283c342010-11-30 11:47:50 -0500782 period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
Bill Cox68e2aee2010-11-23 19:24:41 -0500783 newPeriod = period/pitch;
Bill Coxd76d2222010-11-24 11:42:29 -0500784 if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500785 return 0;
786 }
Bill Coxd76d2222010-11-24 11:42:29 -0500787 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
788 if(pitch >= 1.0f) {
789 rampDown = stream->pitchBuffer + position*numChannels;
790 rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
791 overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
792 } else {
793 rampDown = stream->pitchBuffer + position*numChannels;
794 rampUp = stream->pitchBuffer + position*numChannels;
795 separation = newPeriod - period;
796 overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
Bill Cox68e2aee2010-11-23 19:24:41 -0500797 }
Bill Coxd76d2222010-11-24 11:42:29 -0500798 stream->numOutputSamples += newPeriod;
Bill Cox68e2aee2010-11-23 19:24:41 -0500799 position += period;
800 }
801 removePitchSamples(stream, position);
802 return 1;
Bill Cox9bf11b52010-11-03 05:33:09 -0400803}
804
Bill Cox59e65122010-11-03 10:06:29 -0400805/* Skip over a pitch period, and copy period/speed samples to the output */
806static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400807 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500808 short *samples,
809 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400810 int period)
811{
Bill Cox68e2aee2010-11-23 19:24:41 -0500812 long newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500813 int numChannels = stream->numChannels;
Bill Cox9bf11b52010-11-03 05:33:09 -0400814
Bill Cox6a1bbb12010-11-19 11:14:28 -0500815 if(speed >= 2.0f) {
816 newSamples = period/(speed - 1.0f);
817 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400818 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500819 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400820 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400821 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
822 return 0;
823 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500824 overlapAdd(newSamples, numChannels, stream->outputBuffer +
825 stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
Bill Cox9bf11b52010-11-03 05:33:09 -0400826 stream->numOutputSamples += newSamples;
827 return newSamples;
828}
829
Bill Cox59e65122010-11-03 10:06:29 -0400830/* Insert a pitch period, and determine how much input to copy directly. */
831static int insertPitchPeriod(
832 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500833 short *samples,
834 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400835 int period)
836{
Bill Cox68e2aee2010-11-23 19:24:41 -0500837 long newSamples;
838 short *out;
Bill Cox1a299bb2010-11-19 15:07:17 -0500839 int numChannels = stream->numChannels;
Bill Cox59e65122010-11-03 10:06:29 -0400840
Bill Cox6a1bbb12010-11-19 11:14:28 -0500841 if(speed < 0.5f) {
842 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400843 } else {
844 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500845 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400846 }
847 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
848 return 0;
849 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500850 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
851 memcpy(out, samples, period*sizeof(short)*numChannels);
Bill Cox68e2aee2010-11-23 19:24:41 -0500852 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
853 overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
Bill Cox59e65122010-11-03 10:06:29 -0400854 stream->numOutputSamples += period + newSamples;
855 return newSamples;
856}
857
Bill Cox9bf11b52010-11-03 05:33:09 -0400858/* Resample as many pitch periods as we have buffered on the input. Return 0 if
Bill Coxd544fdb2010-11-23 14:13:46 -0500859 we fail to resize an input or output buffer. Also scale the output by the volume. */
860static int changeSpeed(
861 sonicStream stream,
862 float speed)
Bill Cox9bf11b52010-11-03 05:33:09 -0400863{
Bill Cox1a299bb2010-11-19 15:07:17 -0500864 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -0500865 int numSamples = stream->numInputSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400866 int position = 0, period, newSamples;
867 int maxRequired = stream->maxRequired;
868
Bill Cox9bf11b52010-11-03 05:33:09 -0400869 if(stream->numInputSamples < maxRequired) {
870 return 1;
871 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400872 do {
873 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400874 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400875 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400876 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500877 samples = stream->inputBuffer + position*stream->numChannels;
Bill Cox0283c342010-11-30 11:47:50 -0500878 period = findPitchPeriod(stream, samples, 1);
Bill Cox59e65122010-11-03 10:06:29 -0400879 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500880 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400881 position += period + newSamples;
882 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500883 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400884 position += newSamples;
885 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400886 }
887 if(newSamples == 0) {
888 return 0; /* Failed to resize output buffer */
889 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400890 } while(position + maxRequired <= numSamples);
891 removeInputSamples(stream, position);
892 return 1;
893}
Bill Cox0c4c0602010-11-08 11:46:30 -0500894
Bill Coxd544fdb2010-11-23 14:13:46 -0500895/* Resample as many pitch periods as we have buffered on the input. Return 0 if
896 we fail to resize an input or output buffer. Also scale the output by the volume. */
897static int processStreamInput(
898 sonicStream stream)
899{
900 int originalNumOutputSamples = stream->numOutputSamples;
901 float speed = stream->speed/stream->pitch;
902
903 if(speed > 1.00001 || speed < 0.99999) {
904 changeSpeed(stream, speed);
905 } else {
Bill Cox68e2aee2010-11-23 19:24:41 -0500906 if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
Bill Coxd544fdb2010-11-23 14:13:46 -0500907 return 0;
908 }
909 stream->numInputSamples = 0;
910 }
911 if(stream->pitch != 1.0f) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500912 if(!adjustPitch(stream, originalNumOutputSamples)) {
913 return 0;
914 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500915 }
916 if(stream->volume != 1.0f) {
917 /* Adjust output volume. */
918 scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
919 (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
920 stream->volume);
921 }
922 return 1;
923}
924
Bill Cox0c4c0602010-11-08 11:46:30 -0500925/* Write floating point data to the input buffer and process it. */
926int sonicWriteFloatToStream(
927 sonicStream stream,
928 float *samples,
929 int numSamples)
930{
Bill Cox0c4c0602010-11-08 11:46:30 -0500931 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
932 return 0;
933 }
934 return processStreamInput(stream);
935}
936
937/* Simple wrapper around sonicWriteFloatToStream that does the short to float
938 conversion for you. */
939int sonicWriteShortToStream(
940 sonicStream stream,
941 short *samples,
942 int numSamples)
943{
Bill Cox0c4c0602010-11-08 11:46:30 -0500944 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
945 return 0;
946 }
947 return processStreamInput(stream);
948}
949
Bill Cox8a23d2f2010-11-16 18:49:36 -0500950/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
951 conversion for you. */
952int sonicWriteUnsignedCharToStream(
953 sonicStream stream,
954 unsigned char *samples,
955 int numSamples)
956{
Bill Cox8a23d2f2010-11-16 18:49:36 -0500957 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
958 return 0;
959 }
960 return processStreamInput(stream);
961}
962
Bill Cox036d7322010-11-09 09:29:24 -0500963/* This is a non-stream oriented interface to just change the speed of a sound sample */
964int sonicChangeFloatSpeed(
965 float *samples,
966 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500967 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500968 float pitch,
969 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500970 int sampleRate,
971 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500972{
Bill Coxd544fdb2010-11-23 14:13:46 -0500973 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500974
Bill Coxd544fdb2010-11-23 14:13:46 -0500975 sonicSetSpeed(stream, speed);
976 sonicSetPitch(stream, pitch);
977 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500978 sonicWriteFloatToStream(stream, samples, numSamples);
979 sonicFlushStream(stream);
980 numSamples = sonicSamplesAvailable(stream);
981 sonicReadFloatFromStream(stream, samples, numSamples);
982 sonicDestroyStream(stream);
983 return numSamples;
984}
985
986/* This is a non-stream oriented interface to just change the speed of a sound sample */
987int sonicChangeShortSpeed(
988 short *samples,
989 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500990 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500991 float pitch,
992 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500993 int sampleRate,
994 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500995{
Bill Coxd544fdb2010-11-23 14:13:46 -0500996 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500997
Bill Coxd544fdb2010-11-23 14:13:46 -0500998 sonicSetSpeed(stream, speed);
999 sonicSetPitch(stream, pitch);
1000 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -05001001 sonicWriteShortToStream(stream, samples, numSamples);
1002 sonicFlushStream(stream);
1003 numSamples = sonicSamplesAvailable(stream);
1004 sonicReadShortFromStream(stream, samples, numSamples);
1005 sonicDestroyStream(stream);
1006 return numSamples;
1007}