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