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