1*b290403dSRicardo Garcia /* Sonic library
2*b290403dSRicardo Garcia Copyright 2010
3*b290403dSRicardo Garcia Bill Cox
4*b290403dSRicardo Garcia This file is part of the Sonic Library.
5*b290403dSRicardo Garcia
6*b290403dSRicardo Garcia This file is licensed under the Apache 2.0 license.
7*b290403dSRicardo Garcia */
8*b290403dSRicardo Garcia
9*b290403dSRicardo Garcia /* This file is designed for low-powered microcontrollers, minimizing memory
10*b290403dSRicardo Garcia compared to the fuller sonic.c implementation. */
11*b290403dSRicardo Garcia
12*b290403dSRicardo Garcia #include "sonic_lite.h"
13*b290403dSRicardo Garcia
14*b290403dSRicardo Garcia #include <string.h>
15*b290403dSRicardo Garcia
16*b290403dSRicardo Garcia #define SONIC_MAX_PERIOD (SONIC_SAMPLE_RATE / SONIC_MIN_PITCH)
17*b290403dSRicardo Garcia #define SONIC_MIN_PERIOD (SONIC_SAMPLE_RATE / SONIC_MAX_PITCH)
18*b290403dSRicardo Garcia #define SONIC_SKIP (SONIC_SAMPLE_RATE / SONIC_AMDF_FREQ)
19*b290403dSRicardo Garcia #define SONIC_INPUT_BUFFER_SIZE (2 * SONIC_MAX_PERIOD + SONIC_INPUT_SAMPLES)
20*b290403dSRicardo Garcia
21*b290403dSRicardo Garcia struct sonicStruct {
22*b290403dSRicardo Garcia short inputBuffer[SONIC_INPUT_BUFFER_SIZE];
23*b290403dSRicardo Garcia short outputBuffer [2 * SONIC_MAX_PERIOD];
24*b290403dSRicardo Garcia short downSampleBuffer[(2 * SONIC_MAX_PERIOD) / SONIC_SKIP];
25*b290403dSRicardo Garcia float speed;
26*b290403dSRicardo Garcia float volume;
27*b290403dSRicardo Garcia int numInputSamples;
28*b290403dSRicardo Garcia int numOutputSamples;
29*b290403dSRicardo Garcia int remainingInputToCopy;
30*b290403dSRicardo Garcia int prevPeriod;
31*b290403dSRicardo Garcia int prevMinDiff;
32*b290403dSRicardo Garcia };
33*b290403dSRicardo Garcia
34*b290403dSRicardo Garcia static struct sonicStruct sonicStream;
35*b290403dSRicardo Garcia
36*b290403dSRicardo Garcia /* Scale the samples by the factor. Volume should be no greater than 127X, or
37*b290403dSRicardo Garcia it is possible to overflow the fixed-point mathi. */
scaleSamples(short * samples,int numSamples,float volume)38*b290403dSRicardo Garcia static void scaleSamples(short *samples, int numSamples, float volume) {
39*b290403dSRicardo Garcia /* This is 24-bit integer and 8-bit fraction fixed-point representation. */
40*b290403dSRicardo Garcia int fixedPointVolume;
41*b290403dSRicardo Garcia int value;
42*b290403dSRicardo Garcia
43*b290403dSRicardo Garcia if (volume > 127.0) {
44*b290403dSRicardo Garcia volume = 127.0;
45*b290403dSRicardo Garcia }
46*b290403dSRicardo Garcia fixedPointVolume = volume * 256.0f;
47*b290403dSRicardo Garcia while (numSamples--) {
48*b290403dSRicardo Garcia value = (*samples * fixedPointVolume) >> 8;
49*b290403dSRicardo Garcia if (value > 32767) {
50*b290403dSRicardo Garcia value = 32767;
51*b290403dSRicardo Garcia } else if (value < -32767) {
52*b290403dSRicardo Garcia value = -32767;
53*b290403dSRicardo Garcia }
54*b290403dSRicardo Garcia *samples++ = value;
55*b290403dSRicardo Garcia }
56*b290403dSRicardo Garcia }
57*b290403dSRicardo Garcia
58*b290403dSRicardo Garcia /* Set the speed of the stream. */
sonicSetSpeed(float speed)59*b290403dSRicardo Garcia void sonicSetSpeed(float speed) { sonicStream.speed = speed; }
60*b290403dSRicardo Garcia
61*b290403dSRicardo Garcia /* Set the scaling factor of the stream. */
sonicSetVolume(float volume)62*b290403dSRicardo Garcia void sonicSetVolume(float volume) {
63*b290403dSRicardo Garcia sonicStream.volume = volume;
64*b290403dSRicardo Garcia }
65*b290403dSRicardo Garcia
66*b290403dSRicardo Garcia /* Create a sonic stream. Return NULL only if we are out of memory and cannot
67*b290403dSRicardo Garcia allocate the stream. */
sonicInit(void)68*b290403dSRicardo Garcia void sonicInit(void) {
69*b290403dSRicardo Garcia sonicStream.speed = 1.0;
70*b290403dSRicardo Garcia sonicStream.volume = 1.0f;
71*b290403dSRicardo Garcia sonicStream.numInputSamples = 0;;
72*b290403dSRicardo Garcia sonicStream.numOutputSamples = 0;
73*b290403dSRicardo Garcia sonicStream.remainingInputToCopy = 0;
74*b290403dSRicardo Garcia sonicStream.prevPeriod = 0;
75*b290403dSRicardo Garcia sonicStream.prevMinDiff = 0;
76*b290403dSRicardo Garcia }
77*b290403dSRicardo Garcia
78*b290403dSRicardo Garcia /* Add the input samples to the input buffer. */
addShortSamplesToInputBuffer(short * samples,int numSamples)79*b290403dSRicardo Garcia static int addShortSamplesToInputBuffer(short *samples,
80*b290403dSRicardo Garcia int numSamples) {
81*b290403dSRicardo Garcia if (numSamples == 0) {
82*b290403dSRicardo Garcia return 1;
83*b290403dSRicardo Garcia }
84*b290403dSRicardo Garcia memcpy(sonicStream.inputBuffer + sonicStream.numInputSamples,
85*b290403dSRicardo Garcia samples, numSamples * sizeof(short));
86*b290403dSRicardo Garcia sonicStream.numInputSamples += numSamples;
87*b290403dSRicardo Garcia return 1;
88*b290403dSRicardo Garcia }
89*b290403dSRicardo Garcia
90*b290403dSRicardo Garcia /* Remove input samples that we have already processed. */
removeInputSamples(int position)91*b290403dSRicardo Garcia static void removeInputSamples(int position) {
92*b290403dSRicardo Garcia int remainingSamples = sonicStream.numInputSamples - position;
93*b290403dSRicardo Garcia
94*b290403dSRicardo Garcia if (remainingSamples > 0) {
95*b290403dSRicardo Garcia memmove(sonicStream.inputBuffer,
96*b290403dSRicardo Garcia sonicStream.inputBuffer + position,
97*b290403dSRicardo Garcia remainingSamples * sizeof(short));
98*b290403dSRicardo Garcia }
99*b290403dSRicardo Garcia sonicStream.numInputSamples = remainingSamples;
100*b290403dSRicardo Garcia }
101*b290403dSRicardo Garcia
102*b290403dSRicardo Garcia /* Just copy from the array to the output buffer */
copyToOutput(short * samples,int numSamples)103*b290403dSRicardo Garcia static void copyToOutput(short *samples, int numSamples) {
104*b290403dSRicardo Garcia memcpy(sonicStream.outputBuffer + sonicStream.numOutputSamples,
105*b290403dSRicardo Garcia samples, numSamples * sizeof(short));
106*b290403dSRicardo Garcia sonicStream.numOutputSamples += numSamples;
107*b290403dSRicardo Garcia }
108*b290403dSRicardo Garcia
109*b290403dSRicardo Garcia /* Just copy from the input buffer to the output buffer. */
copyInputToOutput(int position)110*b290403dSRicardo Garcia static int copyInputToOutput(int position) {
111*b290403dSRicardo Garcia int numSamples = sonicStream.remainingInputToCopy;
112*b290403dSRicardo Garcia
113*b290403dSRicardo Garcia if (numSamples > 2 * SONIC_MAX_PERIOD) {
114*b290403dSRicardo Garcia numSamples = 2 * SONIC_MAX_PERIOD;
115*b290403dSRicardo Garcia }
116*b290403dSRicardo Garcia copyToOutput(sonicStream.inputBuffer + position, numSamples);
117*b290403dSRicardo Garcia sonicStream.remainingInputToCopy -= numSamples;
118*b290403dSRicardo Garcia return numSamples;
119*b290403dSRicardo Garcia }
120*b290403dSRicardo Garcia
121*b290403dSRicardo Garcia /* Read short data out of the stream. Sometimes no data will be available, and
122*b290403dSRicardo Garcia zero is returned, which is not an error condition. */
sonicReadShortFromStream(short * samples,int maxSamples)123*b290403dSRicardo Garcia int sonicReadShortFromStream(short *samples, int maxSamples) {
124*b290403dSRicardo Garcia int numSamples = sonicStream.numOutputSamples;
125*b290403dSRicardo Garcia int remainingSamples = 0;
126*b290403dSRicardo Garcia
127*b290403dSRicardo Garcia if (numSamples == 0) {
128*b290403dSRicardo Garcia return 0;
129*b290403dSRicardo Garcia }
130*b290403dSRicardo Garcia if (numSamples > maxSamples) {
131*b290403dSRicardo Garcia remainingSamples = numSamples - maxSamples;
132*b290403dSRicardo Garcia numSamples = maxSamples;
133*b290403dSRicardo Garcia }
134*b290403dSRicardo Garcia memcpy(samples, sonicStream.outputBuffer, numSamples * sizeof(short));
135*b290403dSRicardo Garcia if (remainingSamples > 0) {
136*b290403dSRicardo Garcia memmove(sonicStream.outputBuffer, sonicStream.outputBuffer + numSamples,
137*b290403dSRicardo Garcia remainingSamples * sizeof(short));
138*b290403dSRicardo Garcia }
139*b290403dSRicardo Garcia sonicStream.numOutputSamples = remainingSamples;
140*b290403dSRicardo Garcia return numSamples;
141*b290403dSRicardo Garcia }
142*b290403dSRicardo Garcia
143*b290403dSRicardo Garcia /* Force the sonic stream to generate output using whatever data it currently
144*b290403dSRicardo Garcia has. No extra delay will be added to the output, but flushing in the middle
145*b290403dSRicardo Garcia of words could introduce distortion. */
sonicFlushStream(void)146*b290403dSRicardo Garcia void sonicFlushStream(void) {
147*b290403dSRicardo Garcia int maxRequired = 2 * SONIC_MAX_PERIOD;
148*b290403dSRicardo Garcia int remainingSamples = sonicStream.numInputSamples;
149*b290403dSRicardo Garcia float speed = sonicStream.speed;
150*b290403dSRicardo Garcia int expectedOutputSamples = sonicStream.numOutputSamples + (int)((remainingSamples / speed) + 0.5f);
151*b290403dSRicardo Garcia
152*b290403dSRicardo Garcia memset(sonicStream.inputBuffer + remainingSamples, 0,
153*b290403dSRicardo Garcia sizeof(short) * (SONIC_INPUT_BUFFER_SIZE - remainingSamples));
154*b290403dSRicardo Garcia sonicStream.numInputSamples += 2 * maxRequired;
155*b290403dSRicardo Garcia sonicWriteShortToStream(NULL, 0);
156*b290403dSRicardo Garcia /* Throw away any extra samples we generated due to the silence we added */
157*b290403dSRicardo Garcia if (sonicStream.numOutputSamples > expectedOutputSamples) {
158*b290403dSRicardo Garcia sonicStream.numOutputSamples = expectedOutputSamples;
159*b290403dSRicardo Garcia }
160*b290403dSRicardo Garcia /* Empty input buffer */
161*b290403dSRicardo Garcia sonicStream.numInputSamples = 0;
162*b290403dSRicardo Garcia sonicStream.remainingInputToCopy = 0;
163*b290403dSRicardo Garcia }
164*b290403dSRicardo Garcia
165*b290403dSRicardo Garcia /* Return the number of samples in the output buffer */
sonicSamplesAvailable(void)166*b290403dSRicardo Garcia int sonicSamplesAvailable(void) {
167*b290403dSRicardo Garcia return sonicStream.numOutputSamples;
168*b290403dSRicardo Garcia }
169*b290403dSRicardo Garcia
170*b290403dSRicardo Garcia /* If skip is greater than one, average skip samples together and write them to
171*b290403dSRicardo Garcia the down-sample buffer. */
downSampleInput(short * samples)172*b290403dSRicardo Garcia static void downSampleInput(short *samples) {
173*b290403dSRicardo Garcia int numSamples = 2 * SONIC_MAX_PERIOD / SONIC_SKIP;
174*b290403dSRicardo Garcia int i, j;
175*b290403dSRicardo Garcia int value;
176*b290403dSRicardo Garcia short *downSamples = sonicStream.downSampleBuffer;
177*b290403dSRicardo Garcia
178*b290403dSRicardo Garcia for (i = 0; i < numSamples; i++) {
179*b290403dSRicardo Garcia value = 0;
180*b290403dSRicardo Garcia for (j = 0; j < SONIC_SKIP; j++) {
181*b290403dSRicardo Garcia value += *samples++;
182*b290403dSRicardo Garcia }
183*b290403dSRicardo Garcia value /= SONIC_SKIP;
184*b290403dSRicardo Garcia *downSamples++ = value;
185*b290403dSRicardo Garcia }
186*b290403dSRicardo Garcia }
187*b290403dSRicardo Garcia
188*b290403dSRicardo Garcia /* Find the best frequency match in the range, and given a sample skip multiple.
189*b290403dSRicardo Garcia For now, just find the pitch of the first channel. */
findPitchPeriodInRange(short * samples,int minPeriod,int maxPeriod,int * retMinDiff,int * retMaxDiff)190*b290403dSRicardo Garcia static int findPitchPeriodInRange(short *samples, int minPeriod, int maxPeriod,
191*b290403dSRicardo Garcia int* retMinDiff, int* retMaxDiff) {
192*b290403dSRicardo Garcia int period, bestPeriod = 0, worstPeriod = 255;
193*b290403dSRicardo Garcia short *s;
194*b290403dSRicardo Garcia short *p;
195*b290403dSRicardo Garcia short sVal, pVal;
196*b290403dSRicardo Garcia unsigned long diff, minDiff = 1, maxDiff = 0;
197*b290403dSRicardo Garcia int i;
198*b290403dSRicardo Garcia
199*b290403dSRicardo Garcia for (period = minPeriod; period <= maxPeriod; period++) {
200*b290403dSRicardo Garcia diff = 0;
201*b290403dSRicardo Garcia s = samples;
202*b290403dSRicardo Garcia p = samples + period;
203*b290403dSRicardo Garcia for (i = 0; i < period; i++) {
204*b290403dSRicardo Garcia sVal = *s++;
205*b290403dSRicardo Garcia pVal = *p++;
206*b290403dSRicardo Garcia diff += sVal >= pVal ? (unsigned short)(sVal - pVal)
207*b290403dSRicardo Garcia : (unsigned short)(pVal - sVal);
208*b290403dSRicardo Garcia }
209*b290403dSRicardo Garcia /* Note that the highest number of samples we add into diff will be less
210*b290403dSRicardo Garcia than 256, since we skip samples. Thus, diff is a 24 bit number, and
211*b290403dSRicardo Garcia we can safely multiply by numSamples without overflow */
212*b290403dSRicardo Garcia if (bestPeriod == 0 || diff * bestPeriod < minDiff * period) {
213*b290403dSRicardo Garcia minDiff = diff;
214*b290403dSRicardo Garcia bestPeriod = period;
215*b290403dSRicardo Garcia }
216*b290403dSRicardo Garcia if (diff * worstPeriod > maxDiff * period) {
217*b290403dSRicardo Garcia maxDiff = diff;
218*b290403dSRicardo Garcia worstPeriod = period;
219*b290403dSRicardo Garcia }
220*b290403dSRicardo Garcia }
221*b290403dSRicardo Garcia *retMinDiff = minDiff / bestPeriod;
222*b290403dSRicardo Garcia *retMaxDiff = maxDiff / worstPeriod;
223*b290403dSRicardo Garcia return bestPeriod;
224*b290403dSRicardo Garcia }
225*b290403dSRicardo Garcia
226*b290403dSRicardo Garcia /* At abrupt ends of voiced words, we can have pitch periods that are better
227*b290403dSRicardo Garcia approximated by the previous pitch period estimate. Try to detect this case. */
prevPeriodBetter(int minDiff,int maxDiff,int preferNewPeriod)228*b290403dSRicardo Garcia static int prevPeriodBetter(int minDiff, int maxDiff, int preferNewPeriod) {
229*b290403dSRicardo Garcia if (minDiff == 0 || sonicStream.prevPeriod == 0) {
230*b290403dSRicardo Garcia return 0;
231*b290403dSRicardo Garcia }
232*b290403dSRicardo Garcia if (preferNewPeriod) {
233*b290403dSRicardo Garcia if (maxDiff > minDiff * 3) {
234*b290403dSRicardo Garcia /* Got a reasonable match this period */
235*b290403dSRicardo Garcia return 0;
236*b290403dSRicardo Garcia }
237*b290403dSRicardo Garcia if (minDiff * 2 <= sonicStream.prevMinDiff * 3) {
238*b290403dSRicardo Garcia /* Mismatch is not that much greater this period */
239*b290403dSRicardo Garcia return 0;
240*b290403dSRicardo Garcia }
241*b290403dSRicardo Garcia } else {
242*b290403dSRicardo Garcia if (minDiff <= sonicStream.prevMinDiff) {
243*b290403dSRicardo Garcia return 0;
244*b290403dSRicardo Garcia }
245*b290403dSRicardo Garcia }
246*b290403dSRicardo Garcia return 1;
247*b290403dSRicardo Garcia }
248*b290403dSRicardo Garcia
249*b290403dSRicardo Garcia /* Find the pitch period. This is a critical step, and we may have to try
250*b290403dSRicardo Garcia multiple ways to get a good answer. This version uses Average Magnitude
251*b290403dSRicardo Garcia Difference Function (AMDF). To improve speed, we down sample by an integer
252*b290403dSRicardo Garcia factor get in the 11KHz range, and then do it again with a narrower
253*b290403dSRicardo Garcia frequency range without down sampling */
findPitchPeriod(short * samples,int preferNewPeriod)254*b290403dSRicardo Garcia static int findPitchPeriod(short *samples, int preferNewPeriod) {
255*b290403dSRicardo Garcia int minPeriod = SONIC_MIN_PERIOD;
256*b290403dSRicardo Garcia int maxPeriod = SONIC_MAX_PERIOD;
257*b290403dSRicardo Garcia int minDiff, maxDiff, retPeriod;
258*b290403dSRicardo Garcia int period;
259*b290403dSRicardo Garcia
260*b290403dSRicardo Garcia if (SONIC_SKIP == 1) {
261*b290403dSRicardo Garcia period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
262*b290403dSRicardo Garcia } else {
263*b290403dSRicardo Garcia downSampleInput(samples);
264*b290403dSRicardo Garcia period = findPitchPeriodInRange(sonicStream.downSampleBuffer, minPeriod / SONIC_SKIP,
265*b290403dSRicardo Garcia maxPeriod / SONIC_SKIP, &minDiff, &maxDiff);
266*b290403dSRicardo Garcia period *= SONIC_SKIP;
267*b290403dSRicardo Garcia minPeriod = period - (SONIC_SKIP << 2);
268*b290403dSRicardo Garcia maxPeriod = period + (SONIC_SKIP << 2);
269*b290403dSRicardo Garcia if (minPeriod < SONIC_MIN_PERIOD) {
270*b290403dSRicardo Garcia minPeriod = SONIC_MIN_PERIOD;
271*b290403dSRicardo Garcia }
272*b290403dSRicardo Garcia if (maxPeriod > SONIC_MAX_PERIOD) {
273*b290403dSRicardo Garcia maxPeriod = SONIC_MAX_PERIOD;
274*b290403dSRicardo Garcia }
275*b290403dSRicardo Garcia period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
276*b290403dSRicardo Garcia }
277*b290403dSRicardo Garcia if (prevPeriodBetter(minDiff, maxDiff, preferNewPeriod)) {
278*b290403dSRicardo Garcia retPeriod = sonicStream.prevPeriod;
279*b290403dSRicardo Garcia } else {
280*b290403dSRicardo Garcia retPeriod = period;
281*b290403dSRicardo Garcia }
282*b290403dSRicardo Garcia sonicStream.prevMinDiff = minDiff;
283*b290403dSRicardo Garcia sonicStream.prevPeriod = period;
284*b290403dSRicardo Garcia return retPeriod;
285*b290403dSRicardo Garcia }
286*b290403dSRicardo Garcia
287*b290403dSRicardo Garcia /* Overlap two sound segments, ramp the volume of one down, while ramping the
288*b290403dSRicardo Garcia other one from zero up, and add them, storing the result at the output. */
overlapAdd(int numSamples,short * out,short * rampDown,short * rampUp)289*b290403dSRicardo Garcia static void overlapAdd(int numSamples, short *out, short *rampDown, short *rampUp) {
290*b290403dSRicardo Garcia short *o;
291*b290403dSRicardo Garcia short *u;
292*b290403dSRicardo Garcia short *d;
293*b290403dSRicardo Garcia int t;
294*b290403dSRicardo Garcia
295*b290403dSRicardo Garcia o = out;
296*b290403dSRicardo Garcia u = rampUp;
297*b290403dSRicardo Garcia d = rampDown;
298*b290403dSRicardo Garcia for (t = 0; t < numSamples; t++) {
299*b290403dSRicardo Garcia *o = (*d * (numSamples - t) + *u * t) / numSamples;
300*b290403dSRicardo Garcia o++;
301*b290403dSRicardo Garcia d++;
302*b290403dSRicardo Garcia u++;
303*b290403dSRicardo Garcia }
304*b290403dSRicardo Garcia }
305*b290403dSRicardo Garcia
306*b290403dSRicardo Garcia /* Skip over a pitch period, and copy period/speed samples to the output */
skipPitchPeriod(short * samples,float speed,int period)307*b290403dSRicardo Garcia static int skipPitchPeriod(short *samples, float speed, int period) {
308*b290403dSRicardo Garcia long newSamples;
309*b290403dSRicardo Garcia
310*b290403dSRicardo Garcia if (speed >= 2.0f) {
311*b290403dSRicardo Garcia newSamples = period / (speed - 1.0f);
312*b290403dSRicardo Garcia } else {
313*b290403dSRicardo Garcia newSamples = period;
314*b290403dSRicardo Garcia sonicStream.remainingInputToCopy = period * (2.0f - speed) / (speed - 1.0f);
315*b290403dSRicardo Garcia }
316*b290403dSRicardo Garcia overlapAdd(newSamples, sonicStream.outputBuffer + sonicStream.numOutputSamples,
317*b290403dSRicardo Garcia samples, samples + period);
318*b290403dSRicardo Garcia sonicStream.numOutputSamples += newSamples;
319*b290403dSRicardo Garcia return newSamples;
320*b290403dSRicardo Garcia }
321*b290403dSRicardo Garcia
322*b290403dSRicardo Garcia /* Resample as many pitch periods as we have buffered on the input. */
changeSpeed(float speed)323*b290403dSRicardo Garcia static void changeSpeed(float speed) {
324*b290403dSRicardo Garcia short *samples;
325*b290403dSRicardo Garcia int numSamples = sonicStream.numInputSamples;
326*b290403dSRicardo Garcia int position = 0, period, newSamples;
327*b290403dSRicardo Garcia int maxRequired = 2 * SONIC_MAX_PERIOD;
328*b290403dSRicardo Garcia
329*b290403dSRicardo Garcia /* printf("Changing speed to %f\n", speed); */
330*b290403dSRicardo Garcia if (sonicStream.numInputSamples < maxRequired) {
331*b290403dSRicardo Garcia return;
332*b290403dSRicardo Garcia }
333*b290403dSRicardo Garcia do {
334*b290403dSRicardo Garcia if (sonicStream.remainingInputToCopy > 0) {
335*b290403dSRicardo Garcia newSamples = copyInputToOutput(position);
336*b290403dSRicardo Garcia position += newSamples;
337*b290403dSRicardo Garcia } else {
338*b290403dSRicardo Garcia samples = sonicStream.inputBuffer + position;
339*b290403dSRicardo Garcia period = findPitchPeriod(samples, 1);
340*b290403dSRicardo Garcia newSamples = skipPitchPeriod(samples, speed, period);
341*b290403dSRicardo Garcia position += period + newSamples;
342*b290403dSRicardo Garcia }
343*b290403dSRicardo Garcia } while (position + maxRequired <= numSamples);
344*b290403dSRicardo Garcia removeInputSamples(position);
345*b290403dSRicardo Garcia }
346*b290403dSRicardo Garcia
347*b290403dSRicardo Garcia /* Resample as many pitch periods as we have buffered on the input. Also scale
348*b290403dSRicardo Garcia the output by the volume. */
processStreamInput(void)349*b290403dSRicardo Garcia static void processStreamInput(void) {
350*b290403dSRicardo Garcia int originalNumOutputSamples = sonicStream.numOutputSamples;
351*b290403dSRicardo Garcia float speed = sonicStream.speed;
352*b290403dSRicardo Garcia
353*b290403dSRicardo Garcia if (speed > 1.00001) {
354*b290403dSRicardo Garcia changeSpeed(speed);
355*b290403dSRicardo Garcia } else {
356*b290403dSRicardo Garcia copyToOutput(sonicStream.inputBuffer, sonicStream.numInputSamples);
357*b290403dSRicardo Garcia sonicStream.numInputSamples = 0;
358*b290403dSRicardo Garcia }
359*b290403dSRicardo Garcia if (sonicStream.volume != 1.0f) {
360*b290403dSRicardo Garcia /* Adjust output volume. */
361*b290403dSRicardo Garcia scaleSamples( sonicStream.outputBuffer + originalNumOutputSamples,
362*b290403dSRicardo Garcia (sonicStream.numOutputSamples - originalNumOutputSamples), sonicStream.volume);
363*b290403dSRicardo Garcia }
364*b290403dSRicardo Garcia }
365*b290403dSRicardo Garcia
366*b290403dSRicardo Garcia /* Simple wrapper around sonicWriteFloatToStream that does the short to float
367*b290403dSRicardo Garcia conversion for you. */
sonicWriteShortToStream(short * samples,int numSamples)368*b290403dSRicardo Garcia void sonicWriteShortToStream(short *samples, int numSamples) {
369*b290403dSRicardo Garcia addShortSamplesToInputBuffer(samples, numSamples);
370*b290403dSRicardo Garcia processStreamInput();
371*b290403dSRicardo Garcia }
372