xref: /aosp_15_r20/external/sonic/sonic_lite.c (revision b290403dc9d28f89f133eb7e190ea8185d440ecd)
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