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