blob: b70259f7e3ec1902c69a7e62ce5b05655f652ec9 [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 Cox3276bb02011-01-11 07:39:26 -050020
Bill Cox882fb1d2010-11-02 16:27:20 -040021#include <stdio.h>
Bill Coxca02d872010-11-02 15:10:52 -040022#include <stdlib.h>
23#include <string.h>
Bill Coxa33e3bd2010-11-16 19:57:43 -050024#include <stdarg.h>
Bill Cox4e234d72010-12-17 05:44:02 -050025#ifdef SONIC_USE_SIN
26#include <math.h>
27#ifndef M_PI
28#define M_PI 3.14159265358979323846
29#endif
30#endif
Bill Coxca02d872010-11-02 15:10:52 -040031#include "sonic.h"
32
33struct sonicStreamStruct {
Bill Cox6a1bbb12010-11-19 11:14:28 -050034 short *inputBuffer;
35 short *outputBuffer;
Bill Coxd544fdb2010-11-23 14:13:46 -050036 short *pitchBuffer;
37 short *downSampleBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -050038 float speed;
Bill Coxd544fdb2010-11-23 14:13:46 -050039 float volume;
40 float pitch;
Bill Cox3276bb02011-01-11 07:39:26 -050041 float rate;
42 int oldRatePosition;
43 int newRatePosition;
44 int oldSampleRate;
45 int newSampleRate;
46 int useChordPitch;
Bill Coxc978c392010-12-17 05:04:06 -050047 int quality;
Bill Cox1a299bb2010-11-19 15:07:17 -050048 int numChannels;
Bill Coxca02d872010-11-02 15:10:52 -040049 int inputBufferSize;
Bill Coxd544fdb2010-11-23 14:13:46 -050050 int pitchBufferSize;
Bill Coxca02d872010-11-02 15:10:52 -040051 int outputBufferSize;
52 int numInputSamples;
53 int numOutputSamples;
Bill Coxd544fdb2010-11-23 14:13:46 -050054 int numPitchSamples;
Bill Coxca02d872010-11-02 15:10:52 -040055 int minPeriod;
56 int maxPeriod;
57 int maxRequired;
58 int remainingInputToCopy;
59 int sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -050060 int prevPeriod;
61 int prevMaxDiff;
62 int prevMinDiff;
Bill Coxca02d872010-11-02 15:10:52 -040063};
64
Bill Cox1a299bb2010-11-19 15:07:17 -050065/* Just used for debugging */
Bill Cox59af4672010-12-17 16:57:41 -050066/*
Bill Coxa33e3bd2010-11-16 19:57:43 -050067void sonicMSG(char *format, ...)
68{
69 char buffer[4096];
70 va_list ap;
71 FILE *file;
72
73 va_start(ap, format);
74 vsprintf((char *)buffer, (char *)format, ap);
75 va_end(ap);
76 file=fopen("/tmp/sonic.log", "a");
77 fprintf(file, "%s", buffer);
78 fclose(file);
79}
Bill Cox59af4672010-12-17 16:57:41 -050080*/
Bill Coxa33e3bd2010-11-16 19:57:43 -050081
Bill Coxd544fdb2010-11-23 14:13:46 -050082/* Scale the samples by the factor. */
83static void scaleSamples(
84 short *samples,
85 int numSamples,
86 float volume)
87{
88 int fixedPointVolume = volume*4096.0f;
89 int value;
90
91 while(numSamples--) {
92 value = (*samples*fixedPointVolume) >> 12;
93 if(value > 32767) {
94 value = 32767;
95 } else if(value < -32767) {
96 value = -32767;
97 }
98 *samples++ = value;
99 }
100}
101
Bill Coxaf9a6242010-11-08 09:32:27 -0500102/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -0500103float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -0500104 sonicStream stream)
105{
106 return stream->speed;
107}
108
Bill Coxd544fdb2010-11-23 14:13:46 -0500109/* Set the speed of the stream. */
110void sonicSetSpeed(
111 sonicStream stream,
112 float speed)
113{
114 stream->speed = speed;
115}
116
117/* Get the pitch of the stream. */
118float sonicGetPitch(
119 sonicStream stream)
120{
121 return stream->pitch;
122}
123
124/* Set the pitch of the stream. */
125void sonicSetPitch(
126 sonicStream stream,
127 float pitch)
128{
129 stream->pitch = pitch;
130}
131
Bill Cox3276bb02011-01-11 07:39:26 -0500132/* Get the rate of the stream. */
133float sonicGetRate(
134 sonicStream stream)
135{
136 return stream->rate;
137}
138
139/* Set the playback rate of the stream. This scales pitch and speed at the same time. */
140void sonicSetRate(
141 sonicStream stream,
142 float rate)
143{
144 stream->rate = rate;
145
146 stream->oldRatePosition = 0;
147 stream->newRatePosition = 0;
148}
149
150/* Get the vocal chord pitch setting. */
151int sonicGetChordPitch(
152 sonicStream stream)
153{
154 return stream->useChordPitch;
155}
156
157/* Set the vocal chord mode for pitch computation. Default is off. */
158void sonicSetChordPitch(
159 sonicStream stream,
160 int useChordPitch)
161{
162 stream->useChordPitch = useChordPitch;
163}
164
Bill Coxc978c392010-12-17 05:04:06 -0500165/* Get the quality setting. */
166int sonicGetQuality(
167 sonicStream stream)
168{
169 return stream->quality;
170}
171
172/* Set the "quality". Default 0 is virtually as good as 1, but very much faster. */
173void sonicSetQuality(
174 sonicStream stream,
175 int quality)
176{
177 stream->quality = quality;
178}
179
Bill Coxd544fdb2010-11-23 14:13:46 -0500180/* Get the scaling factor of the stream. */
181float sonicGetVolume(
182 sonicStream stream)
183{
184 return stream->volume;
185}
186
187/* Set the scaling factor of the stream. */
188void sonicSetVolume(
189 sonicStream stream,
190 float volume)
191{
192 stream->volume = volume;
193}
194
Bill Cox69a864b2011-03-04 02:51:43 -0500195/* Free stream buffers. */
196static void freeStreamBuffers(
Bill Coxca02d872010-11-02 15:10:52 -0400197 sonicStream stream)
198{
199 if(stream->inputBuffer != NULL) {
200 free(stream->inputBuffer);
201 }
202 if(stream->outputBuffer != NULL) {
203 free(stream->outputBuffer);
204 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500205 if(stream->pitchBuffer != NULL) {
206 free(stream->pitchBuffer);
207 }
208 if(stream->downSampleBuffer != NULL) {
209 free(stream->downSampleBuffer);
210 }
Bill Cox69a864b2011-03-04 02:51:43 -0500211}
212
213/* Destroy the sonic stream. */
214void sonicDestroyStream(
215 sonicStream stream)
216{
217 freeStreamBuffers(stream);
Bill Coxca02d872010-11-02 15:10:52 -0400218 free(stream);
219}
220
Bill Cox69a864b2011-03-04 02:51:43 -0500221/* Allocate stream buffers. */
222static int allocateStreamBuffers(
223 sonicStream stream,
224 int sampleRate,
225 int numChannels)
226{
227 int minPeriod = sampleRate/SONIC_MAX_PITCH;
228 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
229 int maxRequired = 2*maxPeriod;
230
231 stream->inputBufferSize = maxRequired;
232 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
233 if(stream->inputBuffer == NULL) {
234 sonicDestroyStream(stream);
235 return 0;
236 }
237 stream->outputBufferSize = maxRequired;
238 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
239 if(stream->outputBuffer == NULL) {
240 sonicDestroyStream(stream);
241 return 0;
242 }
243 stream->pitchBufferSize = maxRequired;
244 stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
245 if(stream->pitchBuffer == NULL) {
246 sonicDestroyStream(stream);
247 return 0;
248 }
249 stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
250 if(stream->downSampleBuffer == NULL) {
251 sonicDestroyStream(stream);
252 return 0;
253 }
254 stream->sampleRate = sampleRate;
255 stream->numChannels = numChannels;
256 stream->oldRatePosition = 0;
257 stream->newRatePosition = 0;
258 stream->minPeriod = minPeriod;
259 stream->maxPeriod = maxPeriod;
260 stream->maxRequired = maxRequired;
261 return 1;
262}
263
Bill Coxca02d872010-11-02 15:10:52 -0400264/* Create a sonic stream. Return NULL only if we are out of memory and cannot
265 allocate the stream. */
266sonicStream sonicCreateStream(
Bill Cox1a299bb2010-11-19 15:07:17 -0500267 int sampleRate,
268 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400269{
270 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
Bill Coxca02d872010-11-02 15:10:52 -0400271
272 if(stream == NULL) {
273 return NULL;
274 }
Bill Cox69a864b2011-03-04 02:51:43 -0500275 if(!allocateStreamBuffers(stream, sampleRate, numChannels)) {
276 return NULL;
Bill Coxca02d872010-11-02 15:10:52 -0400277 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500278 stream->speed = 1.0f;
279 stream->pitch = 1.0f;
280 stream->volume = 1.0f;
Bill Cox3276bb02011-01-11 07:39:26 -0500281 stream->rate = 1.0f;
282 stream->oldRatePosition = 0;
283 stream->newRatePosition = 0;
284 stream->useChordPitch = 0;
Bill Coxc978c392010-12-17 05:04:06 -0500285 stream->quality = 0;
Bill Coxca02d872010-11-02 15:10:52 -0400286 return stream;
287}
288
Bill Cox69a864b2011-03-04 02:51:43 -0500289/* Get the sample rate of the stream. */
290int sonicGetSampleRate(
291 sonicStream stream)
292{
293 return stream->sampleRate;
294}
295
296/* Set the sample rate of the stream. This will cause samples buffered in the stream to
297 be lost. */
298void sonicSetSampleRate(
299 sonicStream stream,
300 int sampleRate)
301{
302 freeStreamBuffers(stream);
303 allocateStreamBuffers(stream, sampleRate, stream->numChannels);
304}
305
306/* Get the number of channels. */
307int sonicGetNumChannels(
308 sonicStream stream)
309{
310 return stream->numChannels;
311}
312
313/* Set the num channels of the stream. This will cause samples buffered in the stream to
314 be lost. */
315void sonicSetNumChannels(
316 sonicStream stream,
317 int numChannels)
318{
319 freeStreamBuffers(stream);
320 allocateStreamBuffers(stream, stream->sampleRate, numChannels);
321}
322
Bill Coxca02d872010-11-02 15:10:52 -0400323/* Enlarge the output buffer if needed. */
324static int enlargeOutputBufferIfNeeded(
325 sonicStream stream,
326 int numSamples)
327{
328 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
329 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500330 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500331 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400332 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400333 return 0;
334 }
335 }
336 return 1;
337}
338
Bill Coxca02d872010-11-02 15:10:52 -0400339/* Enlarge the input buffer if needed. */
340static int enlargeInputBufferIfNeeded(
341 sonicStream stream,
342 int numSamples)
343{
344 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
345 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500346 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500347 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400348 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400349 return 0;
350 }
351 }
352 return 1;
353}
354
355/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500356static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400357 sonicStream stream,
358 float *samples,
359 int numSamples)
360{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500361 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500362 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500363
Bill Coxca02d872010-11-02 15:10:52 -0400364 if(numSamples == 0) {
365 return 1;
366 }
367 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
368 return 0;
369 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500370 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500371 while(count--) {
372 *buffer++ = (*samples++)*32767.0f;
373 }
Bill Cox14efa442010-11-02 15:43:58 -0400374 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400375 return 1;
376}
377
Bill Cox0c4c0602010-11-08 11:46:30 -0500378/* Add the input samples to the input buffer. */
379static int addShortSamplesToInputBuffer(
380 sonicStream stream,
381 short *samples,
382 int numSamples)
383{
Bill Cox0c4c0602010-11-08 11:46:30 -0500384 if(numSamples == 0) {
385 return 1;
386 }
387 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
388 return 0;
389 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500390 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
391 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500392 stream->numInputSamples += numSamples;
393 return 1;
394}
395
Bill Cox8a23d2f2010-11-16 18:49:36 -0500396/* Add the input samples to the input buffer. */
397static int addUnsignedCharSamplesToInputBuffer(
398 sonicStream stream,
399 unsigned char *samples,
400 int numSamples)
401{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500402 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500403 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500404
405 if(numSamples == 0) {
406 return 1;
407 }
408 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
409 return 0;
410 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500411 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500412 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500413 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400414 }
415 stream->numInputSamples += numSamples;
416 return 1;
417}
418
419/* Remove input samples that we have already processed. */
420static void removeInputSamples(
421 sonicStream stream,
422 int position)
423{
424 int remainingSamples = stream->numInputSamples - position;
425
426 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500427 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
428 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400429 }
430 stream->numInputSamples = remainingSamples;
431}
432
Bill Cox59e65122010-11-03 10:06:29 -0400433/* Just copy from the array to the output buffer */
Bill Cox68e2aee2010-11-23 19:24:41 -0500434static int copyToOutput(
Bill Cox0c4c0602010-11-08 11:46:30 -0500435 sonicStream stream,
436 short *samples,
437 int numSamples)
438{
Bill Cox0c4c0602010-11-08 11:46:30 -0500439 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
440 return 0;
441 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500442 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
443 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500444 stream->numOutputSamples += numSamples;
445 return numSamples;
446}
447
Bill Cox882fb1d2010-11-02 16:27:20 -0400448/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
449 resize the output buffer. Otherwise, return numSamples */
450static int copyInputToOutput(
451 sonicStream stream,
452 int position)
453{
454 int numSamples = stream->remainingInputToCopy;
455
456 if(numSamples > stream->maxRequired) {
457 numSamples = stream->maxRequired;
458 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500459 if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
Bill Cox1a299bb2010-11-19 15:07:17 -0500460 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400461 return 0;
462 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400463 stream->remainingInputToCopy -= numSamples;
464 return numSamples;
465}
466
Bill Coxca02d872010-11-02 15:10:52 -0400467/* Read data out of the stream. Sometimes no data will be available, and zero
468 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500469int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400470 sonicStream stream,
471 float *samples,
472 int maxSamples)
473{
474 int numSamples = stream->numOutputSamples;
475 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500476 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500477 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400478
479 if(numSamples == 0) {
480 return 0;
481 }
482 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400483 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400484 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400485 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500486 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500487 count = numSamples*stream->numChannels;
488 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500489 *samples++ = (*buffer++)/32767.0f;
490 }
Bill Coxca02d872010-11-02 15:10:52 -0400491 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500492 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
493 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400494 }
495 stream->numOutputSamples = remainingSamples;
496 return numSamples;
497}
498
Bill Cox0c4c0602010-11-08 11:46:30 -0500499/* Read short data out of the stream. Sometimes no data will be available, and zero
500 is returned, which is not an error condition. */
501int sonicReadShortFromStream(
502 sonicStream stream,
503 short *samples,
504 int maxSamples)
505{
506 int numSamples = stream->numOutputSamples;
507 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500508
509 if(numSamples == 0) {
510 return 0;
511 }
512 if(numSamples > maxSamples) {
513 remainingSamples = numSamples - maxSamples;
514 numSamples = maxSamples;
515 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500516 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500517 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500518 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
519 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500520 }
521 stream->numOutputSamples = remainingSamples;
522 return numSamples;
523}
524
Bill Cox8a23d2f2010-11-16 18:49:36 -0500525/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
526 is returned, which is not an error condition. */
527int sonicReadUnsignedCharFromStream(
528 sonicStream stream,
529 unsigned char *samples,
530 int maxSamples)
531{
532 int numSamples = stream->numOutputSamples;
533 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500534 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500535 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500536
537 if(numSamples == 0) {
538 return 0;
539 }
540 if(numSamples > maxSamples) {
541 remainingSamples = numSamples - maxSamples;
542 numSamples = maxSamples;
543 }
544 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500545 count = numSamples*stream->numChannels;
546 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500547 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400548 }
549 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500550 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
551 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400552 }
553 stream->numOutputSamples = remainingSamples;
554 return numSamples;
555}
556
557/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500558 has. No extra delay will be added to the output, but flushing in the middle of
559 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400560int sonicFlushStream(
561 sonicStream stream)
562{
563 int maxRequired = stream->maxRequired;
Bill Cox0283c342010-11-30 11:47:50 -0500564 int remainingSamples = stream->numInputSamples;
565 float speed = stream->speed/stream->pitch;
Bill Cox3276bb02011-01-11 07:39:26 -0500566 float rate = stream->rate*stream->pitch;
Bill Cox0283c342010-11-30 11:47:50 -0500567 int expectedOutputSamples = stream->numOutputSamples +
Bill Cox3276bb02011-01-11 07:39:26 -0500568 (int)((remainingSamples/speed + stream->numPitchSamples)/rate + 0.5f);
Bill Coxca02d872010-11-02 15:10:52 -0400569
Bill Cox0283c342010-11-30 11:47:50 -0500570 /* Add enough silence to flush both input and pitch buffers. */
571 if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
572 return 0;
Bill Coxca02d872010-11-02 15:10:52 -0400573 }
Bill Cox0283c342010-11-30 11:47:50 -0500574 memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
575 2*maxRequired*sizeof(short)*stream->numChannels);
576 stream->numInputSamples += 2*maxRequired;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500577 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500578 return 0;
579 }
580 /* Throw away any extra samples we generated due to the silence we added */
Bill Cox0283c342010-11-30 11:47:50 -0500581 if(stream->numOutputSamples > expectedOutputSamples) {
582 stream->numOutputSamples = expectedOutputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500583 }
Bill Cox0283c342010-11-30 11:47:50 -0500584 /* Empty input and pitch buffers */
585 stream->numInputSamples = 0;
586 stream->remainingInputToCopy = 0;
587 stream->numPitchSamples = 0;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500588 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400589}
590
591/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400592int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400593 sonicStream stream)
594{
595 return stream->numOutputSamples;
596}
Bill Cox9bf11b52010-11-03 05:33:09 -0400597
Bill Coxd544fdb2010-11-23 14:13:46 -0500598/* If skip is greater than one, average skip samples togther and write them to
599 the down-sample buffer. If numChannels is greater than one, mix the channels
600 together as we down sample. */
601static void downSampleInput(
602 sonicStream stream,
603 short *samples,
604 int skip)
605{
606 int numSamples = stream->maxRequired/skip;
607 int samplesPerValue = stream->numChannels*skip;
608 int i, j;
609 int value;
610 short *downSamples = stream->downSampleBuffer;
611
612 for(i = 0; i < numSamples; i++) {
613 value = 0;
614 for(j = 0; j < samplesPerValue; j++) {
615 value += *samples++;
616 }
617 value /= samplesPerValue;
618 *downSamples++ = value;
619 }
620}
621
Bill Cox1a299bb2010-11-19 15:07:17 -0500622/* Find the best frequency match in the range, and given a sample skip multiple.
623 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400624static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500625 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400626 int minPeriod,
Bill Coxc17208e2010-11-26 11:09:15 -0500627 int maxPeriod,
628 int *retMinDiff,
629 int *retMaxDiff)
Bill Cox0cd49c82010-11-03 10:46:22 -0400630{
Bill Cox0283c342010-11-30 11:47:50 -0500631 int period, bestPeriod = 0, worstPeriod = 255;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500632 short *s, *p, sVal, pVal;
Bill Coxc17208e2010-11-26 11:09:15 -0500633 unsigned long diff, minDiff = 1, maxDiff = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500634 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400635
Bill Coxd544fdb2010-11-23 14:13:46 -0500636 for(period = minPeriod; period <= maxPeriod; period++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500637 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400638 s = samples;
Bill Coxd544fdb2010-11-23 14:13:46 -0500639 p = samples + period;
640 for(i = 0; i < period; i++) {
641 sVal = *s++;
642 pVal = *p++;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500643 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
644 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400645 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500646 /* Note that the highest number of samples we add into diff will be less
647 than 256, since we skip samples. Thus, diff is a 24 bit number, and
648 we can safely multiply by numSamples without overflow */
Bill Coxc17208e2010-11-26 11:09:15 -0500649 if(diff*bestPeriod < minDiff*period) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400650 minDiff = diff;
651 bestPeriod = period;
652 }
Bill Cox0283c342010-11-30 11:47:50 -0500653 if(diff*worstPeriod > maxDiff*period) {
Bill Coxc17208e2010-11-26 11:09:15 -0500654 maxDiff = diff;
Bill Cox0283c342010-11-30 11:47:50 -0500655 worstPeriod = period;
Bill Coxc17208e2010-11-26 11:09:15 -0500656 }
Bill Cox0cd49c82010-11-03 10:46:22 -0400657 }
Bill Cox0283c342010-11-30 11:47:50 -0500658 *retMinDiff = minDiff/bestPeriod;
659 *retMaxDiff = maxDiff/worstPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400660 return bestPeriod;
661}
662
Bill Coxc17208e2010-11-26 11:09:15 -0500663/* At abrupt ends of voiced words, we can have pitch periods that are better
664 aproximated by the previous pitch period estimate. Try to detect this case. */
665static int prevPeriodBetter(
666 sonicStream stream,
667 int period,
668 int minDiff,
Bill Cox0283c342010-11-30 11:47:50 -0500669 int maxDiff,
670 int preferNewPeriod)
Bill Coxc17208e2010-11-26 11:09:15 -0500671{
Bill Cox0283c342010-11-30 11:47:50 -0500672 if(minDiff == 0) {
673 return 0;
Bill Coxc17208e2010-11-26 11:09:15 -0500674 }
Bill Cox0283c342010-11-30 11:47:50 -0500675 if(preferNewPeriod) {
676 if(maxDiff > minDiff*3) {
677 /* Got a reasonable match this period */
678 return 0;
679 }
680 if(minDiff*2 <= stream->prevMinDiff*3) {
681 /* Mismatch is not that much greater this period */
682 return 0;
683 }
684 } else {
685 if(minDiff <= stream->prevMinDiff) {
686 return 0;
687 }
688 }
689 return 1;
Bill Coxc17208e2010-11-26 11:09:15 -0500690}
691
Bill Cox9bf11b52010-11-03 05:33:09 -0400692/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400693 multiple ways to get a good answer. This version uses AMDF. To improve
694 speed, we down sample by an integer factor get in the 11KHz range, and then
695 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400696static int findPitchPeriod(
697 sonicStream stream,
Bill Cox0283c342010-11-30 11:47:50 -0500698 short *samples,
699 int preferNewPeriod)
Bill Cox9bf11b52010-11-03 05:33:09 -0400700{
701 int minPeriod = stream->minPeriod;
702 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400703 int sampleRate = stream->sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -0500704 int minDiff, maxDiff, retPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400705 int skip = 1;
706 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400707
Bill Cox4e234d72010-12-17 05:44:02 -0500708 if(sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400709 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400710 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500711 if(stream->numChannels == 1 && skip == 1) {
Bill Coxc17208e2010-11-26 11:09:15 -0500712 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
713 } else {
714 downSampleInput(stream, samples, skip);
715 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
716 maxPeriod/skip, &minDiff, &maxDiff);
717 if(skip != 1) {
718 period *= skip;
719 minPeriod = period - (skip << 2);
720 maxPeriod = period + (skip << 2);
721 if(minPeriod < stream->minPeriod) {
722 minPeriod = stream->minPeriod;
723 }
724 if(maxPeriod > stream->maxPeriod) {
725 maxPeriod = stream->maxPeriod;
726 }
727 if(stream->numChannels == 1) {
728 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
729 &minDiff, &maxDiff);
730 } else {
731 downSampleInput(stream, samples, 1);
732 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
733 maxPeriod, &minDiff, &maxDiff);
734 }
735 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500736 }
Bill Cox0283c342010-11-30 11:47:50 -0500737 if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
Bill Coxc17208e2010-11-26 11:09:15 -0500738 retPeriod = stream->prevPeriod;
739 } else {
740 retPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500741 }
Bill Coxc17208e2010-11-26 11:09:15 -0500742 stream->prevMinDiff = minDiff;
743 stream->prevMaxDiff = maxDiff;
744 stream->prevPeriod = period;
745 return retPeriod;
Bill Coxd544fdb2010-11-23 14:13:46 -0500746}
747
Bill Cox68e2aee2010-11-23 19:24:41 -0500748/* Overlap two sound segments, ramp the volume of one down, while ramping the
749 other one from zero up, and add them, storing the result at the output. */
750static void overlapAdd(
751 int numSamples,
752 int numChannels,
753 short *out,
754 short *rampDown,
755 short *rampUp)
756{
Bill Coxd76d2222010-11-24 11:42:29 -0500757 short *o, *u, *d;
Bill Cox68e2aee2010-11-23 19:24:41 -0500758 int i, t;
759
760 for(i = 0; i < numChannels; i++) {
761 o = out + i;
Bill Coxd76d2222010-11-24 11:42:29 -0500762 u = rampUp + i;
763 d = rampDown + i;
Bill Cox68e2aee2010-11-23 19:24:41 -0500764 for(t = 0; t < numSamples; t++) {
Bill Cox4e234d72010-12-17 05:44:02 -0500765#ifdef SONIC_USE_SIN
766 float ratio = sin(t*M_PI/(2*numSamples));
767 *o = *d*(1.0f - ratio) + *u*ratio;
768#else
Bill Coxd76d2222010-11-24 11:42:29 -0500769 *o = (*d*(numSamples - t) + *u*t)/numSamples;
Bill Cox4e234d72010-12-17 05:44:02 -0500770#endif
Bill Cox68e2aee2010-11-23 19:24:41 -0500771 o += numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500772 d += numChannels;
773 u += numChannels;
774 }
775 }
776}
777
778/* Overlap two sound segments, ramp the volume of one down, while ramping the
779 other one from zero up, and add them, storing the result at the output. */
780static void overlapAddWithSeparation(
781 int numSamples,
782 int numChannels,
783 int separation,
784 short *out,
785 short *rampDown,
786 short *rampUp)
787{
788 short *o, *u, *d;
789 int i, t;
790
791 for(i = 0; i < numChannels; i++) {
792 o = out + i;
793 u = rampUp + i;
794 d = rampDown + i;
795 for(t = 0; t < numSamples + separation; t++) {
796 if(t < separation) {
797 *o = *d*(numSamples - t)/numSamples;
798 d += numChannels;
799 } else if(t < numSamples) {
800 *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
801 d += numChannels;
802 u += numChannels;
803 } else {
804 *o = *u*(t - separation)/numSamples;
805 u += numChannels;
806 }
807 o += numChannels;
Bill Cox68e2aee2010-11-23 19:24:41 -0500808 }
809 }
810}
811
812/* Just move the new samples in the output buffer to the pitch bufer */
813static int moveNewSamplesToPitchBuffer(
Bill Coxd544fdb2010-11-23 14:13:46 -0500814 sonicStream stream,
815 int originalNumOutputSamples)
816{
Bill Cox68e2aee2010-11-23 19:24:41 -0500817 int numSamples = stream->numOutputSamples - originalNumOutputSamples;
818 int numChannels = stream->numChannels;
819
820 if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
821 stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
822 stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
823 stream->pitchBufferSize*sizeof(short)*numChannels);
824 if(stream->pitchBuffer == NULL) {
825 return 0;
826 }
827 }
828 memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
829 stream->outputBuffer + originalNumOutputSamples*numChannels,
830 numSamples*sizeof(short)*numChannels);
831 stream->numOutputSamples = originalNumOutputSamples;
832 stream->numPitchSamples += numSamples;
833 return 1;
834}
835
836/* Remove processed samples from the pitch buffer. */
837static void removePitchSamples(
838 sonicStream stream,
839 int numSamples)
840{
841 int numChannels = stream->numChannels;
842 short *source = stream->pitchBuffer + numSamples*numChannels;
843
844 if(numSamples == 0) {
845 return;
846 }
847 if(numSamples != stream->numPitchSamples) {
848 memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
849 numSamples)*sizeof(short)*numChannels);
850 }
851 stream->numPitchSamples -= numSamples;
852}
853
854/* Change the pitch. The latency this introduces could be reduced by looking at
855 past samples to determine pitch, rather than future. */
856static int adjustPitch(
857 sonicStream stream,
858 int originalNumOutputSamples)
859{
860 float pitch = stream->pitch;
861 int numChannels = stream->numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500862 int period, newPeriod, separation;
Bill Cox68e2aee2010-11-23 19:24:41 -0500863 int position = 0;
864 short *out, *rampDown, *rampUp;
865
866 if(stream->numOutputSamples == originalNumOutputSamples) {
867 return 1;
868 }
869 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
870 return 0;
871 }
872 while(stream->numPitchSamples - position >= stream->maxRequired) {
Bill Cox0283c342010-11-30 11:47:50 -0500873 period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
Bill Cox68e2aee2010-11-23 19:24:41 -0500874 newPeriod = period/pitch;
Bill Coxd76d2222010-11-24 11:42:29 -0500875 if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500876 return 0;
877 }
Bill Coxd76d2222010-11-24 11:42:29 -0500878 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
879 if(pitch >= 1.0f) {
880 rampDown = stream->pitchBuffer + position*numChannels;
881 rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
882 overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
883 } else {
884 rampDown = stream->pitchBuffer + position*numChannels;
885 rampUp = stream->pitchBuffer + position*numChannels;
886 separation = newPeriod - period;
887 overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
Bill Cox68e2aee2010-11-23 19:24:41 -0500888 }
Bill Coxd76d2222010-11-24 11:42:29 -0500889 stream->numOutputSamples += newPeriod;
Bill Cox68e2aee2010-11-23 19:24:41 -0500890 position += period;
891 }
892 removePitchSamples(stream, position);
893 return 1;
Bill Cox9bf11b52010-11-03 05:33:09 -0400894}
895
Bill Cox3276bb02011-01-11 07:39:26 -0500896/* Interpolate the new output sample. */
897static short interpolate(
898 sonicStream stream,
899 short *in,
900 int oldSampleRate,
901 int newSampleRate)
902{
903 short left = *in;
904 short right = in[stream->numChannels];
905 int position = stream->newRatePosition*oldSampleRate;
906 int leftPosition = stream->oldRatePosition*newSampleRate;
907 int rightPosition = (stream->oldRatePosition + 1)*newSampleRate;
908 int ratio = rightPosition - position;
909 int width = rightPosition - leftPosition;
910
911 return (ratio*left + (width - ratio)*right)/width;
912}
913
914/* Change the rate. */
915static int adjustRate(
916 sonicStream stream,
917 float rate,
918 int originalNumOutputSamples)
919{
920 int newSampleRate = stream->sampleRate/rate;
921 int oldSampleRate = stream->sampleRate;
922 int numChannels = stream->numChannels;
923 int position = 0;
924 short *in, *out;
925 int i;
926
927 /* Set these values to help with the integer math */
928 while(newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
929 newSampleRate >>= 1;
930 oldSampleRate >>= 1;
931 }
932 if(stream->numOutputSamples == originalNumOutputSamples) {
933 return 1;
934 }
935 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
936 return 0;
937 }
938 /* Leave at least one pitch sample in the buffer */
939 for(position = 0; position < stream->numPitchSamples - 1; position++) {
940 while((stream->oldRatePosition + 1)*newSampleRate >
941 stream->newRatePosition*oldSampleRate) {
942 if(!enlargeOutputBufferIfNeeded(stream, 1)) {
943 return 0;
944 }
945 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
946 in = stream->pitchBuffer + position;
947 for(i = 0; i < numChannels; i++) {
948 *out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
949 in++;
950 }
951 stream->newRatePosition++;
952 stream->numOutputSamples++;
953 }
954 stream->oldRatePosition++;
955 if(stream->oldRatePosition == oldSampleRate) {
956 stream->oldRatePosition = 0;
957 if(stream->newRatePosition != newSampleRate) {
958 fprintf(stderr,
959 "Assertion failed: stream->newRatePosition != newSampleRate\n");
960 exit(1);
961 }
962 stream->newRatePosition = 0;
963 }
964 }
965 removePitchSamples(stream, position);
966 return 1;
967}
968
969
Bill Cox59e65122010-11-03 10:06:29 -0400970/* Skip over a pitch period, and copy period/speed samples to the output */
971static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400972 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500973 short *samples,
974 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400975 int period)
976{
Bill Cox68e2aee2010-11-23 19:24:41 -0500977 long newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500978 int numChannels = stream->numChannels;
Bill Cox9bf11b52010-11-03 05:33:09 -0400979
Bill Cox6a1bbb12010-11-19 11:14:28 -0500980 if(speed >= 2.0f) {
981 newSamples = period/(speed - 1.0f);
982 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400983 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500984 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400985 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400986 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
987 return 0;
988 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500989 overlapAdd(newSamples, numChannels, stream->outputBuffer +
990 stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
Bill Cox9bf11b52010-11-03 05:33:09 -0400991 stream->numOutputSamples += newSamples;
992 return newSamples;
993}
994
Bill Cox59e65122010-11-03 10:06:29 -0400995/* Insert a pitch period, and determine how much input to copy directly. */
996static int insertPitchPeriod(
997 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500998 short *samples,
999 float speed,
Bill Cox59e65122010-11-03 10:06:29 -04001000 int period)
1001{
Bill Cox68e2aee2010-11-23 19:24:41 -05001002 long newSamples;
1003 short *out;
Bill Cox1a299bb2010-11-19 15:07:17 -05001004 int numChannels = stream->numChannels;
Bill Cox59e65122010-11-03 10:06:29 -04001005
Bill Cox6a1bbb12010-11-19 11:14:28 -05001006 if(speed < 0.5f) {
1007 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -04001008 } else {
1009 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -05001010 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -04001011 }
1012 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
1013 return 0;
1014 }
Bill Cox1a299bb2010-11-19 15:07:17 -05001015 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
1016 memcpy(out, samples, period*sizeof(short)*numChannels);
Bill Cox68e2aee2010-11-23 19:24:41 -05001017 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
1018 overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
Bill Cox59e65122010-11-03 10:06:29 -04001019 stream->numOutputSamples += period + newSamples;
1020 return newSamples;
1021}
1022
Bill Cox9bf11b52010-11-03 05:33:09 -04001023/* Resample as many pitch periods as we have buffered on the input. Return 0 if
Bill Coxd544fdb2010-11-23 14:13:46 -05001024 we fail to resize an input or output buffer. Also scale the output by the volume. */
1025static int changeSpeed(
1026 sonicStream stream,
1027 float speed)
Bill Cox9bf11b52010-11-03 05:33:09 -04001028{
Bill Cox1a299bb2010-11-19 15:07:17 -05001029 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -05001030 int numSamples = stream->numInputSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -04001031 int position = 0, period, newSamples;
1032 int maxRequired = stream->maxRequired;
1033
Bill Cox9bf11b52010-11-03 05:33:09 -04001034 if(stream->numInputSamples < maxRequired) {
1035 return 1;
1036 }
Bill Cox9bf11b52010-11-03 05:33:09 -04001037 do {
1038 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -04001039 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -04001040 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -04001041 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -05001042 samples = stream->inputBuffer + position*stream->numChannels;
Bill Cox0283c342010-11-30 11:47:50 -05001043 period = findPitchPeriod(stream, samples, 1);
Bill Cox59e65122010-11-03 10:06:29 -04001044 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -05001045 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -04001046 position += period + newSamples;
1047 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -05001048 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -04001049 position += newSamples;
1050 }
Bill Cox9bf11b52010-11-03 05:33:09 -04001051 }
1052 if(newSamples == 0) {
1053 return 0; /* Failed to resize output buffer */
1054 }
Bill Cox9bf11b52010-11-03 05:33:09 -04001055 } while(position + maxRequired <= numSamples);
1056 removeInputSamples(stream, position);
1057 return 1;
1058}
Bill Cox0c4c0602010-11-08 11:46:30 -05001059
Bill Coxd544fdb2010-11-23 14:13:46 -05001060/* Resample as many pitch periods as we have buffered on the input. Return 0 if
1061 we fail to resize an input or output buffer. Also scale the output by the volume. */
1062static int processStreamInput(
1063 sonicStream stream)
1064{
1065 int originalNumOutputSamples = stream->numOutputSamples;
1066 float speed = stream->speed/stream->pitch;
Bill Cox3276bb02011-01-11 07:39:26 -05001067 float rate = stream->rate;
Bill Coxd544fdb2010-11-23 14:13:46 -05001068
Bill Cox3276bb02011-01-11 07:39:26 -05001069 if(!stream->useChordPitch) {
1070 rate *= stream->pitch;
1071 }
Bill Coxd544fdb2010-11-23 14:13:46 -05001072 if(speed > 1.00001 || speed < 0.99999) {
1073 changeSpeed(stream, speed);
1074 } else {
Bill Cox68e2aee2010-11-23 19:24:41 -05001075 if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
Bill Coxd544fdb2010-11-23 14:13:46 -05001076 return 0;
1077 }
1078 stream->numInputSamples = 0;
1079 }
Bill Cox3276bb02011-01-11 07:39:26 -05001080 if(stream->useChordPitch) {
1081 if(stream->pitch != 1.0f) {
1082 if(!adjustPitch(stream, originalNumOutputSamples)) {
1083 return 0;
1084 }
1085 }
1086 } else if(rate != 1.0f) {
1087 if(!adjustRate(stream, rate, originalNumOutputSamples)) {
Bill Cox68e2aee2010-11-23 19:24:41 -05001088 return 0;
1089 }
Bill Coxd544fdb2010-11-23 14:13:46 -05001090 }
1091 if(stream->volume != 1.0f) {
1092 /* Adjust output volume. */
1093 scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
1094 (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
1095 stream->volume);
1096 }
1097 return 1;
1098}
1099
Bill Cox0c4c0602010-11-08 11:46:30 -05001100/* Write floating point data to the input buffer and process it. */
1101int sonicWriteFloatToStream(
1102 sonicStream stream,
1103 float *samples,
1104 int numSamples)
1105{
Bill Cox0c4c0602010-11-08 11:46:30 -05001106 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
1107 return 0;
1108 }
1109 return processStreamInput(stream);
1110}
1111
1112/* Simple wrapper around sonicWriteFloatToStream that does the short to float
1113 conversion for you. */
1114int sonicWriteShortToStream(
1115 sonicStream stream,
1116 short *samples,
1117 int numSamples)
1118{
Bill Cox0c4c0602010-11-08 11:46:30 -05001119 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
1120 return 0;
1121 }
1122 return processStreamInput(stream);
1123}
1124
Bill Cox8a23d2f2010-11-16 18:49:36 -05001125/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
1126 conversion for you. */
1127int sonicWriteUnsignedCharToStream(
1128 sonicStream stream,
1129 unsigned char *samples,
1130 int numSamples)
1131{
Bill Cox8a23d2f2010-11-16 18:49:36 -05001132 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
1133 return 0;
1134 }
1135 return processStreamInput(stream);
1136}
1137
Bill Cox036d7322010-11-09 09:29:24 -05001138/* This is a non-stream oriented interface to just change the speed of a sound sample */
1139int sonicChangeFloatSpeed(
1140 float *samples,
1141 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -05001142 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -05001143 float pitch,
Bill Coxd0380df2011-01-11 08:59:52 -05001144 float rate,
Bill Coxd544fdb2010-11-23 14:13:46 -05001145 float volume,
Bill Coxd0380df2011-01-11 08:59:52 -05001146 int useChordPitch,
Bill Cox1a299bb2010-11-19 15:07:17 -05001147 int sampleRate,
1148 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -05001149{
Bill Coxd544fdb2010-11-23 14:13:46 -05001150 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -05001151
Bill Coxd544fdb2010-11-23 14:13:46 -05001152 sonicSetSpeed(stream, speed);
1153 sonicSetPitch(stream, pitch);
Bill Coxd0380df2011-01-11 08:59:52 -05001154 sonicSetRate(stream, rate);
Bill Coxd544fdb2010-11-23 14:13:46 -05001155 sonicSetVolume(stream, volume);
Bill Coxd0380df2011-01-11 08:59:52 -05001156 sonicSetChordPitch(stream, useChordPitch);
Bill Cox036d7322010-11-09 09:29:24 -05001157 sonicWriteFloatToStream(stream, samples, numSamples);
1158 sonicFlushStream(stream);
1159 numSamples = sonicSamplesAvailable(stream);
1160 sonicReadFloatFromStream(stream, samples, numSamples);
1161 sonicDestroyStream(stream);
1162 return numSamples;
1163}
1164
1165/* This is a non-stream oriented interface to just change the speed of a sound sample */
1166int sonicChangeShortSpeed(
1167 short *samples,
1168 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -05001169 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -05001170 float pitch,
Bill Coxd0380df2011-01-11 08:59:52 -05001171 float rate,
Bill Coxd544fdb2010-11-23 14:13:46 -05001172 float volume,
Bill Coxd0380df2011-01-11 08:59:52 -05001173 int useChordPitch,
Bill Cox1a299bb2010-11-19 15:07:17 -05001174 int sampleRate,
1175 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -05001176{
Bill Coxd544fdb2010-11-23 14:13:46 -05001177 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -05001178
Bill Coxd544fdb2010-11-23 14:13:46 -05001179 sonicSetSpeed(stream, speed);
1180 sonicSetPitch(stream, pitch);
Bill Coxd0380df2011-01-11 08:59:52 -05001181 sonicSetRate(stream, rate);
Bill Coxd544fdb2010-11-23 14:13:46 -05001182 sonicSetVolume(stream, volume);
Bill Coxd0380df2011-01-11 08:59:52 -05001183 sonicSetChordPitch(stream, useChordPitch);
Bill Cox036d7322010-11-09 09:29:24 -05001184 sonicWriteShortToStream(stream, samples, numSamples);
1185 sonicFlushStream(stream);
1186 numSamples = sonicSamplesAvailable(stream);
1187 sonicReadShortFromStream(stream, samples, numSamples);
1188 sonicDestroyStream(stream);
1189 return numSamples;
1190}