From 12b25bfb80df63e7cb10562e6cf3239bf8e6a1a7 Mon Sep 17 00:00:00 2001 From: Constantino Fernandez Traba Date: Wed, 26 Jun 2013 23:45:30 +0200 Subject: [PATCH 1/2] Added audio analysis using original FFT. --- android_api.pde | 332 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 328 insertions(+), 4 deletions(-) diff --git a/android_api.pde b/android_api.pde index 8040f72..970a728 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; + } + } } + From 9e19b925554e37e98d5b57ea4430aa95fdb4e6b3 Mon Sep 17 00:00:00 2001 From: Constantino Fernandez Traba Date: Thu, 27 Jun 2013 01:35:29 +0200 Subject: [PATCH 2/2] Fix to recover filters functionality --- android_api.pde | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android_api.pde b/android_api.pde index 970a728..7822304 100644 --- a/android_api.pde +++ b/android_api.pde @@ -515,8 +515,8 @@ public short[] justLoadAudioFile (String filename) { } } - //return sample; - return (short)y3; + return sample; + //return (short)y3; } }