blob: beac399b1a0976f4e471c15c356f3ddea59a7473 [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 Coxaf9a6242010-11-08 09:32:27 -0500195/* Get the sample rate of the stream. */
196int sonicGetSampleRate(
197 sonicStream stream)
198{
199 return stream->sampleRate;
200}
201
Bill Cox527b4e82010-11-24 17:42:58 -0500202/* Get the number of channels. */
203int sonicGetNumChannels(
204 sonicStream stream)
205{
206 return stream->numChannels;
207}
208
Bill Coxca02d872010-11-02 15:10:52 -0400209/* Destroy the sonic stream. */
210void sonicDestroyStream(
211 sonicStream stream)
212{
213 if(stream->inputBuffer != NULL) {
214 free(stream->inputBuffer);
215 }
216 if(stream->outputBuffer != NULL) {
217 free(stream->outputBuffer);
218 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500219 if(stream->pitchBuffer != NULL) {
220 free(stream->pitchBuffer);
221 }
222 if(stream->downSampleBuffer != NULL) {
223 free(stream->downSampleBuffer);
224 }
Bill Coxca02d872010-11-02 15:10:52 -0400225 free(stream);
226}
227
228/* Create a sonic stream. Return NULL only if we are out of memory and cannot
229 allocate the stream. */
230sonicStream sonicCreateStream(
Bill Cox1a299bb2010-11-19 15:07:17 -0500231 int sampleRate,
232 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400233{
234 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
235 int minPeriod = sampleRate/SONIC_MAX_PITCH;
236 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
237 int maxRequired = 2*maxPeriod;
238
239 if(stream == NULL) {
240 return NULL;
241 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500242 stream->inputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500243 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400244 if(stream->inputBuffer == NULL) {
245 sonicDestroyStream(stream);
246 return NULL;
247 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500248 stream->outputBufferSize = maxRequired;
Bill Cox1a299bb2010-11-19 15:07:17 -0500249 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400250 if(stream->outputBuffer == NULL) {
251 sonicDestroyStream(stream);
252 return NULL;
253 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500254 stream->pitchBufferSize = maxRequired;
255 stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
256 if(stream->pitchBuffer == NULL) {
257 sonicDestroyStream(stream);
258 return NULL;
259 }
260 stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
261 stream->speed = 1.0f;
262 stream->pitch = 1.0f;
263 stream->volume = 1.0f;
Bill Cox3276bb02011-01-11 07:39:26 -0500264 stream->rate = 1.0f;
265 stream->oldRatePosition = 0;
266 stream->newRatePosition = 0;
267 stream->useChordPitch = 0;
Bill Coxc978c392010-12-17 05:04:06 -0500268 stream->quality = 0;
Bill Coxca02d872010-11-02 15:10:52 -0400269 stream->sampleRate = sampleRate;
Bill Cox1a299bb2010-11-19 15:07:17 -0500270 stream->numChannels = numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400271 stream->minPeriod = minPeriod;
272 stream->maxPeriod = maxPeriod;
273 stream->maxRequired = maxRequired;
Bill Coxca02d872010-11-02 15:10:52 -0400274 return stream;
275}
276
Bill Coxca02d872010-11-02 15:10:52 -0400277/* Enlarge the output buffer if needed. */
278static int enlargeOutputBufferIfNeeded(
279 sonicStream stream,
280 int numSamples)
281{
282 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
283 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500284 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500285 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400286 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400287 return 0;
288 }
289 }
290 return 1;
291}
292
Bill Coxca02d872010-11-02 15:10:52 -0400293/* Enlarge the input buffer if needed. */
294static int enlargeInputBufferIfNeeded(
295 sonicStream stream,
296 int numSamples)
297{
298 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
299 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500300 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500301 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400302 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400303 return 0;
304 }
305 }
306 return 1;
307}
308
309/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500310static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400311 sonicStream stream,
312 float *samples,
313 int numSamples)
314{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500315 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500316 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500317
Bill Coxca02d872010-11-02 15:10:52 -0400318 if(numSamples == 0) {
319 return 1;
320 }
321 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
322 return 0;
323 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500324 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500325 while(count--) {
326 *buffer++ = (*samples++)*32767.0f;
327 }
Bill Cox14efa442010-11-02 15:43:58 -0400328 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400329 return 1;
330}
331
Bill Cox0c4c0602010-11-08 11:46:30 -0500332/* Add the input samples to the input buffer. */
333static int addShortSamplesToInputBuffer(
334 sonicStream stream,
335 short *samples,
336 int numSamples)
337{
Bill Cox0c4c0602010-11-08 11:46:30 -0500338 if(numSamples == 0) {
339 return 1;
340 }
341 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
342 return 0;
343 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500344 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
345 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500346 stream->numInputSamples += numSamples;
347 return 1;
348}
349
Bill Cox8a23d2f2010-11-16 18:49:36 -0500350/* Add the input samples to the input buffer. */
351static int addUnsignedCharSamplesToInputBuffer(
352 sonicStream stream,
353 unsigned char *samples,
354 int numSamples)
355{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500356 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500357 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500358
359 if(numSamples == 0) {
360 return 1;
361 }
362 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
363 return 0;
364 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500365 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500366 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500367 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400368 }
369 stream->numInputSamples += numSamples;
370 return 1;
371}
372
373/* Remove input samples that we have already processed. */
374static void removeInputSamples(
375 sonicStream stream,
376 int position)
377{
378 int remainingSamples = stream->numInputSamples - position;
379
380 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500381 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
382 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400383 }
384 stream->numInputSamples = remainingSamples;
385}
386
Bill Cox59e65122010-11-03 10:06:29 -0400387/* Just copy from the array to the output buffer */
Bill Cox68e2aee2010-11-23 19:24:41 -0500388static int copyToOutput(
Bill Cox0c4c0602010-11-08 11:46:30 -0500389 sonicStream stream,
390 short *samples,
391 int numSamples)
392{
Bill Cox0c4c0602010-11-08 11:46:30 -0500393 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
394 return 0;
395 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500396 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
397 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500398 stream->numOutputSamples += numSamples;
399 return numSamples;
400}
401
Bill Cox882fb1d2010-11-02 16:27:20 -0400402/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
403 resize the output buffer. Otherwise, return numSamples */
404static int copyInputToOutput(
405 sonicStream stream,
406 int position)
407{
408 int numSamples = stream->remainingInputToCopy;
409
410 if(numSamples > stream->maxRequired) {
411 numSamples = stream->maxRequired;
412 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500413 if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
Bill Cox1a299bb2010-11-19 15:07:17 -0500414 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400415 return 0;
416 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400417 stream->remainingInputToCopy -= numSamples;
418 return numSamples;
419}
420
Bill Coxca02d872010-11-02 15:10:52 -0400421/* Read data out of the stream. Sometimes no data will be available, and zero
422 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500423int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400424 sonicStream stream,
425 float *samples,
426 int maxSamples)
427{
428 int numSamples = stream->numOutputSamples;
429 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500430 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500431 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400432
433 if(numSamples == 0) {
434 return 0;
435 }
436 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400437 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400438 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400439 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500440 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500441 count = numSamples*stream->numChannels;
442 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500443 *samples++ = (*buffer++)/32767.0f;
444 }
Bill Coxca02d872010-11-02 15:10:52 -0400445 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500446 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
447 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400448 }
449 stream->numOutputSamples = remainingSamples;
450 return numSamples;
451}
452
Bill Cox0c4c0602010-11-08 11:46:30 -0500453/* Read short data out of the stream. Sometimes no data will be available, and zero
454 is returned, which is not an error condition. */
455int sonicReadShortFromStream(
456 sonicStream stream,
457 short *samples,
458 int maxSamples)
459{
460 int numSamples = stream->numOutputSamples;
461 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500462
463 if(numSamples == 0) {
464 return 0;
465 }
466 if(numSamples > maxSamples) {
467 remainingSamples = numSamples - maxSamples;
468 numSamples = maxSamples;
469 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500470 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500471 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500472 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
473 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500474 }
475 stream->numOutputSamples = remainingSamples;
476 return numSamples;
477}
478
Bill Cox8a23d2f2010-11-16 18:49:36 -0500479/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
480 is returned, which is not an error condition. */
481int sonicReadUnsignedCharFromStream(
482 sonicStream stream,
483 unsigned char *samples,
484 int maxSamples)
485{
486 int numSamples = stream->numOutputSamples;
487 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500488 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500489 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500490
491 if(numSamples == 0) {
492 return 0;
493 }
494 if(numSamples > maxSamples) {
495 remainingSamples = numSamples - maxSamples;
496 numSamples = maxSamples;
497 }
498 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500499 count = numSamples*stream->numChannels;
500 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500501 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400502 }
503 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500504 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
505 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400506 }
507 stream->numOutputSamples = remainingSamples;
508 return numSamples;
509}
510
511/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500512 has. No extra delay will be added to the output, but flushing in the middle of
513 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400514int sonicFlushStream(
515 sonicStream stream)
516{
517 int maxRequired = stream->maxRequired;
Bill Cox0283c342010-11-30 11:47:50 -0500518 int remainingSamples = stream->numInputSamples;
519 float speed = stream->speed/stream->pitch;
Bill Cox3276bb02011-01-11 07:39:26 -0500520 float rate = stream->rate*stream->pitch;
Bill Cox0283c342010-11-30 11:47:50 -0500521 int expectedOutputSamples = stream->numOutputSamples +
Bill Cox3276bb02011-01-11 07:39:26 -0500522 (int)((remainingSamples/speed + stream->numPitchSamples)/rate + 0.5f);
Bill Coxca02d872010-11-02 15:10:52 -0400523
Bill Cox0283c342010-11-30 11:47:50 -0500524 /* Add enough silence to flush both input and pitch buffers. */
525 if(!enlargeInputBufferIfNeeded(stream, remainingSamples + 2*maxRequired)) {
526 return 0;
Bill Coxca02d872010-11-02 15:10:52 -0400527 }
Bill Cox0283c342010-11-30 11:47:50 -0500528 memset(stream->inputBuffer + remainingSamples*stream->numChannels, 0,
529 2*maxRequired*sizeof(short)*stream->numChannels);
530 stream->numInputSamples += 2*maxRequired;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500531 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500532 return 0;
533 }
534 /* Throw away any extra samples we generated due to the silence we added */
Bill Cox0283c342010-11-30 11:47:50 -0500535 if(stream->numOutputSamples > expectedOutputSamples) {
536 stream->numOutputSamples = expectedOutputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500537 }
Bill Cox0283c342010-11-30 11:47:50 -0500538 /* Empty input and pitch buffers */
539 stream->numInputSamples = 0;
540 stream->remainingInputToCopy = 0;
541 stream->numPitchSamples = 0;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500542 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400543}
544
545/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400546int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400547 sonicStream stream)
548{
549 return stream->numOutputSamples;
550}
Bill Cox9bf11b52010-11-03 05:33:09 -0400551
Bill Coxd544fdb2010-11-23 14:13:46 -0500552/* If skip is greater than one, average skip samples togther and write them to
553 the down-sample buffer. If numChannels is greater than one, mix the channels
554 together as we down sample. */
555static void downSampleInput(
556 sonicStream stream,
557 short *samples,
558 int skip)
559{
560 int numSamples = stream->maxRequired/skip;
561 int samplesPerValue = stream->numChannels*skip;
562 int i, j;
563 int value;
564 short *downSamples = stream->downSampleBuffer;
565
566 for(i = 0; i < numSamples; i++) {
567 value = 0;
568 for(j = 0; j < samplesPerValue; j++) {
569 value += *samples++;
570 }
571 value /= samplesPerValue;
572 *downSamples++ = value;
573 }
574}
575
Bill Cox1a299bb2010-11-19 15:07:17 -0500576/* Find the best frequency match in the range, and given a sample skip multiple.
577 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400578static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500579 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400580 int minPeriod,
Bill Coxc17208e2010-11-26 11:09:15 -0500581 int maxPeriod,
582 int *retMinDiff,
583 int *retMaxDiff)
Bill Cox0cd49c82010-11-03 10:46:22 -0400584{
Bill Cox0283c342010-11-30 11:47:50 -0500585 int period, bestPeriod = 0, worstPeriod = 255;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500586 short *s, *p, sVal, pVal;
Bill Coxc17208e2010-11-26 11:09:15 -0500587 unsigned long diff, minDiff = 1, maxDiff = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500588 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400589
Bill Coxd544fdb2010-11-23 14:13:46 -0500590 for(period = minPeriod; period <= maxPeriod; period++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500591 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400592 s = samples;
Bill Coxd544fdb2010-11-23 14:13:46 -0500593 p = samples + period;
594 for(i = 0; i < period; i++) {
595 sVal = *s++;
596 pVal = *p++;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500597 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
598 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400599 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500600 /* Note that the highest number of samples we add into diff will be less
601 than 256, since we skip samples. Thus, diff is a 24 bit number, and
602 we can safely multiply by numSamples without overflow */
Bill Coxc17208e2010-11-26 11:09:15 -0500603 if(diff*bestPeriod < minDiff*period) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400604 minDiff = diff;
605 bestPeriod = period;
606 }
Bill Cox0283c342010-11-30 11:47:50 -0500607 if(diff*worstPeriod > maxDiff*period) {
Bill Coxc17208e2010-11-26 11:09:15 -0500608 maxDiff = diff;
Bill Cox0283c342010-11-30 11:47:50 -0500609 worstPeriod = period;
Bill Coxc17208e2010-11-26 11:09:15 -0500610 }
Bill Cox0cd49c82010-11-03 10:46:22 -0400611 }
Bill Cox0283c342010-11-30 11:47:50 -0500612 *retMinDiff = minDiff/bestPeriod;
613 *retMaxDiff = maxDiff/worstPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400614 return bestPeriod;
615}
616
Bill Coxc17208e2010-11-26 11:09:15 -0500617/* At abrupt ends of voiced words, we can have pitch periods that are better
618 aproximated by the previous pitch period estimate. Try to detect this case. */
619static int prevPeriodBetter(
620 sonicStream stream,
621 int period,
622 int minDiff,
Bill Cox0283c342010-11-30 11:47:50 -0500623 int maxDiff,
624 int preferNewPeriod)
Bill Coxc17208e2010-11-26 11:09:15 -0500625{
Bill Cox0283c342010-11-30 11:47:50 -0500626 if(minDiff == 0) {
627 return 0;
Bill Coxc17208e2010-11-26 11:09:15 -0500628 }
Bill Cox0283c342010-11-30 11:47:50 -0500629 if(preferNewPeriod) {
630 if(maxDiff > minDiff*3) {
631 /* Got a reasonable match this period */
632 return 0;
633 }
634 if(minDiff*2 <= stream->prevMinDiff*3) {
635 /* Mismatch is not that much greater this period */
636 return 0;
637 }
638 } else {
639 if(minDiff <= stream->prevMinDiff) {
640 return 0;
641 }
642 }
643 return 1;
Bill Coxc17208e2010-11-26 11:09:15 -0500644}
645
Bill Cox9bf11b52010-11-03 05:33:09 -0400646/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400647 multiple ways to get a good answer. This version uses AMDF. To improve
648 speed, we down sample by an integer factor get in the 11KHz range, and then
649 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400650static int findPitchPeriod(
651 sonicStream stream,
Bill Cox0283c342010-11-30 11:47:50 -0500652 short *samples,
653 int preferNewPeriod)
Bill Cox9bf11b52010-11-03 05:33:09 -0400654{
655 int minPeriod = stream->minPeriod;
656 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400657 int sampleRate = stream->sampleRate;
Bill Coxc17208e2010-11-26 11:09:15 -0500658 int minDiff, maxDiff, retPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400659 int skip = 1;
660 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400661
Bill Cox4e234d72010-12-17 05:44:02 -0500662 if(sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400663 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400664 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500665 if(stream->numChannels == 1 && skip == 1) {
Bill Coxc17208e2010-11-26 11:09:15 -0500666 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
667 } else {
668 downSampleInput(stream, samples, skip);
669 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
670 maxPeriod/skip, &minDiff, &maxDiff);
671 if(skip != 1) {
672 period *= skip;
673 minPeriod = period - (skip << 2);
674 maxPeriod = period + (skip << 2);
675 if(minPeriod < stream->minPeriod) {
676 minPeriod = stream->minPeriod;
677 }
678 if(maxPeriod > stream->maxPeriod) {
679 maxPeriod = stream->maxPeriod;
680 }
681 if(stream->numChannels == 1) {
682 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
683 &minDiff, &maxDiff);
684 } else {
685 downSampleInput(stream, samples, 1);
686 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
687 maxPeriod, &minDiff, &maxDiff);
688 }
689 }
Bill Coxd544fdb2010-11-23 14:13:46 -0500690 }
Bill Cox0283c342010-11-30 11:47:50 -0500691 if(prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
Bill Coxc17208e2010-11-26 11:09:15 -0500692 retPeriod = stream->prevPeriod;
693 } else {
694 retPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500695 }
Bill Coxc17208e2010-11-26 11:09:15 -0500696 stream->prevMinDiff = minDiff;
697 stream->prevMaxDiff = maxDiff;
698 stream->prevPeriod = period;
699 return retPeriod;
Bill Coxd544fdb2010-11-23 14:13:46 -0500700}
701
Bill Cox68e2aee2010-11-23 19:24:41 -0500702/* Overlap two sound segments, ramp the volume of one down, while ramping the
703 other one from zero up, and add them, storing the result at the output. */
704static void overlapAdd(
705 int numSamples,
706 int numChannels,
707 short *out,
708 short *rampDown,
709 short *rampUp)
710{
Bill Coxd76d2222010-11-24 11:42:29 -0500711 short *o, *u, *d;
Bill Cox68e2aee2010-11-23 19:24:41 -0500712 int i, t;
713
714 for(i = 0; i < numChannels; i++) {
715 o = out + i;
Bill Coxd76d2222010-11-24 11:42:29 -0500716 u = rampUp + i;
717 d = rampDown + i;
Bill Cox68e2aee2010-11-23 19:24:41 -0500718 for(t = 0; t < numSamples; t++) {
Bill Cox4e234d72010-12-17 05:44:02 -0500719#ifdef SONIC_USE_SIN
720 float ratio = sin(t*M_PI/(2*numSamples));
721 *o = *d*(1.0f - ratio) + *u*ratio;
722#else
Bill Coxd76d2222010-11-24 11:42:29 -0500723 *o = (*d*(numSamples - t) + *u*t)/numSamples;
Bill Cox4e234d72010-12-17 05:44:02 -0500724#endif
Bill Cox68e2aee2010-11-23 19:24:41 -0500725 o += numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500726 d += numChannels;
727 u += numChannels;
728 }
729 }
730}
731
732/* Overlap two sound segments, ramp the volume of one down, while ramping the
733 other one from zero up, and add them, storing the result at the output. */
734static void overlapAddWithSeparation(
735 int numSamples,
736 int numChannels,
737 int separation,
738 short *out,
739 short *rampDown,
740 short *rampUp)
741{
742 short *o, *u, *d;
743 int i, t;
744
745 for(i = 0; i < numChannels; i++) {
746 o = out + i;
747 u = rampUp + i;
748 d = rampDown + i;
749 for(t = 0; t < numSamples + separation; t++) {
750 if(t < separation) {
751 *o = *d*(numSamples - t)/numSamples;
752 d += numChannels;
753 } else if(t < numSamples) {
754 *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
755 d += numChannels;
756 u += numChannels;
757 } else {
758 *o = *u*(t - separation)/numSamples;
759 u += numChannels;
760 }
761 o += numChannels;
Bill Cox68e2aee2010-11-23 19:24:41 -0500762 }
763 }
764}
765
766/* Just move the new samples in the output buffer to the pitch bufer */
767static int moveNewSamplesToPitchBuffer(
Bill Coxd544fdb2010-11-23 14:13:46 -0500768 sonicStream stream,
769 int originalNumOutputSamples)
770{
Bill Cox68e2aee2010-11-23 19:24:41 -0500771 int numSamples = stream->numOutputSamples - originalNumOutputSamples;
772 int numChannels = stream->numChannels;
773
774 if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
775 stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
776 stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
777 stream->pitchBufferSize*sizeof(short)*numChannels);
778 if(stream->pitchBuffer == NULL) {
779 return 0;
780 }
781 }
782 memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
783 stream->outputBuffer + originalNumOutputSamples*numChannels,
784 numSamples*sizeof(short)*numChannels);
785 stream->numOutputSamples = originalNumOutputSamples;
786 stream->numPitchSamples += numSamples;
787 return 1;
788}
789
790/* Remove processed samples from the pitch buffer. */
791static void removePitchSamples(
792 sonicStream stream,
793 int numSamples)
794{
795 int numChannels = stream->numChannels;
796 short *source = stream->pitchBuffer + numSamples*numChannels;
797
798 if(numSamples == 0) {
799 return;
800 }
801 if(numSamples != stream->numPitchSamples) {
802 memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
803 numSamples)*sizeof(short)*numChannels);
804 }
805 stream->numPitchSamples -= numSamples;
806}
807
808/* Change the pitch. The latency this introduces could be reduced by looking at
809 past samples to determine pitch, rather than future. */
810static int adjustPitch(
811 sonicStream stream,
812 int originalNumOutputSamples)
813{
814 float pitch = stream->pitch;
815 int numChannels = stream->numChannels;
Bill Coxd76d2222010-11-24 11:42:29 -0500816 int period, newPeriod, separation;
Bill Cox68e2aee2010-11-23 19:24:41 -0500817 int position = 0;
818 short *out, *rampDown, *rampUp;
819
820 if(stream->numOutputSamples == originalNumOutputSamples) {
821 return 1;
822 }
823 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
824 return 0;
825 }
826 while(stream->numPitchSamples - position >= stream->maxRequired) {
Bill Cox0283c342010-11-30 11:47:50 -0500827 period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels, 0);
Bill Cox68e2aee2010-11-23 19:24:41 -0500828 newPeriod = period/pitch;
Bill Coxd76d2222010-11-24 11:42:29 -0500829 if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
Bill Cox68e2aee2010-11-23 19:24:41 -0500830 return 0;
831 }
Bill Coxd76d2222010-11-24 11:42:29 -0500832 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
833 if(pitch >= 1.0f) {
834 rampDown = stream->pitchBuffer + position*numChannels;
835 rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
836 overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
837 } else {
838 rampDown = stream->pitchBuffer + position*numChannels;
839 rampUp = stream->pitchBuffer + position*numChannels;
840 separation = newPeriod - period;
841 overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
Bill Cox68e2aee2010-11-23 19:24:41 -0500842 }
Bill Coxd76d2222010-11-24 11:42:29 -0500843 stream->numOutputSamples += newPeriod;
Bill Cox68e2aee2010-11-23 19:24:41 -0500844 position += period;
845 }
846 removePitchSamples(stream, position);
847 return 1;
Bill Cox9bf11b52010-11-03 05:33:09 -0400848}
849
Bill Cox3276bb02011-01-11 07:39:26 -0500850/* Interpolate the new output sample. */
851static short interpolate(
852 sonicStream stream,
853 short *in,
854 int oldSampleRate,
855 int newSampleRate)
856{
857 short left = *in;
858 short right = in[stream->numChannels];
859 int position = stream->newRatePosition*oldSampleRate;
860 int leftPosition = stream->oldRatePosition*newSampleRate;
861 int rightPosition = (stream->oldRatePosition + 1)*newSampleRate;
862 int ratio = rightPosition - position;
863 int width = rightPosition - leftPosition;
864
865 return (ratio*left + (width - ratio)*right)/width;
866}
867
868/* Change the rate. */
869static int adjustRate(
870 sonicStream stream,
871 float rate,
872 int originalNumOutputSamples)
873{
874 int newSampleRate = stream->sampleRate/rate;
875 int oldSampleRate = stream->sampleRate;
876 int numChannels = stream->numChannels;
877 int position = 0;
878 short *in, *out;
879 int i;
880
881 /* Set these values to help with the integer math */
882 while(newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
883 newSampleRate >>= 1;
884 oldSampleRate >>= 1;
885 }
886 if(stream->numOutputSamples == originalNumOutputSamples) {
887 return 1;
888 }
889 if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
890 return 0;
891 }
892 /* Leave at least one pitch sample in the buffer */
893 for(position = 0; position < stream->numPitchSamples - 1; position++) {
894 while((stream->oldRatePosition + 1)*newSampleRate >
895 stream->newRatePosition*oldSampleRate) {
896 if(!enlargeOutputBufferIfNeeded(stream, 1)) {
897 return 0;
898 }
899 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
900 in = stream->pitchBuffer + position;
901 for(i = 0; i < numChannels; i++) {
902 *out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
903 in++;
904 }
905 stream->newRatePosition++;
906 stream->numOutputSamples++;
907 }
908 stream->oldRatePosition++;
909 if(stream->oldRatePosition == oldSampleRate) {
910 stream->oldRatePosition = 0;
911 if(stream->newRatePosition != newSampleRate) {
912 fprintf(stderr,
913 "Assertion failed: stream->newRatePosition != newSampleRate\n");
914 exit(1);
915 }
916 stream->newRatePosition = 0;
917 }
918 }
919 removePitchSamples(stream, position);
920 return 1;
921}
922
923
Bill Cox59e65122010-11-03 10:06:29 -0400924/* Skip over a pitch period, and copy period/speed samples to the output */
925static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400926 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500927 short *samples,
928 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400929 int period)
930{
Bill Cox68e2aee2010-11-23 19:24:41 -0500931 long newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500932 int numChannels = stream->numChannels;
Bill Cox9bf11b52010-11-03 05:33:09 -0400933
Bill Cox6a1bbb12010-11-19 11:14:28 -0500934 if(speed >= 2.0f) {
935 newSamples = period/(speed - 1.0f);
936 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400937 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500938 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400939 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400940 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
941 return 0;
942 }
Bill Cox68e2aee2010-11-23 19:24:41 -0500943 overlapAdd(newSamples, numChannels, stream->outputBuffer +
944 stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
Bill Cox9bf11b52010-11-03 05:33:09 -0400945 stream->numOutputSamples += newSamples;
946 return newSamples;
947}
948
Bill Cox59e65122010-11-03 10:06:29 -0400949/* Insert a pitch period, and determine how much input to copy directly. */
950static int insertPitchPeriod(
951 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500952 short *samples,
953 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400954 int period)
955{
Bill Cox68e2aee2010-11-23 19:24:41 -0500956 long newSamples;
957 short *out;
Bill Cox1a299bb2010-11-19 15:07:17 -0500958 int numChannels = stream->numChannels;
Bill Cox59e65122010-11-03 10:06:29 -0400959
Bill Cox6a1bbb12010-11-19 11:14:28 -0500960 if(speed < 0.5f) {
961 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400962 } else {
963 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500964 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400965 }
966 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
967 return 0;
968 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500969 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
970 memcpy(out, samples, period*sizeof(short)*numChannels);
Bill Cox68e2aee2010-11-23 19:24:41 -0500971 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
972 overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
Bill Cox59e65122010-11-03 10:06:29 -0400973 stream->numOutputSamples += period + newSamples;
974 return newSamples;
975}
976
Bill Cox9bf11b52010-11-03 05:33:09 -0400977/* Resample as many pitch periods as we have buffered on the input. Return 0 if
Bill Coxd544fdb2010-11-23 14:13:46 -0500978 we fail to resize an input or output buffer. Also scale the output by the volume. */
979static int changeSpeed(
980 sonicStream stream,
981 float speed)
Bill Cox9bf11b52010-11-03 05:33:09 -0400982{
Bill Cox1a299bb2010-11-19 15:07:17 -0500983 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -0500984 int numSamples = stream->numInputSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400985 int position = 0, period, newSamples;
986 int maxRequired = stream->maxRequired;
987
Bill Cox9bf11b52010-11-03 05:33:09 -0400988 if(stream->numInputSamples < maxRequired) {
989 return 1;
990 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400991 do {
992 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400993 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400994 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400995 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500996 samples = stream->inputBuffer + position*stream->numChannels;
Bill Cox0283c342010-11-30 11:47:50 -0500997 period = findPitchPeriod(stream, samples, 1);
Bill Cox59e65122010-11-03 10:06:29 -0400998 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500999 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -04001000 position += period + newSamples;
1001 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -05001002 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -04001003 position += newSamples;
1004 }
Bill Cox9bf11b52010-11-03 05:33:09 -04001005 }
1006 if(newSamples == 0) {
1007 return 0; /* Failed to resize output buffer */
1008 }
Bill Cox9bf11b52010-11-03 05:33:09 -04001009 } while(position + maxRequired <= numSamples);
1010 removeInputSamples(stream, position);
1011 return 1;
1012}
Bill Cox0c4c0602010-11-08 11:46:30 -05001013
Bill Coxd544fdb2010-11-23 14:13:46 -05001014/* Resample as many pitch periods as we have buffered on the input. Return 0 if
1015 we fail to resize an input or output buffer. Also scale the output by the volume. */
1016static int processStreamInput(
1017 sonicStream stream)
1018{
1019 int originalNumOutputSamples = stream->numOutputSamples;
1020 float speed = stream->speed/stream->pitch;
Bill Cox3276bb02011-01-11 07:39:26 -05001021 float rate = stream->rate;
Bill Coxd544fdb2010-11-23 14:13:46 -05001022
Bill Cox3276bb02011-01-11 07:39:26 -05001023 if(!stream->useChordPitch) {
1024 rate *= stream->pitch;
1025 }
Bill Coxd544fdb2010-11-23 14:13:46 -05001026 if(speed > 1.00001 || speed < 0.99999) {
1027 changeSpeed(stream, speed);
1028 } else {
Bill Cox68e2aee2010-11-23 19:24:41 -05001029 if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
Bill Coxd544fdb2010-11-23 14:13:46 -05001030 return 0;
1031 }
1032 stream->numInputSamples = 0;
1033 }
Bill Cox3276bb02011-01-11 07:39:26 -05001034 if(stream->useChordPitch) {
1035 if(stream->pitch != 1.0f) {
1036 if(!adjustPitch(stream, originalNumOutputSamples)) {
1037 return 0;
1038 }
1039 }
1040 } else if(rate != 1.0f) {
1041 if(!adjustRate(stream, rate, originalNumOutputSamples)) {
Bill Cox68e2aee2010-11-23 19:24:41 -05001042 return 0;
1043 }
Bill Coxd544fdb2010-11-23 14:13:46 -05001044 }
1045 if(stream->volume != 1.0f) {
1046 /* Adjust output volume. */
1047 scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
1048 (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
1049 stream->volume);
1050 }
1051 return 1;
1052}
1053
Bill Cox0c4c0602010-11-08 11:46:30 -05001054/* Write floating point data to the input buffer and process it. */
1055int sonicWriteFloatToStream(
1056 sonicStream stream,
1057 float *samples,
1058 int numSamples)
1059{
Bill Cox0c4c0602010-11-08 11:46:30 -05001060 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
1061 return 0;
1062 }
1063 return processStreamInput(stream);
1064}
1065
1066/* Simple wrapper around sonicWriteFloatToStream that does the short to float
1067 conversion for you. */
1068int sonicWriteShortToStream(
1069 sonicStream stream,
1070 short *samples,
1071 int numSamples)
1072{
Bill Cox0c4c0602010-11-08 11:46:30 -05001073 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
1074 return 0;
1075 }
1076 return processStreamInput(stream);
1077}
1078
Bill Cox8a23d2f2010-11-16 18:49:36 -05001079/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
1080 conversion for you. */
1081int sonicWriteUnsignedCharToStream(
1082 sonicStream stream,
1083 unsigned char *samples,
1084 int numSamples)
1085{
Bill Cox8a23d2f2010-11-16 18:49:36 -05001086 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
1087 return 0;
1088 }
1089 return processStreamInput(stream);
1090}
1091
Bill Cox036d7322010-11-09 09:29:24 -05001092/* This is a non-stream oriented interface to just change the speed of a sound sample */
1093int sonicChangeFloatSpeed(
1094 float *samples,
1095 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -05001096 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -05001097 float pitch,
1098 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -05001099 int sampleRate,
1100 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -05001101{
Bill Coxd544fdb2010-11-23 14:13:46 -05001102 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -05001103
Bill Coxd544fdb2010-11-23 14:13:46 -05001104 sonicSetSpeed(stream, speed);
1105 sonicSetPitch(stream, pitch);
1106 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -05001107 sonicWriteFloatToStream(stream, samples, numSamples);
1108 sonicFlushStream(stream);
1109 numSamples = sonicSamplesAvailable(stream);
1110 sonicReadFloatFromStream(stream, samples, numSamples);
1111 sonicDestroyStream(stream);
1112 return numSamples;
1113}
1114
1115/* This is a non-stream oriented interface to just change the speed of a sound sample */
1116int sonicChangeShortSpeed(
1117 short *samples,
1118 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -05001119 float speed,
Bill Coxd544fdb2010-11-23 14:13:46 -05001120 float pitch,
1121 float volume,
Bill Cox1a299bb2010-11-19 15:07:17 -05001122 int sampleRate,
1123 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -05001124{
Bill Coxd544fdb2010-11-23 14:13:46 -05001125 sonicStream stream = sonicCreateStream(sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -05001126
Bill Coxd544fdb2010-11-23 14:13:46 -05001127 sonicSetSpeed(stream, speed);
1128 sonicSetPitch(stream, pitch);
1129 sonicSetVolume(stream, volume);
Bill Cox036d7322010-11-09 09:29:24 -05001130 sonicWriteShortToStream(stream, samples, numSamples);
1131 sonicFlushStream(stream);
1132 numSamples = sonicSamplesAvailable(stream);
1133 sonicReadShortFromStream(stream, samples, numSamples);
1134 sonicDestroyStream(stream);
1135 return numSamples;
1136}