1dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox/* Sonic library 2dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Copyright 2010, 2011 3dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Bill Cox 4dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox This file is part of the Sonic Library. 5dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 660eeb064f62a106069d2ce148fabd724f9df9780Bill Cox This file is licensed under the Apache 2.0 license. 7e720065e4d31d0ecaffb12bdfedd1b04c978c590Bill Cox*/ 8dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 9dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Coxpackage sonic; 10dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 11dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Coxpublic class Sonic { 12dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 13dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private static final int SONIC_MIN_PITCH = 65; 14dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private static final int SONIC_MAX_PITCH = 400; 15dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox /* This is used to down-sample some inputs to improve speed */ 16dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private static final int SONIC_AMDF_FREQ = 4000; 17dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 18dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private short inputBuffer[]; 19dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private short outputBuffer[]; 20dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private short pitchBuffer[]; 21dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private short downSampleBuffer[]; 22dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private float speed; 23dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private float volume; 24dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private float pitch; 25dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private float rate; 26dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int oldRatePosition; 27dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int newRatePosition; 28dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private boolean useChordPitch; 29dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int quality; 30dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int numChannels; 31dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int inputBufferSize; 32dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int pitchBufferSize; 33dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int outputBufferSize; 34dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int numInputSamples; 35dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int numOutputSamples; 36dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int numPitchSamples; 37dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int minPeriod; 38dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int maxPeriod; 39dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int maxRequired; 40dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int remainingInputToCopy; 41dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int sampleRate; 42dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int prevPeriod; 43dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int prevMinDiff; 44dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 45dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Resize the array. 46dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private short[] resize( 47dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short[] oldArray, 48dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int newLength) 49dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 5032251503ae9a332d8d513370b6cb5d1fe849d088David R. Souto newLength *= numChannels; 51dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short[] newArray = new short[newLength]; 52dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int length = oldArray.length <= newLength? oldArray.length : newLength; 53dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 5432251503ae9a332d8d513370b6cb5d1fe849d088David R. Souto 55dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int x = 0; x < length; x++) { 56dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newArray[x] = oldArray[x]; 57dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 58dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return newArray; 59dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 60dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 61dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Move samples from one array to another. May move samples down within an array, but not up. 62dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void move( 63dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short dest[], 64dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int destPos, 65dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short source[], 66dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int sourcePos, 67dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 68dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 69dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = 0; xSample < numSamples*numChannels; xSample++) { 70dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox dest[destPos*numChannels + xSample] = source[sourcePos*numChannels + xSample]; 71dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 72dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 73dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 74dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Scale the samples by the factor. 75dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void scaleSamples( 76dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 77dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 78dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples, 79dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float volume) 80dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 81dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int fixedPointVolume = (int)(volume*4096.0f); 82dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int start = position*numChannels; 83dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int stop = start + numSamples*numChannels; 84dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 85dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = start; xSample < stop; xSample++) { 86dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int value = (samples[xSample]*fixedPointVolume) >> 12; 87dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(value > 32767) { 88dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox value = 32767; 89dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else if(value < -32767) { 90dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox value = -32767; 91dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 92dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox samples[xSample] = (short)value; 93dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 94dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 95dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 96dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the speed of the stream. 97dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public float getSpeed() 98dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 99dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return speed; 100dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 101dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 102dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the speed of the stream. 103dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setSpeed( 104dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float speed) 105dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 106dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.speed = speed; 107dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 108dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 109dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the pitch of the stream. 110dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public float getPitch() 111dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 112dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return pitch; 113dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 114dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 115dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the pitch of the stream. 116dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setPitch( 117dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float pitch) 118dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 119dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.pitch = pitch; 120dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 121dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 122dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the rate of the stream. 123dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public float getRate() 124dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 125dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return rate; 126dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 127dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 128dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the playback rate of the stream. This scales pitch and speed at the same time. 129dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setRate( 130dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float rate) 131dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 132dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.rate = rate; 133dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.oldRatePosition = 0; 134dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.newRatePosition = 0; 135dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 136dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 137dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the vocal chord pitch setting. 138dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public boolean getChordPitch() 139dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 140dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return useChordPitch; 141dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 142dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 143dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the vocal chord mode for pitch computation. Default is off. 144dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setChordPitch( 145dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox boolean useChordPitch) 146dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 147dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.useChordPitch = useChordPitch; 148dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 149dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 150dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the quality setting. 151dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int getQuality() 152dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 153dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return quality; 154dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 155dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 156dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the "quality". Default 0 is virtually as good as 1, but very much faster. 157dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setQuality( 158dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int quality) 159dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 160dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.quality = quality; 161dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 162dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 163dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the scaling factor of the stream. 164dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public float getVolume() 165dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 166dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return volume; 167dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 168dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 169dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the scaling factor of the stream. 170dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setVolume( 171dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float volume) 172dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 173dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.volume = volume; 174dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 175dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 176dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Allocate stream buffers. 177dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void allocateStreamBuffers( 178dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int sampleRate, 179dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels) 180dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 181dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox minPeriod = sampleRate/SONIC_MAX_PITCH; 182dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox maxPeriod = sampleRate/SONIC_MIN_PITCH; 183dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox maxRequired = 2*maxPeriod; 184dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBufferSize = maxRequired; 185dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBuffer = new short[maxRequired*numChannels]; 186dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox outputBufferSize = maxRequired; 187dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox outputBuffer = new short[maxRequired*numChannels]; 188dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox pitchBufferSize = maxRequired; 189dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox pitchBuffer = new short[maxRequired*numChannels]; 190dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox downSampleBuffer = new short[maxRequired]; 191dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.sampleRate = sampleRate; 192dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox this.numChannels = numChannels; 193dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox oldRatePosition = 0; 194dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newRatePosition = 0; 195dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox prevPeriod = 0; 196dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 197dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 198dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Create a sonic stream. 199dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public Sonic( 200dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int sampleRate, 201dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels) 202dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 203dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox allocateStreamBuffers(sampleRate, numChannels); 204dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox speed = 1.0f; 205dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox pitch = 1.0f; 206dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox volume = 1.0f; 207dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox rate = 1.0f; 208dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox oldRatePosition = 0; 209dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newRatePosition = 0; 210dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox useChordPitch = false; 211dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox quality = 0; 212dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 213dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 214dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the sample rate of the stream. 215dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int getSampleRate() 216dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 217dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return sampleRate; 218dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 219dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 220dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the sample rate of the stream. This will cause samples buffered in the stream to be lost. 221dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setSampleRate( 222dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int sampleRate) 223dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 224dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox allocateStreamBuffers(sampleRate, numChannels); 225dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 226dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 227dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Get the number of channels. 228dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int getNumChannels() 229dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 230dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numChannels; 231dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 232dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 233dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set the num channels of the stream. This will cause samples buffered in the stream to be lost. 234dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void setNumChannels( 235dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels) 236dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 237dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox allocateStreamBuffers(sampleRate, numChannels); 238dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 239dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 240dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Enlarge the output buffer if needed. 241dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void enlargeOutputBufferIfNeeded( 242dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 243dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 244dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numOutputSamples + numSamples > outputBufferSize) { 245dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox outputBufferSize += (outputBufferSize >> 1) + numSamples; 246dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox outputBuffer = resize(outputBuffer, outputBufferSize); 247dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 248dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 249dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 250dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Enlarge the input buffer if needed. 251dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void enlargeInputBufferIfNeeded( 252dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 253dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 254dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numInputSamples + numSamples > inputBufferSize) { 255dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBufferSize += (inputBufferSize >> 1) + numSamples; 256dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBuffer = resize(inputBuffer, inputBufferSize); 257dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 258dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 259dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 260dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Add the input samples to the input buffer. 261dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void addFloatSamplesToInputBuffer( 262dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float samples[], 263dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 264dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 265dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples == 0) { 266dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return; 267dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 268dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeInputBufferIfNeeded(numSamples); 269dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int xBuffer = numInputSamples*numChannels; 270dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = 0; xSample < numSamples*numChannels; xSample++) { 271dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBuffer[xBuffer++] = (short)(samples[xSample]*32767.0f); 272dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 273dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples += numSamples; 274dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 275dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 276dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Add the input samples to the input buffer. 277dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void addShortSamplesToInputBuffer( 278dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 279dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 280dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 281dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples == 0) { 282dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return; 283dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 284dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeInputBufferIfNeeded(numSamples); 285dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(inputBuffer, numInputSamples, samples, 0, numSamples); 286dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples += numSamples; 287dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 288dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 289dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Add the input samples to the input buffer. 290dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void addUnsignedByteSamplesToInputBuffer( 291dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox byte samples[], 292dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 293dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 294dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short sample; 295dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 296dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeInputBufferIfNeeded(numSamples); 297dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int xBuffer = numInputSamples*numChannels; 298dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = 0; xSample < numSamples*numChannels; xSample++) { 299dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox sample = (short)((samples[xSample] & 0xff) - 128); // Convert from unsigned to signed 300dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBuffer[xBuffer++] = (short) (sample << 8); 301dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 302dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples += numSamples; 303dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 304dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 3052e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox // Add the input samples to the input buffer. They must be 16-bit little-endian encoded in a byte array. 3062e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox private void addBytesToInputBuffer( 3072e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox byte inBuffer[], 3082e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int numBytes) 3092e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox { 3102e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int numSamples = numBytes/(2*numChannels); 3112e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox short sample; 3122e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox 3132e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox enlargeInputBufferIfNeeded(numSamples); 3142e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int xBuffer = numInputSamples*numChannels; 3152e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox for(int xByte = 0; xByte + 1 < numBytes; xByte += 2) { 3162e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox sample = (short)((inBuffer[xByte] & 0xff) | (inBuffer[xByte + 1] << 8)); 3172e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox inputBuffer[xBuffer++] = sample; 3182e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 3192e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox numInputSamples += numSamples; 3202e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 3212e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox 322dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Remove input samples that we have already processed. 323dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void removeInputSamples( 324dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position) 325dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 326dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int remainingSamples = numInputSamples - position; 327dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 328dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(inputBuffer, 0, inputBuffer, position, remainingSamples); 329dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples = remainingSamples; 330dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 331dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 332dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Just copy from the array to the output buffer 333dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void copyToOutput( 334dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 335dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 336dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 337dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 338dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeOutputBufferIfNeeded(numSamples); 339dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(outputBuffer, numOutputSamples, samples, position, numSamples); 340dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples += numSamples; 341dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 342dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 343dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Just copy from the input buffer to the output buffer. Return num samples copied. 344dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int copyInputToOutput( 345dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position) 346dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 347dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = remainingInputToCopy; 348dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 349dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples > maxRequired) { 350dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numSamples = maxRequired; 351dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 352dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox copyToOutput(inputBuffer, position, numSamples); 353dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingInputToCopy -= numSamples; 354dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numSamples; 355dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 356dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 357dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Read data out of the stream. Sometimes no data will be available, and zero 358dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // is returned, which is not an error condition. 359dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int readFloatFromStream( 360dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float samples[], 361dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int maxSamples) 362dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 363dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = numOutputSamples; 364dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int remainingSamples = 0; 365dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 366dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples == 0) { 367dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return 0; 368dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 369dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples > maxSamples) { 370dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingSamples = numSamples - maxSamples; 371dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numSamples = maxSamples; 372dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 373dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = 0; xSample < numSamples*numChannels; xSample++) { 374dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox samples[xSample++] = (outputBuffer[xSample])/32767.0f; 375dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 376dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples); 377dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples = remainingSamples; 378dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numSamples; 379dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 380dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 381dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Read short data out of the stream. Sometimes no data will be available, and zero 382dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // is returned, which is not an error condition. 383dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int readShortFromStream( 384dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 385dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int maxSamples) 386dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 387dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = numOutputSamples; 388dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int remainingSamples = 0; 389dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 390dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples == 0) { 391dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return 0; 392dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 393dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples > maxSamples) { 394dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingSamples = numSamples - maxSamples; 395dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numSamples = maxSamples; 396dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 397dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(samples, 0, outputBuffer, 0, numSamples); 398dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples); 399dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples = remainingSamples; 400dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numSamples; 401dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 402dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 403dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Read unsigned byte data out of the stream. Sometimes no data will be available, and zero 404dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // is returned, which is not an error condition. 405dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int readUnsignedByteFromStream( 406dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox byte samples[], 407dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int maxSamples) 408dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 409dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = numOutputSamples; 410dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int remainingSamples = 0; 411dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 412dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples == 0) { 413dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return 0; 414dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 415dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples > maxSamples) { 416dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingSamples = numSamples - maxSamples; 417dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numSamples = maxSamples; 418dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 419dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = 0; xSample < numSamples*numChannels; xSample++) { 420dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox samples[xSample] = (byte)((outputBuffer[xSample] >> 8) + 128); 421dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 422dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples); 423dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples = remainingSamples; 424dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numSamples; 425dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 426dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 4272e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox // Read unsigned byte data out of the stream. Sometimes no data will be available, and zero 4282e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox // is returned, which is not an error condition. 4292e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox public int readBytesFromStream( 4302e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox byte outBuffer[], 4312e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int maxBytes) 4322e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox { 4332e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int maxSamples = maxBytes/(2*numChannels); 4342e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int numSamples = numOutputSamples; 4352e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int remainingSamples = 0; 4362e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox 4372e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox if(numSamples == 0 || maxSamples == 0) { 4382e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox return 0; 4392e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 4402e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox if(numSamples > maxSamples) { 4412e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox remainingSamples = numSamples - maxSamples; 4422e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox numSamples = maxSamples; 4432e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 4442e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox for(int xSample = 0; xSample < numSamples*numChannels; xSample++) { 4452e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox short sample = outputBuffer[xSample]; 4462e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox outBuffer[xSample << 1] = (byte)(sample & 0xff); 4472e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox outBuffer[(xSample << 1) + 1] = (byte)(sample >> 8); 4482e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 4492e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox move(outputBuffer, 0, outputBuffer, numSamples, remainingSamples); 4502e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox numOutputSamples = remainingSamples; 4512e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox return 2*numSamples*numChannels; 4522e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 4532e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox 454dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Force the sonic stream to generate output using whatever data it currently 455dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // has. No extra delay will be added to the output, but flushing in the middle of 456dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // words could introduce distortion. 457dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void flushStream() 458dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 459dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int remainingSamples = numInputSamples; 460dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float s = speed/pitch; 461dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float r = rate*pitch; 462dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int expectedOutputSamples = numOutputSamples + (int)((remainingSamples/s + numPitchSamples)/r + 0.5f); 463dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 464dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Add enough silence to flush both input and pitch buffers. 465dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeInputBufferIfNeeded(remainingSamples + 2*maxRequired); 466dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int xSample = 0; xSample < 2*maxRequired*numChannels; xSample++) { 467dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox inputBuffer[remainingSamples*numChannels + xSample] = 0; 468dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 469dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples += 2*maxRequired; 470dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox writeShortToStream(null, 0); 471dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Throw away any extra samples we generated due to the silence we added. 472dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numOutputSamples > expectedOutputSamples) { 473dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples = expectedOutputSamples; 474dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 475dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Empty input and pitch buffers. 476dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples = 0; 477dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingInputToCopy = 0; 478dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numPitchSamples = 0; 479dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 480dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 481dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Return the number of samples in the output buffer 482dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int samplesAvailable() 483dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 484dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numOutputSamples; 485dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 486dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 487dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // If skip is greater than one, average skip samples together and write them to 488dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // the down-sample buffer. If numChannels is greater than one, mix the channels 489dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // together as we down sample. 490dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void downSampleInput( 491dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 492dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 493dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int skip) 494dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 495dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = maxRequired/skip; 496dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int samplesPerValue = numChannels*skip; 497dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int value; 498dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 499dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position *= numChannels; 500dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int i = 0; i < numSamples; i++) { 501dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox value = 0; 502dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int j = 0; j < samplesPerValue; j++) { 503dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox value += samples[position + i*samplesPerValue + j]; 504dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 505dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox value /= samplesPerValue; 506dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox downSampleBuffer[i] = (short)value; 507dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 508dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 509dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 510dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Find the best frequency match in the range, and given a sample skip multiple. 511dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // For now, just find the pitch of the first channel. Note that retMinDiff and 512dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // retMaxDiff are Int objects, which the caller will need to create with new. 513dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int findPitchPeriodInRange( 514dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 515dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 516dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int minPeriod, 517dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int maxPeriod, 518dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Integer retMinDiff, 519dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Integer retMaxDiff) 520dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 521dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int bestPeriod = 0, worstPeriod = 255; 522dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int minDiff = 1, maxDiff = 0; 523dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 524dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position *= numChannels; 525dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int period = minPeriod; period <= maxPeriod; period++) { 526dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int diff = 0; 527dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int i = 0; i < period; i++) { 528dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short sVal = samples[position + i]; 529dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short pVal = samples[position + period + i]; 530dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox diff += sVal >= pVal? sVal - pVal : pVal - sVal; 531dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 532dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox /* Note that the highest number of samples we add into diff will be less 533dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox than 256, since we skip samples. Thus, diff is a 24 bit number, and 534dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox we can safely multiply by numSamples without overflow */ 535dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(diff*bestPeriod < minDiff*period) { 536dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox minDiff = diff; 537dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox bestPeriod = period; 538dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 539dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(diff*worstPeriod > maxDiff*period) { 540dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox maxDiff = diff; 541dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox worstPeriod = period; 542dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 543dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 544dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox retMinDiff = minDiff/bestPeriod; 545dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox retMaxDiff = maxDiff/worstPeriod; 546dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return bestPeriod; 547dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 548dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 549dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // At abrupt ends of voiced words, we can have pitch periods that are better 550dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // approximated by the previous pitch period estimate. Try to detect this case. 551dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private boolean prevPeriodBetter( 552dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int period, 553dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int minDiff, 554dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int maxDiff, 555dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox boolean preferNewPeriod) 556dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 557dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(minDiff == 0 || prevPeriod == 0) { 558dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return false; 559dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 560dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(preferNewPeriod) { 561dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(maxDiff > minDiff*3) { 562dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Got a reasonable match this period 563dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return false; 564dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 565dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(minDiff*2 <= prevMinDiff*3) { 566dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Mismatch is not that much greater this period 567dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return false; 568dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 569dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 570dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(minDiff <= prevMinDiff) { 571dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return false; 572dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 573dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 574dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return true; 575dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 576dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 577dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Find the pitch period. This is a critical step, and we may have to try 578dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // multiple ways to get a good answer. This version uses AMDF. To improve 579dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // speed, we down sample by an integer factor get in the 11KHz range, and then 580dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // do it again with a narrower frequency range without down sampling 581dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int findPitchPeriod( 582dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 583dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 584dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox boolean preferNewPeriod) 585dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 586dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Integer minDiff = new Integer(0); 587dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Integer maxDiff = new Integer(0); 588dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int period, retPeriod; 589dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int skip = 1; 590dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 591dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(sampleRate > SONIC_AMDF_FREQ && quality == 0) { 592dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox skip = sampleRate/SONIC_AMDF_FREQ; 593dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 594dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numChannels == 1 && skip == 1) { 595dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period = findPitchPeriodInRange(samples, position, minPeriod, maxPeriod, minDiff, maxDiff); 596dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 597dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox downSampleInput(samples, position, skip); 598dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period = findPitchPeriodInRange(downSampleBuffer, 0, minPeriod/skip, 599dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox maxPeriod/skip, minDiff, maxDiff); 600dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(skip != 1) { 601dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period *= skip; 602dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int minP = period - (skip << 2); 603dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int maxP = period + (skip << 2); 604dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(minP < minPeriod) { 605dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox minP = minPeriod; 606dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 607dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(maxP > maxPeriod) { 608dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox maxP = maxPeriod; 609dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 610dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numChannels == 1) { 611dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period = findPitchPeriodInRange(samples, position, minP, maxP, minDiff, maxDiff); 612dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 613dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox downSampleInput(samples, position, 1); 614dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period = findPitchPeriodInRange(downSampleBuffer, 0, minP, maxP, minDiff, maxDiff); 615dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 616dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 617dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 618dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(prevPeriodBetter(period, minDiff, maxDiff, preferNewPeriod)) { 619dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox retPeriod = prevPeriod; 620dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 621dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox retPeriod = period; 622dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 623dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox prevMinDiff = minDiff; 624dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox prevPeriod = period; 625dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return retPeriod; 626dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 627dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 628dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Overlap two sound segments, ramp the volume of one down, while ramping the 629dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // other one from zero up, and add them, storing the result at the output. 630dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void overlapAdd( 631dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples, 632dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels, 633dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short out[], 634dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int outPos, 635dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short rampDown[], 636dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int rampDownPos, 637dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short rampUp[], 638dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int rampUpPos) 639dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 640dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int i = 0; i < numChannels; i++) { 641dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int o = outPos*numChannels + i; 642dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int u = rampUpPos*numChannels + i; 643dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int d = rampDownPos*numChannels + i; 644dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int t = 0; t < numSamples; t++) { 645dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*t)/numSamples); 646dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox o += numChannels; 647dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox d += numChannels; 648dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox u += numChannels; 649dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 650dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 651dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 652dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 653dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Overlap two sound segments, ramp the volume of one down, while ramping the 654dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // other one from zero up, and add them, storing the result at the output. 655dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void overlapAddWithSeparation( 656dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples, 657dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels, 658dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int separation, 659dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short out[], 660dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int outPos, 661dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short rampDown[], 662dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int rampDownPos, 663dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short rampUp[], 664dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int rampUpPos) 665dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 666dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int i = 0; i < numChannels; i++) { 667dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int o = outPos*numChannels + i; 668dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int u = rampUpPos*numChannels + i; 669dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int d = rampDownPos*numChannels + i; 670dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int t = 0; t < numSamples + separation; t++) { 671dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(t < separation) { 672dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox out[o] = (short)(rampDown[d]*(numSamples - t)/numSamples); 673dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox d += numChannels; 674dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else if(t < numSamples) { 675dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox out[o] = (short)((rampDown[d]*(numSamples - t) + rampUp[u]*(t - separation))/numSamples); 676dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox d += numChannels; 677dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox u += numChannels; 678dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 679dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox out[o] = (short)(rampUp[u]*(t - separation)/numSamples); 680dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox u += numChannels; 681dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 682dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox o += numChannels; 683dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 684dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 685dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 686dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 687dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Just move the new samples in the output buffer to the pitch buffer 688dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void moveNewSamplesToPitchBuffer( 689dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int originalNumOutputSamples) 690dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 691dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = numOutputSamples - originalNumOutputSamples; 692dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 693dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numPitchSamples + numSamples > pitchBufferSize) { 694dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox pitchBufferSize += (pitchBufferSize >> 1) + numSamples; 695dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox pitchBuffer = resize(pitchBuffer, pitchBufferSize); 696dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 697dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(pitchBuffer, numPitchSamples, outputBuffer, originalNumOutputSamples, numSamples); 698dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples = originalNumOutputSamples; 699dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numPitchSamples += numSamples; 700dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 701dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 702dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Remove processed samples from the pitch buffer. 703dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void removePitchSamples( 704dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 705dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 706dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numSamples == 0) { 707dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return; 708dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 709dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(pitchBuffer, 0, pitchBuffer, numSamples, numPitchSamples - numSamples); 710dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numPitchSamples -= numSamples; 711dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 712dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 713dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Change the pitch. The latency this introduces could be reduced by looking at 714dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // past samples to determine pitch, rather than future. 715dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void adjustPitch( 716dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int originalNumOutputSamples) 717dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 718dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int period, newPeriod, separation; 719dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position = 0; 720dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 721dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numOutputSamples == originalNumOutputSamples) { 722dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return; 723dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 724dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox moveNewSamplesToPitchBuffer(originalNumOutputSamples); 725dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox while(numPitchSamples - position >= maxRequired) { 726dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period = findPitchPeriod(pitchBuffer, position, false); 727dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newPeriod = (int)(period/pitch); 728dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeOutputBufferIfNeeded(newPeriod); 729dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(pitch >= 1.0f) { 730dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox overlapAdd(newPeriod, numChannels, outputBuffer, numOutputSamples, pitchBuffer, 731dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position, pitchBuffer, position + period - newPeriod); 732dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 733dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox separation = newPeriod - period; 734dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox overlapAddWithSeparation(period, numChannels, separation, outputBuffer, numOutputSamples, 735dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox pitchBuffer, position, pitchBuffer, position); 736dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 737dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples += newPeriod; 738dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position += period; 739dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 740dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox removePitchSamples(position); 741dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 742dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 743dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Interpolate the new output sample. 744dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private short interpolate( 745dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short in[], 746dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int inPos, 747dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int oldSampleRate, 748dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int newSampleRate) 749dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 750dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short left = in[inPos*numChannels]; 751dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short right = in[inPos*numChannels + numChannels]; 752dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position = newRatePosition*oldSampleRate; 753dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int leftPosition = oldRatePosition*newSampleRate; 754dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int rightPosition = (oldRatePosition + 1)*newSampleRate; 755dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int ratio = rightPosition - position; 756dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int width = rightPosition - leftPosition; 757dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 758dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return (short)((ratio*left + (width - ratio)*right)/width); 759dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 760dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 761dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Change the rate. 762dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void adjustRate( 763dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float rate, 764dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int originalNumOutputSamples) 765dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 766dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int newSampleRate = (int)(sampleRate/rate); 767dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int oldSampleRate = sampleRate; 768dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position; 769dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 770dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Set these values to help with the integer math 771dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox while(newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) { 772dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSampleRate >>= 1; 773dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox oldSampleRate >>= 1; 774dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 775dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numOutputSamples == originalNumOutputSamples) { 776dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return; 777dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 778dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox moveNewSamplesToPitchBuffer(originalNumOutputSamples); 779dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Leave at least one pitch sample in the buffer 780dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(position = 0; position < numPitchSamples - 1; position++) { 781dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox while((oldRatePosition + 1)*newSampleRate > newRatePosition*oldSampleRate) { 782dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeOutputBufferIfNeeded(1); 783dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox for(int i = 0; i < numChannels; i++) { 784dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox outputBuffer[numOutputSamples*numChannels + i] = interpolate(pitchBuffer, position + i, 785dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox oldSampleRate, newSampleRate); 786dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 787dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newRatePosition++; 788dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples++; 789dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 790dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox oldRatePosition++; 791dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(oldRatePosition == oldSampleRate) { 792dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox oldRatePosition = 0; 793dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(newRatePosition != newSampleRate) { 794dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox System.out.printf("Assertion failed: newRatePosition != newSampleRate\n"); 795dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox assert false; 796dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 797dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newRatePosition = 0; 798dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 799dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 800dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox removePitchSamples(position); 801dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 802dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 803dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 804dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Skip over a pitch period, and copy period/speed samples to the output 805dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int skipPitchPeriod( 806dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 807dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 808dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float speed, 809dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int period) 810dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 811dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int newSamples; 812dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 813dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(speed >= 2.0f) { 814dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = (int)(period/(speed - 1.0f)); 815dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 816dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = period; 817dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingInputToCopy = (int)(period*(2.0f - speed)/(speed - 1.0f)); 818dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 819dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeOutputBufferIfNeeded(newSamples); 820dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples, samples, position, 821dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox samples, position + period); 822dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples += newSamples; 823dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return newSamples; 824dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 825dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 826dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Insert a pitch period, and determine how much input to copy directly. 827dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private int insertPitchPeriod( 828dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 829dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position, 830dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float speed, 831dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int period) 832dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 833dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int newSamples; 834dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 835dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(speed < 0.5f) { 836dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = (int)(period*speed/(1.0f - speed)); 837dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 838dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = period; 839dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox remainingInputToCopy = (int)(period*(2.0f*speed - 1.0f)/(1.0f - speed)); 840dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 841dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox enlargeOutputBufferIfNeeded(period + newSamples); 842dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox move(outputBuffer, numOutputSamples, samples, position, period); 843dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox overlapAdd(newSamples, numChannels, outputBuffer, numOutputSamples + period, samples, 844dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position + period, samples, position); 845dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numOutputSamples += period + newSamples; 846dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return newSamples; 847dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 848dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 849dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Resample as many pitch periods as we have buffered on the input. Return 0 if 850dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // we fail to resize an input or output buffer. Also scale the output by the volume. 851dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void changeSpeed( 852dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float speed) 853dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 854dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples = numInputSamples; 855dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int position = 0, period, newSamples; 856dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 857dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(numInputSamples < maxRequired) { 858dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return; 859dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 860dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox do { 861dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(remainingInputToCopy > 0) { 862dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = copyInputToOutput(position); 863dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position += newSamples; 864dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 865dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox period = findPitchPeriod(inputBuffer, position, true); 866dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(speed > 1.0) { 867dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = skipPitchPeriod(inputBuffer, position, speed, period); 868dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position += period + newSamples; 869dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 870dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox newSamples = insertPitchPeriod(inputBuffer, position, speed, period); 871dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox position += newSamples; 872dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 873dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 874dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } while(position + maxRequired <= numSamples); 875dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox removeInputSamples(position); 876dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 877dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 878dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Resample as many pitch periods as we have buffered on the input. Scale the output by the volume. 879dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox private void processStreamInput() 880dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 881dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int originalNumOutputSamples = numOutputSamples; 882dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float s = speed/pitch; 883dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float r = rate; 884dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 885dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(!useChordPitch) { 886dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox r *= pitch; 887dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 888dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(s > 1.00001 || s < 0.99999) { 8892e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox changeSpeed(s); 890dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } else { 891dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox copyToOutput(inputBuffer, 0, numInputSamples); 892dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numInputSamples = 0; 893dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 894dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(useChordPitch) { 895dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(pitch != 1.0f) { 896dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox adjustPitch(originalNumOutputSamples); 897dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 8982e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } else if(r != 1.0f) { 8992e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox adjustRate(r, originalNumOutputSamples); 900dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 901dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox if(volume != 1.0f) { 902dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Adjust output volume. 903dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox scaleSamples(outputBuffer, originalNumOutputSamples, numOutputSamples - originalNumOutputSamples, 904dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox volume); 905dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 906dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 907dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 908dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Write floating point data to the input buffer and process it. 909dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void writeFloatToStream( 910dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float samples[], 911dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 912dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 913dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox addFloatSamplesToInputBuffer(samples, numSamples); 914dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox processStreamInput(); 915dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 916dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 917dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Write the data to the input stream, and process it. 918dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void writeShortToStream( 919dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 920dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 921dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 922dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox addShortSamplesToInputBuffer(samples, numSamples); 923dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox processStreamInput(); 924dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 925dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 926dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // Simple wrapper around sonicWriteFloatToStream that does the unsigned byte to short 927dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // conversion for you. 928dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public void writeUnsignedByteToStream( 929dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox byte samples[], 930dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples) 931dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 932dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox addUnsignedByteSamplesToInputBuffer(samples, numSamples); 933dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox processStreamInput(); 934dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 935dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 9362e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox // Simple wrapper around sonicWriteBytesToStream that does the byte to 16-bit LE conversion. 9372e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox public void writeBytesToStream( 9382e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox byte inBuffer[], 9392e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox int numBytes) 9402e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox { 9412e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox addBytesToInputBuffer(inBuffer, numBytes); 9422e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox processStreamInput(); 9432e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox } 9442e48c227bc3817ee76b92bdabcd3c3ff2b7e5b99Bill Cox 945dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox // This is a non-stream oriented interface to just change the speed of a sound sample 946dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public static int changeFloatSpeed( 947dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float samples[], 948dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples, 949dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float speed, 950dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float pitch, 951dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float rate, 952dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float volume, 953dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox boolean useChordPitch, 954dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int sampleRate, 955dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels) 956dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 957dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Sonic stream = new Sonic(sampleRate, numChannels); 958dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 959dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setSpeed(speed); 960dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setPitch(pitch); 961dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setRate(rate); 962dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setVolume(volume); 963dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setChordPitch(useChordPitch); 964dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.writeFloatToStream(samples, numSamples); 965dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.flushStream(); 966dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numSamples = stream.samplesAvailable(); 967dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.readFloatFromStream(samples, numSamples); 968dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numSamples; 969dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 970dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 971dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox /* This is a non-stream oriented interface to just change the speed of a sound sample */ 972dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox public int sonicChangeShortSpeed( 973dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox short samples[], 974dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numSamples, 975dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float speed, 976dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float pitch, 977dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float rate, 978dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox float volume, 979dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox boolean useChordPitch, 980dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int sampleRate, 981dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox int numChannels) 982dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox { 983dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox Sonic stream = new Sonic(sampleRate, numChannels); 984dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox 985dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setSpeed(speed); 986dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setPitch(pitch); 987dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setRate(rate); 988dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setVolume(volume); 989dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.setChordPitch(useChordPitch); 990dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.writeShortToStream(samples, numSamples); 991dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.flushStream(); 992dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox numSamples = stream.samplesAvailable(); 993dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox stream.readShortFromStream(samples, numSamples); 994dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox return numSamples; 995dfe6b3750b9a4f31ea5d8c13a6cf6749bbe9e975Bill Cox } 996e720065e4d31d0ecaffb12bdfedd1b04c978c590Bill Cox} 997