diff --git a/android_api.pde b/android_api.pde index 8040f72..7822304 100644 --- a/android_api.pde +++ b/android_api.pde @@ -6,7 +6,7 @@ //The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. -// Modifications: June 2013 Martin Bruner +// Modifications: June 2013 Martin Bruner - Audio Analysis recovered by Constantino Fernandez Traba import java.io.File; import java.io.FileInputStream; @@ -201,6 +201,12 @@ public class AudioPlayer implements Synth, AudioGenerator { private FXChain fxChain; private boolean isPlaying; private boolean isLooping; + private boolean analysing; + private FFT fft; + private int fftInd; + private float[] fftFrame; + private float[] powerSpectrum; + private int length; private short[] audioData; private float startPos; @@ -212,7 +218,7 @@ public class AudioPlayer implements Synth, AudioGenerator { float x1, x2, y1, y2, x3, y3; public AudioPlayer(float sampleRate) { - this.sampleRate = sampleRate; + this.sampleRate = sampleRate; fxChain = new FXChain(sampleRate); } @@ -308,6 +314,14 @@ public short[] justLoadAudioFile (String filename) { //System.out.println("Read "+sample+" samples expected "+sampleCount+" time "+secs+" secs "); bis.close(); + // unchop + readHead = 0; + startPos = 0; + // default to 1 sample shift per tick + dReadHead = 1; + isPlaying = false; + isLooping = true; + masterVolume = 1; } @@ -327,6 +341,45 @@ public short[] justLoadAudioFile (String filename) { } return myAudioData; } + + + public void setAnalysing(boolean analysing_) { + this.analysing = analysing_; + if (analysing) {// initialise the fft + fft = new FFT(); + fftInd = 0; + fftFrame = new float[1024]; + powerSpectrum = new float[fftFrame.length/2]; + } + } + + public float getAveragePower() { + if (analysing) { + // calc the average + float sum = 0; + for (int i=0;i (audioData.length - 1)) {// got to the end //% (float)audioData.length; @@ -450,8 +504,19 @@ public short[] justLoadAudioFile (String filename) { // calc y3 = y1 + ((x3 - x1) * (y2 - y1)); y3 *= masterVolume; - return fxChain.getSample((short) y3); - //return (short)y3; + sample = fxChain.getSample((short) y3); + if (analysing) { + // accumulate samples for the fft + fftFrame[fftInd] = (float)sample / 32768f; + fftInd ++; + if (fftInd == fftFrame.length - 1) {// got a frame + powerSpectrum = fft.process(fftFrame, true); + fftInd = 0; + } + } + + return sample; + //return (short)y3; } } @@ -1859,6 +1924,265 @@ public class FastFourierTransform { return mag; } +}/** + * FFT performs a Fast Fourier Transform and forwards the complex data to any listeners. + * The complex data is a float of the form float[2][frameSize], with real and imaginary + * parts stored respectively. + * + * @beads.category analysis + */ +public class FFT { + + /** The real part. */ + protected float[] fftReal; + + /** The imaginary part. */ + protected float[] fftImag; + + private float[] dataCopy = null; + private float[][] features; + private float[] powers; + private int numFeatures; + + /** + * Instantiates a new FFT. + */ + public FFT() { + features = new float[2][]; + } + + /* (non-Javadoc) + * @see com.olliebown.beads.core.UGen#calculateBuffer() + */ + public float[] process(float[] data, boolean direction) { + if (powers == null) powers = new float[data.length/2]; + if (dataCopy==null || dataCopy.length!=data.length) + dataCopy = new float[data.length]; + System.arraycopy(data, 0, dataCopy, 0, data.length); + + fft(dataCopy, dataCopy.length, direction); + numFeatures = dataCopy.length; + fftReal = calculateReal(dataCopy, dataCopy.length); + fftImag = calculateImaginary(dataCopy, dataCopy.length); + features[0] = fftReal; + features[1] = fftImag; + // now calc the powers + return specToPowers(fftReal, fftImag, powers); + } + + public float[] specToPowers(float[] real, float[] imag, float[] powers) { + float re, im; + double pow; + for (int i=0;i>1); + if (isign) { + c2 = -.5f; + four1(data, n>>1, true); + } + else { + c2 = .5f; + theta = -theta; + } + wtemp = Math.sin(.5*theta); + wpr = -2.*wtemp*wtemp; + wpi = Math.sin(theta); + wr = 1. + wpr; + wi = wpi; + int np3 = n + 3; + for (int i=2,imax = n >> 2, i1, i2, i3, i4; i <= imax; ++i) { + /** @TODO this can be optimized */ + i4 = 1 + (i3 = np3 - (i2 = 1 + (i1 = i + i - 1))); + --i4; + --i2; + --i3; + --i1; + h1i = c1*(data[i2] - data[i4]); + h2r = -c2*(data[i2] + data[i4]); + h1r = c1*(data[i1] + data[i3]); + h2i = c2*(data[i1] - data[i3]); + data[i1] = (float) ( h1r + wr*h2r - wi*h2i); + data[i2] = (float) ( h1i + wr*h2i + wi*h2r); + data[i3] = (float) ( h1r - wr*h2r + wi*h2i); + data[i4] = (float) (-h1i + wr*h2i + wi*h2r); + wr = (wtemp=wr)*wpr - wi*wpi + wr; + wi = wi*wpr + wtemp*wpi + wi; + } + if (isign) { + float tmp = data[0]; + data[0] += data[1]; + data[1] = tmp - data[1]; + } + else { + float tmp = data[0]; + data[0] = c1 * (tmp + data[1]); + data[1] = c1 * (tmp - data[1]); + four1(data, n>>1, false); + } + } + + /** + * four1 algorithm. + * + * @param data + * the data. + * @param nn + * the nn. + * @param isign + * regular or inverse. + */ + private void four1(float data[], int nn, boolean isign) { + int n, mmax, istep; + double wtemp, wr, wpr, wpi, wi, theta; + float tempr, tempi; + + n = nn << 1; + for (int i = 1, j = 1; i < n; i += 2) { + if (j > i) { + // SWAP(data[j], data[i]); + float swap = data[j-1]; + data[j-1] = data[i-1]; + data[i-1] = swap; + // SWAP(data[j+1], data[i+1]); + swap = data[j]; + data[j] = data[i]; + data[i] = swap; + } + int m = n >> 1; + while (m >= 2 && j > m) { + j -= m; + m >>= 1; + } + j += m; + } + mmax = 2; + while (n > mmax) { + istep = mmax << 1; + theta = 6.28318530717959 / mmax; + if (!isign) + theta = -theta; + wtemp = Math.sin(0.5 * theta); + wpr = -2.0 * wtemp * wtemp; + wpi = Math.sin(theta); + wr = 1.0; + wi = 0.0; + for (int m = 1; m < mmax; m += 2) { + for (int i = m; i <= n; i += istep) { + int j = i + mmax; + tempr = (float) (wr * data[j-1] - wi * data[j]); + tempi = (float) (wr * data[j] + wi * data[j-1]); + data[j-1] = data[i-1] - tempr; + data[j] = data[i] - tempi; + data[i-1] += tempr; + data[i] += tempi; + } + wr = (wtemp = wr) * wpr - wi * wpi + wr; + wi = wi * wpr + wtemp * wpi + wi; + } + mmax = istep; + } + } } +