xref: /aosp_15_r20/external/sonic/Sonic.java (revision b290403dc9d28f89f133eb7e190ea8185d440ecd)
1*b290403dSRicardo Garcia /* Sonic library
2*b290403dSRicardo Garcia    Copyright 2010, 2011
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 package sonic;
10*b290403dSRicardo Garcia 
11*b290403dSRicardo Garcia public class Sonic {
12*b290403dSRicardo Garcia 
13*b290403dSRicardo Garcia     private static final int SONIC_MIN_PITCH = 65;
14*b290403dSRicardo Garcia     private static final int SONIC_MAX_PITCH = 400;
15*b290403dSRicardo Garcia     // This is used to down-sample some inputs to improve speed
16*b290403dSRicardo Garcia     private static final int SONIC_AMDF_FREQ = 4000;
17*b290403dSRicardo Garcia     // The number of points to use in the sinc FIR filter for resampling.
18*b290403dSRicardo Garcia     private static final int SINC_FILTER_POINTS = 12;
19*b290403dSRicardo Garcia     private static final int SINC_TABLE_SIZE = 601;
20*b290403dSRicardo Garcia 
21*b290403dSRicardo Garcia     // Lookup table for windowed sinc function of SINC_FILTER_POINTS points.
22*b290403dSRicardo Garcia     // The code to generate this is in the header comment of sonic.c.
23*b290403dSRicardo Garcia     private static final short sincTable[] = {
24*b290403dSRicardo Garcia         0, 0, 0, 0, 0, 0, 0, -1, -1, -2, -2, -3, -4, -6, -7, -9, -10, -12, -14,
25*b290403dSRicardo Garcia         -17, -19, -21, -24, -26, -29, -32, -34, -37, -40, -42, -44, -47, -48, -50,
26*b290403dSRicardo Garcia         -51, -52, -53, -53, -53, -52, -50, -48, -46, -43, -39, -34, -29, -22, -16,
27*b290403dSRicardo Garcia         -8, 0, 9, 19, 29, 41, 53, 65, 79, 92, 107, 121, 137, 152, 168, 184, 200,
28*b290403dSRicardo Garcia         215, 231, 247, 262, 276, 291, 304, 317, 328, 339, 348, 357, 363, 369, 372,
29*b290403dSRicardo Garcia         374, 375, 373, 369, 363, 355, 345, 332, 318, 300, 281, 259, 234, 208, 178,
30*b290403dSRicardo Garcia         147, 113, 77, 39, 0, -41, -85, -130, -177, -225, -274, -324, -375, -426,
31*b290403dSRicardo Garcia         -478, -530, -581, -632, -682, -731, -779, -825, -870, -912, -951, -989,
32*b290403dSRicardo Garcia         -1023, -1053, -1080, -1104, -1123, -1138, -1149, -1154, -1155, -1151,
33*b290403dSRicardo Garcia         -1141, -1125, -1105, -1078, -1046, -1007, -963, -913, -857, -796, -728,
34*b290403dSRicardo Garcia         -655, -576, -492, -403, -309, -210, -107, 0, 111, 225, 342, 462, 584, 708,
35*b290403dSRicardo Garcia         833, 958, 1084, 1209, 1333, 1455, 1575, 1693, 1807, 1916, 2022, 2122, 2216,
36*b290403dSRicardo Garcia         2304, 2384, 2457, 2522, 2579, 2625, 2663, 2689, 2706, 2711, 2705, 2687,
37*b290403dSRicardo Garcia         2657, 2614, 2559, 2491, 2411, 2317, 2211, 2092, 1960, 1815, 1658, 1489,
38*b290403dSRicardo Garcia         1308, 1115, 912, 698, 474, 241, 0, -249, -506, -769, -1037, -1310, -1586,
39*b290403dSRicardo Garcia         -1864, -2144, -2424, -2703, -2980, -3254, -3523, -3787, -4043, -4291,
40*b290403dSRicardo Garcia         -4529, -4757, -4972, -5174, -5360, -5531, -5685, -5819, -5935, -6029,
41*b290403dSRicardo Garcia         -6101, -6150, -6175, -6175, -6149, -6096, -6015, -5905, -5767, -5599,
42*b290403dSRicardo Garcia         -5401, -5172, -4912, -4621, -4298, -3944, -3558, -3141, -2693, -2214,
43*b290403dSRicardo Garcia         -1705, -1166, -597, 0, 625, 1277, 1955, 2658, 3386, 4135, 4906, 5697, 6506,
44*b290403dSRicardo Garcia         7332, 8173, 9027, 9893, 10769, 11654, 12544, 13439, 14335, 15232, 16128,
45*b290403dSRicardo Garcia         17019, 17904, 18782, 19649, 20504, 21345, 22170, 22977, 23763, 24527,
46*b290403dSRicardo Garcia         25268, 25982, 26669, 27327, 27953, 28547, 29107, 29632, 30119, 30569,
47*b290403dSRicardo Garcia         30979, 31349, 31678, 31964, 32208, 32408, 32565, 32677, 32744, 32767,
48*b290403dSRicardo Garcia         32744, 32677, 32565, 32408, 32208, 31964, 31678, 31349, 30979, 30569,
49*b290403dSRicardo Garcia         30119, 29632, 29107, 28547, 27953, 27327, 26669, 25982, 25268, 24527,
50*b290403dSRicardo Garcia         23763, 22977, 22170, 21345, 20504, 19649, 18782, 17904, 17019, 16128,
51*b290403dSRicardo Garcia         15232, 14335, 13439, 12544, 11654, 10769, 9893, 9027, 8173, 7332, 6506,
52*b290403dSRicardo Garcia         5697, 4906, 4135, 3386, 2658, 1955, 1277, 625, 0, -597, -1166, -1705,
53*b290403dSRicardo Garcia         -2214, -2693, -3141, -3558, -3944, -4298, -4621, -4912, -5172, -5401,
54*b290403dSRicardo Garcia         -5599, -5767, -5905, -6015, -6096, -6149, -6175, -6175, -6150, -6101,
55*b290403dSRicardo Garcia         -6029, -5935, -5819, -5685, -5531, -5360, -5174, -4972, -4757, -4529,
56*b290403dSRicardo Garcia         -4291, -4043, -3787, -3523, -3254, -2980, -2703, -2424, -2144, -1864,
57*b290403dSRicardo Garcia         -1586, -1310, -1037, -769, -506, -249, 0, 241, 474, 698, 912, 1115, 1308,
58*b290403dSRicardo Garcia         1489, 1658, 1815, 1960, 2092, 2211, 2317, 2411, 2491, 2559, 2614, 2657,
59*b290403dSRicardo Garcia         2687, 2705, 2711, 2706, 2689, 2663, 2625, 2579, 2522, 2457, 2384, 2304,
60*b290403dSRicardo Garcia         2216, 2122, 2022, 1916, 1807, 1693, 1575, 1455, 1333, 1209, 1084, 958, 833,
61*b290403dSRicardo Garcia         708, 584, 462, 342, 225, 111, 0, -107, -210, -309, -403, -492, -576, -655,
62*b290403dSRicardo Garcia         -728, -796, -857, -913, -963, -1007, -1046, -1078, -1105, -1125, -1141,
63*b290403dSRicardo Garcia         -1151, -1155, -1154, -1149, -1138, -1123, -1104, -1080, -1053, -1023, -989,
64*b290403dSRicardo Garcia         -951, -912, -870, -825, -779, -731, -682, -632, -581, -530, -478, -426,
65*b290403dSRicardo Garcia         -375, -324, -274, -225, -177, -130, -85, -41, 0, 39, 77, 113, 147, 178,
66*b290403dSRicardo Garcia         208, 234, 259, 281, 300, 318, 332, 345, 355, 363, 369, 373, 375, 374, 372,
67*b290403dSRicardo Garcia         369, 363, 357, 348, 339, 328, 317, 304, 291, 276, 262, 247, 231, 215, 200,
68*b290403dSRicardo Garcia         184, 168, 152, 137, 121, 107, 92, 79, 65, 53, 41, 29, 19, 9, 0, -8, -16,
69*b290403dSRicardo Garcia         -22, -29, -34, -39, -43, -46, -48, -50, -52, -53, -53, -53, -52, -51, -50,
70*b290403dSRicardo Garcia         -48, -47, -44, -42, -40, -37, -34, -32, -29, -26, -24, -21, -19, -17, -14,
71*b290403dSRicardo Garcia         -12, -10, -9, -7, -6, -4, -3, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0
72*b290403dSRicardo Garcia     };
73*b290403dSRicardo Garcia 
74*b290403dSRicardo Garcia     private short inputBuffer[];
75*b290403dSRicardo Garcia     private short outputBuffer[];
76*b290403dSRicardo Garcia     private short pitchBuffer[];
77*b290403dSRicardo Garcia     private short downSampleBuffer[];
78*b290403dSRicardo Garcia     private float speed;
79*b290403dSRicardo Garcia     private float volume;
80*b290403dSRicardo Garcia     private float pitch;
81*b290403dSRicardo Garcia     private float rate;
82*b290403dSRicardo Garcia     private int oldRatePosition;
83*b290403dSRicardo Garcia     private int newRatePosition;
84*b290403dSRicardo Garcia     private boolean useChordPitch;
85*b290403dSRicardo Garcia     private int quality;
86*b290403dSRicardo Garcia     private int numChannels;
87*b290403dSRicardo Garcia     private int inputBufferSize;
88*b290403dSRicardo Garcia     private int pitchBufferSize;
89*b290403dSRicardo Garcia     private int outputBufferSize;
90*b290403dSRicardo Garcia     private int numInputSamples;
91*b290403dSRicardo Garcia     private int numOutputSamples;
92*b290403dSRicardo Garcia     private int numPitchSamples;
93*b290403dSRicardo Garcia     private int minPeriod;
94*b290403dSRicardo Garcia     private int maxPeriod;
95*b290403dSRicardo Garcia     private int maxRequired;
96*b290403dSRicardo Garcia     private int remainingInputToCopy;
97*b290403dSRicardo Garcia     private int sampleRate;
98*b290403dSRicardo Garcia     private int prevPeriod;
99*b290403dSRicardo Garcia     private int prevMinDiff;
100*b290403dSRicardo Garcia     private int minDiff;
101*b290403dSRicardo Garcia     private int maxDiff;
102*b290403dSRicardo Garcia 
103*b290403dSRicardo Garcia     // Resize the array.
resize( short[] oldArray, int newLength)104*b290403dSRicardo Garcia     private short[] resize(
105*b290403dSRicardo Garcia         short[] oldArray,
106*b290403dSRicardo Garcia         int newLength)
107*b290403dSRicardo Garcia     {
108*b290403dSRicardo Garcia         newLength *= numChannels;
109*b290403dSRicardo Garcia         short[]        newArray = new short[newLength];
110*b290403dSRicardo Garcia         int length = oldArray.length <= newLength? oldArray.length : newLength;
111*b290403dSRicardo Garcia 
112*b290403dSRicardo Garcia         System.arraycopy(oldArray, 0, newArray, 0, length);
113*b290403dSRicardo Garcia         return newArray;
114*b290403dSRicardo Garcia     }
115*b290403dSRicardo Garcia 
116*b290403dSRicardo Garcia     // Move samples from one array to another.  May move samples down within an array, but not up.
move( short dest[], int destPos, short source[], int sourcePos, int numSamples)117*b290403dSRicardo Garcia     private void move(
118*b290403dSRicardo Garcia         short dest[],
119*b290403dSRicardo Garcia         int destPos,
120*b290403dSRicardo Garcia         short source[],
121*b290403dSRicardo Garcia         int sourcePos,
122*b290403dSRicardo Garcia         int numSamples)
123*b290403dSRicardo Garcia     {
124*b290403dSRicardo Garcia         System.arraycopy(source, sourcePos*numChannels, dest, destPos*numChannels, numSamples*numChannels);
125*b290403dSRicardo Garcia     }
126*b290403dSRicardo Garcia 
127*b290403dSRicardo Garcia     // Scale the samples by the factor.
scaleSamples( short samples[], int position, int numSamples, float volume)128*b290403dSRicardo Garcia     private void scaleSamples(
129*b290403dSRicardo Garcia         short samples[],
130*b290403dSRicardo Garcia         int position,
131*b290403dSRicardo Garcia         int numSamples,
132*b290403dSRicardo Garcia         float volume)
133*b290403dSRicardo Garcia     {
134*b290403dSRicardo Garcia         // Convert volume to fixed-point, with a 12 bit fraction.
135*b290403dSRicardo Garcia         int fixedPointVolume = (int)(volume*4096.0f);
136*b290403dSRicardo Garcia         int start = position*numChannels;
137*b290403dSRicardo Garcia         int stop = start + numSamples*numChannels;
138*b290403dSRicardo Garcia 
139*b290403dSRicardo Garcia         for(int xSample = start; xSample < stop; xSample++) {
140*b290403dSRicardo Garcia             // Convert back from fixed point to 16-bit integer.
141*b290403dSRicardo Garcia             int value = (samples[xSample]*fixedPointVolume) >> 12;
142*b290403dSRicardo Garcia             if(value > 32767) {
143*b290403dSRicardo Garcia                 value = 32767;
144*b290403dSRicardo Garcia             } else if(value < -32767) {
145*b290403dSRicardo Garcia                 value = -32767;
146*b290403dSRicardo Garcia             }
147*b290403dSRicardo Garcia             samples[xSample] = (short)value;
148*b290403dSRicardo Garcia         }
149*b290403dSRicardo Garcia     }
150*b290403dSRicardo Garcia 
151*b290403dSRicardo Garcia     // Get the speed of the stream.
getSpeed()152*b290403dSRicardo Garcia     public float getSpeed()
153*b290403dSRicardo Garcia     {
154*b290403dSRicardo Garcia         return speed;
155*b290403dSRicardo Garcia     }
156*b290403dSRicardo Garcia 
157*b290403dSRicardo Garcia     // Set the speed of the stream.
setSpeed( float speed)158*b290403dSRicardo Garcia     public void setSpeed(
159*b290403dSRicardo Garcia         float speed)
160*b290403dSRicardo Garcia     {
161*b290403dSRicardo Garcia         this.speed = speed;
162*b290403dSRicardo Garcia     }
163*b290403dSRicardo Garcia 
164*b290403dSRicardo Garcia     // Get the pitch of the stream.
getPitch()165*b290403dSRicardo Garcia     public float getPitch()
166*b290403dSRicardo Garcia     {
167*b290403dSRicardo Garcia         return pitch;
168*b290403dSRicardo Garcia     }
169*b290403dSRicardo Garcia 
170*b290403dSRicardo Garcia     // Set the pitch of the stream.
setPitch( float pitch)171*b290403dSRicardo Garcia     public void setPitch(
172*b290403dSRicardo Garcia         float pitch)
173*b290403dSRicardo Garcia     {
174*b290403dSRicardo Garcia         this.pitch = pitch;
175*b290403dSRicardo Garcia     }
176*b290403dSRicardo Garcia 
177*b290403dSRicardo Garcia     // Get the rate of the stream.
getRate()178*b290403dSRicardo Garcia     public float getRate()
179*b290403dSRicardo Garcia     {
180*b290403dSRicardo Garcia         return rate;
181*b290403dSRicardo Garcia     }
182*b290403dSRicardo Garcia 
183*b290403dSRicardo Garcia     // Set the playback rate of the stream. This scales pitch and speed at the same time.
setRate( float rate)184*b290403dSRicardo Garcia     public void setRate(
185*b290403dSRicardo Garcia         float rate)
186*b290403dSRicardo Garcia     {
187*b290403dSRicardo Garcia         this.rate = rate;
188*b290403dSRicardo Garcia         this.oldRatePosition = 0;
189*b290403dSRicardo Garcia         this.newRatePosition = 0;
190*b290403dSRicardo Garcia     }
191*b290403dSRicardo Garcia 
192*b290403dSRicardo Garcia     // Get the vocal chord pitch setting.
getChordPitch()193*b290403dSRicardo Garcia     public boolean getChordPitch()
194*b290403dSRicardo Garcia     {
195*b290403dSRicardo Garcia         return useChordPitch;
196*b290403dSRicardo Garcia     }
197*b290403dSRicardo Garcia 
198*b290403dSRicardo Garcia     // Set the vocal chord mode for pitch computation.  Default is off.
setChordPitch( boolean useChordPitch)199*b290403dSRicardo Garcia     public void setChordPitch(
200*b290403dSRicardo Garcia         boolean useChordPitch)
201*b290403dSRicardo Garcia     {
202*b290403dSRicardo Garcia         this.useChordPitch = useChordPitch;
203*b290403dSRicardo Garcia     }
204*b290403dSRicardo Garcia 
205*b290403dSRicardo Garcia     // Get the quality setting.
getQuality()206*b290403dSRicardo Garcia     public int getQuality()
207*b290403dSRicardo Garcia     {
208*b290403dSRicardo Garcia         return quality;
209*b290403dSRicardo Garcia     }
210*b290403dSRicardo Garcia 
211*b290403dSRicardo Garcia     // Set the "quality".  Default 0 is virtually as good as 1, but very much faster.
setQuality( int quality)212*b290403dSRicardo Garcia     public void setQuality(
213*b290403dSRicardo Garcia         int quality)
214*b290403dSRicardo Garcia     {
215*b290403dSRicardo Garcia         this.quality = quality;
216*b290403dSRicardo Garcia     }
217*b290403dSRicardo Garcia 
218*b290403dSRicardo Garcia     // Get the scaling factor of the stream.
getVolume()219*b290403dSRicardo Garcia     public float getVolume()
220*b290403dSRicardo Garcia     {
221*b290403dSRicardo Garcia         return volume;
222*b290403dSRicardo Garcia     }
223*b290403dSRicardo Garcia 
224*b290403dSRicardo Garcia     // Set the scaling factor of the stream.
setVolume( float volume)225*b290403dSRicardo Garcia     public void setVolume(
226*b290403dSRicardo Garcia         float volume)
227*b290403dSRicardo Garcia     {
228*b290403dSRicardo Garcia         this.volume = volume;
229*b290403dSRicardo Garcia     }
230*b290403dSRicardo Garcia 
231*b290403dSRicardo Garcia     // Allocate stream buffers.
allocateStreamBuffers( int sampleRate, int numChannels)232*b290403dSRicardo Garcia     private void allocateStreamBuffers(
233*b290403dSRicardo Garcia         int sampleRate,
234*b290403dSRicardo Garcia         int numChannels)
235*b290403dSRicardo Garcia     {
236*b290403dSRicardo Garcia         minPeriod = sampleRate/SONIC_MAX_PITCH;
237*b290403dSRicardo Garcia         maxPeriod = sampleRate/SONIC_MIN_PITCH;
238*b290403dSRicardo Garcia         maxRequired = 2*maxPeriod;
239*b290403dSRicardo Garcia         inputBufferSize = maxRequired;
240*b290403dSRicardo Garcia         inputBuffer = new short[maxRequired*numChannels];
241*b290403dSRicardo Garcia         outputBufferSize = maxRequired;
242*b290403dSRicardo Garcia         outputBuffer = new short[maxRequired*numChannels];
243*b290403dSRicardo Garcia         pitchBufferSize = maxRequired;
244*b290403dSRicardo Garcia         pitchBuffer = new short[maxRequired*numChannels];
245*b290403dSRicardo Garcia         downSampleBuffer = new short[maxRequired];
246*b290403dSRicardo Garcia         this.sampleRate = sampleRate;
247*b290403dSRicardo Garcia         this.numChannels = numChannels;
248*b290403dSRicardo Garcia         oldRatePosition = 0;
249*b290403dSRicardo Garcia         newRatePosition = 0;
250*b290403dSRicardo Garcia         prevPeriod = 0;
251*b290403dSRicardo Garcia     }
252*b290403dSRicardo Garcia 
253*b290403dSRicardo Garcia     // Create a sonic stream.
Sonic( int sampleRate, int numChannels)254*b290403dSRicardo Garcia     public Sonic(
255*b290403dSRicardo Garcia         int sampleRate,
256*b290403dSRicardo Garcia         int numChannels)
257*b290403dSRicardo Garcia     {
258*b290403dSRicardo Garcia         allocateStreamBuffers(sampleRate, numChannels);
259*b290403dSRicardo Garcia         speed = 1.0f;
260*b290403dSRicardo Garcia         pitch = 1.0f;
261*b290403dSRicardo Garcia         volume = 1.0f;
262*b290403dSRicardo Garcia         rate = 1.0f;
263*b290403dSRicardo Garcia         oldRatePosition = 0;
264*b290403dSRicardo Garcia         newRatePosition = 0;
265*b290403dSRicardo Garcia         useChordPitch = false;
266*b290403dSRicardo Garcia         quality = 0;
267*b290403dSRicardo Garcia     }
268*b290403dSRicardo Garcia 
269*b290403dSRicardo Garcia     // Get the sample rate of the stream.
getSampleRate()270*b290403dSRicardo Garcia     public int getSampleRate()
271*b290403dSRicardo Garcia     {
272*b290403dSRicardo Garcia         return sampleRate;
273*b290403dSRicardo Garcia     }
274*b290403dSRicardo Garcia 
275*b290403dSRicardo Garcia     // Set the sample rate of the stream.  This will cause samples buffered in the stream to be lost.
setSampleRate( int sampleRate)276*b290403dSRicardo Garcia     public void setSampleRate(
277*b290403dSRicardo Garcia         int sampleRate)
278*b290403dSRicardo Garcia     {
279*b290403dSRicardo Garcia         allocateStreamBuffers(sampleRate, numChannels);
280*b290403dSRicardo Garcia     }
281*b290403dSRicardo Garcia 
282*b290403dSRicardo Garcia     // Get the number of channels.
getNumChannels()283*b290403dSRicardo Garcia     public int getNumChannels()
284*b290403dSRicardo Garcia     {
285*b290403dSRicardo Garcia         return numChannels;
286*b290403dSRicardo Garcia     }
287*b290403dSRicardo Garcia 
288*b290403dSRicardo Garcia     // Set the num channels of the stream.  This will cause samples buffered in the stream to be lost.
setNumChannels( int numChannels)289*b290403dSRicardo Garcia     public void setNumChannels(
290*b290403dSRicardo Garcia         int numChannels)
291*b290403dSRicardo Garcia     {
292*b290403dSRicardo Garcia         allocateStreamBuffers(sampleRate, numChannels);
293*b290403dSRicardo Garcia     }
294*b290403dSRicardo Garcia 
295*b290403dSRicardo Garcia     // Enlarge the output buffer if needed.
enlargeOutputBufferIfNeeded( int numSamples)296*b290403dSRicardo Garcia     private void enlargeOutputBufferIfNeeded(
297*b290403dSRicardo Garcia         int numSamples)
298*b290403dSRicardo Garcia     {
299*b290403dSRicardo Garcia         if(numOutputSamples + numSamples > outputBufferSize) {
300*b290403dSRicardo Garcia             outputBufferSize += (outputBufferSize >> 1) + numSamples;
301*b290403dSRicardo Garcia             outputBuffer = resize(outputBuffer, outputBufferSize);
302*b290403dSRicardo Garcia         }
303*b290403dSRicardo Garcia     }
304*b290403dSRicardo Garcia 
305*b290403dSRicardo Garcia     // Enlarge the input buffer if needed.
enlargeInputBufferIfNeeded( int numSamples)306*b290403dSRicardo Garcia     private void enlargeInputBufferIfNeeded(
307*b290403dSRicardo Garcia         int numSamples)
308*b290403dSRicardo Garcia     {
309*b290403dSRicardo Garcia         if(numInputSamples + numSamples > inputBufferSize) {
310*b290403dSRicardo Garcia             inputBufferSize += (inputBufferSize >> 1) + numSamples;
311*b290403dSRicardo Garcia             inputBuffer = resize(inputBuffer, inputBufferSize);
312*b290403dSRicardo Garcia         }
313*b290403dSRicardo Garcia     }
314*b290403dSRicardo Garcia 
315*b290403dSRicardo Garcia     // Add the input samples to the input buffer.
addFloatSamplesToInputBuffer( float samples[], int numSamples)316*b290403dSRicardo Garcia     private void addFloatSamplesToInputBuffer(
317*b290403dSRicardo Garcia         float samples[],
318*b290403dSRicardo Garcia         int numSamples)
319*b290403dSRicardo Garcia     {
320*b290403dSRicardo Garcia         if(numSamples == 0) {
321*b290403dSRicardo Garcia             return;
322*b290403dSRicardo Garcia         }
323*b290403dSRicardo Garcia         enlargeInputBufferIfNeeded(numSamples);
324*b290403dSRicardo Garcia         int xBuffer = numInputSamples*numChannels;
325*b290403dSRicardo Garcia         for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
326*b290403dSRicardo Garcia             inputBuffer[xBuffer++] = (short)(samples[xSample]*32767.0f);
327*b290403dSRicardo Garcia         }
328*b290403dSRicardo Garcia         numInputSamples += numSamples;
329*b290403dSRicardo Garcia     }
330*b290403dSRicardo Garcia 
331*b290403dSRicardo Garcia     // Add the input samples to the input buffer.
addShortSamplesToInputBuffer( short samples[], int numSamples)332*b290403dSRicardo Garcia     private void addShortSamplesToInputBuffer(
333*b290403dSRicardo Garcia         short samples[],
334*b290403dSRicardo Garcia         int numSamples)
335*b290403dSRicardo Garcia     {
336*b290403dSRicardo Garcia         if(numSamples == 0) {
337*b290403dSRicardo Garcia             return;
338*b290403dSRicardo Garcia         }
339*b290403dSRicardo Garcia         enlargeInputBufferIfNeeded(numSamples);
340*b290403dSRicardo Garcia         move(inputBuffer, numInputSamples, samples, 0, numSamples);
341*b290403dSRicardo Garcia         numInputSamples += numSamples;
342*b290403dSRicardo Garcia     }
343*b290403dSRicardo Garcia 
344*b290403dSRicardo Garcia     // Add the input samples to the input buffer.
addUnsignedByteSamplesToInputBuffer( byte samples[], int numSamples)345*b290403dSRicardo Garcia     private void addUnsignedByteSamplesToInputBuffer(
346*b290403dSRicardo Garcia         byte samples[],
347*b290403dSRicardo Garcia         int numSamples)
348*b290403dSRicardo Garcia     {
349*b290403dSRicardo Garcia         short sample;
350*b290403dSRicardo Garcia 
351*b290403dSRicardo Garcia         enlargeInputBufferIfNeeded(numSamples);
352*b290403dSRicardo Garcia         int xBuffer = numInputSamples*numChannels;
353*b290403dSRicardo Garcia         for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
354*b290403dSRicardo Garcia                 sample = (short)((samples[xSample] & 0xff) - 128); // Convert from unsigned to signed
355*b290403dSRicardo Garcia             inputBuffer[xBuffer++] = (short) (sample << 8);
356*b290403dSRicardo Garcia         }
357*b290403dSRicardo Garcia         numInputSamples += numSamples;
358*b290403dSRicardo Garcia     }
359*b290403dSRicardo Garcia 
360*b290403dSRicardo Garcia     // Add the input samples to the input buffer.  They must be 16-bit little-endian encoded in a byte array.
addBytesToInputBuffer( byte inBuffer[], int numBytes)361*b290403dSRicardo Garcia     private void addBytesToInputBuffer(
362*b290403dSRicardo Garcia         byte inBuffer[],
363*b290403dSRicardo Garcia         int numBytes)
364*b290403dSRicardo Garcia     {
365*b290403dSRicardo Garcia             int numSamples = numBytes/(2*numChannels);
366*b290403dSRicardo Garcia         short sample;
367*b290403dSRicardo Garcia 
368*b290403dSRicardo Garcia         enlargeInputBufferIfNeeded(numSamples);
369*b290403dSRicardo Garcia         int xBuffer = numInputSamples*numChannels;
370*b290403dSRicardo Garcia         for(int xByte = 0; xByte + 1 < numBytes; xByte += 2) {
371*b290403dSRicardo Garcia                 sample = (short)((inBuffer[xByte] & 0xff) | (inBuffer[xByte + 1] << 8));
372*b290403dSRicardo Garcia             inputBuffer[xBuffer++] = sample;
373*b290403dSRicardo Garcia         }
374*b290403dSRicardo Garcia         numInputSamples += numSamples;
375*b290403dSRicardo Garcia     }
376*b290403dSRicardo Garcia 
377*b290403dSRicardo Garcia     // Remove input samples that we have already processed.
removeInputSamples( int position)378*b290403dSRicardo Garcia     private void removeInputSamples(
379*b290403dSRicardo Garcia         int position)
380*b290403dSRicardo Garcia     {
381*b290403dSRicardo Garcia         int remainingSamples = numInputSamples - position;
382*b290403dSRicardo Garcia 
383*b290403dSRicardo Garcia         move(inputBuffer, 0, inputBuffer, position, remainingSamples);
384*b290403dSRicardo Garcia         numInputSamples = remainingSamples;
385*b290403dSRicardo Garcia     }
386*b290403dSRicardo Garcia 
387*b290403dSRicardo Garcia     // Just copy from the array to the output buffer
copyToOutput( short samples[], int position, int numSamples)388*b290403dSRicardo Garcia     private void copyToOutput(
389*b290403dSRicardo Garcia         short samples[],
390*b290403dSRicardo Garcia         int position,
391*b290403dSRicardo Garcia         int numSamples)
392*b290403dSRicardo Garcia     {
393*b290403dSRicardo Garcia         enlargeOutputBufferIfNeeded(numSamples);
394*b290403dSRicardo Garcia         move(outputBuffer, numOutputSamples, samples, position, numSamples);
395*b290403dSRicardo Garcia         numOutputSamples += numSamples;
396*b290403dSRicardo Garcia     }
397*b290403dSRicardo Garcia 
398*b290403dSRicardo Garcia     // Just copy from the input buffer to the output buffer.  Return num samples copied.
copyInputToOutput( int position)399*b290403dSRicardo Garcia     private int copyInputToOutput(
400*b290403dSRicardo Garcia         int position)
401*b290403dSRicardo Garcia     {
402*b290403dSRicardo Garcia         int numSamples = remainingInputToCopy;
403*b290403dSRicardo Garcia 
404*b290403dSRicardo Garcia         if(numSamples > maxRequired) {
405*b290403dSRicardo Garcia             numSamples = maxRequired;
406*b290403dSRicardo Garcia         }
407*b290403dSRicardo Garcia         copyToOutput(inputBuffer, position, numSamples);
408*b290403dSRicardo Garcia         remainingInputToCopy -= numSamples;
409*b290403dSRicardo Garcia         return numSamples;
410*b290403dSRicardo Garcia     }
411*b290403dSRicardo Garcia 
412*b290403dSRicardo Garcia     // Read data out of the stream.  Sometimes no data will be available, and zero
413*b290403dSRicardo Garcia     // is returned, which is not an error condition.
readFloatFromStream( float samples[], int maxSamples)414*b290403dSRicardo Garcia     public int readFloatFromStream(
415*b290403dSRicardo Garcia         float samples[],
416*b290403dSRicardo Garcia         int maxSamples)
417*b290403dSRicardo Garcia     {
418*b290403dSRicardo Garcia         int numSamples = numOutputSamples;
419*b290403dSRicardo Garcia         int remainingSamples = 0;
420*b290403dSRicardo Garcia 
421*b290403dSRicardo Garcia         if(numSamples == 0) {
422*b290403dSRicardo Garcia             return 0;
423*b290403dSRicardo Garcia         }
424*b290403dSRicardo Garcia         if(numSamples > maxSamples) {
425*b290403dSRicardo Garcia             remainingSamples = numSamples - maxSamples;
426*b290403dSRicardo Garcia             numSamples = maxSamples;
427*b290403dSRicardo Garcia         }
428*b290403dSRicardo Garcia         for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
429*b290403dSRicardo Garcia             samples[xSample] = (outputBuffer[xSample])/32767.0f;
430*b290403dSRicardo Garcia         }
431*b290403dSRicardo Garcia         move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
432*b290403dSRicardo Garcia         numOutputSamples = remainingSamples;
433*b290403dSRicardo Garcia         return numSamples;
434*b290403dSRicardo Garcia     }
435*b290403dSRicardo Garcia 
436*b290403dSRicardo Garcia     // Read short data out of the stream.  Sometimes no data will be available, and zero
437*b290403dSRicardo Garcia     // is returned, which is not an error condition.
readShortFromStream( short samples[], int maxSamples)438*b290403dSRicardo Garcia     public int readShortFromStream(
439*b290403dSRicardo Garcia         short samples[],
440*b290403dSRicardo Garcia         int maxSamples)
441*b290403dSRicardo Garcia     {
442*b290403dSRicardo Garcia         int numSamples = numOutputSamples;
443*b290403dSRicardo Garcia         int remainingSamples = 0;
444*b290403dSRicardo Garcia 
445*b290403dSRicardo Garcia         if(numSamples == 0) {
446*b290403dSRicardo Garcia             return 0;
447*b290403dSRicardo Garcia         }
448*b290403dSRicardo Garcia         if(numSamples > maxSamples) {
449*b290403dSRicardo Garcia             remainingSamples = numSamples - maxSamples;
450*b290403dSRicardo Garcia             numSamples = maxSamples;
451*b290403dSRicardo Garcia         }
452*b290403dSRicardo Garcia         move(samples, 0, outputBuffer, 0, numSamples);
453*b290403dSRicardo Garcia         move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
454*b290403dSRicardo Garcia         numOutputSamples = remainingSamples;
455*b290403dSRicardo Garcia         return numSamples;
456*b290403dSRicardo Garcia     }
457*b290403dSRicardo Garcia 
458*b290403dSRicardo Garcia     // Read unsigned byte data out of the stream.  Sometimes no data will be available, and zero
459*b290403dSRicardo Garcia     // is returned, which is not an error condition.
readUnsignedByteFromStream( byte samples[], int maxSamples)460*b290403dSRicardo Garcia     public int readUnsignedByteFromStream(
461*b290403dSRicardo Garcia         byte samples[],
462*b290403dSRicardo Garcia         int maxSamples)
463*b290403dSRicardo Garcia     {
464*b290403dSRicardo Garcia         int numSamples = numOutputSamples;
465*b290403dSRicardo Garcia         int remainingSamples = 0;
466*b290403dSRicardo Garcia 
467*b290403dSRicardo Garcia         if(numSamples == 0) {
468*b290403dSRicardo Garcia             return 0;
469*b290403dSRicardo Garcia         }
470*b290403dSRicardo Garcia         if(numSamples > maxSamples) {
471*b290403dSRicardo Garcia             remainingSamples = numSamples - maxSamples;
472*b290403dSRicardo Garcia             numSamples = maxSamples;
473*b290403dSRicardo Garcia         }
474*b290403dSRicardo Garcia         for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
475*b290403dSRicardo Garcia                 samples[xSample] = (byte)((outputBuffer[xSample] >> 8) + 128);
476*b290403dSRicardo Garcia         }
477*b290403dSRicardo Garcia         move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
478*b290403dSRicardo Garcia         numOutputSamples = remainingSamples;
479*b290403dSRicardo Garcia         return numSamples;
480*b290403dSRicardo Garcia     }
481*b290403dSRicardo Garcia 
482*b290403dSRicardo Garcia     // Read unsigned byte data out of the stream.  Sometimes no data will be available, and zero
483*b290403dSRicardo Garcia     // is returned, which is not an error condition.
readBytesFromStream( byte outBuffer[], int maxBytes)484*b290403dSRicardo Garcia     public int readBytesFromStream(
485*b290403dSRicardo Garcia         byte outBuffer[],
486*b290403dSRicardo Garcia         int maxBytes)
487*b290403dSRicardo Garcia     {
488*b290403dSRicardo Garcia             int maxSamples = maxBytes/(2*numChannels);
489*b290403dSRicardo Garcia         int numSamples = numOutputSamples;
490*b290403dSRicardo Garcia         int remainingSamples = 0;
491*b290403dSRicardo Garcia 
492*b290403dSRicardo Garcia         if(numSamples == 0 || maxSamples == 0) {
493*b290403dSRicardo Garcia             return 0;
494*b290403dSRicardo Garcia         }
495*b290403dSRicardo Garcia         if(numSamples > maxSamples) {
496*b290403dSRicardo Garcia             remainingSamples = numSamples - maxSamples;
497*b290403dSRicardo Garcia             numSamples = maxSamples;
498*b290403dSRicardo Garcia         }
499*b290403dSRicardo Garcia         for(int xSample = 0; xSample < numSamples*numChannels; xSample++) {
500*b290403dSRicardo Garcia                 short sample = outputBuffer[xSample];
501*b290403dSRicardo Garcia                 outBuffer[xSample << 1] = (byte)(sample & 0xff);
502*b290403dSRicardo Garcia                 outBuffer[(xSample << 1) + 1] = (byte)(sample >> 8);
503*b290403dSRicardo Garcia         }
504*b290403dSRicardo Garcia         move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples);
505*b290403dSRicardo Garcia         numOutputSamples = remainingSamples;
506*b290403dSRicardo Garcia         return 2*numSamples*numChannels;
507*b290403dSRicardo Garcia     }
508*b290403dSRicardo Garcia 
509*b290403dSRicardo Garcia     // Force the sonic stream to generate output using whatever data it currently
510*b290403dSRicardo Garcia     // has.  No extra delay will be added to the output, but flushing in the middle of
511*b290403dSRicardo Garcia     // words could introduce distortion.
flushStream()512*b290403dSRicardo Garcia     public void flushStream()
513*b290403dSRicardo Garcia     {
514*b290403dSRicardo Garcia         int remainingSamples = numInputSamples;
515*b290403dSRicardo Garcia         float s = speed/pitch;
516*b290403dSRicardo Garcia         float r = rate*pitch;
517*b290403dSRicardo Garcia         int expectedOutputSamples = numOutputSamples + (int)((remainingSamples/s + numPitchSamples)/r + 0.5f);
518*b290403dSRicardo Garcia 
519*b290403dSRicardo Garcia         // Add enough silence to flush both input and pitch buffers.
520*b290403dSRicardo Garcia         enlargeInputBufferIfNeeded(remainingSamples + 2*maxRequired);
521*b290403dSRicardo Garcia         for(int xSample = 0; xSample < 2*maxRequired*numChannels; xSample++) {
522*b290403dSRicardo Garcia             inputBuffer[remainingSamples*numChannels + xSample] = 0;
523*b290403dSRicardo Garcia         }
524*b290403dSRicardo Garcia         numInputSamples += 2*maxRequired;
525*b290403dSRicardo Garcia         writeShortToStream(null, 0);
526*b290403dSRicardo Garcia         // Throw away any extra samples we generated due to the silence we added.
527*b290403dSRicardo Garcia         if(numOutputSamples > expectedOutputSamples) {
528*b290403dSRicardo Garcia             numOutputSamples = expectedOutputSamples;
529*b290403dSRicardo Garcia         }
530*b290403dSRicardo Garcia         // Empty input and pitch buffers.
531*b290403dSRicardo Garcia         numInputSamples = 0;
532*b290403dSRicardo Garcia         remainingInputToCopy = 0;
533*b290403dSRicardo Garcia         numPitchSamples = 0;
534*b290403dSRicardo Garcia     }
535*b290403dSRicardo Garcia 
536*b290403dSRicardo Garcia     // Return the number of samples in the output buffer
samplesAvailable()537*b290403dSRicardo Garcia     public int samplesAvailable()
538*b290403dSRicardo Garcia     {
539*b290403dSRicardo Garcia         return numOutputSamples;
540*b290403dSRicardo Garcia     }
541*b290403dSRicardo Garcia 
542*b290403dSRicardo Garcia     // If skip is greater than one, average skip samples together and write them to
543*b290403dSRicardo Garcia     // the down-sample buffer.  If numChannels is greater than one, mix the channels
544*b290403dSRicardo Garcia     // together as we down sample.
downSampleInput( short samples[], int position, int skip)545*b290403dSRicardo Garcia     private void downSampleInput(
546*b290403dSRicardo Garcia         short samples[],
547*b290403dSRicardo Garcia         int position,
548*b290403dSRicardo Garcia         int skip)
549*b290403dSRicardo Garcia     {
550*b290403dSRicardo Garcia         int numSamples = maxRequired/skip;
551*b290403dSRicardo Garcia         int samplesPerValue = numChannels*skip;
552*b290403dSRicardo Garcia         int value;
553*b290403dSRicardo Garcia 
554*b290403dSRicardo Garcia         position *= numChannels;
555*b290403dSRicardo Garcia         for(int i = 0; i < numSamples; i++) {
556*b290403dSRicardo Garcia             value = 0;
557*b290403dSRicardo Garcia             for(int j = 0; j < samplesPerValue; j++) {
558*b290403dSRicardo Garcia                 value += samples[position + i*samplesPerValue + j];
559*b290403dSRicardo Garcia             }
560*b290403dSRicardo Garcia             value /= samplesPerValue;
561*b290403dSRicardo Garcia             downSampleBuffer[i] = (short)value;
562*b290403dSRicardo Garcia         }
563*b290403dSRicardo Garcia     }
564*b290403dSRicardo Garcia 
565*b290403dSRicardo Garcia     // Find the best frequency match in the range, and given a sample skip multiple.
566*b290403dSRicardo Garcia     // For now, just find the pitch of the first channel.
findPitchPeriodInRange( short samples[], int position, int minPeriod, int maxPeriod)567*b290403dSRicardo Garcia     private int findPitchPeriodInRange(
568*b290403dSRicardo Garcia         short samples[],
569*b290403dSRicardo Garcia         int position,
570*b290403dSRicardo Garcia         int minPeriod,
571*b290403dSRicardo Garcia         int maxPeriod)
572*b290403dSRicardo Garcia     {
573*b290403dSRicardo Garcia         int bestPeriod = 0, worstPeriod = 255;
574*b290403dSRicardo Garcia         int minDiff = 1, maxDiff = 0;
575*b290403dSRicardo Garcia 
576*b290403dSRicardo Garcia         position *= numChannels;
577*b290403dSRicardo Garcia         for(int period = minPeriod; period <= maxPeriod; period++) {
578*b290403dSRicardo Garcia             int diff = 0;
579*b290403dSRicardo Garcia             for(int i = 0; i < period; i++) {
580*b290403dSRicardo Garcia                 short sVal = samples[position + i];
581*b290403dSRicardo Garcia                 short pVal = samples[position + period + i];
582*b290403dSRicardo Garcia                 diff += sVal >= pVal? sVal - pVal : pVal - sVal;
583*b290403dSRicardo Garcia             }
584*b290403dSRicardo Garcia             /* Note that the highest number of samples we add into diff will be less
585*b290403dSRicardo Garcia                than 256, since we skip samples.  Thus, diff is a 24 bit number, and
586*b290403dSRicardo Garcia                we can safely multiply by numSamples without overflow */
587*b290403dSRicardo Garcia             if(diff*bestPeriod < minDiff*period) {
588*b290403dSRicardo Garcia                 minDiff = diff;
589*b290403dSRicardo Garcia                 bestPeriod = period;
590*b290403dSRicardo Garcia             }
591*b290403dSRicardo Garcia             if(diff*worstPeriod > maxDiff*period) {
592*b290403dSRicardo Garcia                 maxDiff = diff;
593*b290403dSRicardo Garcia                 worstPeriod = period;
594*b290403dSRicardo Garcia             }
595*b290403dSRicardo Garcia         }
596*b290403dSRicardo Garcia         this.minDiff = minDiff/bestPeriod;
597*b290403dSRicardo Garcia         this.maxDiff = maxDiff/worstPeriod;
598*b290403dSRicardo Garcia 
599*b290403dSRicardo Garcia         return bestPeriod;
600*b290403dSRicardo Garcia     }
601*b290403dSRicardo Garcia 
602*b290403dSRicardo Garcia     // At abrupt ends of voiced words, we can have pitch periods that are better
603*b290403dSRicardo Garcia     // approximated by the previous pitch period estimate.  Try to detect this case.
prevPeriodBetter( int minDiff, int maxDiff, boolean preferNewPeriod)604*b290403dSRicardo Garcia     private boolean prevPeriodBetter(
605*b290403dSRicardo Garcia         int minDiff,
606*b290403dSRicardo Garcia         int maxDiff,
607*b290403dSRicardo Garcia         boolean preferNewPeriod)
608*b290403dSRicardo Garcia     {
609*b290403dSRicardo Garcia         if(minDiff == 0 || prevPeriod == 0) {
610*b290403dSRicardo Garcia             return false;
611*b290403dSRicardo Garcia         }
612*b290403dSRicardo Garcia         if(preferNewPeriod) {
613*b290403dSRicardo Garcia             if(maxDiff > minDiff*3) {
614*b290403dSRicardo Garcia                 // Got a reasonable match this period
615*b290403dSRicardo Garcia                 return false;
616*b290403dSRicardo Garcia             }
617*b290403dSRicardo Garcia             if(minDiff*2 <= prevMinDiff*3) {
618*b290403dSRicardo Garcia                 // Mismatch is not that much greater this period
619*b290403dSRicardo Garcia                 return false;
620*b290403dSRicardo Garcia             }
621*b290403dSRicardo Garcia         } else {
622*b290403dSRicardo Garcia             if(minDiff <= prevMinDiff) {
623*b290403dSRicardo Garcia                 return false;
624*b290403dSRicardo Garcia             }
625*b290403dSRicardo Garcia         }
626*b290403dSRicardo Garcia         return true;
627*b290403dSRicardo Garcia     }
628*b290403dSRicardo Garcia 
629*b290403dSRicardo Garcia     // Find the pitch period.  This is a critical step, and we may have to try
630*b290403dSRicardo Garcia     // multiple ways to get a good answer.  This version uses AMDF.  To improve
631*b290403dSRicardo Garcia     // speed, we down sample by an integer factor get in the 11KHz range, and then
632*b290403dSRicardo Garcia     // do it again with a narrower frequency range without down sampling
findPitchPeriod( short samples[], int position, boolean preferNewPeriod)633*b290403dSRicardo Garcia     private int findPitchPeriod(
634*b290403dSRicardo Garcia         short samples[],
635*b290403dSRicardo Garcia         int position,
636*b290403dSRicardo Garcia         boolean preferNewPeriod)
637*b290403dSRicardo Garcia     {
638*b290403dSRicardo Garcia         int period, retPeriod;
639*b290403dSRicardo Garcia         int skip = 1;
640*b290403dSRicardo Garcia 
641*b290403dSRicardo Garcia         if(sampleRate > SONIC_AMDF_FREQ && quality == 0) {
642*b290403dSRicardo Garcia             skip = sampleRate/SONIC_AMDF_FREQ;
643*b290403dSRicardo Garcia         }
644*b290403dSRicardo Garcia         if(numChannels == 1 && skip == 1) {
645*b290403dSRicardo Garcia             period = findPitchPeriodInRange(samples, position, minPeriod, maxPeriod);
646*b290403dSRicardo Garcia         } else {
647*b290403dSRicardo Garcia             downSampleInput(samples, position, skip);
648*b290403dSRicardo Garcia             period = findPitchPeriodInRange(downSampleBuffer, 0, minPeriod/skip,
649*b290403dSRicardo Garcia                 maxPeriod/skip);
650*b290403dSRicardo Garcia             if(skip != 1) {
651*b290403dSRicardo Garcia                 period *= skip;
652*b290403dSRicardo Garcia                 int minP = period - (skip << 2);
653*b290403dSRicardo Garcia                 int maxP = period + (skip << 2);
654*b290403dSRicardo Garcia                 if(minP < minPeriod) {
655*b290403dSRicardo Garcia                     minP = minPeriod;
656*b290403dSRicardo Garcia                 }
657*b290403dSRicardo Garcia                 if(maxP > maxPeriod) {
658*b290403dSRicardo Garcia                     maxP = maxPeriod;
659*b290403dSRicardo Garcia                 }
660*b290403dSRicardo Garcia                 if(numChannels == 1) {
661*b290403dSRicardo Garcia                     period = findPitchPeriodInRange(samples, position, minP, maxP);
662*b290403dSRicardo Garcia                 } else {
663*b290403dSRicardo Garcia                     downSampleInput(samples, position, 1);
664*b290403dSRicardo Garcia                     period = findPitchPeriodInRange(downSampleBuffer, 0, minP, maxP);
665*b290403dSRicardo Garcia                 }
666*b290403dSRicardo Garcia             }
667*b290403dSRicardo Garcia         }
668*b290403dSRicardo Garcia         if(prevPeriodBetter(minDiff, maxDiff, preferNewPeriod)) {
669*b290403dSRicardo Garcia             retPeriod = prevPeriod;
670*b290403dSRicardo Garcia         } else {
671*b290403dSRicardo Garcia             retPeriod = period;
672*b290403dSRicardo Garcia         }
673*b290403dSRicardo Garcia         prevMinDiff = minDiff;
674*b290403dSRicardo Garcia         prevPeriod = period;
675*b290403dSRicardo Garcia         return retPeriod;
676*b290403dSRicardo Garcia     }
677*b290403dSRicardo Garcia 
678*b290403dSRicardo Garcia     // Overlap two sound segments, ramp the volume of one down, while ramping the
679*b290403dSRicardo Garcia     // other one from zero up, and add them, storing the result at the output.
overlapAdd( int numSamples, int numChannels, short out[], int outPos, short rampDown[], int rampDownPos, short rampUp[], int rampUpPos)680*b290403dSRicardo Garcia     private void overlapAdd(
681*b290403dSRicardo Garcia         int numSamples,
682*b290403dSRicardo Garcia         int numChannels,
683*b290403dSRicardo Garcia         short out[],
684*b290403dSRicardo Garcia         int outPos,
685*b290403dSRicardo Garcia         short rampDown[],
686*b290403dSRicardo Garcia         int rampDownPos,
687*b290403dSRicardo Garcia         short rampUp[],
688*b290403dSRicardo Garcia         int rampUpPos)
689*b290403dSRicardo Garcia     {
690*b290403dSRicardo Garcia          for(int i = 0; i < numChannels; i++) {
691*b290403dSRicardo Garcia             int o = outPos*numChannels + i;
692*b290403dSRicardo Garcia             int u = rampUpPos*numChannels + i;
693*b290403dSRicardo Garcia             int d = rampDownPos*numChannels + i;
694*b290403dSRicardo Garcia             for(int t = 0; t < numSamples; t++) {
695*b290403dSRicardo Garcia                 out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*t)/numSamples);
696*b290403dSRicardo Garcia                 o += numChannels;
697*b290403dSRicardo Garcia                 d += numChannels;
698*b290403dSRicardo Garcia                 u += numChannels;
699*b290403dSRicardo Garcia             }
700*b290403dSRicardo Garcia         }
701*b290403dSRicardo Garcia     }
702*b290403dSRicardo Garcia 
703*b290403dSRicardo Garcia     // Overlap two sound segments, ramp the volume of one down, while ramping the
704*b290403dSRicardo Garcia     // other one from zero up, and add them, storing the result at the output.
overlapAddWithSeparation( int numSamples, int numChannels, int separation, short out[], int outPos, short rampDown[], int rampDownPos, short rampUp[], int rampUpPos)705*b290403dSRicardo Garcia     private void overlapAddWithSeparation(
706*b290403dSRicardo Garcia         int numSamples,
707*b290403dSRicardo Garcia         int numChannels,
708*b290403dSRicardo Garcia         int separation,
709*b290403dSRicardo Garcia         short out[],
710*b290403dSRicardo Garcia         int outPos,
711*b290403dSRicardo Garcia         short rampDown[],
712*b290403dSRicardo Garcia         int rampDownPos,
713*b290403dSRicardo Garcia         short rampUp[],
714*b290403dSRicardo Garcia         int rampUpPos)
715*b290403dSRicardo Garcia     {
716*b290403dSRicardo Garcia         for(int i = 0; i < numChannels; i++) {
717*b290403dSRicardo Garcia             int o = outPos*numChannels + i;
718*b290403dSRicardo Garcia             int u = rampUpPos*numChannels + i;
719*b290403dSRicardo Garcia             int d = rampDownPos*numChannels + i;
720*b290403dSRicardo Garcia             for(int t = 0; t < numSamples + separation; t++) {
721*b290403dSRicardo Garcia                 if(t < separation) {
722*b290403dSRicardo Garcia                     out[o] = (short)(rampDown[d]*(numSamples - t)/numSamples);
723*b290403dSRicardo Garcia                     d += numChannels;
724*b290403dSRicardo Garcia                 } else if(t < numSamples) {
725*b290403dSRicardo Garcia                     out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*(t - separation))/numSamples);
726*b290403dSRicardo Garcia                     d += numChannels;
727*b290403dSRicardo Garcia                     u += numChannels;
728*b290403dSRicardo Garcia                 } else {
729*b290403dSRicardo Garcia                     out[o] = (short)(rampUp[u]*(t - separation)/numSamples);
730*b290403dSRicardo Garcia                     u += numChannels;
731*b290403dSRicardo Garcia                 }
732*b290403dSRicardo Garcia                 o += numChannels;
733*b290403dSRicardo Garcia             }
734*b290403dSRicardo Garcia         }
735*b290403dSRicardo Garcia     }
736*b290403dSRicardo Garcia 
737*b290403dSRicardo Garcia     // Just move the new samples in the output buffer to the pitch buffer
moveNewSamplesToPitchBuffer( int originalNumOutputSamples)738*b290403dSRicardo Garcia     private void moveNewSamplesToPitchBuffer(
739*b290403dSRicardo Garcia         int originalNumOutputSamples)
740*b290403dSRicardo Garcia     {
741*b290403dSRicardo Garcia         int numSamples = numOutputSamples - originalNumOutputSamples;
742*b290403dSRicardo Garcia 
743*b290403dSRicardo Garcia         if(numPitchSamples + numSamples > pitchBufferSize) {
744*b290403dSRicardo Garcia             pitchBufferSize += (pitchBufferSize >> 1) + numSamples;
745*b290403dSRicardo Garcia             pitchBuffer = resize(pitchBuffer, pitchBufferSize);
746*b290403dSRicardo Garcia         }
747*b290403dSRicardo Garcia         move(pitchBuffer, numPitchSamples, outputBuffer, originalNumOutputSamples, numSamples);
748*b290403dSRicardo Garcia         numOutputSamples = originalNumOutputSamples;
749*b290403dSRicardo Garcia         numPitchSamples += numSamples;
750*b290403dSRicardo Garcia     }
751*b290403dSRicardo Garcia 
752*b290403dSRicardo Garcia     // Remove processed samples from the pitch buffer.
removePitchSamples( int numSamples)753*b290403dSRicardo Garcia     private void removePitchSamples(
754*b290403dSRicardo Garcia         int numSamples)
755*b290403dSRicardo Garcia     {
756*b290403dSRicardo Garcia         if(numSamples == 0) {
757*b290403dSRicardo Garcia             return;
758*b290403dSRicardo Garcia         }
759*b290403dSRicardo Garcia         move(pitchBuffer, 0, pitchBuffer, numSamples, numPitchSamples - numSamples);
760*b290403dSRicardo Garcia         numPitchSamples -= numSamples;
761*b290403dSRicardo Garcia     }
762*b290403dSRicardo Garcia 
763*b290403dSRicardo Garcia     // Change the pitch.  The latency this introduces could be reduced by looking at
764*b290403dSRicardo Garcia     // past samples to determine pitch, rather than future.
adjustPitch( int originalNumOutputSamples)765*b290403dSRicardo Garcia     private void adjustPitch(
766*b290403dSRicardo Garcia         int originalNumOutputSamples)
767*b290403dSRicardo Garcia     {
768*b290403dSRicardo Garcia         int period, newPeriod, separation;
769*b290403dSRicardo Garcia         int position = 0;
770*b290403dSRicardo Garcia 
771*b290403dSRicardo Garcia         if(numOutputSamples == originalNumOutputSamples) {
772*b290403dSRicardo Garcia             return;
773*b290403dSRicardo Garcia         }
774*b290403dSRicardo Garcia         moveNewSamplesToPitchBuffer(originalNumOutputSamples);
775*b290403dSRicardo Garcia         while(numPitchSamples - position >= maxRequired) {
776*b290403dSRicardo Garcia             period = findPitchPeriod(pitchBuffer, position, false);
777*b290403dSRicardo Garcia             newPeriod = (int)(period/pitch);
778*b290403dSRicardo Garcia             enlargeOutputBufferIfNeeded(newPeriod);
779*b290403dSRicardo Garcia             if(pitch >= 1.0f) {
780*b290403dSRicardo Garcia                 overlapAdd(newPeriod, numChannels, outputBuffer, numOutputSamples, pitchBuffer,
781*b290403dSRicardo Garcia                         position, pitchBuffer, position + period - newPeriod);
782*b290403dSRicardo Garcia             } else {
783*b290403dSRicardo Garcia                 separation = newPeriod - period;
784*b290403dSRicardo Garcia                 overlapAddWithSeparation(period, numChannels, separation, outputBuffer, numOutputSamples,
785*b290403dSRicardo Garcia                         pitchBuffer, position, pitchBuffer, position);
786*b290403dSRicardo Garcia             }
787*b290403dSRicardo Garcia             numOutputSamples += newPeriod;
788*b290403dSRicardo Garcia             position += period;
789*b290403dSRicardo Garcia         }
790*b290403dSRicardo Garcia         removePitchSamples(position);
791*b290403dSRicardo Garcia     }
792*b290403dSRicardo Garcia 
793*b290403dSRicardo Garcia     // Approximate the sinc function times a Hann window from the sinc table.
findSincCoefficient(int i, int ratio, int width)794*b290403dSRicardo Garcia     private int findSincCoefficient(int i, int ratio, int width) {
795*b290403dSRicardo Garcia         int lobePoints = (SINC_TABLE_SIZE-1)/SINC_FILTER_POINTS;
796*b290403dSRicardo Garcia         int left = i*lobePoints + (ratio*lobePoints)/width;
797*b290403dSRicardo Garcia         int right = left + 1;
798*b290403dSRicardo Garcia         int position = i*lobePoints*width + ratio*lobePoints - left*width;
799*b290403dSRicardo Garcia         int leftVal = sincTable[left];
800*b290403dSRicardo Garcia         int rightVal = sincTable[right];
801*b290403dSRicardo Garcia 
802*b290403dSRicardo Garcia         return ((leftVal*(width - position) + rightVal*position) << 1)/width;
803*b290403dSRicardo Garcia     }
804*b290403dSRicardo Garcia 
805*b290403dSRicardo Garcia     // Return 1 if value >= 0, else -1.  This represents the sign of value.
getSign(int value)806*b290403dSRicardo Garcia     private int getSign(int value) {
807*b290403dSRicardo Garcia         return value >= 0? 1 : -1;
808*b290403dSRicardo Garcia     }
809*b290403dSRicardo Garcia 
810*b290403dSRicardo Garcia     // Interpolate the new output sample.
interpolate( short in[], int inPos, int oldSampleRate, int newSampleRate)811*b290403dSRicardo Garcia     private short interpolate(
812*b290403dSRicardo Garcia         short in[],
813*b290403dSRicardo Garcia         int inPos,  // Index to first sample which already includes channel offset.
814*b290403dSRicardo Garcia         int oldSampleRate,
815*b290403dSRicardo Garcia         int newSampleRate)
816*b290403dSRicardo Garcia     {
817*b290403dSRicardo Garcia         // Compute N-point sinc FIR-filter here.  Clip rather than overflow.
818*b290403dSRicardo Garcia         int i;
819*b290403dSRicardo Garcia         int total = 0;
820*b290403dSRicardo Garcia         int position = newRatePosition*oldSampleRate;
821*b290403dSRicardo Garcia         int leftPosition = oldRatePosition*newSampleRate;
822*b290403dSRicardo Garcia         int rightPosition = (oldRatePosition + 1)*newSampleRate;
823*b290403dSRicardo Garcia         int ratio = rightPosition - position - 1;
824*b290403dSRicardo Garcia         int width = rightPosition - leftPosition;
825*b290403dSRicardo Garcia         int weight, value;
826*b290403dSRicardo Garcia         int oldSign;
827*b290403dSRicardo Garcia         int overflowCount = 0;
828*b290403dSRicardo Garcia 
829*b290403dSRicardo Garcia         for (i = 0; i < SINC_FILTER_POINTS; i++) {
830*b290403dSRicardo Garcia             weight = findSincCoefficient(i, ratio, width);
831*b290403dSRicardo Garcia             /* printf("%u %f\n", i, weight); */
832*b290403dSRicardo Garcia             value = in[inPos + i*numChannels]*weight;
833*b290403dSRicardo Garcia             oldSign = getSign(total);
834*b290403dSRicardo Garcia             total += value;
835*b290403dSRicardo Garcia             if (oldSign != getSign(total) && getSign(value) == oldSign) {
836*b290403dSRicardo Garcia                 /* We must have overflowed.  This can happen with a sinc filter. */
837*b290403dSRicardo Garcia                 overflowCount += oldSign;
838*b290403dSRicardo Garcia             }
839*b290403dSRicardo Garcia         }
840*b290403dSRicardo Garcia         /* It is better to clip than to wrap if there was a overflow. */
841*b290403dSRicardo Garcia         if (overflowCount > 0) {
842*b290403dSRicardo Garcia             return Short.MAX_VALUE;
843*b290403dSRicardo Garcia         } else if (overflowCount < 0) {
844*b290403dSRicardo Garcia             return Short.MIN_VALUE;
845*b290403dSRicardo Garcia         }
846*b290403dSRicardo Garcia         return (short)(total >> 16);
847*b290403dSRicardo Garcia     }
848*b290403dSRicardo Garcia 
849*b290403dSRicardo Garcia     // Change the rate.
adjustRate( float rate, int originalNumOutputSamples)850*b290403dSRicardo Garcia     private void adjustRate(
851*b290403dSRicardo Garcia         float rate,
852*b290403dSRicardo Garcia         int originalNumOutputSamples)
853*b290403dSRicardo Garcia     {
854*b290403dSRicardo Garcia         int newSampleRate = (int)(sampleRate/rate);
855*b290403dSRicardo Garcia         int oldSampleRate = sampleRate;
856*b290403dSRicardo Garcia         int position;
857*b290403dSRicardo Garcia         int N = SINC_FILTER_POINTS;
858*b290403dSRicardo Garcia 
859*b290403dSRicardo Garcia         // Set these values to help with the integer math
860*b290403dSRicardo Garcia         while(newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
861*b290403dSRicardo Garcia             newSampleRate >>= 1;
862*b290403dSRicardo Garcia             oldSampleRate >>= 1;
863*b290403dSRicardo Garcia         }
864*b290403dSRicardo Garcia         if(numOutputSamples == originalNumOutputSamples) {
865*b290403dSRicardo Garcia             return;
866*b290403dSRicardo Garcia         }
867*b290403dSRicardo Garcia         moveNewSamplesToPitchBuffer(originalNumOutputSamples);
868*b290403dSRicardo Garcia         // Leave at least N pitch samples in the buffer
869*b290403dSRicardo Garcia         for(position = 0; position < numPitchSamples - N; position++) {
870*b290403dSRicardo Garcia             while((oldRatePosition + 1)*newSampleRate > newRatePosition*oldSampleRate) {
871*b290403dSRicardo Garcia                 enlargeOutputBufferIfNeeded(1);
872*b290403dSRicardo Garcia                 for(int i = 0; i < numChannels; i++) {
873*b290403dSRicardo Garcia                     outputBuffer[numOutputSamples*numChannels + i] = interpolate(pitchBuffer,
874*b290403dSRicardo Garcia                             position*numChannels + i, oldSampleRate, newSampleRate);
875*b290403dSRicardo Garcia                 }
876*b290403dSRicardo Garcia                 newRatePosition++;
877*b290403dSRicardo Garcia                 numOutputSamples++;
878*b290403dSRicardo Garcia             }
879*b290403dSRicardo Garcia             oldRatePosition++;
880*b290403dSRicardo Garcia             if(oldRatePosition == oldSampleRate) {
881*b290403dSRicardo Garcia                 oldRatePosition = 0;
882*b290403dSRicardo Garcia                 if(newRatePosition != newSampleRate) {
883*b290403dSRicardo Garcia                     System.out.printf("Assertion failed: newRatePosition != newSampleRate\n");
884*b290403dSRicardo Garcia                     assert false;
885*b290403dSRicardo Garcia                 }
886*b290403dSRicardo Garcia                 newRatePosition = 0;
887*b290403dSRicardo Garcia             }
888*b290403dSRicardo Garcia         }
889*b290403dSRicardo Garcia         removePitchSamples(position);
890*b290403dSRicardo Garcia     }
891*b290403dSRicardo Garcia 
892*b290403dSRicardo Garcia 
893*b290403dSRicardo Garcia     // Skip over a pitch period, and copy period/speed samples to the output
skipPitchPeriod( short samples[], int position, float speed, int period)894*b290403dSRicardo Garcia     private int skipPitchPeriod(
895*b290403dSRicardo Garcia         short samples[],
896*b290403dSRicardo Garcia         int position,
897*b290403dSRicardo Garcia         float speed,
898*b290403dSRicardo Garcia         int period)
899*b290403dSRicardo Garcia     {
900*b290403dSRicardo Garcia         int newSamples;
901*b290403dSRicardo Garcia 
902*b290403dSRicardo Garcia         if(speed >= 2.0f) {
903*b290403dSRicardo Garcia             newSamples = (int)(period/(speed - 1.0f));
904*b290403dSRicardo Garcia         } else {
905*b290403dSRicardo Garcia             newSamples = period;
906*b290403dSRicardo Garcia             remainingInputToCopy = (int)(period*(2.0f - speed)/(speed - 1.0f));
907*b290403dSRicardo Garcia         }
908*b290403dSRicardo Garcia         enlargeOutputBufferIfNeeded(newSamples);
909*b290403dSRicardo Garcia         overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples, samples, position,
910*b290403dSRicardo Garcia                 samples, position + period);
911*b290403dSRicardo Garcia         numOutputSamples += newSamples;
912*b290403dSRicardo Garcia         return newSamples;
913*b290403dSRicardo Garcia     }
914*b290403dSRicardo Garcia 
915*b290403dSRicardo Garcia     // Insert a pitch period, and determine how much input to copy directly.
insertPitchPeriod( short samples[], int position, float speed, int period)916*b290403dSRicardo Garcia     private int insertPitchPeriod(
917*b290403dSRicardo Garcia         short samples[],
918*b290403dSRicardo Garcia         int position,
919*b290403dSRicardo Garcia         float speed,
920*b290403dSRicardo Garcia         int period)
921*b290403dSRicardo Garcia     {
922*b290403dSRicardo Garcia         int newSamples;
923*b290403dSRicardo Garcia 
924*b290403dSRicardo Garcia         if(speed < 0.5f) {
925*b290403dSRicardo Garcia             newSamples = (int)(period*speed/(1.0f - speed));
926*b290403dSRicardo Garcia         } else {
927*b290403dSRicardo Garcia             newSamples = period;
928*b290403dSRicardo Garcia             remainingInputToCopy = (int)(period*(2.0f*speed - 1.0f)/(1.0f - speed));
929*b290403dSRicardo Garcia         }
930*b290403dSRicardo Garcia         enlargeOutputBufferIfNeeded(period + newSamples);
931*b290403dSRicardo Garcia         move(outputBuffer, numOutputSamples, samples, position, period);
932*b290403dSRicardo Garcia         overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples + period, samples,
933*b290403dSRicardo Garcia                 position + period, samples, position);
934*b290403dSRicardo Garcia         numOutputSamples += period + newSamples;
935*b290403dSRicardo Garcia         return newSamples;
936*b290403dSRicardo Garcia     }
937*b290403dSRicardo Garcia 
938*b290403dSRicardo Garcia     // Resample as many pitch periods as we have buffered on the input.  Return 0 if
939*b290403dSRicardo Garcia     // we fail to resize an input or output buffer.  Also scale the output by the volume.
changeSpeed( float speed)940*b290403dSRicardo Garcia     private void changeSpeed(
941*b290403dSRicardo Garcia         float speed)
942*b290403dSRicardo Garcia     {
943*b290403dSRicardo Garcia         int numSamples = numInputSamples;
944*b290403dSRicardo Garcia         int position = 0, period, newSamples;
945*b290403dSRicardo Garcia 
946*b290403dSRicardo Garcia         if(numInputSamples < maxRequired) {
947*b290403dSRicardo Garcia             return;
948*b290403dSRicardo Garcia         }
949*b290403dSRicardo Garcia         do {
950*b290403dSRicardo Garcia             if(remainingInputToCopy > 0) {
951*b290403dSRicardo Garcia                 newSamples = copyInputToOutput(position);
952*b290403dSRicardo Garcia                 position += newSamples;
953*b290403dSRicardo Garcia             } else {
954*b290403dSRicardo Garcia                 period = findPitchPeriod(inputBuffer, position, true);
955*b290403dSRicardo Garcia                 if(speed > 1.0) {
956*b290403dSRicardo Garcia                     newSamples = skipPitchPeriod(inputBuffer, position, speed, period);
957*b290403dSRicardo Garcia                     position += period + newSamples;
958*b290403dSRicardo Garcia                 } else {
959*b290403dSRicardo Garcia                     newSamples = insertPitchPeriod(inputBuffer, position, speed, period);
960*b290403dSRicardo Garcia                     position += newSamples;
961*b290403dSRicardo Garcia                 }
962*b290403dSRicardo Garcia             }
963*b290403dSRicardo Garcia         } while(position + maxRequired <= numSamples);
964*b290403dSRicardo Garcia         removeInputSamples(position);
965*b290403dSRicardo Garcia     }
966*b290403dSRicardo Garcia 
967*b290403dSRicardo Garcia     // Resample as many pitch periods as we have buffered on the input.  Scale the output by the volume.
processStreamInput()968*b290403dSRicardo Garcia     private void processStreamInput()
969*b290403dSRicardo Garcia     {
970*b290403dSRicardo Garcia         int originalNumOutputSamples = numOutputSamples;
971*b290403dSRicardo Garcia         float s = speed/pitch;
972*b290403dSRicardo Garcia         float r = rate;
973*b290403dSRicardo Garcia 
974*b290403dSRicardo Garcia         if(!useChordPitch) {
975*b290403dSRicardo Garcia             r *= pitch;
976*b290403dSRicardo Garcia         }
977*b290403dSRicardo Garcia         if(s > 1.00001 || s < 0.99999) {
978*b290403dSRicardo Garcia             changeSpeed(s);
979*b290403dSRicardo Garcia         } else {
980*b290403dSRicardo Garcia             copyToOutput(inputBuffer, 0, numInputSamples);
981*b290403dSRicardo Garcia             numInputSamples = 0;
982*b290403dSRicardo Garcia         }
983*b290403dSRicardo Garcia         if(useChordPitch) {
984*b290403dSRicardo Garcia             if(pitch != 1.0f) {
985*b290403dSRicardo Garcia                 adjustPitch(originalNumOutputSamples);
986*b290403dSRicardo Garcia             }
987*b290403dSRicardo Garcia         } else if(r != 1.0f) {
988*b290403dSRicardo Garcia             adjustRate(r, originalNumOutputSamples);
989*b290403dSRicardo Garcia         }
990*b290403dSRicardo Garcia         if(volume != 1.0f) {
991*b290403dSRicardo Garcia             // Adjust output volume.
992*b290403dSRicardo Garcia             scaleSamples(outputBuffer, originalNumOutputSamples, numOutputSamples - originalNumOutputSamples,
993*b290403dSRicardo Garcia                 volume);
994*b290403dSRicardo Garcia         }
995*b290403dSRicardo Garcia     }
996*b290403dSRicardo Garcia 
997*b290403dSRicardo Garcia     // Write floating point data to the input buffer and process it.
writeFloatToStream( float samples[], int numSamples)998*b290403dSRicardo Garcia     public void writeFloatToStream(
999*b290403dSRicardo Garcia         float samples[],
1000*b290403dSRicardo Garcia         int numSamples)
1001*b290403dSRicardo Garcia     {
1002*b290403dSRicardo Garcia         addFloatSamplesToInputBuffer(samples, numSamples);
1003*b290403dSRicardo Garcia         processStreamInput();
1004*b290403dSRicardo Garcia     }
1005*b290403dSRicardo Garcia 
1006*b290403dSRicardo Garcia     // Write the data to the input stream, and process it.
writeShortToStream( short samples[], int numSamples)1007*b290403dSRicardo Garcia     public void writeShortToStream(
1008*b290403dSRicardo Garcia         short samples[],
1009*b290403dSRicardo Garcia         int numSamples)
1010*b290403dSRicardo Garcia     {
1011*b290403dSRicardo Garcia         addShortSamplesToInputBuffer(samples, numSamples);
1012*b290403dSRicardo Garcia         processStreamInput();
1013*b290403dSRicardo Garcia     }
1014*b290403dSRicardo Garcia 
1015*b290403dSRicardo Garcia     // Simple wrapper around sonicWriteFloatToStream that does the unsigned byte to short
1016*b290403dSRicardo Garcia     // conversion for you.
writeUnsignedByteToStream( byte samples[], int numSamples)1017*b290403dSRicardo Garcia     public void writeUnsignedByteToStream(
1018*b290403dSRicardo Garcia         byte samples[],
1019*b290403dSRicardo Garcia         int numSamples)
1020*b290403dSRicardo Garcia     {
1021*b290403dSRicardo Garcia         addUnsignedByteSamplesToInputBuffer(samples, numSamples);
1022*b290403dSRicardo Garcia         processStreamInput();
1023*b290403dSRicardo Garcia     }
1024*b290403dSRicardo Garcia 
1025*b290403dSRicardo Garcia     // Simple wrapper around sonicWriteBytesToStream that does the byte to 16-bit LE conversion.
writeBytesToStream( byte inBuffer[], int numBytes)1026*b290403dSRicardo Garcia     public void writeBytesToStream(
1027*b290403dSRicardo Garcia         byte inBuffer[],
1028*b290403dSRicardo Garcia         int numBytes)
1029*b290403dSRicardo Garcia     {
1030*b290403dSRicardo Garcia         addBytesToInputBuffer(inBuffer, numBytes);
1031*b290403dSRicardo Garcia         processStreamInput();
1032*b290403dSRicardo Garcia     }
1033*b290403dSRicardo Garcia 
1034*b290403dSRicardo Garcia     // This is a non-stream oriented interface to just change the speed of a sound sample
changeFloatSpeed( float samples[], int numSamples, float speed, float pitch, float rate, float volume, boolean useChordPitch, int sampleRate, int numChannels)1035*b290403dSRicardo Garcia     public static int changeFloatSpeed(
1036*b290403dSRicardo Garcia         float samples[],
1037*b290403dSRicardo Garcia         int numSamples,
1038*b290403dSRicardo Garcia         float speed,
1039*b290403dSRicardo Garcia         float pitch,
1040*b290403dSRicardo Garcia         float rate,
1041*b290403dSRicardo Garcia         float volume,
1042*b290403dSRicardo Garcia         boolean useChordPitch,
1043*b290403dSRicardo Garcia         int sampleRate,
1044*b290403dSRicardo Garcia         int numChannels)
1045*b290403dSRicardo Garcia     {
1046*b290403dSRicardo Garcia         Sonic stream = new Sonic(sampleRate, numChannels);
1047*b290403dSRicardo Garcia 
1048*b290403dSRicardo Garcia         stream.setSpeed(speed);
1049*b290403dSRicardo Garcia         stream.setPitch(pitch);
1050*b290403dSRicardo Garcia         stream.setRate(rate);
1051*b290403dSRicardo Garcia         stream.setVolume(volume);
1052*b290403dSRicardo Garcia         stream.setChordPitch(useChordPitch);
1053*b290403dSRicardo Garcia         stream.writeFloatToStream(samples, numSamples);
1054*b290403dSRicardo Garcia         stream.flushStream();
1055*b290403dSRicardo Garcia         numSamples = stream.samplesAvailable();
1056*b290403dSRicardo Garcia         stream.readFloatFromStream(samples, numSamples);
1057*b290403dSRicardo Garcia         return numSamples;
1058*b290403dSRicardo Garcia     }
1059*b290403dSRicardo Garcia 
1060*b290403dSRicardo Garcia     /* This is a non-stream oriented interface to just change the speed of a sound sample */
sonicChangeShortSpeed( short samples[], int numSamples, float speed, float pitch, float rate, float volume, boolean useChordPitch, int sampleRate, int numChannels)1061*b290403dSRicardo Garcia     public int sonicChangeShortSpeed(
1062*b290403dSRicardo Garcia         short samples[],
1063*b290403dSRicardo Garcia         int numSamples,
1064*b290403dSRicardo Garcia         float speed,
1065*b290403dSRicardo Garcia         float pitch,
1066*b290403dSRicardo Garcia         float rate,
1067*b290403dSRicardo Garcia         float volume,
1068*b290403dSRicardo Garcia         boolean useChordPitch,
1069*b290403dSRicardo Garcia         int sampleRate,
1070*b290403dSRicardo Garcia         int numChannels)
1071*b290403dSRicardo Garcia     {
1072*b290403dSRicardo Garcia         Sonic stream = new Sonic(sampleRate, numChannels);
1073*b290403dSRicardo Garcia 
1074*b290403dSRicardo Garcia         stream.setSpeed(speed);
1075*b290403dSRicardo Garcia         stream.setPitch(pitch);
1076*b290403dSRicardo Garcia         stream.setRate(rate);
1077*b290403dSRicardo Garcia         stream.setVolume(volume);
1078*b290403dSRicardo Garcia         stream.setChordPitch(useChordPitch);
1079*b290403dSRicardo Garcia         stream.writeShortToStream(samples, numSamples);
1080*b290403dSRicardo Garcia         stream.flushStream();
1081*b290403dSRicardo Garcia         numSamples = stream.samplesAvailable();
1082*b290403dSRicardo Garcia         stream.readShortFromStream(samples, numSamples);
1083*b290403dSRicardo Garcia         return numSamples;
1084*b290403dSRicardo Garcia     }
1085*b290403dSRicardo Garcia }
1086