blob: fa5b817ee33a18067cd5fa3ed9fc1ca3d71060d1 [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 Coxc978c392010-12-17 05:04:06 -050034 int quality;
Bill Cox1a299bb2010-11-19 15:07:17 -050035 int numChannels;
Bill Coxca02d872010-11-02 15:10:52 -040036 int inputBufferSize;
Bill Coxd544fdb2010-11-23 14:13:46 -050037 int pitchBufferSize;
Bill Coxca02d872010-11-02 15:10:52 -040038 int outputBufferSize;
39 int numInputSamples;
40 int numOutputSamples;
Bill Coxd544fdb2010-11-23 14:13:46 -050041 int numPitchSamples;
Bill Coxca02d872010-11-02 15:10:52 -040042 int minPeriod;
43 int maxPeriod;
44 int maxRequired;
45 int remainingInputToCopy;
46 int sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -050047 int prevPeriod;
48 int prevMaxDiff;
49 int prevMinDiff;
Bill Coxca02d872010-11-02 15:10:52 -040050};
51
Bill Cox1a299bb2010-11-19 15:07:17 -050052/* Just used for debugging */
Bill Coxa33e3bd2010-11-16 19:57:43 -050053void sonicMSG(char *format, ...)
54{
55 char buffer[4096];
56 va_list ap;
57 FILE *file;
58
59 va_start(ap, format);
60 vsprintf((char *)buffer, (char *)format, ap);
61 va_end(ap);
62 file=fopen("/tmp/sonic.log", "a");
63 fprintf(file, "%s", buffer);
64 fclose(file);
65}
66
Bill Coxd544fdb2010-11-23 14:13:46 -050067/* Scale the samples by the factor. */
68static void scaleSamples(
69 short *samples,
70 int numSamples,
71 float volume)
72{
73 int fixedPointVolume = volume*4096.0f;
74 int value;
75
76 while(numSamples--) {
77 value = (*samples*fixedPointVolume) >> 12;
78 if(value > 32767) {
79 value = 32767;
80 } else if(value < -32767) {
81 value = -32767;
82 }
83 *samples++ = value;
84 }
85}
86
Bill Coxaf9a6242010-11-08 09:32:27 -050087/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -050088float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -050089 sonicStream stream)
90{
91 return stream->speed;
92}
93
Bill Coxd544fdb2010-11-23 14:13:46 -050094/* Set the speed of the stream. */
95void sonicSetSpeed(
96 sonicStream stream,
97 float speed)
98{
99 stream->speed = speed;
100}
101
102/* Get the pitch of the stream. */
103float sonicGetPitch(
104 sonicStream stream)
105{
106 return stream->pitch;
107}
108
109/* Set the pitch of the stream. */
110void sonicSetPitch(
111 sonicStream stream,
112 float pitch)
113{
114 stream->pitch = pitch;
115}
116
Bill Coxc978c392010-12-17 05:04:06 -0500117/* Get the quality setting. */
118int sonicGetQuality(
119 sonicStream stream)
120{
121 return stream->quality;
122}
123
124/* Set the "quality". Default 0 is virtually as good as 1, but very much faster. */
125void sonicSetQuality(
126 sonicStream stream,
127 int quality)
128{
129 stream->quality = quality;
130}
131
Bill Coxd544fdb2010-11-23 14:13:46 -0500132/* Get the scaling factor of the stream. */
133float sonicGetVolume(
134 sonicStream stream)
135{
136 return stream->volume;
137}
138
139/* Set the scaling factor of the stream. */
140void sonicSetVolume(
141 sonicStream stream,
142 float volume)
143{
144 stream->volume = volume;
145}
146
Bill Coxaf9a6242010-11-08 09:32:27 -0500147/* Get the sample rate of the stream. */
148int sonicGetSampleRate(
149 sonicStream stream)
150{
151 return stream->sampleRate;
152}
153
Bill Cox527b4e82010-11-24 17:42:58 -0500154/* Get the number of channels. */
155int sonicGetNumChannels(
156 sonicStream stream)
157{
158 return stream->numChannels;
159}
160
Bill Coxca02d872010-11-02 15:10:52 -0400161/* Destroy the sonic stream. */
162void sonicDestroyStream(
163 sonicStream stream)
164{
165 if(stream->inputBuffer != NULL) {
166 free(stream->inputBuffer);
167 }
168 if(stream->outputBuffer != NULL) {
169 free(stream->outputBuffer);
170 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500171 if(stream->pitchBuffer != NULL) {
172 free(stream->pitchBuffer);
173 }
174 if(stream->downSampleBuffer != NULL) {
175 free(stream->downSampleBuffer);
176 }
Bill Coxca02d872010-11-02 15:10:52 -0400177 free(stream);
178}
179
180/* Create a sonic stream. Return NULL only if we are out of memory and cannot
181 allocate the stream. */
182sonicStream sonicCreateStream(
Bill Cox1a299bb2010-11-19 15:07:17 -0500183 int sampleRate,
184 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400185{
186 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
187 int minPeriod = sampleRate/SONIC_MAX_PITCH;
188 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
189 int maxRequired = 2*maxPeriod;
190
191 if(stream == NULL) {
192 return NULL;
193 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500194 stream->inputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500195 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400196 if(stream->inputBuffer == NULL) {
197 sonicDestroyStream(stream);
198 return NULL;
199 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500200 stream->outputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500201 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400202 if(stream->outputBuffer == NULL) {
203 sonicDestroyStream(stream);
204 return NULL;
205 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500206 stream->pitchBufferSize = maxRequired;
207 stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
208 if(stream->pitchBuffer == NULL) {
209 sonicDestroyStream(stream);
210 return NULL;
211 }
212 stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
213 stream->speed = 1.0f;
214 stream->pitch = 1.0f;
215 stream->volume = 1.0f;
Bill Coxc978c392010-12-17 05:04:06 -0500216 stream->quality = 0;
Bill Coxca02d872010-11-02 15:10:52 -0400217 stream->sampleRate = sampleRate;
Bill Cox1a299bb2010-11-19 15:07:17 -0500218 stream->numChannels = numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400219 stream->minPeriod = minPeriod;
220 stream->maxPeriod = maxPeriod;
221 stream->maxRequired = maxRequired;
Bill Coxca02d872010-11-02 15:10:52 -0400222 return stream;
223}
224
Bill Coxca02d872010-11-02 15:10:52 -0400225/* Enlarge the output buffer if needed. */
226static int enlargeOutputBufferIfNeeded(
227 sonicStream stream,
228 int numSamples)
229{
230 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
231 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500232 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500233 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400234 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400235 return 0;
236 }
237 }
238 return 1;
239}
240
Bill Coxca02d872010-11-02 15:10:52 -0400241/* Enlarge the input buffer if needed. */
242static int enlargeInputBufferIfNeeded(
243 sonicStream stream,
244 int numSamples)
245{
246 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
247 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500248 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500249 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400250 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400251 return 0;
252 }
253 }
254 return 1;
255}
256
257/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500258static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400259 sonicStream stream,
260 float *samples,
261 int numSamples)
262{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500263 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500264 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500265
Bill Coxca02d872010-11-02 15:10:52 -0400266 if(numSamples == 0) {
267 return 1;
268 }
269 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
270 return 0;
271 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500272 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500273 while(count--) {
274 *buffer++ = (*samples++)*32767.0f;
275 }
Bill Cox14efa442010-11-02 15:43:58 -0400276 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400277 return 1;
278}
279
Bill Cox0c4c0602010-11-08 11:46:30 -0500280/* Add the input samples to the input buffer. */
281static int addShortSamplesToInputBuffer(
282 sonicStream stream,
283 short *samples,
284 int numSamples)
285{
Bill Cox0c4c0602010-11-08 11:46:30 -0500286 if(numSamples == 0) {
287 return 1;
288 }
289 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
290 return 0;
291 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500292 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
293 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500294 stream->numInputSamples += numSamples;
295 return 1;
296}
297
Bill Cox8a23d2f2010-11-16 18:49:36 -0500298/* Add the input samples to the input buffer. */
299static int addUnsignedCharSamplesToInputBuffer(
300 sonicStream stream,
301 unsigned char *samples,
302 int numSamples)
303{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500304 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500305 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500306
307 if(numSamples == 0) {
308 return 1;
309 }
310 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
311 return 0;
312 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500313 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500314 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500315 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400316 }
317 stream->numInputSamples += numSamples;
318 return 1;
319}
320
321/* Remove input samples that we have already processed. */
322static void removeInputSamples(
323 sonicStream stream,
324 int position)
325{
326 int remainingSamples = stream->numInputSamples - position;
327
328 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500329 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
330 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400331 }
332 stream->numInputSamples = remainingSamples;
333}
334
Bill Cox59e65122010-11-03 10:06:29 -0400335/* Just copy from the array to the output buffer */
Bill Cox68e2aee2010-11-23 19:24:41 -0500336static int copyToOutput(
Bill Cox0c4c0602010-11-08 11:46:30 -0500337 sonicStream stream,
338 short *samples,
339 int numSamples)
340{
Bill Cox0c4c0602010-11-08 11:46:30 -0500341 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
342 return 0;
343 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500344 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
345 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500346 stream->numOutputSamples += numSamples;
347 return numSamples;
348}
349
Bill Cox882fb1d2010-11-02 16:27:20 -0400350/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
351 resize the output buffer. Otherwise, return numSamples */
352static int copyInputToOutput(
353 sonicStream stream,
354 int position)
355{
356 int numSamples = stream->remainingInputToCopy;
357
358 if(numSamples > stream->maxRequired) {
359 numSamples = stream->maxRequired;
360 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500361 if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
Bill Cox1a299bb2010-11-19 15:07:17 -0500362 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400363 return 0;
364 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400365 stream->remainingInputToCopy -= numSamples;
366 return numSamples;
367}
368
Bill Coxca02d872010-11-02 15:10:52 -0400369/* Read data out of the stream. Sometimes no data will be available, and zero
370 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500371int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400372 sonicStream stream,
373 float *samples,
374 int maxSamples)
375{
376 int numSamples = stream->numOutputSamples;
377 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500378 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500379 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400380
381 if(numSamples == 0) {
382 return 0;
383 }
384 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400385 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400386 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400387 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500388 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500389 count = numSamples*stream->numChannels;
390 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500391 *samples++ = (*buffer++)/32767.0f;
392 }
Bill Coxca02d872010-11-02 15:10:52 -0400393 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500394 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
395 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400396 }
397 stream->numOutputSamples = remainingSamples;
398 return numSamples;
399}
400
Bill Cox0c4c0602010-11-08 11:46:30 -0500401/* Read short data out of the stream. Sometimes no data will be available, and zero
402 is returned, which is not an error condition. */
403int sonicReadShortFromStream(
404 sonicStream stream,
405 short *samples,
406 int maxSamples)
407{
408 int numSamples = stream->numOutputSamples;
409 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500410
411 if(numSamples == 0) {
412 return 0;
413 }
414 if(numSamples > maxSamples) {
415 remainingSamples = numSamples - maxSamples;
416 numSamples = maxSamples;
417 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500418 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500419 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500420 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
421 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500422 }
423 stream->numOutputSamples = remainingSamples;
424 return numSamples;
425}
426
Bill Cox8a23d2f2010-11-16 18:49:36 -0500427/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
428 is returned, which is not an error condition. */
429int sonicReadUnsignedCharFromStream(
430 sonicStream stream,
431 unsigned char *samples,
432 int maxSamples)
433{
434 int numSamples = stream->numOutputSamples;
435 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500436 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500437 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500438
439 if(numSamples == 0) {
440 return 0;
441 }
442 if(numSamples > maxSamples) {
443 remainingSamples = numSamples - maxSamples;
444 numSamples = maxSamples;
445 }
446 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500447 count = numSamples*stream->numChannels;
448 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500449 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400450 }
451 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500452 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
453 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400454 }
455 stream->numOutputSamples = remainingSamples;
456 return numSamples;
457}
458
459/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500460 has. No extra delay will be added to the output, but flushing in the middle of
461 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400462int sonicFlushStream(
463 sonicStream stream)
464{
465 int maxRequired = stream->maxRequired;
Bill Cox0283c342010-11-30 11:47:50 -0500466 int remainingSamples = stream->numInputSamples;
467 float speed = stream->speed/stream->pitch;
468 int expectedOutputSamples = stream->numOutputSamples +
469 (int)(remainingSamples/speed + stream->numPitchSamples/stream->pitch + 0.5f);
Bill Coxca02d872010-11-02 15:10:52 -0400470
Bill Cox0283c342010-11-30 11:47:50 -0500471 /* Add enough silence to flush both input and pitch buffers. */
472 if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
473 return 0;
Bill Coxca02d872010-11-02 15:10:52 -0400474 }
Bill Cox0283c342010-11-30 11:47:50 -0500475 memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
476 2*maxRequired*sizeof(short)*stream->numChannels);
477 stream->numInputSamples += 2*maxRequired;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500478 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500479 return 0;
480 }
481 /* Throw away any extra samples we generated due to the silence we added */
Bill Cox0283c342010-11-30 11:47:50 -0500482 if(stream->numOutputSamples > expectedOutputSamples) {
483 stream->numOutputSamples = expectedOutputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500484 }
Bill Cox0283c342010-11-30 11:47:50 -0500485 /* Empty input and pitch buffers */
486 stream->numInputSamples = 0;
487 stream->remainingInputToCopy = 0;
488 stream->numPitchSamples = 0;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500489 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400490}
491
492/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400493int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400494 sonicStream stream)
495{
496 return stream->numOutputSamples;
497}
Bill Cox9bf11b52010-11-03 05:33:09 -0400498
Bill Coxd544fdb2010-11-23 14:13:46 -0500499/* If skip is greater than one, average skip samples togther and write them to
500 the down-sample buffer. If numChannels is greater than one, mix the channels
501 together as we down sample. */
502static void downSampleInput(
503 sonicStream stream,
504 short *samples,
505 int skip)
506{
507 int numSamples = stream->maxRequired/skip;
508 int samplesPerValue = stream->numChannels*skip;
509 int i, j;
510 int value;
511 short *downSamples = stream->downSampleBuffer;
512
513 for(i = 0; i < numSamples; i++) {
514 value = 0;
515 for(j = 0; j < samplesPerValue; j++) {
516 value += *samples++;
517 }
518 value /= samplesPerValue;
519 *downSamples++ = value;
520 }
521}
522
Bill Cox1a299bb2010-11-19 15:07:17 -0500523/* Find the best frequency match in the range, and given a sample skip multiple.
524 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400525static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500526 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400527 int minPeriod,
Bill Coxc17208e2010-11-26 11:09:15 -0500528 int maxPeriod,
529 int *retMinDiff,
530 int *retMaxDiff)
Bill Cox0cd49c82010-11-03 10:46:22 -0400531{
Bill Cox0283c342010-11-30 11:47:50 -0500532 int period, bestPeriod = 0, worstPeriod = 255;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500533 short *s, *p, sVal, pVal;
Bill Coxc17208e2010-11-26 11:09:15 -0500534 unsigned long diff, minDiff = 1, maxDiff = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500535 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400536
Bill Coxd544fdb2010-11-23 14:13:46 -0500537 for(period = minPeriod; period <= maxPeriod; period++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500538 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400539 s = samples;
Bill Coxd544fdb2010-11-23 14:13:46 -0500540 p = samples + period;
541 for(i = 0; i < period; i++) {
542 sVal = *s++;
543 pVal = *p++;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500544 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
545 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400546 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500547 /* Note that the highest number of samples we add into diff will be less
548 than 256, since we skip samples. Thus, diff is a 24 bit number, and
549 we can safely multiply by numSamples without overflow */
Bill Coxc17208e2010-11-26 11:09:15 -0500550 if(diff*bestPeriod < minDiff*period) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400551 minDiff = diff;
552 bestPeriod = period;
553 }
Bill Cox0283c342010-11-30 11:47:50 -0500554 if(diff*worstPeriod > maxDiff*period) {
Bill Coxc17208e2010-11-26 11:09:15 -0500555 maxDiff = diff;
Bill Cox0283c342010-11-30 11:47:50 -0500556 worstPeriod = period;
Bill Coxc17208e2010-11-26 11:09:15 -0500557 }
Bill Cox0cd49c82010-11-03 10:46:22 -0400558 }
Bill Cox0283c342010-11-30 11:47:50 -0500559 *retMinDiff = minDiff/bestPeriod;
560 *retMaxDiff = maxDiff/worstPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400561 return bestPeriod;
562}
563
Bill Coxc17208e2010-11-26 11:09:15 -0500564/* At abrupt ends of voiced words, we can have pitch periods that are better
565 aproximated by the previous pitch period estimate. Try to detect this case. */
566static int prevPeriodBetter(
567 sonicStream stream,
568 int period,
569 int minDiff,
Bill Cox0283c342010-11-30 11:47:50 -0500570 int maxDiff,
571 int preferNewPeriod)
Bill Coxc17208e2010-11-26 11:09:15 -0500572{
Bill Cox0283c342010-11-30 11:47:50 -0500573 if(minDiff == 0) {
574 return 0;
Bill Coxc17208e2010-11-26 11:09:15 -0500575 }
Bill Cox0283c342010-11-30 11:47:50 -0500576 if(preferNewPeriod) {
577 if(maxDiff > minDiff*3) {
578 /* Got a reasonable match this period */
579 return 0;
580 }
581 if(minDiff*2 <= stream->prevMinDiff*3) {
582 /* Mismatch is not that much greater this period */
583 return 0;
584 }
585 } else {
586 if(minDiff <= stream->prevMinDiff) {
587 return 0;
588 }
589 }
590 return 1;
Bill Coxc17208e2010-11-26 11:09:15 -0500591}
592
Bill Cox9bf11b52010-11-03 05:33:09 -0400593/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400594 multiple ways to get a good answer. This version uses AMDF. To improve
595 speed, we down sample by an integer factor get in the 11KHz range, and then
596 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400597static int findPitchPeriod(
598 sonicStream stream,
Bill Cox0283c342010-11-30 11:47:50 -0500599 short *samples,
600 int preferNewPeriod)
Bill Cox9bf11b52010-11-03 05:33:09 -0400601{
602 int minPeriod = stream->minPeriod;
603 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400604 int sampleRate = stream->sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -0500605 int minDiff, maxDiff, retPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400606 int skip = 1;
607 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400608
Bill Coxc978c392010-12-17 05:04:06 -0500609 if(sampleRate > SONIC_AMDF_FREQ && stream->quality != 0) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400610 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400611 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500612 if(stream->numChannels == 1 && skip == 1) {
Bill Coxc17208e2010-11-26 11:09:15 -0500613 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
614 } else {
615 downSampleInput(stream, samples, skip);
616 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
617 maxPeriod/skip, &minDiff, &maxDiff);
618 if(skip != 1) {
619 period *= skip;
620 minPeriod = period - (skip << 2);
621 maxPeriod = period + (skip << 2);
622 if(minPeriod < stream->minPeriod) {
623 minPeriod = stream->minPeriod;
624 }
625 if(maxPeriod > stream->maxPeriod) {
626 maxPeriod = stream->maxPeriod;
627 }
628 if(stream->numChannels == 1) {
629 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
630 &minDiff, &maxDiff);
631 } else {
632 downSampleInput(stream, samples, 1);
633 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
634 maxPeriod, &minDiff, &maxDiff);
635 }
636 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500637 }
Bill Cox0283c342010-11-30 11:47:50 -0500638 if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
Bill Coxc17208e2010-11-26 11:09:15 -0500639 retPeriod = stream->prevPeriod;
640 } else {
641 retPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500642 }
Bill Coxc17208e2010-11-26 11:09:15 -0500643 stream->prevMinDiff = minDiff;
644 stream->prevMaxDiff = maxDiff;
645 stream->prevPeriod = period;
646 return retPeriod;
Bill Coxd544fdb2010-11-23 14:13:46 -0500647}
648
Bill Cox68e2aee2010-11-23 19:24:41 -0500649/* Overlap two sound segments, ramp the volume of one down, while ramping the
650 other one from zero up, and add them, storing the result at the output. */
651static void overlapAdd(
652 int numSamples,
653 int numChannels,
654 short *out,
655 short *rampDown,
656 short *rampUp)
657{
Bill Coxd76d2222010-11-24 11:42:29 -0500658 short *o, *u, *d;
Bill Cox68e2aee2010-11-23 19:24:41 -0500659 int i, t;
660
661 for(i = 0; i < numChannels; i++) {
662 o = out + i;
Bill Coxd76d2222010-11-24 11:42:29 -0500663 u = rampUp + i;
664 d = rampDown + i;
Bill Cox68e2aee2010-11-23 19:24:41 -0500665 for(t = 0; t < numSamples; t++) {
Bill Coxd76d2222010-11-24 11:42:29 -0500666 *o = (*d*(numSamples - t) + *u*t)/numSamples;
Bill Cox68e2aee2010-11-23 19:24:41 -0500667 o += numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500668 d += numChannels;
669 u += numChannels;
670 }
671 }
672}
673
674/* Overlap two sound segments, ramp the volume of one down, while ramping the
675 other one from zero up, and add them, storing the result at the output. */
676static void overlapAddWithSeparation(
677 int numSamples,
678 int numChannels,
679 int separation,
680 short *out,
681 short *rampDown,
682 short *rampUp)
683{
684 short *o, *u, *d;
685 int i, t;
686
687 for(i = 0; i < numChannels; i++) {
688 o = out + i;
689 u = rampUp + i;
690 d = rampDown + i;
691 for(t = 0; t < numSamples + separation; t++) {
692 if(t < separation) {
693 *o = *d*(numSamples - t)/numSamples;
694 d += numChannels;
695 } else if(t < numSamples) {
696 *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
697 d += numChannels;
698 u += numChannels;
699 } else {
700 *o = *u*(t - separation)/numSamples;
701 u += numChannels;
702 }
703 o += numChannels;
Bill Cox68e2aee2010-11-23 19:24:41 -0500704 }
705 }
706}
707
708/* Just move the new samples in the output buffer to the pitch bufer */
709static int moveNewSamplesToPitchBuffer(
Bill Coxd544fdb2010-11-23 14:13:46 -0500710 sonicStream stream,
711 int originalNumOutputSamples)
712{
Bill Cox68e2aee2010-11-23 19:24:41 -0500713 int numSamples = stream->numOutputSamples - originalNumOutputSamples;
714 int numChannels = stream->numChannels;
715
716 if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
717 stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
718 stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
719 stream->pitchBufferSize*sizeof(short)*numChannels);
720 if(stream->pitchBuffer == NULL) {
721 return 0;
722 }
723 }
724 memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
725 stream->outputBuffer + originalNumOutputSamples*numChannels,
726 numSamples*sizeof(short)*numChannels);
727 stream->numOutputSamples = originalNumOutputSamples;
728 stream->numPitchSamples += numSamples;
729 return 1;
730}
731
732/* Remove processed samples from the pitch buffer. */
733static void removePitchSamples(
734 sonicStream stream,
735 int numSamples)
736{
737 int numChannels = stream->numChannels;
738 short *source = stream->pitchBuffer + numSamples*numChannels;
739
740 if(numSamples == 0) {
741 return;
742 }
743 if(numSamples != stream->numPitchSamples) {
744 memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
745 numSamples)*sizeof(short)*numChannels);
746 }
747 stream->numPitchSamples -= numSamples;
748}
749
750/* Change the pitch. The latency this introduces could be reduced by looking at
751 past samples to determine pitch, rather than future. */
752static int adjustPitch(
753 sonicStream stream,
754 int originalNumOutputSamples)
755{
756 float pitch = stream->pitch;
757 int numChannels = stream->numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500758 int period, newPeriod, separation;
Bill Cox68e2aee2010-11-23 19:24:41 -0500759 int position = 0;
760 short *out, *rampDown, *rampUp;
761
762 if(stream->numOutputSamples == originalNumOutputSamples) {
763 return 1;
764 }
765 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
766 return 0;
767 }
768 while(stream->numPitchSamples - position >= stream->maxRequired) {
Bill Cox0283c342010-11-30 11:47:50 -0500769 period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
Bill Cox68e2aee2010-11-23 19:24:41 -0500770 newPeriod = period/pitch;
Bill Coxd76d2222010-11-24 11:42:29 -0500771 if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500772 return 0;
773 }
Bill Coxd76d2222010-11-24 11:42:29 -0500774 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
775 if(pitch >= 1.0f) {
776 rampDown = stream->pitchBuffer + position*numChannels;
777 rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
778 overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
779 } else {
780 rampDown = stream->pitchBuffer + position*numChannels;
781 rampUp = stream->pitchBuffer + position*numChannels;
782 separation = newPeriod - period;
783 overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
Bill Cox68e2aee2010-11-23 19:24:41 -0500784 }
Bill Coxd76d2222010-11-24 11:42:29 -0500785 stream->numOutputSamples += newPeriod;
Bill Cox68e2aee2010-11-23 19:24:41 -0500786 position += period;
787 }
788 removePitchSamples(stream, position);
789 return 1;
Bill Cox9bf11b52010-11-03 05:33:09 -0400790}
791
Bill Cox59e65122010-11-03 10:06:29 -0400792/* Skip over a pitch period, and copy period/speed samples to the output */
793static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400794 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500795 short *samples,
796 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400797 int period)
798{
Bill Cox68e2aee2010-11-23 19:24:41 -0500799 long newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500800 int numChannels = stream->numChannels;
Bill Cox9bf11b52010-11-03 05:33:09 -0400801
Bill Cox6a1bbb12010-11-19 11:14:28 -0500802 if(speed >= 2.0f) {
803 newSamples = period/(speed - 1.0f);
804 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400805 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500806 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400807 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400808 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
809 return 0;
810 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500811 overlapAdd(newSamples, numChannels, stream->outputBuffer +
812 stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
Bill Cox9bf11b52010-11-03 05:33:09 -0400813 stream->numOutputSamples += newSamples;
814 return newSamples;
815}
816
Bill Cox59e65122010-11-03 10:06:29 -0400817/* Insert a pitch period, and determine how much input to copy directly. */
818static int insertPitchPeriod(
819 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500820 short *samples,
821 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400822 int period)
823{
Bill Cox68e2aee2010-11-23 19:24:41 -0500824 long newSamples;
825 short *out;
Bill Cox1a299bb2010-11-19 15:07:17 -0500826 int numChannels = stream->numChannels;
Bill Cox59e65122010-11-03 10:06:29 -0400827
Bill Cox6a1bbb12010-11-19 11:14:28 -0500828 if(speed < 0.5f) {
829 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400830 } else {
831 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500832 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400833 }
834 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
835 return 0;
836 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500837 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
838 memcpy(out, samples, period*sizeof(short)*numChannels);
Bill Cox68e2aee2010-11-23 19:24:41 -0500839 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
840 overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
Bill Cox59e65122010-11-03 10:06:29 -0400841 stream->numOutputSamples += period + newSamples;
842 return newSamples;
843}
844
Bill Cox9bf11b52010-11-03 05:33:09 -0400845/* Resample as many pitch periods as we have buffered on the input. Return 0 if
Bill Coxd544fdb2010-11-23 14:13:46 -0500846 we fail to resize an input or output buffer. Also scale the output by the volume. */
847static int changeSpeed(
848 sonicStream stream,
849 float speed)
Bill Cox9bf11b52010-11-03 05:33:09 -0400850{
Bill Cox1a299bb2010-11-19 15:07:17 -0500851 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -0500852 int numSamples = stream->numInputSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400853 int position = 0, period, newSamples;
854 int maxRequired = stream->maxRequired;
855
Bill Cox9bf11b52010-11-03 05:33:09 -0400856 if(stream->numInputSamples < maxRequired) {
857 return 1;
858 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400859 do {
860 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400861 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400862 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400863 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500864 samples = stream->inputBuffer + position*stream->numChannels;
Bill Cox0283c342010-11-30 11:47:50 -0500865 period = findPitchPeriod(stream, samples, 1);
Bill Cox59e65122010-11-03 10:06:29 -0400866 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500867 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400868 position += period + newSamples;
869 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500870 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400871 position += newSamples;
872 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400873 }
874 if(newSamples == 0) {
875 return 0; /* Failed to resize output buffer */
876 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400877 } while(position + maxRequired <= numSamples);
878 removeInputSamples(stream, position);
879 return 1;
880}
Bill Cox0c4c0602010-11-08 11:46:30 -0500881
Bill Coxd544fdb2010-11-23 14:13:46 -0500882/* Resample as many pitch periods as we have buffered on the input. Return 0 if
883 we fail to resize an input or output buffer. Also scale the output by the volume. */
884static int processStreamInput(
885 sonicStream stream)
886{
887 int originalNumOutputSamples = stream->numOutputSamples;
888 float speed = stream->speed/stream->pitch;
889
890 if(speed > 1.00001 || speed < 0.99999) {
891 changeSpeed(stream, speed);
892 } else {
Bill Cox68e2aee2010-11-23 19:24:41 -0500893 if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
Bill Coxd544fdb2010-11-23 14:13:46 -0500894 return 0;
895 }
896 stream->numInputSamples = 0;
897 }
898 if(stream->pitch != 1.0f) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500899 if(!adjustPitch(stream, originalNumOutputSamples)) {
900 return 0;
901 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500902 }
903 if(stream->volume != 1.0f) {
904 /* Adjust output volume. */
905 scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
906 (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
907 stream->volume);
908 }
909 return 1;
910}
911
Bill Cox0c4c0602010-11-08 11:46:30 -0500912/* Write floating point data to the input buffer and process it. */
913int sonicWriteFloatToStream(
914 sonicStream stream,
915 float *samples,
916 int numSamples)
917{
Bill Cox0c4c0602010-11-08 11:46:30 -0500918 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
919 return 0;
920 }
921 return processStreamInput(stream);
922}
923
924/* Simple wrapper around sonicWriteFloatToStream that does the short to float
925 conversion for you. */
926int sonicWriteShortToStream(
927 sonicStream stream,
928 short *samples,
929 int numSamples)
930{
Bill Cox0c4c0602010-11-08 11:46:30 -0500931 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
932 return 0;
933 }
934 return processStreamInput(stream);
935}
936
Bill Cox8a23d2f2010-11-16 18:49:36 -0500937/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
938 conversion for you. */
939int sonicWriteUnsignedCharToStream(
940 sonicStream stream,
941 unsigned char *samples,
942 int numSamples)
943{
Bill Cox8a23d2f2010-11-16 18:49:36 -0500944 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
945 return 0;
946 }
947 return processStreamInput(stream);
948}
949
Bill Cox036d7322010-11-09 09:29:24 -0500950/* This is a non-stream oriented interface to just change the speed of a sound sample */
951int sonicChangeFloatSpeed(
952 float *samples,
953 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500954 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500955 float pitch,
956 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500957 int sampleRate,
958 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500959{
Bill Coxd544fdb2010-11-23 14:13:46 -0500960 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500961
Bill Coxd544fdb2010-11-23 14:13:46 -0500962 sonicSetSpeed(stream, speed);
963 sonicSetPitch(stream, pitch);
964 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500965 sonicWriteFloatToStream(stream, samples, numSamples);
966 sonicFlushStream(stream);
967 numSamples = sonicSamplesAvailable(stream);
968 sonicReadFloatFromStream(stream, samples, numSamples);
969 sonicDestroyStream(stream);
970 return numSamples;
971}
972
973/* This is a non-stream oriented interface to just change the speed of a sound sample */
974int sonicChangeShortSpeed(
975 short *samples,
976 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500977 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -0500978 float pitch,
979 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -0500980 int sampleRate,
981 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500982{
Bill Coxd544fdb2010-11-23 14:13:46 -0500983 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500984
Bill Coxd544fdb2010-11-23 14:13:46 -0500985 sonicSetSpeed(stream, speed);
986 sonicSetPitch(stream, pitch);
987 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -0500988 sonicWriteShortToStream(stream, samples, numSamples);
989 sonicFlushStream(stream);
990 numSamples = sonicSamplesAvailable(stream);
991 sonicReadShortFromStream(stream, samples, numSamples);
992 sonicDestroyStream(stream);
993 return numSamples;
994}