blob: d85629bcacdd7311d968e6b43afdbcc49d0fffca [file] [log] [blame]
Bill Coxca02d872010-11-02 15:10:52 -04001/* Sonic library
2 Copyright 2010
3 Bill Cox
4 This file is part of the Sonic Library.
5
Bill Coxa9999872010-11-11 14:36:59 -05006 The Sonic Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
Bill Coxca02d872010-11-02 15:10:52 -040010
Bill Coxa9999872010-11-11 14:36:59 -050011 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
Bill Coxca02d872010-11-02 15:10:52 -040015
Bill Coxa9999872010-11-11 14:36:59 -050016 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
Bill Cox882fb1d2010-11-02 16:27:20 -040020#include <stdio.h>
Bill Coxca02d872010-11-02 15:10:52 -040021#include <stdlib.h>
22#include <string.h>
Bill Coxa33e3bd2010-11-16 19:57:43 -050023#include <stdarg.h>
Bill Coxca02d872010-11-02 15:10:52 -040024#include "sonic.h"
25
26struct sonicStreamStruct {
Bill Cox6a1bbb12010-11-19 11:14:28 -050027 float speed;
28 short *inputBuffer;
29 short *outputBuffer;
Bill Coxca02d872010-11-02 15:10:52 -040030 int inputBufferSize;
31 int outputBufferSize;
32 int numInputSamples;
33 int numOutputSamples;
34 int minPeriod;
35 int maxPeriod;
36 int maxRequired;
37 int remainingInputToCopy;
38 int sampleRate;
39};
40
Bill Coxa33e3bd2010-11-16 19:57:43 -050041void sonicMSG(char *format, ...)
42{
43 char buffer[4096];
44 va_list ap;
45 FILE *file;
46
47 va_start(ap, format);
48 vsprintf((char *)buffer, (char *)format, ap);
49 va_end(ap);
50 file=fopen("/tmp/sonic.log", "a");
51 fprintf(file, "%s", buffer);
52 fclose(file);
53}
54
Bill Coxaf9a6242010-11-08 09:32:27 -050055/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -050056float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -050057 sonicStream stream)
58{
59 return stream->speed;
60}
61
62/* Get the sample rate of the stream. */
63int sonicGetSampleRate(
64 sonicStream stream)
65{
66 return stream->sampleRate;
67}
68
Bill Coxca02d872010-11-02 15:10:52 -040069/* Destroy the sonic stream. */
70void sonicDestroyStream(
71 sonicStream stream)
72{
73 if(stream->inputBuffer != NULL) {
74 free(stream->inputBuffer);
75 }
76 if(stream->outputBuffer != NULL) {
77 free(stream->outputBuffer);
78 }
79 free(stream);
80}
81
82/* Create a sonic stream. Return NULL only if we are out of memory and cannot
83 allocate the stream. */
84sonicStream sonicCreateStream(
Bill Cox6a1bbb12010-11-19 11:14:28 -050085 float speed,
Bill Coxca02d872010-11-02 15:10:52 -040086 int sampleRate)
87{
88 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
89 int minPeriod = sampleRate/SONIC_MAX_PITCH;
90 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
91 int maxRequired = 2*maxPeriod;
92
93 if(stream == NULL) {
94 return NULL;
95 }
Bill Cox6a1bbb12010-11-19 11:14:28 -050096 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -040097 if(stream->inputBuffer == NULL) {
98 sonicDestroyStream(stream);
99 return NULL;
100 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500101 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400102 if(stream->outputBuffer == NULL) {
103 sonicDestroyStream(stream);
104 return NULL;
105 }
106 stream->speed = speed;
107 stream->sampleRate = sampleRate;
108 stream->minPeriod = minPeriod;
109 stream->maxPeriod = maxPeriod;
110 stream->maxRequired = maxRequired;
111 stream->inputBufferSize = maxRequired;
112 stream->outputBufferSize = maxRequired;
113 return stream;
114}
115
Bill Coxca02d872010-11-02 15:10:52 -0400116/* Enlarge the output buffer if needed. */
117static int enlargeOutputBufferIfNeeded(
118 sonicStream stream,
119 int numSamples)
120{
121 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
122 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500123 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
124 stream->outputBufferSize*sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400125 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400126 return 0;
127 }
128 }
129 return 1;
130}
131
Bill Coxca02d872010-11-02 15:10:52 -0400132/* Enlarge the input buffer if needed. */
133static int enlargeInputBufferIfNeeded(
134 sonicStream stream,
135 int numSamples)
136{
137 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
138 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500139 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
140 stream->inputBufferSize*sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400141 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400142 return 0;
143 }
144 }
145 return 1;
146}
147
148/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500149static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400150 sonicStream stream,
151 float *samples,
152 int numSamples)
153{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500154 short *buffer;
155 int count = numSamples;
156
Bill Coxca02d872010-11-02 15:10:52 -0400157 if(numSamples == 0) {
158 return 1;
159 }
160 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
161 return 0;
162 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500163 buffer = stream->inputBuffer + stream->numInputSamples;
164 while(count--) {
165 *buffer++ = (*samples++)*32767.0f;
166 }
Bill Cox14efa442010-11-02 15:43:58 -0400167 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400168 return 1;
169}
170
Bill Cox0c4c0602010-11-08 11:46:30 -0500171/* Add the input samples to the input buffer. */
172static int addShortSamplesToInputBuffer(
173 sonicStream stream,
174 short *samples,
175 int numSamples)
176{
Bill Cox0c4c0602010-11-08 11:46:30 -0500177 if(numSamples == 0) {
178 return 1;
179 }
180 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
181 return 0;
182 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500183 memcpy(stream->inputBuffer + stream->numInputSamples, samples, numSamples*sizeof(short));
Bill Cox0c4c0602010-11-08 11:46:30 -0500184 stream->numInputSamples += numSamples;
185 return 1;
186}
187
Bill Cox8a23d2f2010-11-16 18:49:36 -0500188/* Add the input samples to the input buffer. */
189static int addUnsignedCharSamplesToInputBuffer(
190 sonicStream stream,
191 unsigned char *samples,
192 int numSamples)
193{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500194 short *buffer;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500195 int count = numSamples;
196
197 if(numSamples == 0) {
198 return 1;
199 }
200 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
201 return 0;
202 }
203 buffer = stream->inputBuffer + stream->numInputSamples;
204 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500205 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400206 }
207 stream->numInputSamples += numSamples;
208 return 1;
209}
210
211/* Remove input samples that we have already processed. */
212static void removeInputSamples(
213 sonicStream stream,
214 int position)
215{
216 int remainingSamples = stream->numInputSamples - position;
217
218 if(remainingSamples > 0) {
219 memmove(stream->inputBuffer, stream->inputBuffer + position,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500220 remainingSamples*sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400221 }
222 stream->numInputSamples = remainingSamples;
223}
224
Bill Cox59e65122010-11-03 10:06:29 -0400225/* Just copy from the array to the output buffer */
Bill Cox0c4c0602010-11-08 11:46:30 -0500226static int copyFloatToOutput(
Bill Cox59e65122010-11-03 10:06:29 -0400227 sonicStream stream,
228 float *samples,
229 int numSamples)
230{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500231 short *buffer;
232 int count = numSamples;
233
Bill Cox59e65122010-11-03 10:06:29 -0400234 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
235 return 0;
236 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500237 buffer = stream->outputBuffer + stream->numOutputSamples;
238 while(count--) {
239 *buffer++ = (*samples++)*32767.0f;
240 }
Bill Cox59e65122010-11-03 10:06:29 -0400241 stream->numOutputSamples += numSamples;
242 return numSamples;
243}
244
Bill Cox0c4c0602010-11-08 11:46:30 -0500245/* Just copy from the array to the output buffer */
246static int copyShortToOutput(
247 sonicStream stream,
248 short *samples,
249 int numSamples)
250{
Bill Cox0c4c0602010-11-08 11:46:30 -0500251 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
252 return 0;
253 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500254 memcpy(stream->outputBuffer + stream->numOutputSamples, samples, numSamples*sizeof(short));
Bill Cox0c4c0602010-11-08 11:46:30 -0500255 stream->numOutputSamples += numSamples;
256 return numSamples;
257}
258
Bill Cox8a23d2f2010-11-16 18:49:36 -0500259/* Just copy from the array to the output buffer */
260static int copyUnsignedCharToOutput(
261 sonicStream stream,
262 unsigned char *samples,
263 int numSamples)
264{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500265 short *buffer;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500266 int count = numSamples;
267
268 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
269 return 0;
270 }
271 buffer = stream->outputBuffer + stream->numOutputSamples;
272 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500273 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400274 }
275 stream->numOutputSamples += numSamples;
276 return numSamples;
277}
278
Bill Cox882fb1d2010-11-02 16:27:20 -0400279/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
280 resize the output buffer. Otherwise, return numSamples */
281static int copyInputToOutput(
282 sonicStream stream,
283 int position)
284{
285 int numSamples = stream->remainingInputToCopy;
286
287 if(numSamples > stream->maxRequired) {
288 numSamples = stream->maxRequired;
289 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500290 if(!copyShortToOutput(stream, stream->inputBuffer + position, numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400291 return 0;
292 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400293 stream->remainingInputToCopy -= numSamples;
294 return numSamples;
295}
296
Bill Coxca02d872010-11-02 15:10:52 -0400297/* Read data out of the stream. Sometimes no data will be available, and zero
298 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500299int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400300 sonicStream stream,
301 float *samples,
302 int maxSamples)
303{
304 int numSamples = stream->numOutputSamples;
305 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500306 short *buffer;
307 int i;
Bill Coxca02d872010-11-02 15:10:52 -0400308
309 if(numSamples == 0) {
310 return 0;
311 }
312 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400313 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400314 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400315 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500316 buffer = stream->outputBuffer;
317 for(i = 0; i < numSamples; i++) {
318 *samples++ = (*buffer++)/32767.0f;
319 }
Bill Coxca02d872010-11-02 15:10:52 -0400320 if(remainingSamples > 0) {
321 memmove(stream->outputBuffer, stream->outputBuffer + numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500322 remainingSamples*sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400323 }
324 stream->numOutputSamples = remainingSamples;
325 return numSamples;
326}
327
Bill Cox0c4c0602010-11-08 11:46:30 -0500328/* Read short data out of the stream. Sometimes no data will be available, and zero
329 is returned, which is not an error condition. */
330int sonicReadShortFromStream(
331 sonicStream stream,
332 short *samples,
333 int maxSamples)
334{
335 int numSamples = stream->numOutputSamples;
336 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500337
338 if(numSamples == 0) {
339 return 0;
340 }
341 if(numSamples > maxSamples) {
342 remainingSamples = numSamples - maxSamples;
343 numSamples = maxSamples;
344 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500345 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short));
Bill Cox0c4c0602010-11-08 11:46:30 -0500346 if(remainingSamples > 0) {
347 memmove(stream->outputBuffer, stream->outputBuffer + numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500348 remainingSamples*sizeof(short));
Bill Cox0c4c0602010-11-08 11:46:30 -0500349 }
350 stream->numOutputSamples = remainingSamples;
351 return numSamples;
352}
353
Bill Cox8a23d2f2010-11-16 18:49:36 -0500354/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
355 is returned, which is not an error condition. */
356int sonicReadUnsignedCharFromStream(
357 sonicStream stream,
358 unsigned char *samples,
359 int maxSamples)
360{
361 int numSamples = stream->numOutputSamples;
362 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500363 short *buffer;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500364 int i;
365
366 if(numSamples == 0) {
367 return 0;
368 }
369 if(numSamples > maxSamples) {
370 remainingSamples = numSamples - maxSamples;
371 numSamples = maxSamples;
372 }
373 buffer = stream->outputBuffer;
374 for(i = 0; i < numSamples; i++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500375 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400376 }
377 if(remainingSamples > 0) {
378 memmove(stream->outputBuffer, stream->outputBuffer + numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500379 remainingSamples*sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400380 }
381 stream->numOutputSamples = remainingSamples;
382 return numSamples;
383}
384
385/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500386 has. No extra delay will be added to the output, but flushing in the middle of
387 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400388int sonicFlushStream(
389 sonicStream stream)
390{
391 int maxRequired = stream->maxRequired;
392 int numSamples = stream->numInputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500393 int remainingSpace, numOutputSamples, expectedSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400394
395 if(numSamples == 0) {
396 return 1;
397 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500398 if(numSamples >= maxRequired && !sonicWriteShortToStream(stream, NULL, 0)) {
Bill Coxca02d872010-11-02 15:10:52 -0400399 return 0;
400 }
401 numSamples = stream->numInputSamples; /* Now numSamples < maxRequired */
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500402 if(numSamples == 0) {
403 return 1;
404 }
Bill Coxca02d872010-11-02 15:10:52 -0400405 remainingSpace = maxRequired - numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500406 memset(stream->inputBuffer + numSamples, 0, remainingSpace*sizeof(short));
Bill Coxca02d872010-11-02 15:10:52 -0400407 stream->numInputSamples = maxRequired;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500408 numOutputSamples = stream->numOutputSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500409 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500410 return 0;
411 }
412 /* Throw away any extra samples we generated due to the silence we added */
413 expectedSamples = (int)(numSamples*stream->speed + 0.5);
414 if(stream->numOutputSamples > numOutputSamples + expectedSamples) {
415 stream->numOutputSamples = numOutputSamples + expectedSamples;
416 }
417 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400418}
419
420/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400421int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400422 sonicStream stream)
423{
424 return stream->numOutputSamples;
425}
Bill Cox9bf11b52010-11-03 05:33:09 -0400426
Bill Cox0cd49c82010-11-03 10:46:22 -0400427/* Find the best frequency match in the range, and given a sample skip multiple. */
428static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500429 short *samples,
Bill Cox0cd49c82010-11-03 10:46:22 -0400430 int minPeriod,
431 int maxPeriod,
432 int skip)
433{
434 int period, bestPeriod = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500435 short *s, *p, sVal, pVal;
436 unsigned long diff, minDiff = 0;
437 unsigned int numSamples = (minPeriod + skip - 1)/skip;
438 unsigned int bestNumSamples = 0;
439 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400440
441 for(period = minPeriod; period <= maxPeriod; period += skip) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500442 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400443 s = samples;
444 p = samples + period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500445 for(i = 0; i < numSamples; i++) {
446 sVal = *s;
447 pVal = *p;
Bill Cox0cd49c82010-11-03 10:46:22 -0400448 s += skip;
449 p += skip;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500450 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
451 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400452 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500453 /* Note that the highest number of samples we add into diff will be less
454 than 256, since we skip samples. Thus, diff is a 24 bit number, and
455 we can safely multiply by numSamples without overflow */
456 if(bestPeriod == 0 || diff*bestNumSamples < minDiff*numSamples) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400457 minDiff = diff;
458 bestPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500459 bestNumSamples = numSamples;
Bill Cox0cd49c82010-11-03 10:46:22 -0400460 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500461 numSamples++;
Bill Cox0cd49c82010-11-03 10:46:22 -0400462 }
463 return bestPeriod;
464}
465
Bill Cox9bf11b52010-11-03 05:33:09 -0400466/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400467 multiple ways to get a good answer. This version uses AMDF. To improve
468 speed, we down sample by an integer factor get in the 11KHz range, and then
469 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400470static int findPitchPeriod(
471 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500472 short *samples)
Bill Cox9bf11b52010-11-03 05:33:09 -0400473{
474 int minPeriod = stream->minPeriod;
475 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400476 int sampleRate = stream->sampleRate;
477 int skip = 1;
478 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400479
Bill Cox0cd49c82010-11-03 10:46:22 -0400480 if(sampleRate > SONIC_AMDF_FREQ) {
481 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400482 }
Bill Coxa33e3bd2010-11-16 19:57:43 -0500483 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, skip);
Bill Cox6a1bbb12010-11-19 11:14:28 -0500484 if(skip == 1) {
485 return period;
486 }
487 minPeriod = period - (skip << 2);
488 maxPeriod = period + (skip << 2);
Bill Cox0cd49c82010-11-03 10:46:22 -0400489 if(minPeriod < stream->minPeriod) {
490 minPeriod = stream->minPeriod;
491 }
492 if(maxPeriod > stream->maxPeriod) {
493 maxPeriod = stream->maxPeriod;
494 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500495 return findPitchPeriodInRange(samples, minPeriod, maxPeriod, 1);
Bill Cox9bf11b52010-11-03 05:33:09 -0400496}
497
Bill Cox59e65122010-11-03 10:06:29 -0400498/* Skip over a pitch period, and copy period/speed samples to the output */
499static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400500 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500501 short *samples,
502 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400503 int period)
504{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500505 long t, newSamples;
506 short *out;
Bill Cox9bf11b52010-11-03 05:33:09 -0400507
Bill Cox6a1bbb12010-11-19 11:14:28 -0500508 if(speed >= 2.0f) {
509 newSamples = period/(speed - 1.0f);
510 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400511 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500512 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400513 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400514 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
515 return 0;
516 }
517 out = stream->outputBuffer + stream->numOutputSamples;
518 for(t = 0; t < newSamples; t++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500519 out[t] = (samples[t]*(newSamples - t) + samples[t + period]*t)/newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400520 }
521 stream->numOutputSamples += newSamples;
522 return newSamples;
523}
524
Bill Cox59e65122010-11-03 10:06:29 -0400525/* Insert a pitch period, and determine how much input to copy directly. */
526static int insertPitchPeriod(
527 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500528 short *samples,
529 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400530 int period)
531{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500532 unsigned long t, newSamples;
533 short *out;
Bill Cox59e65122010-11-03 10:06:29 -0400534
Bill Cox6a1bbb12010-11-19 11:14:28 -0500535 if(speed < 0.5f) {
536 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400537 } else {
538 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500539 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400540 }
541 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
542 return 0;
543 }
544 out = stream->outputBuffer + stream->numOutputSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500545 memcpy(out, samples, period*sizeof(short));
Bill Cox59e65122010-11-03 10:06:29 -0400546 out += period;
Bill Cox59e65122010-11-03 10:06:29 -0400547 for(t = 0; t < newSamples; t++) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500548 out[t] = (samples[t]*t + samples[t + period]*(newSamples - t))/newSamples;
Bill Cox59e65122010-11-03 10:06:29 -0400549 }
550 stream->numOutputSamples += period + newSamples;
551 return newSamples;
552}
553
Bill Cox9bf11b52010-11-03 05:33:09 -0400554/* Resample as many pitch periods as we have buffered on the input. Return 0 if
555 we fail to resize an input or output buffer */
Bill Cox0c4c0602010-11-08 11:46:30 -0500556static int processStreamInput(
557 sonicStream stream)
Bill Cox9bf11b52010-11-03 05:33:09 -0400558{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500559 short *samples = stream->inputBuffer;
Bill Cox0c4c0602010-11-08 11:46:30 -0500560 int numSamples = stream->numInputSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500561 float speed = stream->speed;
Bill Cox9bf11b52010-11-03 05:33:09 -0400562 int position = 0, period, newSamples;
563 int maxRequired = stream->maxRequired;
564
Bill Cox9bf11b52010-11-03 05:33:09 -0400565 if(stream->numInputSamples < maxRequired) {
566 return 1;
567 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400568 do {
569 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400570 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400571 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400572 } else {
573 period = findPitchPeriod(stream, samples + position);
Bill Cox59e65122010-11-03 10:06:29 -0400574 if(speed > 1.0) {
575 newSamples = skipPitchPeriod(stream, samples + position, speed, period);
576 position += period + newSamples;
577 } else {
578 newSamples = insertPitchPeriod(stream, samples + position, speed, period);
579 position += newSamples;
580 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400581 }
582 if(newSamples == 0) {
583 return 0; /* Failed to resize output buffer */
584 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400585 } while(position + maxRequired <= numSamples);
586 removeInputSamples(stream, position);
587 return 1;
588}
Bill Cox0c4c0602010-11-08 11:46:30 -0500589
590/* Write floating point data to the input buffer and process it. */
591int sonicWriteFloatToStream(
592 sonicStream stream,
593 float *samples,
594 int numSamples)
595{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500596 float speed = stream->speed;
Bill Cox0c4c0602010-11-08 11:46:30 -0500597
598 if(speed > 0.999999 && speed < 1.000001) {
599 /* No speed change - just copy to the output */
600 return copyFloatToOutput(stream, samples, numSamples);
601 }
602 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
603 return 0;
604 }
605 return processStreamInput(stream);
606}
607
608/* Simple wrapper around sonicWriteFloatToStream that does the short to float
609 conversion for you. */
610int sonicWriteShortToStream(
611 sonicStream stream,
612 short *samples,
613 int numSamples)
614{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500615 float speed = stream->speed;
Bill Cox0c4c0602010-11-08 11:46:30 -0500616
617 if(speed > 0.999999 && speed < 1.000001) {
618 /* No speed change - just copy to the output */
619 return copyShortToOutput(stream, samples, numSamples);
620 }
621 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
622 return 0;
623 }
624 return processStreamInput(stream);
625}
626
Bill Cox8a23d2f2010-11-16 18:49:36 -0500627/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
628 conversion for you. */
629int sonicWriteUnsignedCharToStream(
630 sonicStream stream,
631 unsigned char *samples,
632 int numSamples)
633{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500634 float speed = stream->speed;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500635
636 if(speed > 0.999999 && speed < 1.000001) {
637 /* No speed change - just copy to the output */
638 return copyUnsignedCharToOutput(stream, samples, numSamples);
639 }
640 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
641 return 0;
642 }
643 return processStreamInput(stream);
644}
645
Bill Cox036d7322010-11-09 09:29:24 -0500646/* This is a non-stream oriented interface to just change the speed of a sound sample */
647int sonicChangeFloatSpeed(
648 float *samples,
649 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500650 float speed,
Bill Cox036d7322010-11-09 09:29:24 -0500651 int sampleRate)
652{
653 sonicStream stream = sonicCreateStream(speed, sampleRate);
654
655 sonicWriteFloatToStream(stream, samples, numSamples);
656 sonicFlushStream(stream);
657 numSamples = sonicSamplesAvailable(stream);
658 sonicReadFloatFromStream(stream, samples, numSamples);
659 sonicDestroyStream(stream);
660 return numSamples;
661}
662
663/* This is a non-stream oriented interface to just change the speed of a sound sample */
664int sonicChangeShortSpeed(
665 short *samples,
666 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500667 float speed,
Bill Cox036d7322010-11-09 09:29:24 -0500668 int sampleRate)
669{
670 sonicStream stream = sonicCreateStream(speed, sampleRate);
671
672 sonicWriteShortToStream(stream, samples, numSamples);
673 sonicFlushStream(stream);
674 numSamples = sonicSamplesAvailable(stream);
675 sonicReadShortFromStream(stream, samples, numSamples);
676 sonicDestroyStream(stream);
677 return numSamples;
678}
679