blob: 0aa37203f5765b6e4f8201a86ba51e6032668b77 [file] [log] [blame]
Bill Coxca02d872010-11-02 15:10:52 -04001/* Sonic library
2 Copyright 2010
3 Bill Cox
4 This file is part of the Sonic Library.
5
Bill Coxa9999872010-11-11 14:36:59 -05006 The Sonic Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
Bill Coxca02d872010-11-02 15:10:52 -040010
Bill Coxa9999872010-11-11 14:36:59 -050011 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
Bill Coxca02d872010-11-02 15:10:52 -040015
Bill Coxa9999872010-11-11 14:36:59 -050016 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
Bill Cox882fb1d2010-11-02 16:27:20 -040020#include <stdio.h>
Bill Coxca02d872010-11-02 15:10:52 -040021#include <stdlib.h>
22#include <string.h>
Bill Coxa33e3bd2010-11-16 19:57:43 -050023#include <stdarg.h>
Bill Coxca02d872010-11-02 15:10:52 -040024#include "sonic.h"
25
26struct sonicStreamStruct {
Bill Cox6a1bbb12010-11-19 11:14:28 -050027 short *inputBuffer;
28 short *outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -050029 float speed;
30 int numChannels;
Bill Coxca02d872010-11-02 15:10:52 -040031 int inputBufferSize;
32 int outputBufferSize;
33 int numInputSamples;
34 int numOutputSamples;
35 int minPeriod;
36 int maxPeriod;
37 int maxRequired;
38 int remainingInputToCopy;
39 int sampleRate;
40};
41
Bill Cox1a299bb2010-11-19 15:07:17 -050042/* Just used for debugging */
Bill Coxa33e3bd2010-11-16 19:57:43 -050043void sonicMSG(char *format, ...)
44{
45 char buffer[4096];
46 va_list ap;
47 FILE *file;
48
49 va_start(ap, format);
50 vsprintf((char *)buffer, (char *)format, ap);
51 va_end(ap);
52 file=fopen("/tmp/sonic.log", "a");
53 fprintf(file, "%s", buffer);
54 fclose(file);
55}
56
Bill Coxaf9a6242010-11-08 09:32:27 -050057/* Get the speed of the stream. */
Bill Cox6a1bbb12010-11-19 11:14:28 -050058float sonicGetSpeed(
Bill Coxaf9a6242010-11-08 09:32:27 -050059 sonicStream stream)
60{
61 return stream->speed;
62}
63
64/* Get the sample rate of the stream. */
65int sonicGetSampleRate(
66 sonicStream stream)
67{
68 return stream->sampleRate;
69}
70
Bill Coxca02d872010-11-02 15:10:52 -040071/* Destroy the sonic stream. */
72void sonicDestroyStream(
73 sonicStream stream)
74{
75 if(stream->inputBuffer != NULL) {
76 free(stream->inputBuffer);
77 }
78 if(stream->outputBuffer != NULL) {
79 free(stream->outputBuffer);
80 }
81 free(stream);
82}
83
84/* Create a sonic stream. Return NULL only if we are out of memory and cannot
85 allocate the stream. */
86sonicStream sonicCreateStream(
Bill Cox6a1bbb12010-11-19 11:14:28 -050087 float speed,
Bill Cox1a299bb2010-11-19 15:07:17 -050088 int sampleRate,
89 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -040090{
91 sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
92 int minPeriod = sampleRate/SONIC_MAX_PITCH;
93 int maxPeriod = sampleRate/SONIC_MIN_PITCH;
94 int maxRequired = 2*maxPeriod;
95
96 if(stream == NULL) {
97 return NULL;
98 }
Bill Cox1a299bb2010-11-19 15:07:17 -050099 stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400100 if(stream->inputBuffer == NULL) {
101 sonicDestroyStream(stream);
102 return NULL;
103 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500104 stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400105 if(stream->outputBuffer == NULL) {
106 sonicDestroyStream(stream);
107 return NULL;
108 }
109 stream->speed = speed;
110 stream->sampleRate = sampleRate;
Bill Cox1a299bb2010-11-19 15:07:17 -0500111 stream->numChannels = numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400112 stream->minPeriod = minPeriod;
113 stream->maxPeriod = maxPeriod;
114 stream->maxRequired = maxRequired;
115 stream->inputBufferSize = maxRequired;
116 stream->outputBufferSize = maxRequired;
117 return stream;
118}
119
Bill Coxca02d872010-11-02 15:10:52 -0400120/* Enlarge the output buffer if needed. */
121static int enlargeOutputBufferIfNeeded(
122 sonicStream stream,
123 int numSamples)
124{
125 if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
126 stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500127 stream->outputBuffer = (short *)realloc(stream->outputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500128 stream->outputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400129 if(stream->outputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400130 return 0;
131 }
132 }
133 return 1;
134}
135
Bill Coxca02d872010-11-02 15:10:52 -0400136/* Enlarge the input buffer if needed. */
137static int enlargeInputBufferIfNeeded(
138 sonicStream stream,
139 int numSamples)
140{
141 if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
142 stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500143 stream->inputBuffer = (short *)realloc(stream->inputBuffer,
Bill Cox1a299bb2010-11-19 15:07:17 -0500144 stream->inputBufferSize*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400145 if(stream->inputBuffer == NULL) {
Bill Coxca02d872010-11-02 15:10:52 -0400146 return 0;
147 }
148 }
149 return 1;
150}
151
152/* Add the input samples to the input buffer. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500153static int addFloatSamplesToInputBuffer(
Bill Coxca02d872010-11-02 15:10:52 -0400154 sonicStream stream,
155 float *samples,
156 int numSamples)
157{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500158 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500159 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500160
Bill Coxca02d872010-11-02 15:10:52 -0400161 if(numSamples == 0) {
162 return 1;
163 }
164 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
165 return 0;
166 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500167 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500168 while(count--) {
169 *buffer++ = (*samples++)*32767.0f;
170 }
Bill Cox14efa442010-11-02 15:43:58 -0400171 stream->numInputSamples += numSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400172 return 1;
173}
174
Bill Cox0c4c0602010-11-08 11:46:30 -0500175/* Add the input samples to the input buffer. */
176static int addShortSamplesToInputBuffer(
177 sonicStream stream,
178 short *samples,
179 int numSamples)
180{
Bill Cox0c4c0602010-11-08 11:46:30 -0500181 if(numSamples == 0) {
182 return 1;
183 }
184 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
185 return 0;
186 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500187 memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
188 numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500189 stream->numInputSamples += numSamples;
190 return 1;
191}
192
Bill Cox8a23d2f2010-11-16 18:49:36 -0500193/* Add the input samples to the input buffer. */
194static int addUnsignedCharSamplesToInputBuffer(
195 sonicStream stream,
196 unsigned char *samples,
197 int numSamples)
198{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500199 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500200 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500201
202 if(numSamples == 0) {
203 return 1;
204 }
205 if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
206 return 0;
207 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500208 buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500209 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500210 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400211 }
212 stream->numInputSamples += numSamples;
213 return 1;
214}
215
216/* Remove input samples that we have already processed. */
217static void removeInputSamples(
218 sonicStream stream,
219 int position)
220{
221 int remainingSamples = stream->numInputSamples - position;
222
223 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500224 memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
225 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400226 }
227 stream->numInputSamples = remainingSamples;
228}
229
Bill Cox59e65122010-11-03 10:06:29 -0400230/* Just copy from the array to the output buffer */
Bill Cox0c4c0602010-11-08 11:46:30 -0500231static int copyFloatToOutput(
Bill Cox59e65122010-11-03 10:06:29 -0400232 sonicStream stream,
233 float *samples,
234 int numSamples)
235{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500236 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500237 int count = numSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500238
Bill Cox59e65122010-11-03 10:06:29 -0400239 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
240 return 0;
241 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500242 buffer = stream->outputBuffer + stream->numOutputSamples*stream->numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500243 while(count--) {
244 *buffer++ = (*samples++)*32767.0f;
245 }
Bill Cox59e65122010-11-03 10:06:29 -0400246 stream->numOutputSamples += numSamples;
247 return numSamples;
248}
249
Bill Cox0c4c0602010-11-08 11:46:30 -0500250/* Just copy from the array to the output buffer */
251static int copyShortToOutput(
252 sonicStream stream,
253 short *samples,
254 int numSamples)
255{
Bill Cox0c4c0602010-11-08 11:46:30 -0500256 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
257 return 0;
258 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500259 memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
260 samples, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500261 stream->numOutputSamples += numSamples;
262 return numSamples;
263}
264
Bill Cox8a23d2f2010-11-16 18:49:36 -0500265/* Just copy from the array to the output buffer */
266static int copyUnsignedCharToOutput(
267 sonicStream stream,
268 unsigned char *samples,
269 int numSamples)
270{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500271 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500272 int count = numSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500273
274 if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
275 return 0;
276 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500277 buffer = stream->outputBuffer + stream->numOutputSamples*stream->numChannels;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500278 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500279 *buffer++ = (*samples++ - 128) << 8;
Bill Coxca02d872010-11-02 15:10:52 -0400280 }
281 stream->numOutputSamples += numSamples;
282 return numSamples;
283}
284
Bill Cox882fb1d2010-11-02 16:27:20 -0400285/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
286 resize the output buffer. Otherwise, return numSamples */
287static int copyInputToOutput(
288 sonicStream stream,
289 int position)
290{
291 int numSamples = stream->remainingInputToCopy;
292
293 if(numSamples > stream->maxRequired) {
294 numSamples = stream->maxRequired;
295 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500296 if(!copyShortToOutput(stream, stream->inputBuffer + position*stream->numChannels,
297 numSamples)) {
Bill Cox882fb1d2010-11-02 16:27:20 -0400298 return 0;
299 }
Bill Cox882fb1d2010-11-02 16:27:20 -0400300 stream->remainingInputToCopy -= numSamples;
301 return numSamples;
302}
303
Bill Coxca02d872010-11-02 15:10:52 -0400304/* Read data out of the stream. Sometimes no data will be available, and zero
305 is returned, which is not an error condition. */
Bill Cox0c4c0602010-11-08 11:46:30 -0500306int sonicReadFloatFromStream(
Bill Coxca02d872010-11-02 15:10:52 -0400307 sonicStream stream,
308 float *samples,
309 int maxSamples)
310{
311 int numSamples = stream->numOutputSamples;
312 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500313 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500314 int count;
Bill Coxca02d872010-11-02 15:10:52 -0400315
316 if(numSamples == 0) {
317 return 0;
318 }
319 if(numSamples > maxSamples) {
Bill Coxca02d872010-11-02 15:10:52 -0400320 remainingSamples = numSamples - maxSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400321 numSamples = maxSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400322 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500323 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500324 count = numSamples*stream->numChannels;
325 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500326 *samples++ = (*buffer++)/32767.0f;
327 }
Bill Coxca02d872010-11-02 15:10:52 -0400328 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500329 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
330 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400331 }
332 stream->numOutputSamples = remainingSamples;
333 return numSamples;
334}
335
Bill Cox0c4c0602010-11-08 11:46:30 -0500336/* Read short data out of the stream. Sometimes no data will be available, and zero
337 is returned, which is not an error condition. */
338int sonicReadShortFromStream(
339 sonicStream stream,
340 short *samples,
341 int maxSamples)
342{
343 int numSamples = stream->numOutputSamples;
344 int remainingSamples = 0;
Bill Cox0c4c0602010-11-08 11:46:30 -0500345
346 if(numSamples == 0) {
347 return 0;
348 }
349 if(numSamples > maxSamples) {
350 remainingSamples = numSamples - maxSamples;
351 numSamples = maxSamples;
352 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500353 memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500354 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500355 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
356 remainingSamples*sizeof(short)*stream->numChannels);
Bill Cox0c4c0602010-11-08 11:46:30 -0500357 }
358 stream->numOutputSamples = remainingSamples;
359 return numSamples;
360}
361
Bill Cox8a23d2f2010-11-16 18:49:36 -0500362/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
363 is returned, which is not an error condition. */
364int sonicReadUnsignedCharFromStream(
365 sonicStream stream,
366 unsigned char *samples,
367 int maxSamples)
368{
369 int numSamples = stream->numOutputSamples;
370 int remainingSamples = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500371 short *buffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500372 int count;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500373
374 if(numSamples == 0) {
375 return 0;
376 }
377 if(numSamples > maxSamples) {
378 remainingSamples = numSamples - maxSamples;
379 numSamples = maxSamples;
380 }
381 buffer = stream->outputBuffer;
Bill Cox1a299bb2010-11-19 15:07:17 -0500382 count = numSamples*stream->numChannels;
383 while(count--) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500384 *samples++ = (char)((*buffer++) >> 8) + 128;
Bill Coxca02d872010-11-02 15:10:52 -0400385 }
386 if(remainingSamples > 0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500387 memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
388 remainingSamples*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400389 }
390 stream->numOutputSamples = remainingSamples;
391 return numSamples;
392}
393
394/* Force the sonic stream to generate output using whatever data it currently
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500395 has. No extra delay will be added to the output, but flushing in the middle of
396 words could introduce distortion. */
Bill Coxca02d872010-11-02 15:10:52 -0400397int sonicFlushStream(
398 sonicStream stream)
399{
400 int maxRequired = stream->maxRequired;
401 int numSamples = stream->numInputSamples;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500402 int remainingSpace, numOutputSamples, expectedSamples;
Bill Coxca02d872010-11-02 15:10:52 -0400403
404 if(numSamples == 0) {
405 return 1;
406 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500407 if(numSamples >= maxRequired && !sonicWriteShortToStream(stream, NULL, 0)) {
Bill Coxca02d872010-11-02 15:10:52 -0400408 return 0;
409 }
410 numSamples = stream->numInputSamples; /* Now numSamples < maxRequired */
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500411 if(numSamples == 0) {
412 return 1;
413 }
Bill Coxca02d872010-11-02 15:10:52 -0400414 remainingSpace = maxRequired - numSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500415 memset(stream->inputBuffer + numSamples*stream->numChannels, 0,
416 remainingSpace*sizeof(short)*stream->numChannels);
Bill Coxca02d872010-11-02 15:10:52 -0400417 stream->numInputSamples = maxRequired;
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500418 numOutputSamples = stream->numOutputSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500419 if(!sonicWriteShortToStream(stream, NULL, 0)) {
Bill Cox4bbbbcc2010-11-09 05:32:38 -0500420 return 0;
421 }
422 /* Throw away any extra samples we generated due to the silence we added */
423 expectedSamples = (int)(numSamples*stream->speed + 0.5);
424 if(stream->numOutputSamples > numOutputSamples + expectedSamples) {
425 stream->numOutputSamples = numOutputSamples + expectedSamples;
426 }
427 return 1;
Bill Coxca02d872010-11-02 15:10:52 -0400428}
429
430/* Return the number of samples in the output buffer */
Bill Cox3a7abf92010-11-06 15:18:49 -0400431int sonicSamplesAvailable(
Bill Coxca02d872010-11-02 15:10:52 -0400432 sonicStream stream)
433{
434 return stream->numOutputSamples;
435}
Bill Cox9bf11b52010-11-03 05:33:09 -0400436
Bill Cox1a299bb2010-11-19 15:07:17 -0500437/* Find the best frequency match in the range, and given a sample skip multiple.
438 For now, just find the pitch of the first channel. */
Bill Cox0cd49c82010-11-03 10:46:22 -0400439static int findPitchPeriodInRange(
Bill Cox6a1bbb12010-11-19 11:14:28 -0500440 short *samples,
Bill Cox1a299bb2010-11-19 15:07:17 -0500441 int numChannels,
Bill Cox0cd49c82010-11-03 10:46:22 -0400442 int minPeriod,
443 int maxPeriod,
444 int skip)
445{
446 int period, bestPeriod = 0;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500447 short *s, *p, sVal, pVal;
448 unsigned long diff, minDiff = 0;
449 unsigned int numSamples = (minPeriod + skip - 1)/skip;
450 unsigned int bestNumSamples = 0;
Bill Cox1a299bb2010-11-19 15:07:17 -0500451 int sampleSkip = skip*numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500452 int i;
Bill Cox0cd49c82010-11-03 10:46:22 -0400453
454 for(period = minPeriod; period <= maxPeriod; period += skip) {
Bill Cox6a1bbb12010-11-19 11:14:28 -0500455 diff = 0;
Bill Cox0cd49c82010-11-03 10:46:22 -0400456 s = samples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500457 p = samples + period*numChannels;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500458 for(i = 0; i < numSamples; i++) {
459 sVal = *s;
460 pVal = *p;
Bill Cox1a299bb2010-11-19 15:07:17 -0500461 s += sampleSkip;
462 p += sampleSkip;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500463 diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
464 (unsigned short)(pVal - sVal);
Bill Cox0cd49c82010-11-03 10:46:22 -0400465 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500466 /* Note that the highest number of samples we add into diff will be less
467 than 256, since we skip samples. Thus, diff is a 24 bit number, and
468 we can safely multiply by numSamples without overflow */
469 if(bestPeriod == 0 || diff*bestNumSamples < minDiff*numSamples) {
Bill Cox0cd49c82010-11-03 10:46:22 -0400470 minDiff = diff;
471 bestPeriod = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500472 bestNumSamples = numSamples;
Bill Cox0cd49c82010-11-03 10:46:22 -0400473 }
Bill Cox6a1bbb12010-11-19 11:14:28 -0500474 numSamples++;
Bill Cox0cd49c82010-11-03 10:46:22 -0400475 }
476 return bestPeriod;
477}
478
Bill Cox9bf11b52010-11-03 05:33:09 -0400479/* Find the pitch period. This is a critical step, and we may have to try
Bill Cox0cd49c82010-11-03 10:46:22 -0400480 multiple ways to get a good answer. This version uses AMDF. To improve
481 speed, we down sample by an integer factor get in the 11KHz range, and then
482 do it again with a narrower frequency range without down sampling */
Bill Cox9bf11b52010-11-03 05:33:09 -0400483static int findPitchPeriod(
484 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500485 short *samples)
Bill Cox9bf11b52010-11-03 05:33:09 -0400486{
487 int minPeriod = stream->minPeriod;
488 int maxPeriod = stream->maxPeriod;
Bill Cox0cd49c82010-11-03 10:46:22 -0400489 int sampleRate = stream->sampleRate;
490 int skip = 1;
491 int period;
Bill Cox9bf11b52010-11-03 05:33:09 -0400492
Bill Cox0cd49c82010-11-03 10:46:22 -0400493 if(sampleRate > SONIC_AMDF_FREQ) {
494 skip = sampleRate/SONIC_AMDF_FREQ;
Bill Cox9bf11b52010-11-03 05:33:09 -0400495 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500496 period = findPitchPeriodInRange(samples, stream->numChannels, minPeriod, maxPeriod, skip);
Bill Cox6a1bbb12010-11-19 11:14:28 -0500497 if(skip == 1) {
498 return period;
499 }
500 minPeriod = period - (skip << 2);
501 maxPeriod = period + (skip << 2);
Bill Cox0cd49c82010-11-03 10:46:22 -0400502 if(minPeriod < stream->minPeriod) {
503 minPeriod = stream->minPeriod;
504 }
505 if(maxPeriod > stream->maxPeriod) {
506 maxPeriod = stream->maxPeriod;
507 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500508 return findPitchPeriodInRange(samples, stream->numChannels, minPeriod, maxPeriod, 1);
Bill Cox9bf11b52010-11-03 05:33:09 -0400509}
510
Bill Cox59e65122010-11-03 10:06:29 -0400511/* Skip over a pitch period, and copy period/speed samples to the output */
512static int skipPitchPeriod(
Bill Cox9bf11b52010-11-03 05:33:09 -0400513 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500514 short *samples,
515 float speed,
Bill Cox9bf11b52010-11-03 05:33:09 -0400516 int period)
517{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500518 long t, newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500519 short *out, *prevPeriodSamples, *nextPeriodSamples;
520 int numChannels = stream->numChannels;
521 int i;
Bill Cox9bf11b52010-11-03 05:33:09 -0400522
Bill Cox6a1bbb12010-11-19 11:14:28 -0500523 if(speed >= 2.0f) {
524 newSamples = period/(speed - 1.0f);
525 } else if(speed > 1.0f) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400526 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500527 stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
Bill Cox9bf11b52010-11-03 05:33:09 -0400528 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400529 if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
530 return 0;
531 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500532 for(i = 0; i < numChannels; i++) {
533 out = stream->outputBuffer + stream->numOutputSamples*numChannels + i;
534 prevPeriodSamples = samples + i;
535 nextPeriodSamples = samples + i + period*numChannels;
536 for(t = 0; t < newSamples; t++) {
537 *out = (*prevPeriodSamples*(newSamples - t) + *nextPeriodSamples*t)/newSamples;
538 out += numChannels;
539 prevPeriodSamples += numChannels;
540 nextPeriodSamples += numChannels;
541 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400542 }
543 stream->numOutputSamples += newSamples;
544 return newSamples;
545}
546
Bill Cox59e65122010-11-03 10:06:29 -0400547/* Insert a pitch period, and determine how much input to copy directly. */
548static int insertPitchPeriod(
549 sonicStream stream,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500550 short *samples,
551 float speed,
Bill Cox59e65122010-11-03 10:06:29 -0400552 int period)
553{
Bill Cox1dcc64a2010-11-21 04:50:24 -0500554 long t, newSamples;
Bill Cox1a299bb2010-11-19 15:07:17 -0500555 short *out, *prevPeriodSamples, *nextPeriodSamples;
556 int numChannels = stream->numChannels;
557 int i;
Bill Cox59e65122010-11-03 10:06:29 -0400558
Bill Cox6a1bbb12010-11-19 11:14:28 -0500559 if(speed < 0.5f) {
560 newSamples = period*speed/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400561 } else {
562 newSamples = period;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500563 stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
Bill Cox59e65122010-11-03 10:06:29 -0400564 }
565 if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
566 return 0;
567 }
Bill Cox1a299bb2010-11-19 15:07:17 -0500568 out = stream->outputBuffer + stream->numOutputSamples*numChannels;
569 memcpy(out, samples, period*sizeof(short)*numChannels);
570 for(i = 0; i < numChannels; i++) {
571 out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels + i;
572 prevPeriodSamples = samples + i;
573 nextPeriodSamples = samples + i + period*numChannels;
574 for(t = 0; t < newSamples; t++) {
575 *out = (*prevPeriodSamples*t + *nextPeriodSamples*(newSamples - t))/newSamples;
576 out += numChannels;
577 prevPeriodSamples += numChannels;
578 nextPeriodSamples += numChannels;
579 }
Bill Cox59e65122010-11-03 10:06:29 -0400580 }
581 stream->numOutputSamples += period + newSamples;
582 return newSamples;
583}
584
Bill Cox9bf11b52010-11-03 05:33:09 -0400585/* Resample as many pitch periods as we have buffered on the input. Return 0 if
586 we fail to resize an input or output buffer */
Bill Cox0c4c0602010-11-08 11:46:30 -0500587static int processStreamInput(
588 sonicStream stream)
Bill Cox9bf11b52010-11-03 05:33:09 -0400589{
Bill Cox1a299bb2010-11-19 15:07:17 -0500590 short *samples;
Bill Cox0c4c0602010-11-08 11:46:30 -0500591 int numSamples = stream->numInputSamples;
Bill Cox6a1bbb12010-11-19 11:14:28 -0500592 float speed = stream->speed;
Bill Cox9bf11b52010-11-03 05:33:09 -0400593 int position = 0, period, newSamples;
594 int maxRequired = stream->maxRequired;
595
Bill Cox9bf11b52010-11-03 05:33:09 -0400596 if(stream->numInputSamples < maxRequired) {
597 return 1;
598 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400599 do {
600 if(stream->remainingInputToCopy > 0) {
Bill Cox9bf11b52010-11-03 05:33:09 -0400601 newSamples = copyInputToOutput(stream, position);
Bill Cox59e65122010-11-03 10:06:29 -0400602 position += newSamples;
Bill Cox9bf11b52010-11-03 05:33:09 -0400603 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500604 samples = stream->inputBuffer + position*stream->numChannels;
605 period = findPitchPeriod(stream, samples);
Bill Cox59e65122010-11-03 10:06:29 -0400606 if(speed > 1.0) {
Bill Cox1a299bb2010-11-19 15:07:17 -0500607 newSamples = skipPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400608 position += period + newSamples;
609 } else {
Bill Cox1a299bb2010-11-19 15:07:17 -0500610 newSamples = insertPitchPeriod(stream, samples, speed, period);
Bill Cox59e65122010-11-03 10:06:29 -0400611 position += newSamples;
612 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400613 }
614 if(newSamples == 0) {
615 return 0; /* Failed to resize output buffer */
616 }
Bill Cox9bf11b52010-11-03 05:33:09 -0400617 } while(position + maxRequired <= numSamples);
618 removeInputSamples(stream, position);
619 return 1;
620}
Bill Cox0c4c0602010-11-08 11:46:30 -0500621
622/* Write floating point data to the input buffer and process it. */
623int sonicWriteFloatToStream(
624 sonicStream stream,
625 float *samples,
626 int numSamples)
627{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500628 float speed = stream->speed;
Bill Cox0c4c0602010-11-08 11:46:30 -0500629
630 if(speed > 0.999999 && speed < 1.000001) {
631 /* No speed change - just copy to the output */
632 return copyFloatToOutput(stream, samples, numSamples);
633 }
634 if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
635 return 0;
636 }
637 return processStreamInput(stream);
638}
639
640/* Simple wrapper around sonicWriteFloatToStream that does the short to float
641 conversion for you. */
642int sonicWriteShortToStream(
643 sonicStream stream,
644 short *samples,
645 int numSamples)
646{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500647 float speed = stream->speed;
Bill Cox0c4c0602010-11-08 11:46:30 -0500648
649 if(speed > 0.999999 && speed < 1.000001) {
650 /* No speed change - just copy to the output */
651 return copyShortToOutput(stream, samples, numSamples);
652 }
653 if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
654 return 0;
655 }
656 return processStreamInput(stream);
657}
658
Bill Cox8a23d2f2010-11-16 18:49:36 -0500659/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
660 conversion for you. */
661int sonicWriteUnsignedCharToStream(
662 sonicStream stream,
663 unsigned char *samples,
664 int numSamples)
665{
Bill Cox6a1bbb12010-11-19 11:14:28 -0500666 float speed = stream->speed;
Bill Cox8a23d2f2010-11-16 18:49:36 -0500667
668 if(speed > 0.999999 && speed < 1.000001) {
669 /* No speed change - just copy to the output */
670 return copyUnsignedCharToOutput(stream, samples, numSamples);
671 }
672 if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
673 return 0;
674 }
675 return processStreamInput(stream);
676}
677
Bill Cox036d7322010-11-09 09:29:24 -0500678/* This is a non-stream oriented interface to just change the speed of a sound sample */
679int sonicChangeFloatSpeed(
680 float *samples,
681 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500682 float speed,
Bill Cox1a299bb2010-11-19 15:07:17 -0500683 int sampleRate,
684 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500685{
Bill Cox1a299bb2010-11-19 15:07:17 -0500686 sonicStream stream = sonicCreateStream(speed, sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500687
688 sonicWriteFloatToStream(stream, samples, numSamples);
689 sonicFlushStream(stream);
690 numSamples = sonicSamplesAvailable(stream);
691 sonicReadFloatFromStream(stream, samples, numSamples);
692 sonicDestroyStream(stream);
693 return numSamples;
694}
695
696/* This is a non-stream oriented interface to just change the speed of a sound sample */
697int sonicChangeShortSpeed(
698 short *samples,
699 int numSamples,
Bill Cox6a1bbb12010-11-19 11:14:28 -0500700 float speed,
Bill Cox1a299bb2010-11-19 15:07:17 -0500701 int sampleRate,
702 int numChannels)
Bill Cox036d7322010-11-09 09:29:24 -0500703{
Bill Cox1a299bb2010-11-19 15:07:17 -0500704 sonicStream stream = sonicCreateStream(speed, sampleRate, numChannels);
Bill Cox036d7322010-11-09 09:29:24 -0500705
706 sonicWriteShortToStream(stream, samples, numSamples);
707 sonicFlushStream(stream);
708 numSamples = sonicSamplesAvailable(stream);
709 sonicReadShortFromStream(stream, samples, numSamples);
710 sonicDestroyStream(stream);
711 return numSamples;
712}