xref: /aosp_15_r20/external/sonic/sonic.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 #include "sonic.h"
10*b290403dSRicardo Garcia 
11*b290403dSRicardo Garcia #include <limits.h>
12*b290403dSRicardo Garcia #include <math.h>
13*b290403dSRicardo Garcia #include <stdlib.h>
14*b290403dSRicardo Garcia #include <string.h>
15*b290403dSRicardo Garcia 
16*b290403dSRicardo Garcia /*
17*b290403dSRicardo Garcia     The following code was used to generate the following sinc lookup table.
18*b290403dSRicardo Garcia 
19*b290403dSRicardo Garcia     #include <limits.h>
20*b290403dSRicardo Garcia     #include <math.h>
21*b290403dSRicardo Garcia     #include <stdio.h>
22*b290403dSRicardo Garcia 
23*b290403dSRicardo Garcia     double findHannWeight(int N, double x) {
24*b290403dSRicardo Garcia         return 0.5*(1.0 - cos(2*M_PI*x/N));
25*b290403dSRicardo Garcia     }
26*b290403dSRicardo Garcia 
27*b290403dSRicardo Garcia     double findSincCoefficient(int N, double x) {
28*b290403dSRicardo Garcia         double hannWindowWeight = findHannWeight(N, x);
29*b290403dSRicardo Garcia         double sincWeight;
30*b290403dSRicardo Garcia 
31*b290403dSRicardo Garcia         x -= N/2.0;
32*b290403dSRicardo Garcia         if (x > 1e-9 || x < -1e-9) {
33*b290403dSRicardo Garcia             sincWeight = sin(M_PI*x)/(M_PI*x);
34*b290403dSRicardo Garcia         } else {
35*b290403dSRicardo Garcia             sincWeight = 1.0;
36*b290403dSRicardo Garcia         }
37*b290403dSRicardo Garcia         return hannWindowWeight*sincWeight;
38*b290403dSRicardo Garcia     }
39*b290403dSRicardo Garcia 
40*b290403dSRicardo Garcia     int main() {
41*b290403dSRicardo Garcia         double x;
42*b290403dSRicardo Garcia         int i;
43*b290403dSRicardo Garcia         int N = 12;
44*b290403dSRicardo Garcia 
45*b290403dSRicardo Garcia         for (i = 0, x = 0.0; x <= N; x += 0.02, i++) {
46*b290403dSRicardo Garcia             printf("%u %d\n", i, (int)(SHRT_MAX*findSincCoefficient(N, x)));
47*b290403dSRicardo Garcia         }
48*b290403dSRicardo Garcia         return 0;
49*b290403dSRicardo Garcia     }
50*b290403dSRicardo Garcia */
51*b290403dSRicardo Garcia 
52*b290403dSRicardo Garcia /* The number of points to use in the sinc FIR filter for resampling. */
53*b290403dSRicardo Garcia #define SINC_FILTER_POINTS \
54*b290403dSRicardo Garcia   12 /* I am not able to hear improvement with higher N. */
55*b290403dSRicardo Garcia #define SINC_TABLE_SIZE 601
56*b290403dSRicardo Garcia 
57*b290403dSRicardo Garcia /* Lookup table for windowed sinc function of SINC_FILTER_POINTS points. */
58*b290403dSRicardo Garcia static short sincTable[SINC_TABLE_SIZE] = {
59*b290403dSRicardo Garcia     0,     0,     0,     0,     0,     0,     0,     -1,    -1,    -2,    -2,
60*b290403dSRicardo Garcia     -3,    -4,    -6,    -7,    -9,    -10,   -12,   -14,   -17,   -19,   -21,
61*b290403dSRicardo Garcia     -24,   -26,   -29,   -32,   -34,   -37,   -40,   -42,   -44,   -47,   -48,
62*b290403dSRicardo Garcia     -50,   -51,   -52,   -53,   -53,   -53,   -52,   -50,   -48,   -46,   -43,
63*b290403dSRicardo Garcia     -39,   -34,   -29,   -22,   -16,   -8,    0,     9,     19,    29,    41,
64*b290403dSRicardo Garcia     53,    65,    79,    92,    107,   121,   137,   152,   168,   184,   200,
65*b290403dSRicardo Garcia     215,   231,   247,   262,   276,   291,   304,   317,   328,   339,   348,
66*b290403dSRicardo Garcia     357,   363,   369,   372,   374,   375,   373,   369,   363,   355,   345,
67*b290403dSRicardo Garcia     332,   318,   300,   281,   259,   234,   208,   178,   147,   113,   77,
68*b290403dSRicardo Garcia     39,    0,     -41,   -85,   -130,  -177,  -225,  -274,  -324,  -375,  -426,
69*b290403dSRicardo Garcia     -478,  -530,  -581,  -632,  -682,  -731,  -779,  -825,  -870,  -912,  -951,
70*b290403dSRicardo Garcia     -989,  -1023, -1053, -1080, -1104, -1123, -1138, -1149, -1154, -1155, -1151,
71*b290403dSRicardo Garcia     -1141, -1125, -1105, -1078, -1046, -1007, -963,  -913,  -857,  -796,  -728,
72*b290403dSRicardo Garcia     -655,  -576,  -492,  -403,  -309,  -210,  -107,  0,     111,   225,   342,
73*b290403dSRicardo Garcia     462,   584,   708,   833,   958,   1084,  1209,  1333,  1455,  1575,  1693,
74*b290403dSRicardo Garcia     1807,  1916,  2022,  2122,  2216,  2304,  2384,  2457,  2522,  2579,  2625,
75*b290403dSRicardo Garcia     2663,  2689,  2706,  2711,  2705,  2687,  2657,  2614,  2559,  2491,  2411,
76*b290403dSRicardo Garcia     2317,  2211,  2092,  1960,  1815,  1658,  1489,  1308,  1115,  912,   698,
77*b290403dSRicardo Garcia     474,   241,   0,     -249,  -506,  -769,  -1037, -1310, -1586, -1864, -2144,
78*b290403dSRicardo Garcia     -2424, -2703, -2980, -3254, -3523, -3787, -4043, -4291, -4529, -4757, -4972,
79*b290403dSRicardo Garcia     -5174, -5360, -5531, -5685, -5819, -5935, -6029, -6101, -6150, -6175, -6175,
80*b290403dSRicardo Garcia     -6149, -6096, -6015, -5905, -5767, -5599, -5401, -5172, -4912, -4621, -4298,
81*b290403dSRicardo Garcia     -3944, -3558, -3141, -2693, -2214, -1705, -1166, -597,  0,     625,   1277,
82*b290403dSRicardo Garcia     1955,  2658,  3386,  4135,  4906,  5697,  6506,  7332,  8173,  9027,  9893,
83*b290403dSRicardo Garcia     10769, 11654, 12544, 13439, 14335, 15232, 16128, 17019, 17904, 18782, 19649,
84*b290403dSRicardo Garcia     20504, 21345, 22170, 22977, 23763, 24527, 25268, 25982, 26669, 27327, 27953,
85*b290403dSRicardo Garcia     28547, 29107, 29632, 30119, 30569, 30979, 31349, 31678, 31964, 32208, 32408,
86*b290403dSRicardo Garcia     32565, 32677, 32744, 32767, 32744, 32677, 32565, 32408, 32208, 31964, 31678,
87*b290403dSRicardo Garcia     31349, 30979, 30569, 30119, 29632, 29107, 28547, 27953, 27327, 26669, 25982,
88*b290403dSRicardo Garcia     25268, 24527, 23763, 22977, 22170, 21345, 20504, 19649, 18782, 17904, 17019,
89*b290403dSRicardo Garcia     16128, 15232, 14335, 13439, 12544, 11654, 10769, 9893,  9027,  8173,  7332,
90*b290403dSRicardo Garcia     6506,  5697,  4906,  4135,  3386,  2658,  1955,  1277,  625,   0,     -597,
91*b290403dSRicardo Garcia     -1166, -1705, -2214, -2693, -3141, -3558, -3944, -4298, -4621, -4912, -5172,
92*b290403dSRicardo Garcia     -5401, -5599, -5767, -5905, -6015, -6096, -6149, -6175, -6175, -6150, -6101,
93*b290403dSRicardo Garcia     -6029, -5935, -5819, -5685, -5531, -5360, -5174, -4972, -4757, -4529, -4291,
94*b290403dSRicardo Garcia     -4043, -3787, -3523, -3254, -2980, -2703, -2424, -2144, -1864, -1586, -1310,
95*b290403dSRicardo Garcia     -1037, -769,  -506,  -249,  0,     241,   474,   698,   912,   1115,  1308,
96*b290403dSRicardo Garcia     1489,  1658,  1815,  1960,  2092,  2211,  2317,  2411,  2491,  2559,  2614,
97*b290403dSRicardo Garcia     2657,  2687,  2705,  2711,  2706,  2689,  2663,  2625,  2579,  2522,  2457,
98*b290403dSRicardo Garcia     2384,  2304,  2216,  2122,  2022,  1916,  1807,  1693,  1575,  1455,  1333,
99*b290403dSRicardo Garcia     1209,  1084,  958,   833,   708,   584,   462,   342,   225,   111,   0,
100*b290403dSRicardo Garcia     -107,  -210,  -309,  -403,  -492,  -576,  -655,  -728,  -796,  -857,  -913,
101*b290403dSRicardo Garcia     -963,  -1007, -1046, -1078, -1105, -1125, -1141, -1151, -1155, -1154, -1149,
102*b290403dSRicardo Garcia     -1138, -1123, -1104, -1080, -1053, -1023, -989,  -951,  -912,  -870,  -825,
103*b290403dSRicardo Garcia     -779,  -731,  -682,  -632,  -581,  -530,  -478,  -426,  -375,  -324,  -274,
104*b290403dSRicardo Garcia     -225,  -177,  -130,  -85,   -41,   0,     39,    77,    113,   147,   178,
105*b290403dSRicardo Garcia     208,   234,   259,   281,   300,   318,   332,   345,   355,   363,   369,
106*b290403dSRicardo Garcia     373,   375,   374,   372,   369,   363,   357,   348,   339,   328,   317,
107*b290403dSRicardo Garcia     304,   291,   276,   262,   247,   231,   215,   200,   184,   168,   152,
108*b290403dSRicardo Garcia     137,   121,   107,   92,    79,    65,    53,    41,    29,    19,    9,
109*b290403dSRicardo Garcia     0,     -8,    -16,   -22,   -29,   -34,   -39,   -43,   -46,   -48,   -50,
110*b290403dSRicardo Garcia     -52,   -53,   -53,   -53,   -52,   -51,   -50,   -48,   -47,   -44,   -42,
111*b290403dSRicardo Garcia     -40,   -37,   -34,   -32,   -29,   -26,   -24,   -21,   -19,   -17,   -14,
112*b290403dSRicardo Garcia     -12,   -10,   -9,    -7,    -6,    -4,    -3,    -2,    -2,    -1,    -1,
113*b290403dSRicardo Garcia     0,     0,     0,     0,     0,     0,     0};
114*b290403dSRicardo Garcia 
115*b290403dSRicardo Garcia /* These functions allocate out of a static array rather than calling
116*b290403dSRicardo Garcia    calloc/realloc/free if the NO_MALLOC flag is defined.  Otherwise, call
117*b290403dSRicardo Garcia    calloc/realloc/free as usual.  This is useful for running on small
118*b290403dSRicardo Garcia    microcontrollers. */
119*b290403dSRicardo Garcia #ifndef SONIC_NO_MALLOC
120*b290403dSRicardo Garcia 
121*b290403dSRicardo Garcia /* Just call calloc. */
sonicCalloc(int num,int size)122*b290403dSRicardo Garcia static void *sonicCalloc(int num, int size) {
123*b290403dSRicardo Garcia   return calloc(num, size);
124*b290403dSRicardo Garcia }
125*b290403dSRicardo Garcia 
126*b290403dSRicardo Garcia /* Just call realloc */
sonicRealloc(void * p,int oldNum,int newNum,int size)127*b290403dSRicardo Garcia static void *sonicRealloc(void *p, int oldNum, int newNum, int size) {
128*b290403dSRicardo Garcia   return realloc(p, newNum * size);
129*b290403dSRicardo Garcia }
130*b290403dSRicardo Garcia 
131*b290403dSRicardo Garcia /* Just call free. */
sonicFree(void * p)132*b290403dSRicardo Garcia static void sonicFree(void *p) {
133*b290403dSRicardo Garcia   free(p);
134*b290403dSRicardo Garcia }
135*b290403dSRicardo Garcia 
136*b290403dSRicardo Garcia #else
137*b290403dSRicardo Garcia 
138*b290403dSRicardo Garcia #ifndef SONIC_MAX_MEMORY
139*b290403dSRicardo Garcia /* Large enough for speedup/slowdown at 8KHz, 16-bit mono samples/second. */
140*b290403dSRicardo Garcia #define SONIC_MAX_MEMORY (16 * 1024)
141*b290403dSRicardo Garcia #endif
142*b290403dSRicardo Garcia 
143*b290403dSRicardo Garcia /* This static buffer is used to hold data allocated for the sonicStream struct
144*b290403dSRicardo Garcia    and its buffers.  There should never be more than one sonicStream in use at a
145*b290403dSRicardo Garcia    time when using SONIC_NO_MALLOC mode.  Calls to realloc move the data to the
146*b290403dSRicardo Garcia    end of memoryBuffer.  Calls to free reset the memory buffer to empty. */
147*b290403dSRicardo Garcia static void*
148*b290403dSRicardo Garcia     memoryBufferAligned[(SONIC_MAX_MEMORY + sizeof(void) - 1) / sizeof(void*)];
149*b290403dSRicardo Garcia static unsigned char* memoryBuffer = (unsigned char*)memoryBufferAligned;
150*b290403dSRicardo Garcia static int memoryBufferPos = 0;
151*b290403dSRicardo Garcia 
152*b290403dSRicardo Garcia /* Allocate elements from a static memory buffer. */
sonicCalloc(int num,int size)153*b290403dSRicardo Garcia static void *sonicCalloc(int num, int size) {
154*b290403dSRicardo Garcia   int len = num * size;
155*b290403dSRicardo Garcia 
156*b290403dSRicardo Garcia   if (memoryBufferPos + len > SONIC_MAX_MEMORY) {
157*b290403dSRicardo Garcia     return 0;
158*b290403dSRicardo Garcia   }
159*b290403dSRicardo Garcia   unsigned char *p = memoryBuffer + memoryBufferPos;
160*b290403dSRicardo Garcia   memoryBufferPos += len;
161*b290403dSRicardo Garcia   memset(p, 0, len);
162*b290403dSRicardo Garcia   return p;
163*b290403dSRicardo Garcia }
164*b290403dSRicardo Garcia 
165*b290403dSRicardo Garcia /* Preferably, SONIC_MAX_MEMORY has been set large enough that this is never
166*b290403dSRicardo Garcia  * called. */
sonicRealloc(void * p,int oldNum,int newNum,int size)167*b290403dSRicardo Garcia static void *sonicRealloc(void *p, int oldNum, int newNum, int size) {
168*b290403dSRicardo Garcia   if (newNum <= oldNum) {
169*b290403dSRicardo Garcia     return p;
170*b290403dSRicardo Garcia   }
171*b290403dSRicardo Garcia   void *newBuffer = sonicCalloc(newNum, size);
172*b290403dSRicardo Garcia   if (newBuffer == NULL) {
173*b290403dSRicardo Garcia     return NULL;
174*b290403dSRicardo Garcia   }
175*b290403dSRicardo Garcia   memcpy(newBuffer, p, oldNum * size);
176*b290403dSRicardo Garcia   return newBuffer;
177*b290403dSRicardo Garcia }
178*b290403dSRicardo Garcia 
179*b290403dSRicardo Garcia /* Reset memoryBufferPos to 0.  We asssume all data is freed at the same time. */
sonicFree(void * p)180*b290403dSRicardo Garcia static void sonicFree(void *p) {
181*b290403dSRicardo Garcia   memoryBufferPos = 0;
182*b290403dSRicardo Garcia }
183*b290403dSRicardo Garcia 
184*b290403dSRicardo Garcia #endif
185*b290403dSRicardo Garcia 
186*b290403dSRicardo Garcia struct sonicStreamStruct {
187*b290403dSRicardo Garcia #ifdef SONIC_SPECTROGRAM
188*b290403dSRicardo Garcia   sonicSpectrogram spectrogram;
189*b290403dSRicardo Garcia #endif  /* SONIC_SPECTROGRAM */
190*b290403dSRicardo Garcia   short* inputBuffer;
191*b290403dSRicardo Garcia   short* outputBuffer;
192*b290403dSRicardo Garcia   short* pitchBuffer;
193*b290403dSRicardo Garcia   short* downSampleBuffer;
194*b290403dSRicardo Garcia   void* userData;
195*b290403dSRicardo Garcia   float speed;
196*b290403dSRicardo Garcia   float volume;
197*b290403dSRicardo Garcia   float pitch;
198*b290403dSRicardo Garcia   float rate;
199*b290403dSRicardo Garcia   /* The point of the following 3 new variables is to gracefully handle rapidly
200*b290403dSRicardo Garcia      changing input speed.
201*b290403dSRicardo Garcia 
202*b290403dSRicardo Garcia      samplePeriod is just 1.0/sampleRate.  It is used in accumulating
203*b290403dSRicardo Garcia      inputPlayTime, which is how long we expect the total time should be to play
204*b290403dSRicardo Garcia      the current input samples in the input buffer.  timeError keeps track of
205*b290403dSRicardo Garcia      the error in play time created when playing < 2.0X speed, where we either
206*b290403dSRicardo Garcia      insert or delete a whole pitch period.  This can cause the output generated
207*b290403dSRicardo Garcia      from the input to be off in play time by up to a pitch period.  timeError
208*b290403dSRicardo Garcia      replaces PICOLA's concept of the number of samples to play unmodified after
209*b290403dSRicardo Garcia      a pitch period insertion or deletion.  If speeding up, and the error is >=
210*b290403dSRicardo Garcia      0.0, then remove a pitch period, and play samples unmodified until
211*b290403dSRicardo Garcia      timeError is >= 0 again.  If slowing down, and the error is <= 0.0,
212*b290403dSRicardo Garcia      then add a pitch period, and play samples unmodified until timeError is <=
213*b290403dSRicardo Garcia      0 again. */
214*b290403dSRicardo Garcia   float samplePeriod;  /* How long each output sample takes to play. */
215*b290403dSRicardo Garcia   /* How long we expect the entire input buffer to take to play. */
216*b290403dSRicardo Garcia   float inputPlayTime;
217*b290403dSRicardo Garcia   /* The difference in when the latest output sample was played vs when we wanted.  */
218*b290403dSRicardo Garcia   float timeError;
219*b290403dSRicardo Garcia   int oldRatePosition;
220*b290403dSRicardo Garcia   int newRatePosition;
221*b290403dSRicardo Garcia   int quality;
222*b290403dSRicardo Garcia   int numChannels;
223*b290403dSRicardo Garcia   int inputBufferSize;
224*b290403dSRicardo Garcia   int pitchBufferSize;
225*b290403dSRicardo Garcia   int outputBufferSize;
226*b290403dSRicardo Garcia   int numInputSamples;
227*b290403dSRicardo Garcia   int numOutputSamples;
228*b290403dSRicardo Garcia   int numPitchSamples;
229*b290403dSRicardo Garcia   int minPeriod;
230*b290403dSRicardo Garcia   int maxPeriod;
231*b290403dSRicardo Garcia   int maxRequired;
232*b290403dSRicardo Garcia   int remainingInputToCopy;
233*b290403dSRicardo Garcia   int sampleRate;
234*b290403dSRicardo Garcia   int prevPeriod;
235*b290403dSRicardo Garcia   int prevMinDiff;
236*b290403dSRicardo Garcia };
237*b290403dSRicardo Garcia 
238*b290403dSRicardo Garcia /* Attach user data to the stream. */
sonicSetUserData(sonicStream stream,void * userData)239*b290403dSRicardo Garcia void sonicSetUserData(sonicStream stream, void *userData) {
240*b290403dSRicardo Garcia   stream->userData = userData;
241*b290403dSRicardo Garcia }
242*b290403dSRicardo Garcia 
243*b290403dSRicardo Garcia /* Retrieve user data attached to the stream. */
sonicGetUserData(sonicStream stream)244*b290403dSRicardo Garcia void *sonicGetUserData(sonicStream stream) {
245*b290403dSRicardo Garcia   return stream->userData;
246*b290403dSRicardo Garcia }
247*b290403dSRicardo Garcia 
248*b290403dSRicardo Garcia #ifdef SONIC_SPECTROGRAM
249*b290403dSRicardo Garcia 
250*b290403dSRicardo Garcia /* Compute a spectrogram on the fly. */
sonicComputeSpectrogram(sonicStream stream)251*b290403dSRicardo Garcia void sonicComputeSpectrogram(sonicStream stream) {
252*b290403dSRicardo Garcia   stream->spectrogram = sonicCreateSpectrogram(stream->sampleRate);
253*b290403dSRicardo Garcia   /* Force changeSpeed to be called to compute the spectrogram. */
254*b290403dSRicardo Garcia   sonicSetSpeed(stream, 2.0);
255*b290403dSRicardo Garcia }
256*b290403dSRicardo Garcia 
257*b290403dSRicardo Garcia /* Get the spectrogram. */
sonicGetSpectrogram(sonicStream stream)258*b290403dSRicardo Garcia sonicSpectrogram sonicGetSpectrogram(sonicStream stream) {
259*b290403dSRicardo Garcia   return stream->spectrogram;
260*b290403dSRicardo Garcia }
261*b290403dSRicardo Garcia 
262*b290403dSRicardo Garcia #endif
263*b290403dSRicardo Garcia 
264*b290403dSRicardo Garcia /* Scale the samples by the factor. */
scaleSamples(short * samples,int numSamples,float volume)265*b290403dSRicardo Garcia static void scaleSamples(short* samples, int numSamples, float volume) {
266*b290403dSRicardo Garcia   /* This is 24-bit integer and 8-bit fraction fixed-point representation. */
267*b290403dSRicardo Garcia   int fixedPointVolume = volume * 256.0f;
268*b290403dSRicardo Garcia   int value;
269*b290403dSRicardo Garcia 
270*b290403dSRicardo Garcia   while (numSamples--) {
271*b290403dSRicardo Garcia     value = (*samples * fixedPointVolume) >> 8;
272*b290403dSRicardo Garcia     if (value > 32767) {
273*b290403dSRicardo Garcia       value = 32767;
274*b290403dSRicardo Garcia     } else if (value < -32767) {
275*b290403dSRicardo Garcia       value = -32767;
276*b290403dSRicardo Garcia     }
277*b290403dSRicardo Garcia     *samples++ = value;
278*b290403dSRicardo Garcia   }
279*b290403dSRicardo Garcia }
280*b290403dSRicardo Garcia 
281*b290403dSRicardo Garcia /* Get the speed of the stream. */
sonicGetSpeed(sonicStream stream)282*b290403dSRicardo Garcia float sonicGetSpeed(sonicStream stream) { return stream->speed; }
283*b290403dSRicardo Garcia 
284*b290403dSRicardo Garcia /* Set the speed of the stream. */
sonicSetSpeed(sonicStream stream,float speed)285*b290403dSRicardo Garcia void sonicSetSpeed(sonicStream stream, float speed) { stream->speed = speed; }
286*b290403dSRicardo Garcia 
287*b290403dSRicardo Garcia /* Get the pitch of the stream. */
sonicGetPitch(sonicStream stream)288*b290403dSRicardo Garcia float sonicGetPitch(sonicStream stream) { return stream->pitch; }
289*b290403dSRicardo Garcia 
290*b290403dSRicardo Garcia /* Set the pitch of the stream. */
sonicSetPitch(sonicStream stream,float pitch)291*b290403dSRicardo Garcia void sonicSetPitch(sonicStream stream, float pitch) { stream->pitch = pitch; }
292*b290403dSRicardo Garcia 
293*b290403dSRicardo Garcia /* Get the rate of the stream. */
sonicGetRate(sonicStream stream)294*b290403dSRicardo Garcia float sonicGetRate(sonicStream stream) { return stream->rate; }
295*b290403dSRicardo Garcia 
296*b290403dSRicardo Garcia /* Set the playback rate of the stream. This scales pitch and speed at the same
297*b290403dSRicardo Garcia    time. */
sonicSetRate(sonicStream stream,float rate)298*b290403dSRicardo Garcia void sonicSetRate(sonicStream stream, float rate) {
299*b290403dSRicardo Garcia   stream->rate = rate;
300*b290403dSRicardo Garcia 
301*b290403dSRicardo Garcia   stream->oldRatePosition = 0;
302*b290403dSRicardo Garcia   stream->newRatePosition = 0;
303*b290403dSRicardo Garcia }
304*b290403dSRicardo Garcia 
305*b290403dSRicardo Garcia /* DEPRECATED.  Get the vocal chord pitch setting. */
sonicGetChordPitch(sonicStream stream)306*b290403dSRicardo Garcia int sonicGetChordPitch(sonicStream stream) {
307*b290403dSRicardo Garcia   return 0;
308*b290403dSRicardo Garcia }
309*b290403dSRicardo Garcia 
310*b290403dSRicardo Garcia /* DEPRECATED. Set the vocal chord mode for pitch computation.  Default is off. */
sonicSetChordPitch(sonicStream stream,int useChordPitch)311*b290403dSRicardo Garcia void sonicSetChordPitch(sonicStream stream, int useChordPitch) {
312*b290403dSRicardo Garcia }
313*b290403dSRicardo Garcia 
314*b290403dSRicardo Garcia /* Get the quality setting. */
sonicGetQuality(sonicStream stream)315*b290403dSRicardo Garcia int sonicGetQuality(sonicStream stream) { return stream->quality; }
316*b290403dSRicardo Garcia 
317*b290403dSRicardo Garcia /* Set the "quality".  Default 0 is virtually as good as 1, but very much
318*b290403dSRicardo Garcia    faster. */
sonicSetQuality(sonicStream stream,int quality)319*b290403dSRicardo Garcia void sonicSetQuality(sonicStream stream, int quality) {
320*b290403dSRicardo Garcia   stream->quality = quality;
321*b290403dSRicardo Garcia }
322*b290403dSRicardo Garcia 
323*b290403dSRicardo Garcia /* Get the scaling factor of the stream. */
sonicGetVolume(sonicStream stream)324*b290403dSRicardo Garcia float sonicGetVolume(sonicStream stream) { return stream->volume; }
325*b290403dSRicardo Garcia 
326*b290403dSRicardo Garcia /* Set the scaling factor of the stream. */
sonicSetVolume(sonicStream stream,float volume)327*b290403dSRicardo Garcia void sonicSetVolume(sonicStream stream, float volume) {
328*b290403dSRicardo Garcia   stream->volume = volume;
329*b290403dSRicardo Garcia }
330*b290403dSRicardo Garcia 
331*b290403dSRicardo Garcia /* Free stream buffers. */
freeStreamBuffers(sonicStream stream)332*b290403dSRicardo Garcia static void freeStreamBuffers(sonicStream stream) {
333*b290403dSRicardo Garcia   if (stream->inputBuffer != NULL) {
334*b290403dSRicardo Garcia     sonicFree(stream->inputBuffer);
335*b290403dSRicardo Garcia   }
336*b290403dSRicardo Garcia   if (stream->outputBuffer != NULL) {
337*b290403dSRicardo Garcia     sonicFree(stream->outputBuffer);
338*b290403dSRicardo Garcia   }
339*b290403dSRicardo Garcia   if (stream->pitchBuffer != NULL) {
340*b290403dSRicardo Garcia     sonicFree(stream->pitchBuffer);
341*b290403dSRicardo Garcia   }
342*b290403dSRicardo Garcia   if (stream->downSampleBuffer != NULL) {
343*b290403dSRicardo Garcia     sonicFree(stream->downSampleBuffer);
344*b290403dSRicardo Garcia   }
345*b290403dSRicardo Garcia }
346*b290403dSRicardo Garcia 
347*b290403dSRicardo Garcia /* Destroy the sonic stream. */
sonicDestroyStream(sonicStream stream)348*b290403dSRicardo Garcia void sonicDestroyStream(sonicStream stream) {
349*b290403dSRicardo Garcia #ifdef SONIC_SPECTROGRAM
350*b290403dSRicardo Garcia   if (stream->spectrogram != NULL) {
351*b290403dSRicardo Garcia     sonicDestroySpectrogram(stream->spectrogram);
352*b290403dSRicardo Garcia   }
353*b290403dSRicardo Garcia #endif  /* SONIC_SPECTROGRAM */
354*b290403dSRicardo Garcia   freeStreamBuffers(stream);
355*b290403dSRicardo Garcia   sonicFree(stream);
356*b290403dSRicardo Garcia }
357*b290403dSRicardo Garcia 
358*b290403dSRicardo Garcia /* Compute the number of samples to skip to down-sample the input. */
computeSkip(sonicStream stream)359*b290403dSRicardo Garcia static int computeSkip(sonicStream stream) {
360*b290403dSRicardo Garcia   int skip = 1;
361*b290403dSRicardo Garcia   if (stream->sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
362*b290403dSRicardo Garcia     skip = stream->sampleRate / SONIC_AMDF_FREQ;
363*b290403dSRicardo Garcia   }
364*b290403dSRicardo Garcia   return skip;
365*b290403dSRicardo Garcia }
366*b290403dSRicardo Garcia 
367*b290403dSRicardo Garcia /* Allocate stream buffers. */
allocateStreamBuffers(sonicStream stream,int sampleRate,int numChannels)368*b290403dSRicardo Garcia static int allocateStreamBuffers(sonicStream stream, int sampleRate,
369*b290403dSRicardo Garcia                                  int numChannels) {
370*b290403dSRicardo Garcia   int minPeriod = sampleRate / SONIC_MAX_PITCH;
371*b290403dSRicardo Garcia   int maxPeriod = sampleRate / SONIC_MIN_PITCH;
372*b290403dSRicardo Garcia   int maxRequired = 2 * maxPeriod;
373*b290403dSRicardo Garcia   int skip = computeSkip(stream);
374*b290403dSRicardo Garcia 
375*b290403dSRicardo Garcia   /* Allocate 25% more than needed so we hopefully won't grow. */
376*b290403dSRicardo Garcia   stream->inputBufferSize = maxRequired + (maxRequired >> 2);;
377*b290403dSRicardo Garcia   stream->inputBuffer =
378*b290403dSRicardo Garcia       (short*)sonicCalloc(stream->inputBufferSize, sizeof(short) * numChannels);
379*b290403dSRicardo Garcia   if (stream->inputBuffer == NULL) {
380*b290403dSRicardo Garcia     sonicDestroyStream(stream);
381*b290403dSRicardo Garcia     return 0;
382*b290403dSRicardo Garcia   }
383*b290403dSRicardo Garcia   /* Allocate 25% more than needed so we hopefully won't grow. */
384*b290403dSRicardo Garcia   stream->outputBufferSize = maxRequired + (maxRequired >> 2);
385*b290403dSRicardo Garcia   stream->outputBuffer =
386*b290403dSRicardo Garcia       (short*)sonicCalloc(stream->outputBufferSize, sizeof(short) * numChannels);
387*b290403dSRicardo Garcia   if (stream->outputBuffer == NULL) {
388*b290403dSRicardo Garcia     sonicDestroyStream(stream);
389*b290403dSRicardo Garcia     return 0;
390*b290403dSRicardo Garcia   }
391*b290403dSRicardo Garcia   /* Allocate 25% more than needed so we hopefully won't grow. */
392*b290403dSRicardo Garcia   stream->pitchBufferSize = maxRequired + (maxRequired >> 2);
393*b290403dSRicardo Garcia   stream->pitchBuffer =
394*b290403dSRicardo Garcia       (short*)sonicCalloc(maxRequired, sizeof(short) * numChannels);
395*b290403dSRicardo Garcia   if (stream->pitchBuffer == NULL) {
396*b290403dSRicardo Garcia     sonicDestroyStream(stream);
397*b290403dSRicardo Garcia     return 0;
398*b290403dSRicardo Garcia   }
399*b290403dSRicardo Garcia   int downSampleBufferSize = (maxRequired + skip - 1)/ skip;
400*b290403dSRicardo Garcia   stream->downSampleBuffer = (short*)sonicCalloc(downSampleBufferSize, sizeof(short));
401*b290403dSRicardo Garcia   if (stream->downSampleBuffer == NULL) {
402*b290403dSRicardo Garcia     sonicDestroyStream(stream);
403*b290403dSRicardo Garcia     return 0;
404*b290403dSRicardo Garcia   }
405*b290403dSRicardo Garcia   stream->sampleRate = sampleRate;
406*b290403dSRicardo Garcia   stream->samplePeriod = 1.0 / sampleRate;
407*b290403dSRicardo Garcia   stream->numChannels = numChannels;
408*b290403dSRicardo Garcia   stream->oldRatePosition = 0;
409*b290403dSRicardo Garcia   stream->newRatePosition = 0;
410*b290403dSRicardo Garcia   stream->minPeriod = minPeriod;
411*b290403dSRicardo Garcia   stream->maxPeriod = maxPeriod;
412*b290403dSRicardo Garcia   stream->maxRequired = maxRequired;
413*b290403dSRicardo Garcia   stream->prevPeriod = 0;
414*b290403dSRicardo Garcia   return 1;
415*b290403dSRicardo Garcia }
416*b290403dSRicardo Garcia 
417*b290403dSRicardo Garcia /* Create a sonic stream.  Return NULL only if we are out of memory and cannot
418*b290403dSRicardo Garcia    allocate the stream. */
sonicCreateStream(int sampleRate,int numChannels)419*b290403dSRicardo Garcia sonicStream sonicCreateStream(int sampleRate, int numChannels) {
420*b290403dSRicardo Garcia   sonicStream stream = (sonicStream)sonicCalloc(
421*b290403dSRicardo Garcia       1, sizeof(struct sonicStreamStruct));
422*b290403dSRicardo Garcia 
423*b290403dSRicardo Garcia   if (stream == NULL) {
424*b290403dSRicardo Garcia     return NULL;
425*b290403dSRicardo Garcia   }
426*b290403dSRicardo Garcia   if (!allocateStreamBuffers(stream, sampleRate, numChannels)) {
427*b290403dSRicardo Garcia     return NULL;
428*b290403dSRicardo Garcia   }
429*b290403dSRicardo Garcia   stream->speed = 1.0f;
430*b290403dSRicardo Garcia   stream->pitch = 1.0f;
431*b290403dSRicardo Garcia   stream->volume = 1.0f;
432*b290403dSRicardo Garcia   stream->rate = 1.0f;
433*b290403dSRicardo Garcia   stream->oldRatePosition = 0;
434*b290403dSRicardo Garcia   stream->newRatePosition = 0;
435*b290403dSRicardo Garcia   stream->quality = 0;
436*b290403dSRicardo Garcia   return stream;
437*b290403dSRicardo Garcia }
438*b290403dSRicardo Garcia 
439*b290403dSRicardo Garcia /* Get the sample rate of the stream. */
sonicGetSampleRate(sonicStream stream)440*b290403dSRicardo Garcia int sonicGetSampleRate(sonicStream stream) { return stream->sampleRate; }
441*b290403dSRicardo Garcia 
442*b290403dSRicardo Garcia /* Set the sample rate of the stream.  This will cause samples buffered in the
443*b290403dSRicardo Garcia    stream to be lost. */
sonicSetSampleRate(sonicStream stream,int sampleRate)444*b290403dSRicardo Garcia void sonicSetSampleRate(sonicStream stream, int sampleRate) {
445*b290403dSRicardo Garcia   freeStreamBuffers(stream);
446*b290403dSRicardo Garcia   allocateStreamBuffers(stream, sampleRate, stream->numChannels);
447*b290403dSRicardo Garcia }
448*b290403dSRicardo Garcia 
449*b290403dSRicardo Garcia /* Get the number of channels. */
sonicGetNumChannels(sonicStream stream)450*b290403dSRicardo Garcia int sonicGetNumChannels(sonicStream stream) { return stream->numChannels; }
451*b290403dSRicardo Garcia 
452*b290403dSRicardo Garcia /* Set the num channels of the stream.  This will cause samples buffered in the
453*b290403dSRicardo Garcia    stream to be lost. */
sonicSetNumChannels(sonicStream stream,int numChannels)454*b290403dSRicardo Garcia void sonicSetNumChannels(sonicStream stream, int numChannels) {
455*b290403dSRicardo Garcia   freeStreamBuffers(stream);
456*b290403dSRicardo Garcia   allocateStreamBuffers(stream, stream->sampleRate, numChannels);
457*b290403dSRicardo Garcia }
458*b290403dSRicardo Garcia 
459*b290403dSRicardo Garcia /* Enlarge the output buffer if needed. */
enlargeOutputBufferIfNeeded(sonicStream stream,int numSamples)460*b290403dSRicardo Garcia static int enlargeOutputBufferIfNeeded(sonicStream stream, int numSamples) {
461*b290403dSRicardo Garcia   int outputBufferSize = stream->outputBufferSize;
462*b290403dSRicardo Garcia 
463*b290403dSRicardo Garcia   if (stream->numOutputSamples + numSamples > outputBufferSize) {
464*b290403dSRicardo Garcia     stream->outputBufferSize += (outputBufferSize >> 1) + numSamples;
465*b290403dSRicardo Garcia     stream->outputBuffer = (short*)sonicRealloc(
466*b290403dSRicardo Garcia         stream->outputBuffer,
467*b290403dSRicardo Garcia         outputBufferSize,
468*b290403dSRicardo Garcia         stream->outputBufferSize,
469*b290403dSRicardo Garcia         sizeof(short) * stream->numChannels);
470*b290403dSRicardo Garcia     if (stream->outputBuffer == NULL) {
471*b290403dSRicardo Garcia       return 0;
472*b290403dSRicardo Garcia     }
473*b290403dSRicardo Garcia   }
474*b290403dSRicardo Garcia   return 1;
475*b290403dSRicardo Garcia }
476*b290403dSRicardo Garcia 
477*b290403dSRicardo Garcia /* Enlarge the input buffer if needed. */
enlargeInputBufferIfNeeded(sonicStream stream,int numSamples)478*b290403dSRicardo Garcia static int enlargeInputBufferIfNeeded(sonicStream stream, int numSamples) {
479*b290403dSRicardo Garcia   int inputBufferSize = stream->inputBufferSize;
480*b290403dSRicardo Garcia 
481*b290403dSRicardo Garcia   if (stream->numInputSamples + numSamples > inputBufferSize) {
482*b290403dSRicardo Garcia     stream->inputBufferSize += (inputBufferSize >> 1) + numSamples;
483*b290403dSRicardo Garcia     stream->inputBuffer = (short*)sonicRealloc(
484*b290403dSRicardo Garcia         stream->inputBuffer,
485*b290403dSRicardo Garcia         inputBufferSize,
486*b290403dSRicardo Garcia         stream->inputBufferSize,
487*b290403dSRicardo Garcia         sizeof(short) * stream->numChannels);
488*b290403dSRicardo Garcia     if (stream->inputBuffer == NULL) {
489*b290403dSRicardo Garcia       return 0;
490*b290403dSRicardo Garcia     }
491*b290403dSRicardo Garcia   }
492*b290403dSRicardo Garcia   return 1;
493*b290403dSRicardo Garcia }
494*b290403dSRicardo Garcia 
495*b290403dSRicardo Garcia /* Update stream->numInputSamples, and update stream->inputPlayTime.  Call this
496*b290403dSRicardo Garcia    whenever adding samples to the input buffer, to keep track of total expected
497*b290403dSRicardo Garcia    input play time accounting. */
updateNumInputSamples(sonicStream stream,int numSamples)498*b290403dSRicardo Garcia static void updateNumInputSamples(sonicStream stream, int numSamples) {
499*b290403dSRicardo Garcia   float speed = stream->speed / stream->pitch;
500*b290403dSRicardo Garcia 
501*b290403dSRicardo Garcia   stream->numInputSamples += numSamples;
502*b290403dSRicardo Garcia   stream->inputPlayTime += numSamples * stream->samplePeriod / speed;
503*b290403dSRicardo Garcia }
504*b290403dSRicardo Garcia 
505*b290403dSRicardo Garcia /* Add the input samples to the input buffer. */
addFloatSamplesToInputBuffer(sonicStream stream,const float * samples,int numSamples)506*b290403dSRicardo Garcia static int addFloatSamplesToInputBuffer(sonicStream stream, const float* samples,
507*b290403dSRicardo Garcia                                         int numSamples) {
508*b290403dSRicardo Garcia   short* buffer;
509*b290403dSRicardo Garcia   int count = numSamples * stream->numChannels;
510*b290403dSRicardo Garcia 
511*b290403dSRicardo Garcia   if (numSamples == 0) {
512*b290403dSRicardo Garcia     return 1;
513*b290403dSRicardo Garcia   }
514*b290403dSRicardo Garcia   if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
515*b290403dSRicardo Garcia     return 0;
516*b290403dSRicardo Garcia   }
517*b290403dSRicardo Garcia   buffer = stream->inputBuffer + stream->numInputSamples * stream->numChannels;
518*b290403dSRicardo Garcia   while (count--) {
519*b290403dSRicardo Garcia     *buffer++ = (*samples++) * 32767.0f;
520*b290403dSRicardo Garcia   }
521*b290403dSRicardo Garcia   updateNumInputSamples(stream, numSamples);
522*b290403dSRicardo Garcia   return 1;
523*b290403dSRicardo Garcia }
524*b290403dSRicardo Garcia 
525*b290403dSRicardo Garcia /* Add the input samples to the input buffer. */
addShortSamplesToInputBuffer(sonicStream stream,const short * samples,int numSamples)526*b290403dSRicardo Garcia static int addShortSamplesToInputBuffer(sonicStream stream, const short* samples,
527*b290403dSRicardo Garcia                                         int numSamples) {
528*b290403dSRicardo Garcia   if (numSamples == 0) {
529*b290403dSRicardo Garcia     return 1;
530*b290403dSRicardo Garcia   }
531*b290403dSRicardo Garcia   if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
532*b290403dSRicardo Garcia     return 0;
533*b290403dSRicardo Garcia   }
534*b290403dSRicardo Garcia   memcpy(stream->inputBuffer + stream->numInputSamples * stream->numChannels,
535*b290403dSRicardo Garcia          samples, numSamples * sizeof(short) * stream->numChannels);
536*b290403dSRicardo Garcia   updateNumInputSamples(stream, numSamples);
537*b290403dSRicardo Garcia   return 1;
538*b290403dSRicardo Garcia }
539*b290403dSRicardo Garcia 
540*b290403dSRicardo Garcia /* Add the input samples to the input buffer. */
addUnsignedCharSamplesToInputBuffer(sonicStream stream,const unsigned char * samples,int numSamples)541*b290403dSRicardo Garcia static int addUnsignedCharSamplesToInputBuffer(sonicStream stream,
542*b290403dSRicardo Garcia                                                const unsigned char* samples,
543*b290403dSRicardo Garcia                                                int numSamples) {
544*b290403dSRicardo Garcia   short* buffer;
545*b290403dSRicardo Garcia   int count = numSamples * stream->numChannels;
546*b290403dSRicardo Garcia 
547*b290403dSRicardo Garcia   if (numSamples == 0) {
548*b290403dSRicardo Garcia     return 1;
549*b290403dSRicardo Garcia   }
550*b290403dSRicardo Garcia   if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
551*b290403dSRicardo Garcia     return 0;
552*b290403dSRicardo Garcia   }
553*b290403dSRicardo Garcia   buffer = stream->inputBuffer + stream->numInputSamples * stream->numChannels;
554*b290403dSRicardo Garcia   while (count--) {
555*b290403dSRicardo Garcia     *buffer++ = (*samples++ - 128) << 8;
556*b290403dSRicardo Garcia   }
557*b290403dSRicardo Garcia   updateNumInputSamples(stream, numSamples);
558*b290403dSRicardo Garcia   return 1;
559*b290403dSRicardo Garcia }
560*b290403dSRicardo Garcia 
561*b290403dSRicardo Garcia /* Remove input samples that we have already processed. */
removeInputSamples(sonicStream stream,int position)562*b290403dSRicardo Garcia static void removeInputSamples(sonicStream stream, int position) {
563*b290403dSRicardo Garcia   int remainingSamples = stream->numInputSamples - position;
564*b290403dSRicardo Garcia 
565*b290403dSRicardo Garcia   if (remainingSamples > 0) {
566*b290403dSRicardo Garcia     memmove(stream->inputBuffer,
567*b290403dSRicardo Garcia             stream->inputBuffer + position * stream->numChannels,
568*b290403dSRicardo Garcia             remainingSamples * sizeof(short) * stream->numChannels);
569*b290403dSRicardo Garcia   }
570*b290403dSRicardo Garcia   /* If we play 3/4ths of the samples, then the expected play time of the
571*b290403dSRicardo Garcia      remaining samples is 1/4th of the original expected play time. */
572*b290403dSRicardo Garcia   stream->inputPlayTime =
573*b290403dSRicardo Garcia       (stream->inputPlayTime * remainingSamples) / stream->numInputSamples;
574*b290403dSRicardo Garcia   stream->numInputSamples = remainingSamples;
575*b290403dSRicardo Garcia }
576*b290403dSRicardo Garcia 
577*b290403dSRicardo Garcia /* Copy from the input buffer to the output buffer, and remove the samples from
578*b290403dSRicardo Garcia    the input buffer. */
copyInputToOutput(sonicStream stream,int numSamples)579*b290403dSRicardo Garcia static int copyInputToOutput(sonicStream stream, int numSamples) {
580*b290403dSRicardo Garcia   if (!enlargeOutputBufferIfNeeded(stream, numSamples)) {
581*b290403dSRicardo Garcia     return 0;
582*b290403dSRicardo Garcia   }
583*b290403dSRicardo Garcia   memcpy(stream->outputBuffer + stream->numOutputSamples * stream->numChannels,
584*b290403dSRicardo Garcia          stream->inputBuffer, numSamples * sizeof(short) * stream->numChannels);
585*b290403dSRicardo Garcia   stream->numOutputSamples += numSamples;
586*b290403dSRicardo Garcia   removeInputSamples(stream, numSamples);
587*b290403dSRicardo Garcia   return 1;
588*b290403dSRicardo Garcia }
589*b290403dSRicardo Garcia 
590*b290403dSRicardo Garcia /* Copy from samples to the output buffer */
copyToOutput(sonicStream stream,short * samples,int numSamples)591*b290403dSRicardo Garcia static int copyToOutput(sonicStream stream, short* samples, int numSamples) {
592*b290403dSRicardo Garcia   if (!enlargeOutputBufferIfNeeded(stream, numSamples)) {
593*b290403dSRicardo Garcia     return 0;
594*b290403dSRicardo Garcia   }
595*b290403dSRicardo Garcia   memcpy(stream->outputBuffer + stream->numOutputSamples * stream->numChannels,
596*b290403dSRicardo Garcia          samples, numSamples * sizeof(short) * stream->numChannels);
597*b290403dSRicardo Garcia   stream->numOutputSamples += numSamples;
598*b290403dSRicardo Garcia   return 1;
599*b290403dSRicardo Garcia }
600*b290403dSRicardo Garcia 
601*b290403dSRicardo Garcia /* Read data out of the stream.  Sometimes no data will be available, and zero
602*b290403dSRicardo Garcia    is returned, which is not an error condition. */
sonicReadFloatFromStream(sonicStream stream,float * samples,int maxSamples)603*b290403dSRicardo Garcia int sonicReadFloatFromStream(sonicStream stream, float* samples,
604*b290403dSRicardo Garcia                              int maxSamples) {
605*b290403dSRicardo Garcia   int numSamples = stream->numOutputSamples;
606*b290403dSRicardo Garcia   int remainingSamples = 0;
607*b290403dSRicardo Garcia   short* buffer;
608*b290403dSRicardo Garcia   int count;
609*b290403dSRicardo Garcia 
610*b290403dSRicardo Garcia   if (numSamples == 0) {
611*b290403dSRicardo Garcia     return 0;
612*b290403dSRicardo Garcia   }
613*b290403dSRicardo Garcia   if (numSamples > maxSamples) {
614*b290403dSRicardo Garcia     remainingSamples = numSamples - maxSamples;
615*b290403dSRicardo Garcia     numSamples = maxSamples;
616*b290403dSRicardo Garcia   }
617*b290403dSRicardo Garcia   buffer = stream->outputBuffer;
618*b290403dSRicardo Garcia   count = numSamples * stream->numChannels;
619*b290403dSRicardo Garcia   while (count--) {
620*b290403dSRicardo Garcia     *samples++ = (*buffer++) / 32767.0f;
621*b290403dSRicardo Garcia   }
622*b290403dSRicardo Garcia   if (remainingSamples > 0) {
623*b290403dSRicardo Garcia     memmove(stream->outputBuffer,
624*b290403dSRicardo Garcia             stream->outputBuffer + numSamples * stream->numChannels,
625*b290403dSRicardo Garcia             remainingSamples * sizeof(short) * stream->numChannels);
626*b290403dSRicardo Garcia   }
627*b290403dSRicardo Garcia   stream->numOutputSamples = remainingSamples;
628*b290403dSRicardo Garcia   return numSamples;
629*b290403dSRicardo Garcia }
630*b290403dSRicardo Garcia 
631*b290403dSRicardo Garcia /* Read short data out of the stream.  Sometimes no data will be available, and
632*b290403dSRicardo Garcia    zero is returned, which is not an error condition. */
sonicReadShortFromStream(sonicStream stream,short * samples,int maxSamples)633*b290403dSRicardo Garcia int sonicReadShortFromStream(sonicStream stream, short* samples,
634*b290403dSRicardo Garcia                              int maxSamples) {
635*b290403dSRicardo Garcia   int numSamples = stream->numOutputSamples;
636*b290403dSRicardo Garcia   int remainingSamples = 0;
637*b290403dSRicardo Garcia 
638*b290403dSRicardo Garcia   if (numSamples == 0) {
639*b290403dSRicardo Garcia     return 0;
640*b290403dSRicardo Garcia   }
641*b290403dSRicardo Garcia   if (numSamples > maxSamples) {
642*b290403dSRicardo Garcia     remainingSamples = numSamples - maxSamples;
643*b290403dSRicardo Garcia     numSamples = maxSamples;
644*b290403dSRicardo Garcia   }
645*b290403dSRicardo Garcia   memcpy(samples, stream->outputBuffer,
646*b290403dSRicardo Garcia          numSamples * sizeof(short) * stream->numChannels);
647*b290403dSRicardo Garcia   if (remainingSamples > 0) {
648*b290403dSRicardo Garcia     memmove(stream->outputBuffer,
649*b290403dSRicardo Garcia             stream->outputBuffer + numSamples * stream->numChannels,
650*b290403dSRicardo Garcia             remainingSamples * sizeof(short) * stream->numChannels);
651*b290403dSRicardo Garcia   }
652*b290403dSRicardo Garcia   stream->numOutputSamples = remainingSamples;
653*b290403dSRicardo Garcia   return numSamples;
654*b290403dSRicardo Garcia }
655*b290403dSRicardo Garcia 
656*b290403dSRicardo Garcia /* Read unsigned char data out of the stream.  Sometimes no data will be
657*b290403dSRicardo Garcia    available, and zero is returned, which is not an error condition. */
sonicReadUnsignedCharFromStream(sonicStream stream,unsigned char * samples,int maxSamples)658*b290403dSRicardo Garcia int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char* samples,
659*b290403dSRicardo Garcia                                     int maxSamples) {
660*b290403dSRicardo Garcia   int numSamples = stream->numOutputSamples;
661*b290403dSRicardo Garcia   int remainingSamples = 0;
662*b290403dSRicardo Garcia   short* buffer;
663*b290403dSRicardo Garcia   int count;
664*b290403dSRicardo Garcia 
665*b290403dSRicardo Garcia   if (numSamples == 0) {
666*b290403dSRicardo Garcia     return 0;
667*b290403dSRicardo Garcia   }
668*b290403dSRicardo Garcia   if (numSamples > maxSamples) {
669*b290403dSRicardo Garcia     remainingSamples = numSamples - maxSamples;
670*b290403dSRicardo Garcia     numSamples = maxSamples;
671*b290403dSRicardo Garcia   }
672*b290403dSRicardo Garcia   buffer = stream->outputBuffer;
673*b290403dSRicardo Garcia   count = numSamples * stream->numChannels;
674*b290403dSRicardo Garcia   while (count--) {
675*b290403dSRicardo Garcia     *samples++ = (char)((*buffer++) >> 8) + 128;
676*b290403dSRicardo Garcia   }
677*b290403dSRicardo Garcia   if (remainingSamples > 0) {
678*b290403dSRicardo Garcia     memmove(stream->outputBuffer,
679*b290403dSRicardo Garcia             stream->outputBuffer + numSamples * stream->numChannels,
680*b290403dSRicardo Garcia             remainingSamples * sizeof(short) * stream->numChannels);
681*b290403dSRicardo Garcia   }
682*b290403dSRicardo Garcia   stream->numOutputSamples = remainingSamples;
683*b290403dSRicardo Garcia   return numSamples;
684*b290403dSRicardo Garcia }
685*b290403dSRicardo Garcia 
686*b290403dSRicardo Garcia /* Force the sonic stream to generate output using whatever data it currently
687*b290403dSRicardo Garcia    has.  No extra delay will be added to the output, but flushing in the middle
688*b290403dSRicardo Garcia    of words could introduce distortion. */
sonicFlushStream(sonicStream stream)689*b290403dSRicardo Garcia int sonicFlushStream(sonicStream stream) {
690*b290403dSRicardo Garcia   int maxRequired = stream->maxRequired;
691*b290403dSRicardo Garcia   int remainingSamples = stream->numInputSamples;
692*b290403dSRicardo Garcia   float speed = stream->speed / stream->pitch;
693*b290403dSRicardo Garcia   float rate = stream->rate * stream->pitch;
694*b290403dSRicardo Garcia   int expectedOutputSamples =
695*b290403dSRicardo Garcia       stream->numOutputSamples +
696*b290403dSRicardo Garcia       (int)((remainingSamples / speed + stream->numPitchSamples) / rate + 0.5f);
697*b290403dSRicardo Garcia 
698*b290403dSRicardo Garcia   /* Add enough silence to flush both input and pitch buffers. */
699*b290403dSRicardo Garcia   if (!enlargeInputBufferIfNeeded(stream, remainingSamples + 2 * maxRequired)) {
700*b290403dSRicardo Garcia     return 0;
701*b290403dSRicardo Garcia   }
702*b290403dSRicardo Garcia   memset(stream->inputBuffer + remainingSamples * stream->numChannels, 0,
703*b290403dSRicardo Garcia          2 * maxRequired * sizeof(short) * stream->numChannels);
704*b290403dSRicardo Garcia   stream->numInputSamples += 2 * maxRequired;
705*b290403dSRicardo Garcia   if (!sonicWriteShortToStream(stream, NULL, 0)) {
706*b290403dSRicardo Garcia     return 0;
707*b290403dSRicardo Garcia   }
708*b290403dSRicardo Garcia   /* Throw away any extra samples we generated due to the silence we added */
709*b290403dSRicardo Garcia   if (stream->numOutputSamples > expectedOutputSamples) {
710*b290403dSRicardo Garcia     stream->numOutputSamples = expectedOutputSamples;
711*b290403dSRicardo Garcia   }
712*b290403dSRicardo Garcia   /* Empty input and pitch buffers */
713*b290403dSRicardo Garcia   stream->numInputSamples = 0;
714*b290403dSRicardo Garcia   stream->inputPlayTime = 0.0f;
715*b290403dSRicardo Garcia   stream->timeError = 0.0f;
716*b290403dSRicardo Garcia   stream->numPitchSamples = 0;
717*b290403dSRicardo Garcia   return 1;
718*b290403dSRicardo Garcia }
719*b290403dSRicardo Garcia 
720*b290403dSRicardo Garcia /* Return the number of samples in the output buffer */
sonicSamplesAvailable(sonicStream stream)721*b290403dSRicardo Garcia int sonicSamplesAvailable(sonicStream stream) {
722*b290403dSRicardo Garcia   return stream->numOutputSamples;
723*b290403dSRicardo Garcia }
724*b290403dSRicardo Garcia 
725*b290403dSRicardo Garcia /* If skip is greater than one, average skip samples together and write them to
726*b290403dSRicardo Garcia    the down-sample buffer.  If numChannels is greater than one, mix the channels
727*b290403dSRicardo Garcia    together as we down sample. */
downSampleInput(sonicStream stream,short * samples,int skip)728*b290403dSRicardo Garcia static void downSampleInput(sonicStream stream, short* samples, int skip) {
729*b290403dSRicardo Garcia   int numSamples = stream->maxRequired / skip;
730*b290403dSRicardo Garcia   int samplesPerValue = stream->numChannels * skip;
731*b290403dSRicardo Garcia   int i, j;
732*b290403dSRicardo Garcia   int value;
733*b290403dSRicardo Garcia   short* downSamples = stream->downSampleBuffer;
734*b290403dSRicardo Garcia 
735*b290403dSRicardo Garcia   for (i = 0; i < numSamples; i++) {
736*b290403dSRicardo Garcia     value = 0;
737*b290403dSRicardo Garcia     for (j = 0; j < samplesPerValue; j++) {
738*b290403dSRicardo Garcia       value += *samples++;
739*b290403dSRicardo Garcia     }
740*b290403dSRicardo Garcia     value /= samplesPerValue;
741*b290403dSRicardo Garcia     *downSamples++ = value;
742*b290403dSRicardo Garcia   }
743*b290403dSRicardo Garcia }
744*b290403dSRicardo Garcia 
745*b290403dSRicardo Garcia /* Find the best frequency match in the range, and given a sample skip multiple.
746*b290403dSRicardo Garcia    For now, just find the pitch of the first channel. */
findPitchPeriodInRange(short * samples,int minPeriod,int maxPeriod,int * retMinDiff,int * retMaxDiff)747*b290403dSRicardo Garcia static int findPitchPeriodInRange(short* samples, int minPeriod, int maxPeriod,
748*b290403dSRicardo Garcia                                   int* retMinDiff, int* retMaxDiff) {
749*b290403dSRicardo Garcia   int period, bestPeriod = 0, worstPeriod = 255;
750*b290403dSRicardo Garcia   short* s;
751*b290403dSRicardo Garcia   short* p;
752*b290403dSRicardo Garcia   short sVal, pVal;
753*b290403dSRicardo Garcia   unsigned long diff, minDiff = 1, maxDiff = 0;
754*b290403dSRicardo Garcia   int i;
755*b290403dSRicardo Garcia 
756*b290403dSRicardo Garcia   for (period = minPeriod; period <= maxPeriod; period++) {
757*b290403dSRicardo Garcia     diff = 0;
758*b290403dSRicardo Garcia     s = samples;
759*b290403dSRicardo Garcia     p = samples + period;
760*b290403dSRicardo Garcia     for (i = 0; i < period; i++) {
761*b290403dSRicardo Garcia       sVal = *s++;
762*b290403dSRicardo Garcia       pVal = *p++;
763*b290403dSRicardo Garcia       diff += sVal >= pVal ? (unsigned short)(sVal - pVal)
764*b290403dSRicardo Garcia                            : (unsigned short)(pVal - sVal);
765*b290403dSRicardo Garcia     }
766*b290403dSRicardo Garcia     /* Note that the highest number of samples we add into diff will be less
767*b290403dSRicardo Garcia        than 256, since we skip samples.  Thus, diff is a 24 bit number, and
768*b290403dSRicardo Garcia        we can safely multiply by numSamples without overflow */
769*b290403dSRicardo Garcia     if (bestPeriod == 0 || diff * bestPeriod < minDiff * period) {
770*b290403dSRicardo Garcia       minDiff = diff;
771*b290403dSRicardo Garcia       bestPeriod = period;
772*b290403dSRicardo Garcia     }
773*b290403dSRicardo Garcia     if (diff * worstPeriod > maxDiff * period) {
774*b290403dSRicardo Garcia       maxDiff = diff;
775*b290403dSRicardo Garcia       worstPeriod = period;
776*b290403dSRicardo Garcia     }
777*b290403dSRicardo Garcia   }
778*b290403dSRicardo Garcia   *retMinDiff = minDiff / bestPeriod;
779*b290403dSRicardo Garcia   *retMaxDiff = maxDiff / worstPeriod;
780*b290403dSRicardo Garcia   return bestPeriod;
781*b290403dSRicardo Garcia }
782*b290403dSRicardo Garcia 
783*b290403dSRicardo Garcia /* At abrupt ends of voiced words, we can have pitch periods that are better
784*b290403dSRicardo Garcia    approximated by the previous pitch period estimate.  Try to detect this case.
785*b290403dSRicardo Garcia  */
prevPeriodBetter(sonicStream stream,int minDiff,int maxDiff,int preferNewPeriod)786*b290403dSRicardo Garcia static int prevPeriodBetter(sonicStream stream, int minDiff,
787*b290403dSRicardo Garcia                             int maxDiff, int preferNewPeriod) {
788*b290403dSRicardo Garcia   if (minDiff == 0 || stream->prevPeriod == 0) {
789*b290403dSRicardo Garcia     return 0;
790*b290403dSRicardo Garcia   }
791*b290403dSRicardo Garcia   if (preferNewPeriod) {
792*b290403dSRicardo Garcia     if (maxDiff > minDiff * 3) {
793*b290403dSRicardo Garcia       /* Got a reasonable match this period */
794*b290403dSRicardo Garcia       return 0;
795*b290403dSRicardo Garcia     }
796*b290403dSRicardo Garcia     if (minDiff * 2 <= stream->prevMinDiff * 3) {
797*b290403dSRicardo Garcia       /* Mismatch is not that much greater this period */
798*b290403dSRicardo Garcia       return 0;
799*b290403dSRicardo Garcia     }
800*b290403dSRicardo Garcia   } else {
801*b290403dSRicardo Garcia     if (minDiff <= stream->prevMinDiff) {
802*b290403dSRicardo Garcia       return 0;
803*b290403dSRicardo Garcia     }
804*b290403dSRicardo Garcia   }
805*b290403dSRicardo Garcia   return 1;
806*b290403dSRicardo Garcia }
807*b290403dSRicardo Garcia 
808*b290403dSRicardo Garcia /* Find the pitch period.  This is a critical step, and we may have to try
809*b290403dSRicardo Garcia    multiple ways to get a good answer.  This version uses Average Magnitude
810*b290403dSRicardo Garcia    Difference Function (AMDF).  To improve speed, we down sample by an integer
811*b290403dSRicardo Garcia    factor get in the 11KHz range, and then do it again with a narrower
812*b290403dSRicardo Garcia    frequency range without down sampling */
findPitchPeriod(sonicStream stream,short * samples,int preferNewPeriod)813*b290403dSRicardo Garcia static int findPitchPeriod(sonicStream stream, short* samples,
814*b290403dSRicardo Garcia                            int preferNewPeriod) {
815*b290403dSRicardo Garcia   int minPeriod = stream->minPeriod;
816*b290403dSRicardo Garcia   int maxPeriod = stream->maxPeriod;
817*b290403dSRicardo Garcia   int minDiff, maxDiff, retPeriod;
818*b290403dSRicardo Garcia   int skip = computeSkip(stream);
819*b290403dSRicardo Garcia   int period;
820*b290403dSRicardo Garcia 
821*b290403dSRicardo Garcia   if (stream->numChannels == 1 && skip == 1) {
822*b290403dSRicardo Garcia     period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff,
823*b290403dSRicardo Garcia                                     &maxDiff);
824*b290403dSRicardo Garcia   } else {
825*b290403dSRicardo Garcia     downSampleInput(stream, samples, skip);
826*b290403dSRicardo Garcia     period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod / skip,
827*b290403dSRicardo Garcia                                     maxPeriod / skip, &minDiff, &maxDiff);
828*b290403dSRicardo Garcia     if (skip != 1) {
829*b290403dSRicardo Garcia       period *= skip;
830*b290403dSRicardo Garcia       minPeriod = period - (skip << 2);
831*b290403dSRicardo Garcia       maxPeriod = period + (skip << 2);
832*b290403dSRicardo Garcia       if (minPeriod < stream->minPeriod) {
833*b290403dSRicardo Garcia         minPeriod = stream->minPeriod;
834*b290403dSRicardo Garcia       }
835*b290403dSRicardo Garcia       if (maxPeriod > stream->maxPeriod) {
836*b290403dSRicardo Garcia         maxPeriod = stream->maxPeriod;
837*b290403dSRicardo Garcia       }
838*b290403dSRicardo Garcia       if (stream->numChannels == 1) {
839*b290403dSRicardo Garcia         period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff,
840*b290403dSRicardo Garcia                                         &maxDiff);
841*b290403dSRicardo Garcia       } else {
842*b290403dSRicardo Garcia         downSampleInput(stream, samples, 1);
843*b290403dSRicardo Garcia         period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
844*b290403dSRicardo Garcia                                         maxPeriod, &minDiff, &maxDiff);
845*b290403dSRicardo Garcia       }
846*b290403dSRicardo Garcia     }
847*b290403dSRicardo Garcia   }
848*b290403dSRicardo Garcia   if (prevPeriodBetter(stream, minDiff, maxDiff, preferNewPeriod)) {
849*b290403dSRicardo Garcia     retPeriod = stream->prevPeriod;
850*b290403dSRicardo Garcia   } else {
851*b290403dSRicardo Garcia     retPeriod = period;
852*b290403dSRicardo Garcia   }
853*b290403dSRicardo Garcia   stream->prevMinDiff = minDiff;
854*b290403dSRicardo Garcia   stream->prevPeriod = period;
855*b290403dSRicardo Garcia   return retPeriod;
856*b290403dSRicardo Garcia }
857*b290403dSRicardo Garcia 
858*b290403dSRicardo Garcia /* Overlap two sound segments, ramp the volume of one down, while ramping the
859*b290403dSRicardo Garcia    other one from zero up, and add them, storing the result at the output. */
overlapAdd(int numSamples,int numChannels,short * out,short * rampDown,short * rampUp)860*b290403dSRicardo Garcia static void overlapAdd(int numSamples, int numChannels, short* out,
861*b290403dSRicardo Garcia                        short* rampDown, short* rampUp) {
862*b290403dSRicardo Garcia   short* o;
863*b290403dSRicardo Garcia   short* u;
864*b290403dSRicardo Garcia   short* d;
865*b290403dSRicardo Garcia   int i, t;
866*b290403dSRicardo Garcia 
867*b290403dSRicardo Garcia   for (i = 0; i < numChannels; i++) {
868*b290403dSRicardo Garcia     o = out + i;
869*b290403dSRicardo Garcia     u = rampUp + i;
870*b290403dSRicardo Garcia     d = rampDown + i;
871*b290403dSRicardo Garcia     for (t = 0; t < numSamples; t++) {
872*b290403dSRicardo Garcia #ifdef SONIC_USE_SIN
873*b290403dSRicardo Garcia       float ratio = sin(t * M_PI / (2 * numSamples));
874*b290403dSRicardo Garcia       *o = *d * (1.0f - ratio) + *u * ratio;
875*b290403dSRicardo Garcia #else
876*b290403dSRicardo Garcia       *o = (*d * (numSamples - t) + *u * t) / numSamples;
877*b290403dSRicardo Garcia #endif
878*b290403dSRicardo Garcia       o += numChannels;
879*b290403dSRicardo Garcia       d += numChannels;
880*b290403dSRicardo Garcia       u += numChannels;
881*b290403dSRicardo Garcia     }
882*b290403dSRicardo Garcia   }
883*b290403dSRicardo Garcia }
884*b290403dSRicardo Garcia 
885*b290403dSRicardo Garcia /* Just move the new samples in the output buffer to the pitch buffer */
moveNewSamplesToPitchBuffer(sonicStream stream,int originalNumOutputSamples)886*b290403dSRicardo Garcia static int moveNewSamplesToPitchBuffer(sonicStream stream,
887*b290403dSRicardo Garcia                                        int originalNumOutputSamples) {
888*b290403dSRicardo Garcia   int numSamples = stream->numOutputSamples - originalNumOutputSamples;
889*b290403dSRicardo Garcia   int numChannels = stream->numChannels;
890*b290403dSRicardo Garcia 
891*b290403dSRicardo Garcia   if (stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
892*b290403dSRicardo Garcia     int pitchBufferSize = stream->pitchBufferSize;
893*b290403dSRicardo Garcia     stream->pitchBufferSize += (pitchBufferSize >> 1) + numSamples;
894*b290403dSRicardo Garcia     stream->pitchBuffer = (short*)sonicRealloc(
895*b290403dSRicardo Garcia         stream->pitchBuffer,
896*b290403dSRicardo Garcia         pitchBufferSize,
897*b290403dSRicardo Garcia         stream->pitchBufferSize,
898*b290403dSRicardo Garcia         sizeof(short) * numChannels);
899*b290403dSRicardo Garcia   }
900*b290403dSRicardo Garcia   memcpy(stream->pitchBuffer + stream->numPitchSamples * numChannels,
901*b290403dSRicardo Garcia          stream->outputBuffer + originalNumOutputSamples * numChannels,
902*b290403dSRicardo Garcia          numSamples * sizeof(short) * numChannels);
903*b290403dSRicardo Garcia   stream->numOutputSamples = originalNumOutputSamples;
904*b290403dSRicardo Garcia   stream->numPitchSamples += numSamples;
905*b290403dSRicardo Garcia   return 1;
906*b290403dSRicardo Garcia }
907*b290403dSRicardo Garcia 
908*b290403dSRicardo Garcia /* Remove processed samples from the pitch buffer. */
removePitchSamples(sonicStream stream,int numSamples)909*b290403dSRicardo Garcia static void removePitchSamples(sonicStream stream, int numSamples) {
910*b290403dSRicardo Garcia   int numChannels = stream->numChannels;
911*b290403dSRicardo Garcia   short* source = stream->pitchBuffer + numSamples * numChannels;
912*b290403dSRicardo Garcia 
913*b290403dSRicardo Garcia   if (numSamples == 0) {
914*b290403dSRicardo Garcia     return;
915*b290403dSRicardo Garcia   }
916*b290403dSRicardo Garcia   if (numSamples != stream->numPitchSamples) {
917*b290403dSRicardo Garcia     memmove(
918*b290403dSRicardo Garcia         stream->pitchBuffer, source,
919*b290403dSRicardo Garcia         (stream->numPitchSamples - numSamples) * sizeof(short) * numChannels);
920*b290403dSRicardo Garcia   }
921*b290403dSRicardo Garcia   stream->numPitchSamples -= numSamples;
922*b290403dSRicardo Garcia }
923*b290403dSRicardo Garcia 
924*b290403dSRicardo Garcia /* Approximate the sinc function times a Hann window from the sinc table. */
findSincCoefficient(int i,int ratio,int width)925*b290403dSRicardo Garcia static int findSincCoefficient(int i, int ratio, int width) {
926*b290403dSRicardo Garcia   int lobePoints = (SINC_TABLE_SIZE - 1) / SINC_FILTER_POINTS;
927*b290403dSRicardo Garcia   int left = i * lobePoints + (ratio * lobePoints) / width;
928*b290403dSRicardo Garcia   int right = left + 1;
929*b290403dSRicardo Garcia   int position = i * lobePoints * width + ratio * lobePoints - left * width;
930*b290403dSRicardo Garcia   int leftVal = sincTable[left];
931*b290403dSRicardo Garcia   int rightVal = sincTable[right];
932*b290403dSRicardo Garcia 
933*b290403dSRicardo Garcia   return ((leftVal * (width - position) + rightVal * position) << 1) / width;
934*b290403dSRicardo Garcia }
935*b290403dSRicardo Garcia 
936*b290403dSRicardo Garcia /* Return 1 if value >= 0, else -1.  This represents the sign of value. */
getSign(int value)937*b290403dSRicardo Garcia static int getSign(int value) { return value >= 0 ? 1 : -1; }
938*b290403dSRicardo Garcia 
939*b290403dSRicardo Garcia /* Interpolate the new output sample. */
interpolate(sonicStream stream,short * in,int oldSampleRate,int newSampleRate)940*b290403dSRicardo Garcia static short interpolate(sonicStream stream, short* in, int oldSampleRate,
941*b290403dSRicardo Garcia                          int newSampleRate) {
942*b290403dSRicardo Garcia   /* Compute N-point sinc FIR-filter here.  Clip rather than overflow. */
943*b290403dSRicardo Garcia   int i;
944*b290403dSRicardo Garcia   int total = 0;
945*b290403dSRicardo Garcia   int position = stream->newRatePosition * oldSampleRate;
946*b290403dSRicardo Garcia   int leftPosition = stream->oldRatePosition * newSampleRate;
947*b290403dSRicardo Garcia   int rightPosition = (stream->oldRatePosition + 1) * newSampleRate;
948*b290403dSRicardo Garcia   int ratio = rightPosition - position - 1;
949*b290403dSRicardo Garcia   int width = rightPosition - leftPosition;
950*b290403dSRicardo Garcia   int weight, value;
951*b290403dSRicardo Garcia   int oldSign;
952*b290403dSRicardo Garcia   int overflowCount = 0;
953*b290403dSRicardo Garcia 
954*b290403dSRicardo Garcia   for (i = 0; i < SINC_FILTER_POINTS; i++) {
955*b290403dSRicardo Garcia     weight = findSincCoefficient(i, ratio, width);
956*b290403dSRicardo Garcia     value = in[i * stream->numChannels] * weight;
957*b290403dSRicardo Garcia     oldSign = getSign(total);
958*b290403dSRicardo Garcia     total += value;
959*b290403dSRicardo Garcia     if (oldSign != getSign(total) && getSign(value) == oldSign) {
960*b290403dSRicardo Garcia       /* We must have overflowed.  This can happen with a sinc filter. */
961*b290403dSRicardo Garcia       overflowCount += oldSign;
962*b290403dSRicardo Garcia     }
963*b290403dSRicardo Garcia   }
964*b290403dSRicardo Garcia   /* It is better to clip than to wrap if there was a overflow. */
965*b290403dSRicardo Garcia   if (overflowCount > 0) {
966*b290403dSRicardo Garcia     return SHRT_MAX;
967*b290403dSRicardo Garcia   } else if (overflowCount < 0) {
968*b290403dSRicardo Garcia     return SHRT_MIN;
969*b290403dSRicardo Garcia   }
970*b290403dSRicardo Garcia   return total >> 16;
971*b290403dSRicardo Garcia }
972*b290403dSRicardo Garcia 
973*b290403dSRicardo Garcia /* Change the rate.  Interpolate with a sinc FIR filter using a Hann window. */
adjustRate(sonicStream stream,float rate,int originalNumOutputSamples)974*b290403dSRicardo Garcia static int adjustRate(sonicStream stream, float rate,
975*b290403dSRicardo Garcia                       int originalNumOutputSamples) {
976*b290403dSRicardo Garcia   int newSampleRate = stream->sampleRate / rate;
977*b290403dSRicardo Garcia   int oldSampleRate = stream->sampleRate;
978*b290403dSRicardo Garcia   int numChannels = stream->numChannels;
979*b290403dSRicardo Garcia   int position;
980*b290403dSRicardo Garcia   short *in, *out;
981*b290403dSRicardo Garcia   int i;
982*b290403dSRicardo Garcia   int N = SINC_FILTER_POINTS;
983*b290403dSRicardo Garcia 
984*b290403dSRicardo Garcia   /* Set these values to help with the integer math */
985*b290403dSRicardo Garcia   while (newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
986*b290403dSRicardo Garcia     newSampleRate >>= 1;
987*b290403dSRicardo Garcia     oldSampleRate >>= 1;
988*b290403dSRicardo Garcia   }
989*b290403dSRicardo Garcia   if (stream->numOutputSamples == originalNumOutputSamples) {
990*b290403dSRicardo Garcia     return 1;
991*b290403dSRicardo Garcia   }
992*b290403dSRicardo Garcia   if (!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
993*b290403dSRicardo Garcia     return 0;
994*b290403dSRicardo Garcia   }
995*b290403dSRicardo Garcia   /* Leave at least N pitch sample in the buffer */
996*b290403dSRicardo Garcia   for (position = 0; position < stream->numPitchSamples - N; position++) {
997*b290403dSRicardo Garcia     while ((stream->oldRatePosition + 1) * newSampleRate >
998*b290403dSRicardo Garcia            stream->newRatePosition * oldSampleRate) {
999*b290403dSRicardo Garcia       if (!enlargeOutputBufferIfNeeded(stream, 1)) {
1000*b290403dSRicardo Garcia         return 0;
1001*b290403dSRicardo Garcia       }
1002*b290403dSRicardo Garcia       out = stream->outputBuffer + stream->numOutputSamples * numChannels;
1003*b290403dSRicardo Garcia       in = stream->pitchBuffer + position * numChannels;
1004*b290403dSRicardo Garcia       for (i = 0; i < numChannels; i++) {
1005*b290403dSRicardo Garcia         *out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
1006*b290403dSRicardo Garcia         in++;
1007*b290403dSRicardo Garcia       }
1008*b290403dSRicardo Garcia       stream->newRatePosition++;
1009*b290403dSRicardo Garcia       stream->numOutputSamples++;
1010*b290403dSRicardo Garcia     }
1011*b290403dSRicardo Garcia     stream->oldRatePosition++;
1012*b290403dSRicardo Garcia     if (stream->oldRatePosition == oldSampleRate) {
1013*b290403dSRicardo Garcia       stream->oldRatePosition = 0;
1014*b290403dSRicardo Garcia       stream->newRatePosition = 0;
1015*b290403dSRicardo Garcia     }
1016*b290403dSRicardo Garcia   }
1017*b290403dSRicardo Garcia   removePitchSamples(stream, position);
1018*b290403dSRicardo Garcia   return 1;
1019*b290403dSRicardo Garcia }
1020*b290403dSRicardo Garcia 
1021*b290403dSRicardo Garcia /* Skip over a pitch period.  Return the number of output samples. */
skipPitchPeriod(sonicStream stream,short * samples,float speed,int period)1022*b290403dSRicardo Garcia static int skipPitchPeriod(sonicStream stream, short* samples, float speed,
1023*b290403dSRicardo Garcia                            int period) {
1024*b290403dSRicardo Garcia   long newSamples;
1025*b290403dSRicardo Garcia   int numChannels = stream->numChannels;
1026*b290403dSRicardo Garcia 
1027*b290403dSRicardo Garcia   if (speed >= 2.0f) {
1028*b290403dSRicardo Garcia     /* For speeds >= 2.0, we skip over a portion of each pitch period rather
1029*b290403dSRicardo Garcia        than dropping whole pitch periods. */
1030*b290403dSRicardo Garcia     newSamples = period / (speed - 1.0f);
1031*b290403dSRicardo Garcia   } else {
1032*b290403dSRicardo Garcia     newSamples = period;
1033*b290403dSRicardo Garcia   }
1034*b290403dSRicardo Garcia   if (!enlargeOutputBufferIfNeeded(stream, newSamples)) {
1035*b290403dSRicardo Garcia     return 0;
1036*b290403dSRicardo Garcia   }
1037*b290403dSRicardo Garcia   overlapAdd(newSamples, numChannels,
1038*b290403dSRicardo Garcia              stream->outputBuffer + stream->numOutputSamples * numChannels,
1039*b290403dSRicardo Garcia              samples, samples + period * numChannels);
1040*b290403dSRicardo Garcia   stream->numOutputSamples += newSamples;
1041*b290403dSRicardo Garcia   return newSamples;
1042*b290403dSRicardo Garcia }
1043*b290403dSRicardo Garcia 
1044*b290403dSRicardo Garcia /* Insert a pitch period, and determine how much input to copy directly. */
insertPitchPeriod(sonicStream stream,short * samples,float speed,int period)1045*b290403dSRicardo Garcia static int insertPitchPeriod(sonicStream stream, short* samples, float speed,
1046*b290403dSRicardo Garcia                              int period) {
1047*b290403dSRicardo Garcia   long newSamples;
1048*b290403dSRicardo Garcia   short* out;
1049*b290403dSRicardo Garcia   int numChannels = stream->numChannels;
1050*b290403dSRicardo Garcia 
1051*b290403dSRicardo Garcia   if (speed <= 0.5f) {
1052*b290403dSRicardo Garcia     newSamples = period * speed / (1.0f - speed);
1053*b290403dSRicardo Garcia   } else {
1054*b290403dSRicardo Garcia     newSamples = period;
1055*b290403dSRicardo Garcia   }
1056*b290403dSRicardo Garcia   if (!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
1057*b290403dSRicardo Garcia     return 0;
1058*b290403dSRicardo Garcia   }
1059*b290403dSRicardo Garcia   out = stream->outputBuffer + stream->numOutputSamples * numChannels;
1060*b290403dSRicardo Garcia   memcpy(out, samples, period * sizeof(short) * numChannels);
1061*b290403dSRicardo Garcia   out =
1062*b290403dSRicardo Garcia       stream->outputBuffer + (stream->numOutputSamples + period) * numChannels;
1063*b290403dSRicardo Garcia   overlapAdd(newSamples, numChannels, out, samples + period * numChannels,
1064*b290403dSRicardo Garcia              samples);
1065*b290403dSRicardo Garcia   stream->numOutputSamples += period + newSamples;
1066*b290403dSRicardo Garcia   return newSamples;
1067*b290403dSRicardo Garcia }
1068*b290403dSRicardo Garcia 
1069*b290403dSRicardo Garcia /* PICOLA copies input to output until the total output samples == consumed
1070*b290403dSRicardo Garcia    input samples * speed. */
copyUnmodifiedSamples(sonicStream stream,short * samples,float speed,int position,int * newSamples)1071*b290403dSRicardo Garcia static int copyUnmodifiedSamples(sonicStream stream, short* samples,
1072*b290403dSRicardo Garcia                                  float speed, int position, int* newSamples) {
1073*b290403dSRicardo Garcia   int availableSamples = stream->numInputSamples - position;
1074*b290403dSRicardo Garcia   float inputToCopyFloat =
1075*b290403dSRicardo Garcia       1 - stream->timeError * speed / (stream->samplePeriod * (speed - 1.0));
1076*b290403dSRicardo Garcia 
1077*b290403dSRicardo Garcia   *newSamples = inputToCopyFloat > availableSamples ? availableSamples
1078*b290403dSRicardo Garcia                                                     : (int)inputToCopyFloat;
1079*b290403dSRicardo Garcia   if (!copyToOutput(stream, samples, *newSamples)) {
1080*b290403dSRicardo Garcia     return 0;
1081*b290403dSRicardo Garcia   }
1082*b290403dSRicardo Garcia   stream->timeError +=
1083*b290403dSRicardo Garcia       *newSamples * stream->samplePeriod * (speed - 1.0) / speed;
1084*b290403dSRicardo Garcia   return 1;
1085*b290403dSRicardo Garcia }
1086*b290403dSRicardo Garcia 
1087*b290403dSRicardo Garcia /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1088*b290403dSRicardo Garcia    we fail to resize an input or output buffer. */
changeSpeed(sonicStream stream,float speed)1089*b290403dSRicardo Garcia static int changeSpeed(sonicStream stream, float speed) {
1090*b290403dSRicardo Garcia   short* samples;
1091*b290403dSRicardo Garcia   int numSamples = stream->numInputSamples;
1092*b290403dSRicardo Garcia   int position = 0, period, newSamples;
1093*b290403dSRicardo Garcia   int maxRequired = stream->maxRequired;
1094*b290403dSRicardo Garcia 
1095*b290403dSRicardo Garcia   if (stream->numInputSamples < maxRequired) {
1096*b290403dSRicardo Garcia     return 1;
1097*b290403dSRicardo Garcia   }
1098*b290403dSRicardo Garcia   do {
1099*b290403dSRicardo Garcia     samples = stream->inputBuffer + position * stream->numChannels;
1100*b290403dSRicardo Garcia     if ((speed > 1.0f && speed < 2.0f && stream->timeError < 0.0f) ||
1101*b290403dSRicardo Garcia         (speed < 1.0f && speed > 0.5f && stream->timeError > 0.0f)) {
1102*b290403dSRicardo Garcia       /* Deal with the case where PICOLA is still copying input samples to
1103*b290403dSRicardo Garcia          output unmodified, */
1104*b290403dSRicardo Garcia       if (!copyUnmodifiedSamples(stream, samples, speed, position,
1105*b290403dSRicardo Garcia                                  &newSamples)) {
1106*b290403dSRicardo Garcia         return 0;
1107*b290403dSRicardo Garcia       }
1108*b290403dSRicardo Garcia       position += newSamples;
1109*b290403dSRicardo Garcia     } else {
1110*b290403dSRicardo Garcia       /* We are in the remaining cases, either inserting/removing a pitch period
1111*b290403dSRicardo Garcia          for speed < 2.0X, or a portion of one for speed >= 2.0X. */
1112*b290403dSRicardo Garcia       period = findPitchPeriod(stream, samples, 1);
1113*b290403dSRicardo Garcia #ifdef SONIC_SPECTROGRAM
1114*b290403dSRicardo Garcia       if (stream->spectrogram != NULL) {
1115*b290403dSRicardo Garcia         sonicAddPitchPeriodToSpectrogram(stream->spectrogram, samples, period,
1116*b290403dSRicardo Garcia                                          stream->numChannels);
1117*b290403dSRicardo Garcia         newSamples = period;
1118*b290403dSRicardo Garcia         position += period;
1119*b290403dSRicardo Garcia       } else
1120*b290403dSRicardo Garcia #endif /* SONIC_SPECTROGRAM */
1121*b290403dSRicardo Garcia         if (speed > 1.0) {
1122*b290403dSRicardo Garcia           newSamples = skipPitchPeriod(stream, samples, speed, period);
1123*b290403dSRicardo Garcia           position += period + newSamples;
1124*b290403dSRicardo Garcia           if (speed < 2.0) {
1125*b290403dSRicardo Garcia             stream->timeError += newSamples * stream->samplePeriod -
1126*b290403dSRicardo Garcia                                  (period + newSamples) * stream->inputPlayTime /
1127*b290403dSRicardo Garcia                                      stream->numInputSamples;
1128*b290403dSRicardo Garcia           }
1129*b290403dSRicardo Garcia         } else {
1130*b290403dSRicardo Garcia           newSamples = insertPitchPeriod(stream, samples, speed, period);
1131*b290403dSRicardo Garcia           position += newSamples;
1132*b290403dSRicardo Garcia           if (speed > 0.5) {
1133*b290403dSRicardo Garcia             stream->timeError +=
1134*b290403dSRicardo Garcia                 (period + newSamples) * stream->samplePeriod -
1135*b290403dSRicardo Garcia                 newSamples * stream->inputPlayTime / stream->numInputSamples;
1136*b290403dSRicardo Garcia           }
1137*b290403dSRicardo Garcia         }
1138*b290403dSRicardo Garcia       if (newSamples == 0) {
1139*b290403dSRicardo Garcia         return 0; /* Failed to resize output buffer */
1140*b290403dSRicardo Garcia       }
1141*b290403dSRicardo Garcia     }
1142*b290403dSRicardo Garcia   } while (position + maxRequired <= numSamples);
1143*b290403dSRicardo Garcia   removeInputSamples(stream, position);
1144*b290403dSRicardo Garcia   return 1;
1145*b290403dSRicardo Garcia }
1146*b290403dSRicardo Garcia 
1147*b290403dSRicardo Garcia /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1148*b290403dSRicardo Garcia    we fail to resize an input or output buffer.  Also scale the output by the
1149*b290403dSRicardo Garcia    volume. */
processStreamInput(sonicStream stream)1150*b290403dSRicardo Garcia static int processStreamInput(sonicStream stream) {
1151*b290403dSRicardo Garcia   int originalNumOutputSamples = stream->numOutputSamples;
1152*b290403dSRicardo Garcia   float rate = stream->rate * stream->pitch;
1153*b290403dSRicardo Garcia   float localSpeed;
1154*b290403dSRicardo Garcia 
1155*b290403dSRicardo Garcia   if (stream->numInputSamples == 0) {
1156*b290403dSRicardo Garcia     return 1;
1157*b290403dSRicardo Garcia   }
1158*b290403dSRicardo Garcia   localSpeed =
1159*b290403dSRicardo Garcia       stream->numInputSamples * stream->samplePeriod / stream->inputPlayTime;
1160*b290403dSRicardo Garcia   if (localSpeed > 1.00001 || localSpeed < 0.99999) {
1161*b290403dSRicardo Garcia     changeSpeed(stream, localSpeed);
1162*b290403dSRicardo Garcia   } else {
1163*b290403dSRicardo Garcia     if (!copyInputToOutput(stream, stream->numInputSamples)) {
1164*b290403dSRicardo Garcia       return 0;
1165*b290403dSRicardo Garcia     }
1166*b290403dSRicardo Garcia   }
1167*b290403dSRicardo Garcia   if (rate != 1.0f) {
1168*b290403dSRicardo Garcia     if (!adjustRate(stream, rate, originalNumOutputSamples)) {
1169*b290403dSRicardo Garcia       return 0;
1170*b290403dSRicardo Garcia     }
1171*b290403dSRicardo Garcia   }
1172*b290403dSRicardo Garcia   if (stream->volume != 1.0f) {
1173*b290403dSRicardo Garcia     /* Adjust output volume. */
1174*b290403dSRicardo Garcia     scaleSamples(
1175*b290403dSRicardo Garcia         stream->outputBuffer + originalNumOutputSamples * stream->numChannels,
1176*b290403dSRicardo Garcia         (stream->numOutputSamples - originalNumOutputSamples) *
1177*b290403dSRicardo Garcia             stream->numChannels,
1178*b290403dSRicardo Garcia         stream->volume);
1179*b290403dSRicardo Garcia   }
1180*b290403dSRicardo Garcia   return 1;
1181*b290403dSRicardo Garcia }
1182*b290403dSRicardo Garcia 
1183*b290403dSRicardo Garcia /* Write floating point data to the input buffer and process it. */
sonicWriteFloatToStream(sonicStream stream,const float * samples,int numSamples)1184*b290403dSRicardo Garcia int sonicWriteFloatToStream(sonicStream stream, const float* samples,
1185*b290403dSRicardo Garcia                             int numSamples) {
1186*b290403dSRicardo Garcia   if (!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
1187*b290403dSRicardo Garcia     return 0;
1188*b290403dSRicardo Garcia   }
1189*b290403dSRicardo Garcia   return processStreamInput(stream);
1190*b290403dSRicardo Garcia }
1191*b290403dSRicardo Garcia 
1192*b290403dSRicardo Garcia /* Simple wrapper around sonicWriteFloatToStream that does the short to float
1193*b290403dSRicardo Garcia    conversion for you. */
sonicWriteShortToStream(sonicStream stream,const short * samples,int numSamples)1194*b290403dSRicardo Garcia int sonicWriteShortToStream(sonicStream stream, const short* samples,
1195*b290403dSRicardo Garcia                             int numSamples) {
1196*b290403dSRicardo Garcia   if (!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
1197*b290403dSRicardo Garcia     return 0;
1198*b290403dSRicardo Garcia   }
1199*b290403dSRicardo Garcia   return processStreamInput(stream);
1200*b290403dSRicardo Garcia }
1201*b290403dSRicardo Garcia 
1202*b290403dSRicardo Garcia /* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to
1203*b290403dSRicardo Garcia    float conversion for you. */
sonicWriteUnsignedCharToStream(sonicStream stream,const unsigned char * samples,int numSamples)1204*b290403dSRicardo Garcia int sonicWriteUnsignedCharToStream(sonicStream stream, const unsigned char* samples,
1205*b290403dSRicardo Garcia                                    int numSamples) {
1206*b290403dSRicardo Garcia   if (!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
1207*b290403dSRicardo Garcia     return 0;
1208*b290403dSRicardo Garcia   }
1209*b290403dSRicardo Garcia   return processStreamInput(stream);
1210*b290403dSRicardo Garcia }
1211*b290403dSRicardo Garcia 
1212*b290403dSRicardo Garcia /* This is a non-stream oriented interface to just change the speed of a sound
1213*b290403dSRicardo Garcia  * sample */
sonicChangeFloatSpeed(float * samples,int numSamples,float speed,float pitch,float rate,float volume,int useChordPitch,int sampleRate,int numChannels)1214*b290403dSRicardo Garcia int sonicChangeFloatSpeed(float* samples, int numSamples, float speed,
1215*b290403dSRicardo Garcia                           float pitch, float rate, float volume,
1216*b290403dSRicardo Garcia                           int useChordPitch, int sampleRate, int numChannels) {
1217*b290403dSRicardo Garcia   sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1218*b290403dSRicardo Garcia 
1219*b290403dSRicardo Garcia   sonicSetSpeed(stream, speed);
1220*b290403dSRicardo Garcia   sonicSetPitch(stream, pitch);
1221*b290403dSRicardo Garcia   sonicSetRate(stream, rate);
1222*b290403dSRicardo Garcia   sonicSetVolume(stream, volume);
1223*b290403dSRicardo Garcia   sonicWriteFloatToStream(stream, samples, numSamples);
1224*b290403dSRicardo Garcia   sonicFlushStream(stream);
1225*b290403dSRicardo Garcia   numSamples = sonicSamplesAvailable(stream);
1226*b290403dSRicardo Garcia   sonicReadFloatFromStream(stream, samples, numSamples);
1227*b290403dSRicardo Garcia   sonicDestroyStream(stream);
1228*b290403dSRicardo Garcia   return numSamples;
1229*b290403dSRicardo Garcia }
1230*b290403dSRicardo Garcia 
1231*b290403dSRicardo Garcia /* This is a non-stream oriented interface to just change the speed of a sound
1232*b290403dSRicardo Garcia  * sample */
sonicChangeShortSpeed(short * samples,int numSamples,float speed,float pitch,float rate,float volume,int useChordPitch,int sampleRate,int numChannels)1233*b290403dSRicardo Garcia int sonicChangeShortSpeed(short* samples, int numSamples, float speed,
1234*b290403dSRicardo Garcia                           float pitch, float rate, float volume,
1235*b290403dSRicardo Garcia                           int useChordPitch, int sampleRate, int numChannels) {
1236*b290403dSRicardo Garcia   sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1237*b290403dSRicardo Garcia 
1238*b290403dSRicardo Garcia   sonicSetSpeed(stream, speed);
1239*b290403dSRicardo Garcia   sonicSetPitch(stream, pitch);
1240*b290403dSRicardo Garcia   sonicSetRate(stream, rate);
1241*b290403dSRicardo Garcia   sonicSetVolume(stream, volume);
1242*b290403dSRicardo Garcia   sonicWriteShortToStream(stream, samples, numSamples);
1243*b290403dSRicardo Garcia   sonicFlushStream(stream);
1244*b290403dSRicardo Garcia   numSamples = sonicSamplesAvailable(stream);
1245*b290403dSRicardo Garcia   sonicReadShortFromStream(stream, samples, numSamples);
1246*b290403dSRicardo Garcia   sonicDestroyStream(stream);
1247*b290403dSRicardo Garcia   return numSamples;
1248*b290403dSRicardo Garcia }
1249