Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
332 changes: 328 additions & 4 deletions android_api.pde
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;


}
Expand All @@ -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<powerSpectrum.length;i++) {
sum += powerSpectrum[i];
}
sum /= powerSpectrum.length;
return sum;
}
else {
System.out.println("call setAnalysing to enable power analysis");
return 0;
}
}
public float[] getPowerSpectrum() {
if (analysing) {
return powerSpectrum;
}
else {
System.out.println("call setAnalysing to enable power analysis");
return null;
}
}




/**
*convert the sent byte array into an int. Assumes little endian byte ordering.
Expand Down Expand Up @@ -426,6 +479,7 @@ public short[] justLoadAudioFile (String filename) {
return 0;
}
else {
short sample;
readHead += dReadHead;
if (readHead > (audioData.length - 1)) {// got to the end
//% (float)audioData.length;
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -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<powers.length;i++) {
//real = spectrum[i][j].re();
//imag = spectrum[i][j].im();
re = real[i];
im = imag[i];
powers[i] = (re*re + im * im);
powers[i] = (float) Math.sqrt(powers[i]) / 10;
// convert to dB
pow = (double) powers[i];
powers[i] = (float)(10 * Math.log10(pow * pow)); // (-100 - 100)
powers[i] = (powers[i] + 100) * 0.005f; // 0-1
}
return powers;
}

/**
* The frequency corresponding to a specific bin
*
* @param samplingFrequency The Sampling Frequency of the AudioContext
* @param blockSize The size of the block analysed
* @param binNumber
*/
public float binFrequency(float samplingFrequency, int blockSize, float binNumber)
{
return binNumber*samplingFrequency/blockSize;
}

/**
* Returns the average bin number corresponding to a particular frequency.
* Note: This function returns a float. Take the Math.round() of the returned value to get an integral bin number.
*
* @param samplingFrequency The Sampling Frequency of the AudioContext
* @param blockSize The size of the fft block
* @param freq The frequency
*/

public float binNumber(float samplingFrequency, int blockSize, float freq)
{
return blockSize*freq/samplingFrequency;
}

/** The nyquist frequency for this samplingFrequency
*
* @params samplingFrequency the sample
*/
public float nyquist(float samplingFrequency)
{
return samplingFrequency/2;
}

/*
* All of the code below this line is taken from Holger Crysandt's MPEG7AudioEnc project.
* See http://mpeg7audioenc.sourceforge.net/copyright.html for license and copyright.
*/

/**
* Gets the real part from the complex spectrum.
*
* @param spectrum
* complex spectrum.
* @param length
* length of data to use.
*
* @return real part of given length of complex spectrum.
*/
protected float[] calculateReal(float[] spectrum, int length) {
float[] real = new float[length];
real[0] = spectrum[0];
real[real.length/2] = spectrum[1];
for (int i=1, j=real.length-1; i<j; ++i, --j)
real[j] = real[i] = spectrum[2*i];
return real;
}

/**
* Gets the imaginary part from the complex spectrum.
*
* @param spectrum
* complex spectrum.
* @param length
* length of data to use.
*
* @return imaginary part of given length of complex spectrum.
*/
protected float[] calculateImaginary(float[] spectrum, int length) {
float[] imag = new float[length];
for (int i=1, j=imag.length-1; i<j; ++i, --j)
imag[i] = -(imag[j] = spectrum[2*i+1]);
return imag;
}

/**
* Perform FFT on data with given length, regular or inverse.
*
* @param data the data
* @param n the length
* @param isign true for regular, false for inverse.
*/
protected void fft(float[] data, int n, boolean isign) {
float c1 = 0.5f;
float c2, h1r, h1i, h2r, h2i;
double wr, wi, wpr, wpi, wtemp;
double theta = 3.141592653589793/(n>>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;
}
}
}