Download - Code Audio Analyzer
-
8/12/2019 Code Audio Analyzer
1/52
1. DFT.java
/** Copyright (c) 2007 - 2008 by Damien Di Fede
** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU Library General Public License aspublished* by the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU Library General Public License for more details.** You should have received a copy of the GNU Library General Public* License along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
package com.badlogic.audio.analysis;
/*** DFT stands for Discrete Fourier Transform and is the most widely usedFourier* Transform. You will never want to use this class due to the fact that itis a* brute force implementation of the DFT and as such is quite slow. Use anFFT
* instead. This exists primarily as a way to ensure that otherimplementations* of the DFT are working properly. This implementation expects an even* timeSize and will throw and IllegalArgumentException if this* is not the case.** @author Damien Di Fede** @see FourierTransform* @see FFT* @see The Discrete FourierTransform**/public class DFT extends FourierTransform{/*** Constructs a DFT that expects audio buffers of length
timeSize that* have been recorded with a sample rate of sampleRate. Will
throw an* IllegalArgumentException if timeSize is not even.*
-
8/12/2019 Code Audio Analyzer
2/52
* @param timeSize the length of the audio buffers you plan to analyze* @param sampleRate the sample rate of the audio samples you plan to
analyze*/public DFT(int timeSize, float sampleRate){super(timeSize, sampleRate);if (timeSize % 2 != 0)throw new IllegalArgumentException("DFT: timeSize must be even.");
buildTrigTables();}
protected void allocateArrays(){spectrum = new float[timeSize / 2 + 1];real = new float[timeSize / 2 + 1];imag = new float[timeSize / 2 + 1];
}
/**
* Not currently implemented.*/public void scaleBand(int i, float s){}
/*** Not currently implemented.*/public void setBand(int i, float a){}
public void forward(float[] samples){if (samples.length != timeSize){
throw new IllegalArgumentException("DFT.forward: The length of thepassed sample buffer must be equal to DFT.timeSize().");
}doWindow(samples);int N = samples.length;for (int f = 0; f
-
8/12/2019 Code Audio Analyzer
3/52
int N = buffer.length;real[0] /= N;imag[0] = -imag[0] / (N / 2);real[N / 2] /= N;imag[N / 2] = -imag[0] / (N / 2);for (int i = 0; i < N / 2; i++){real[i] /= (N / 2);imag[i] = -imag[i] / (N / 2);
}for (int t = 0; t < N; t++){buffer[t] = 0.0f;for (int f = 0; f < N / 2; f++){buffer[t] += real[f] * cos(t * f) + imag[f] * sin(t * f);
}}
}
// lookup table data and functions
private float[] sinlookup;private float[] coslookup;
private void buildTrigTables(){int N = spectrum.length * timeSize;sinlookup = new float[N];coslookup = new float[N];for (int i = 0; i < N; i++){sinlookup[i] = (float) Math.sin(i * TWO_PI / timeSize);coslookup[i] = (float) Math.cos(i * TWO_PI / timeSize);
}}
private float sin(int i){return sinlookup[i];
}
private float cos(int i){return coslookup[i];
}}
-
8/12/2019 Code Audio Analyzer
4/52
2.FFT.java
/** Copyright (c) 2007 - 2008 by Damien Di Fede ** This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License aspublished* by the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU Library General Public License for more details.** You should have received a copy of the GNU Library General Public* License along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/
package com.badlogic.audio.analysis;
/*** FFT stands for Fast Fourier Transform. It is an efficient way to calculatethe Complex* Discrete Fourier Transform. There is not much to say about this classother than the fact* that when you want to analyze the spectrum of an audio buffer you willalmost always use* this class. One restriction of this class is that the audio buffers youwant to analyze
* must have a length that is a power of two. If you try to construct an FFTwith a* timeSize that is not a power of two, anIllegalArgumentException will be* thrown.** @see FourierTransform* @see The Fast FourierTransform** @author Damien Di Fede**/public class FFT extends FourierTransform
{/*** Constructs an FFT that will accept sample buffers that are* timeSize long and have been recorded with a sample rate of* sampleRate. timeSize must be a* power of two. This will throw an exception if it is not.** @param timeSize* the length of the sample buffers you will be analyzing
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FFT.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FFT.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FFT.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FFT.java?r=11 -
8/12/2019 Code Audio Analyzer
5/52
* @param sampleRate* the sample rate of the audio you will be analyzing*/public FFT(int timeSize, float sampleRate){super(timeSize, sampleRate);if ((timeSize & (timeSize - 1)) != 0)throw new IllegalArgumentException(
"FFT: timeSize must be a power of two.");buildReverseTable();buildTrigTables();
}
protected void allocateArrays(){spectrum = new float[timeSize / 2 + 1];real = new float[timeSize];imag = new float[timeSize];
}
public void scaleBand(int i, float s){if (s < 0){throw new IllegalArgumentException("Can't scale a frequency band by a
negative value.");}if (spectrum[i] != 0){real[i] /= spectrum[i];imag[i] /= spectrum[i];spectrum[i] *= s;real[i] *= spectrum[i];imag[i] *= spectrum[i];
}if (i != 0 && i != timeSize / 2){real[timeSize - i] = real[i];imag[timeSize - i] = -imag[i];
}}
public void setBand(int i, float a){if (a < 0){throw new IllegalArgumentException("Can't set a frequency band to a
negative value.");}if (real[i] == 0 && imag[i] == 0){real[i] = a;spectrum[i] = a;
}else{real[i] /= spectrum[i];
-
8/12/2019 Code Audio Analyzer
6/52
imag[i] /= spectrum[i];spectrum[i] = a;real[i] *= spectrum[i];imag[i] *= spectrum[i];
}if (i != 0 && i != timeSize / 2){real[timeSize - i] = real[i];imag[timeSize - i] = -imag[i];
}}
// performs an in-place fft on the data in the real and imag arrays// bit reversing is not necessary as the data will already be bit reversedprivate void fft(){for (int halfSize = 1; halfSize < real.length; halfSize *= 2){// float k = -(float)Math.PI/halfSize;// phase shift step
// float phaseShiftStepR = (float)Math.cos(k);// float phaseShiftStepI = (float)Math.sin(k);// using lookup tablefloat phaseShiftStepR = cos(halfSize);float phaseShiftStepI = sin(halfSize);// current phase shiftfloat currentPhaseShiftR = 1.0f;float currentPhaseShiftI = 0.0f;for (int fftStep = 0; fftStep < halfSize; fftStep++){for (int i = fftStep; i < real.length; i += 2 * halfSize){int off = i + halfSize;float tr = (currentPhaseShiftR * real[off]) - (currentPhaseShiftI *
imag[off]);float ti = (currentPhaseShiftR * imag[off]) + (currentPhaseShiftI *
real[off]);real[off] = real[i] - tr;imag[off] = imag[i] - ti;real[i] += tr;imag[i] += ti;
}float tmpR = currentPhaseShiftR;currentPhaseShiftR = (tmpR * phaseShiftStepR) - (currentPhaseShiftI *
phaseShiftStepI);currentPhaseShiftI = (tmpR * phaseShiftStepI) + (currentPhaseShiftI *
phaseShiftStepR);
}}
}
public void forward(float[] buffer){if (buffer.length != timeSize){
throw new IllegalArgumentException("FFT.forward: The length of thepassed sample buffer must be equal to timeSize().");
-
8/12/2019 Code Audio Analyzer
7/52
}doWindow(buffer);// copy samples to real/imag in bit-reversed orderbitReverseSamples(buffer);// perform the fftfft();// fill the spectrum buffer with amplitudesfillSpectrum();
}
/*** Performs a forward transform on the passed buffers.** @param buffReal the real part of the time domain signal to transform* @param buffImag the imaginary part of the time domain signal to
transform*/public void forward(float[] buffReal, float[] buffImag){if (buffReal.length != timeSize || buffImag.length != timeSize)
{throw new IllegalArgumentException("FFT.forward: The length of the
passed buffers must be equal to timeSize().");}setComplex(buffReal, buffImag);bitReverseComplex();fft();fillSpectrum();
}
public void inverse(float[] buffer){if (buffer.length > real.length){
throw new IllegalArgumentException("FFT.inverse: the passed array'slength must equal FFT.timeSize().");
}// conjugatefor (int i = 0; i < timeSize; i++){imag[i] *= -1;
}bitReverseComplex();fft();// copy the result in real into buffer, scaling as we dofor (int i = 0; i < buffer.length; i++){
buffer[i] = real[i] / real.length;}
}
private int[] reverse;
private void buildReverseTable(){int N = timeSize;reverse = new int[N];
-
8/12/2019 Code Audio Analyzer
8/52
-
8/12/2019 Code Audio Analyzer
9/52
}}
}
-
8/12/2019 Code Audio Analyzer
10/52
3.FourierTransform.java
/*
* Copyright (c) 2007 - 2008 by Damien Di Fede
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.badlogic.audio.analysis;
/**
* A Fourier Transform is an algorithm that transforms a signal in the time
* domain, such as a sample buffer, into a signal in the frequency domain, often* called the spectrum. The spectrum does not represent individual frequencies,
* but actually represents frequency bands centered on particular frequencies.
* The center frequency of each band is usually expressed as a fraction of the
* sampling rate of the time domain signal and is equal to the index of the
* frequency band divided by the total number of bands. The total number of
* frequency bands is usually equal to the length of the time domain signal, but
* access is only provided to frequency bands with indices less than half the
* length, because they correspond to frequencies below the Nyquist frequency.
* In other words, given a signal of length N, there will be
* N/2 frequency bands in the spectrum.
*
* As an example, if you construct a FourierTransform with a
* timeSize of 1024 and and a sampleRate of 44100
* Hz, then the spectrum will contain values for frequencies below 22010 Hz,
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FourierTransform.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FourierTransform.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FourierTransform.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/FourierTransform.java?r=11 -
8/12/2019 Code Audio Analyzer
11/52
* which is the Nyquist frequency (half the sample rate). If you ask for the
* value of band number 5, this will correspond to a frequency band centered on
* 5/1024 * 44100 = 0.0048828125 * 44100 = 215 Hz. The width of
* that frequency band is equal to 2/1024, expressed as a
* fraction of the total bandwidth of the spectrum. The total bandwith of the* spectrum is equal to the Nyquist frequency, which in this case is 22100, so
* the bandwidth is equal to about 50 Hz. It is not necessary for you to
* remember all of these relationships, though it is good to be aware of them.
* The function getFreq() allows you to query the spectrum with a
* frequency in Hz and the function getBandWidth() will return
* the bandwidth in Hz of each frequency band in the spectrum.
*
* Usage
*
* A typical usage of a FourierTransform is to analyze a signal so that the
* frequency spectrum may be represented in some way, typically with vertical
* lines. You could do this in Processing with the following code, where
* audio is an AudioSource and fft is an FFT (one
* of the derived classes of FourierTransform).
*
*
* fft.forward(audio.left);
* for (int i = 0; i < fft.specSize(); i++)
* {
* // draw the line for frequency band i, scaling it by 4 so we can see it a bit better
* line(i, height, i, height - fft.getBand(i) * 4);
* }
*
*
* Windowing
*
* Windowing is the process of shaping the audio samples before transforming them
* to the frequency domain. If you call the window() function
* with an appropriate constant, such as FourierTransform.HAMMING, the sample
* buffers passed to the object for analysis will be shaped by the current
* window before being transformed. The result of using a window is to reduce
* the noise in the spectrum somewhat.
*
-
8/12/2019 Code Audio Analyzer
12/52
* Averages
*
* FourierTransform also has functions that allow you to request the creation of
* an average spectrum. An average spectrum is simply a spectrum with fewer
* bands than the full spectrum where each average band is the average of the* amplitudes of some number of contiguous frequency bands in the full spectrum.
*
* linAverages() allows you to specify the number of averages
* that you want and will group frequency bands into groups of equal number. So
* if you have a spectrum with 512 frequency bands and you ask for 64 averages,
* each average will span 8 bands of the full spectrum.
*
* logAverages() will group frequency bands by octave and allows
* you to specify the size of the smallest octave to use (in Hz) and also how
* many bands to split each octave into. So you might ask for the smallest
* octave to be 60 Hz and to split each octave into two bands. The result is
* that the bandwidth of each average is different. One frequency is an octave
* above another when it's frequency is twice that of the lower frequency. So,
* 120 Hz is an octave above 60 Hz, 240 Hz is an octave above 120 Hz, and so on.
* When octaves are split, they are split based on Hz, so if you split the
* octave 60-120 Hz in half, you will get 60-90Hz and 90-120Hz. You can see how
* these bandwidths increase as your octave sizes grow. For instance, the last
* octave will always span sampleRate/4 - sampleRate/2, which in
* the case of audio sampled at 44100 Hz is 11025-22010 Hz. These
* logarithmically spaced averages are usually much more useful than the full
* spectrum or the linearly spaced averages because they map more directly to
* how humans perceive sound.
*
* calcAvg() allows you to specify the frequency band you want an
* average calculated for. You might ask for 60-500Hz and this function will
* group together the bands from the full spectrum that fall into that range and
* average their amplitudes for you.
*
* If you don't want any averages calculated, then you can call
* noAverages(). This will not impact your ability to use
* calcAvg(), it will merely prevent the object from calculating
* an average array every time you use forward().
*
-
8/12/2019 Code Audio Analyzer
13/52
-
8/12/2019 Code Audio Analyzer
14/52
*
* @param ts
* the length of the buffers that will be analyzed
* @param sr
* the sample rate of the samples that will be analyzed*/
FourierTransform(int ts, float sr)
{
timeSize = ts;
sampleRate = (int)sr;
bandWidth = (2f / timeSize) * ((float)sampleRate / 2f);
noAverages();
allocateArrays();
whichWindow = NONE;
}
// allocating real, imag, and spectrum are the responsibility of derived
// classes
// because the size of the arrays will depend on the implementation being used
// this enforces that responsibility
protected abstract void allocateArrays();
protected void setComplex(float[] r, float[] i)
{
if (real.length != r.length && imag.length != i.length){
throw new IllegalArgumentException( "This won't work" );
}
else
{
System.arraycopy(r, 0, real, 0, r.length);
System.arraycopy(i, 0, imag, 0, i.length);
}
}// fill the spectrum array with the amps of the data in real and imag
// used so that this class can handle creating the average array
// and also do spectrum shaping if necessary
protected void fillSpectrum()
{
for (int i = 0; i < spectrum.length; i++)
-
8/12/2019 Code Audio Analyzer
15/52
{
spectrum[i] = (float) Math.sqrt(real[i] * real[i] + imag[i] * imag[i]);
}
if (whichAverage == LINAVG)
{int avgWidth = (int) spectrum.length / averages.length;
for (int i = 0; i < averages.length; i++)
{
float avg = 0;
int j;
for (j = 0; j < avgWidth; j++)
{
int offset = j + i * avgWidth;
if (offset < spectrum.length){
avg += spectrum[offset];
}
else
{
break;
}
}
avg /= j + 1;averages[i] = avg;
}
}
else if (whichAverage == LOGAVG)
{
for (int i = 0; i < octaves; i++)
{
float lowFreq, hiFreq, freqStep;
if (i == 0)
{
lowFreq = 0;
}
else
{
lowFreq = (sampleRate / 2) / (float) Math.pow(2, octaves - i);
-
8/12/2019 Code Audio Analyzer
16/52
}
hiFreq = (sampleRate / 2) / (float) Math.pow(2, octaves - i - 1);
freqStep = (hiFreq - lowFreq) / avgPerOctave;
float f = lowFreq;
for (int j = 0; j < avgPerOctave; j++){
int offset = j + i * avgPerOctave;
averages[offset] = calcAvg(f, f + freqStep);
f += freqStep;
}
}
}
}
/*** Sets the object to not compute averages.
*
*/
public void noAverages()
{
averages = new float[0];
whichAverage = NOAVG;
}
/*** Sets the number of averages used when computing the spectrum and spaces the
* averages in a linear manner. In other words, each average band will be
* specSize() / numAvg bands wide.
*
* @param numAvg
* how many averages to compute
*/
public void linAverages(int numAvg)
{if (numAvg > spectrum.length / 2)
{
throw new IllegalArgumentException("The number of averages for this transform can be at
most " + spectrum.length / 2 + ".");
}
else
-
8/12/2019 Code Audio Analyzer
17/52
{
averages = new float[numAvg];
}
whichAverage = LINAVG;
}/**
* Sets the number of averages used when computing the spectrum based on the
* minimum bandwidth for an octave and the number of bands per octave. For
* example, with audio that has a sample rate of 44100 Hz,
* logAverages(11, 1) will result in 12 averages, each
* corresponding to an octave, the first spanning 0 to 11 Hz. To ensure that
* each octave band is a full octave, the number of octaves is computed by
* dividing the Nyquist frequency by two, and then the result of that by two,
* and so on. This means that the actual bandwidth of the lowest octave may* not be exactly the value specified.
*
* @param minBandwidth
* the minimum bandwidth used for an octave
* @param bandsPerOctave
* how many bands to split each octave into
*/
public void logAverages(int minBandwidth, int bandsPerOctave)
{float nyq = (float) sampleRate / 2f;
octaves = 1;
while ((nyq /= 2) > minBandwidth)
{
octaves++;
}
avgPerOctave = bandsPerOctave;
averages = new float[octaves * bandsPerOctave];
whichAverage = LOGAVG;
}
/**
* Sets the window to use on the samples before taking the forward transform.
* If an invalid window is asked for, an error will be reported and the
* current window will not be changed.
*
-
8/12/2019 Code Audio Analyzer
18/52
* @param which
* FourierTransform.HAMMING or FourierTransform.NONE
*/
public void window(int which)
{if (which < 0 || which > 1)
{
throw new IllegalArgumentException("Invalid window type.");
}
else
{
whichWindow = which;
}
}
protected void doWindow(float[] samples)
{
switch (whichWindow)
{
case HAMMING:
hamming(samples);
break;
}
}// windows the data in samples with a Hamming window
protected void hamming(float[] samples)
{
for (int i = 0; i < samples.length; i++)
{
samples[i] *= (0.54f - 0.46f * Math.cos(TWO_PI * i / (samples.length - 1)));
}
}
/*** Returns the length of the time domain signal expected by this transform.
*
* @return the length of the time domain signal expected by this transform
*/
public int timeSize()
{
-
8/12/2019 Code Audio Analyzer
19/52
return timeSize;
}
/**
* Returns the size of the spectrum created by this transform. In other words,
* the number of frequency bands produced by this transform. This is typically* equal to timeSize()/2 + 1, see above for an explanation.
*
* @return the size of the spectrum
*/
public int specSize()
{
return spectrum.length;
}
/*** Returns the amplitude of the requested frequency band.
*
* @param i
* the index of a frequency band
* @return the amplitude of the requested frequency band
*/
public float getBand(int i)
{
if (i < 0) i = 0;if (i > spectrum.length - 1) i = spectrum.length - 1;
return spectrum[i];
}
/**
* Returns the width of each frequency band in the spectrum (in Hz). It should
* be noted that the bandwidth of the first and last frequency bands is half
* as large as the value returned by this function.
*
* @return the width of each frequency band in Hz.*/
public float getBandWidth()
{
return bandWidth;
}
-
8/12/2019 Code Audio Analyzer
20/52
/**
* Sets the amplitude of the ith frequency band to
* a. You can use this to shape the spectrum before using
* inverse().
** @param i
* the frequency band to modify
* @param a
* the new amplitude
*/
public abstract void setBand(int i, float a);
/**
* Scales the amplitude of the ith frequency band
* by s. You can use this to shape the spectrum before using* inverse().
*
* @param i
* the frequency band to modify
* @param s
* the scaling factor
*/
public abstract void scaleBand(int i, float s);
/*** Returns the index of the frequency band that contains the requested
* frequency.
*
* @param freq
* the frequency you want the index for (in Hz)
* @return the index of the frequency band that contains freq
*/
public int freqToIndex(float freq)
{// special case: freq is lower than the bandwidth of spectrum[0]
if (freq < getBandWidth() / 2) return 0;
// special case: freq is within the bandwidth of spectrum[spectrum.length - 1]
if (freq > sampleRate / 2 - getBandWidth() / 2) return spectrum.length - 1;
// all other cases
float fraction = freq / (float) sampleRate;
-
8/12/2019 Code Audio Analyzer
21/52
int i = Math.round(timeSize * fraction);
return i;
}
/*** Returns the middle frequency of the ith band.
* @param i
* the index of the band you want to middle frequency of
*/
public float indexToFreq(int i)
{
float bw = getBandWidth();
// special case: the width of the first bin is half that of the others.
// so the center frequency is a quarter of the way.
if ( i == 0 ) return bw * 0.25f;
// special case: the width of the last bin is half that of the others.
if ( i == spectrum.length - 1 )
{
float lastBinBeginFreq = (sampleRate / 2) - (bw / 2);
float binHalfWidth = bw * 0.25f;
return lastBinBeginFreq + binHalfWidth;
}
// the center frequency of the ith band is simply i*bw
// because the first band is half the width of all others.
// treating it as if it wasn't offsets us to the middle
// of the band.
return i*bw;
}
/**
* Returns the center frequency of the ith average band.
*
* @param i
* which average band you want the center frequency of.
*/
public float getAverageCenterFrequency(int i)
{
if ( whichAverage == LINAVG )
-
8/12/2019 Code Audio Analyzer
22/52
{
// an average represents a certain number of bands in the spectrum
int avgWidth = (int) spectrum.length / averages.length;
// the "center" bin of the average, this is fudgy.
int centerBinIndex = i*avgWidth + avgWidth/2;return indexToFreq(centerBinIndex);
}
else if ( whichAverage == LOGAVG )
{
// which "octave" is this index in?
int octave = i / avgPerOctave;
// which band within that octave is this?
int offset = i % avgPerOctave;
float lowFreq, hiFreq, freqStep;
// figure out the low frequency for this octave
if (octave == 0)
{
lowFreq = 0;
}
else
{
lowFreq = (sampleRate / 2) / (float) Math.pow(2, octaves - octave);
}
// and the high frequency for this octave
hiFreq = (sampleRate / 2) / (float) Math.pow(2, octaves - octave - 1);
// each average band within the octave will be this big
freqStep = (hiFreq - lowFreq) / avgPerOctave;
// figure out the low frequency of the band we care about
float f = lowFreq + offset*freqStep;
// the center of the band will be the low plus half the width
return f + freqStep/2;
}
return 0;
}
/**
-
8/12/2019 Code Audio Analyzer
23/52
* Gets the amplitude of the requested frequency in the spectrum.
*
* @param freq
* the frequency in Hz
* @return the amplitude of the frequency in the spectrum*/
public float getFreq(float freq)
{
return getBand(freqToIndex(freq));
}
/**
* Sets the amplitude of the requested frequency in the spectrum to
* a.
** @param freq
* the frequency in Hz
* @param a
* the new amplitude
*/
public void setFreq(float freq, float a)
{
setBand(freqToIndex(freq), a);
}/**
* Scales the amplitude of the requested frequency by a.
*
* @param freq
* the frequency in Hz
* @param s
* the scaling factor
*/
public void scaleFreq(float freq, float s){
scaleBand(freqToIndex(freq), s);
}
/**
* Returns the number of averages currently being calculated.
*
-
8/12/2019 Code Audio Analyzer
24/52
* @return the length of the averages array
*/
public int avgSize()
{
return averages.length;}
/**
* Gets the value of the ith average.
*
* @param i
* the average you want the value of
* @return the value of the requested average band
*/
public float getAvg(int i){
float ret;
if (averages.length > 0)
ret = averages[i];
else
ret = 0;
return ret;
}
/*** Calculate the average amplitude of the frequency band bounded by
* lowFreq and hiFreq, inclusive.
*
* @param lowFreq
* the lower bound of the band
* @param hiFreq
* the upper bound of the band
* @return the average of all spectrum values within the bounds
*/public float calcAvg(float lowFreq, float hiFreq)
{
int lowBound = freqToIndex(lowFreq);
int hiBound = freqToIndex(hiFreq);
float avg = 0;
for (int i = lowBound; i
-
8/12/2019 Code Audio Analyzer
25/52
{
avg += spectrum[i];
}
avg /= (hiBound - lowBound + 1);
return avg;}
/**
* Performs a forward transform on buffer.
*
* @param buffer
* the buffer to analyze
*/
public abstract void forward(float[] buffer);
/**
* Performs a forward transform on values in buffer.
*
* @param buffer
* the buffer of samples
* @param startAt
* the index to start at in the buffer. there must be at least timeSize() samples
* between the starting index and the end of the buffer. If there aren't, an
* error will be issued and the operation will not be performed.*
*/
public void forward(float[] buffer, int startAt)
{
if ( buffer.length - startAt < timeSize )
{
throw new IllegalArgumentException( "FourierTransform.forward: not enough samples inthe buffer between " + startAt + " and " + buffer.length + " to perform a transform." );
}
// copy the section of samples we want to analyze
float[] section = new float[timeSize];
System.arraycopy(buffer, startAt, section, 0, section.length);
forward(section);
}
-
8/12/2019 Code Audio Analyzer
26/52
/**
* Performs an inverse transform of the frequency spectrum and places the
* result in buffer.
** @param buffer
* the buffer to place the result of the inverse transform in
*/
public abstract void inverse(float[] buffer);
/**
* Performs an inverse transform of the frequency spectrum represented by
* freqReal and freqImag and places the result in buffer.
*
* @param freqReal* the real part of the frequency spectrum
* @param freqImag
* the imaginary part the frequency spectrum
* @param buffer
* the buffer to place the inverse transform in
*/
public void inverse(float[] freqReal, float[] freqImag, float[] buffer)
{
setComplex(freqReal, freqImag);inverse(buffer);
}
/**
* @return the spectrum of the last FourierTransform.forward() call.
*/
public float[] getSpectrum( )
{
return spectrum;
}
/**
* @return the real part of the last FourierTransform.forward() call.
*/
public float[] getRealPart( )
-
8/12/2019 Code Audio Analyzer
27/52
{
return real;
}
/*** @return the imaginary part of the last FourierTransform.forward() call.
*/
public float[] getImaginaryPart( )
{
return imag;
}
}
-
8/12/2019 Code Audio Analyzer
28/52
4.AudioDevice.java
package com.badlogic.audio.io;
import java.io.FileInputStream;
import javax.sound.sampled.AudioFormat;import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat.Encoding;
/**
* Class that allows directly passing PCM float mono
* data to the sound card for playback. The sampling
* rate of the PCM data must be 44100Hz.
*
* @author mzechner*
*/
public class AudioDevice
{
/** the buffer size in samples **/
private final static int BUFFER_SIZE = 1024;
/** the java sound line we write our samples to **/
private final SourceDataLine out;
/** buffer for BUFFER_SIZE 16-bit samples **/
private byte[] buffer = new byte[BUFFER_SIZE*2];
/**
* Constructor, initializes the audio system for
* 44100Hz 16-bit signed mono output.
*
* @throws Exception in case the audio system could not be initialized*/
public AudioDevice( ) throws Exception
{
AudioFormat format = new AudioFormat( Encoding.PCM_SIGNED, 44100, 16, 1, 2,44100, false );
out = AudioSystem.getSourceDataLine( format );
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/AudioDevice.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/AudioDevice.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/AudioDevice.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/AudioDevice.java?r=11 -
8/12/2019 Code Audio Analyzer
29/52
out.open(format);
out.start();
}
/*** Writes the given samples to the audio device. The samples
* have to be sampled at 44100Hz, mono and have to be in
* the range [-1,1].
*
* @param samples The samples.
*/
public void writeSamples( float[] samples )
{
fillBuffer( samples );
out.write( buffer, 0, buffer.length );
}
private void fillBuffer( float[] samples )
{
for( int i = 0, j = 0; i < samples.length; i++, j+=2 )
{
short value = (short)(samples[i] * Short.MAX_VALUE);
buffer[j] = (byte)(value | 0xff);
buffer[j+1] = (byte)(value >> 8 );
}
}
public static void main( String[] argv ) throws Exception
{
float[] samples = new float[1024];
WaveDecoder reader = new WaveDecoder( new FileInputStream(
"samples/sample.wav" ) );
AudioDevice device = new AudioDevice( );
while( reader.readSamples( samples ) > 0 )
{
device.writeSamples( samples );
}
-
8/12/2019 Code Audio Analyzer
30/52
Thread.sleep( 10000 );
}
}
-
8/12/2019 Code Audio Analyzer
31/52
5.EndianDataInputStream.java
package com.badlogic.audio.io;
import java.io.DataInputStream;
import java.io.InputStream;
public class EndianDataInputStream extends DataInputStream
{
public EndianDataInputStream(InputStream in)
{
super(in);
}
public String read4ByteString( ) throws Exception
{
byte[] bytes = new byte[4];readFully(bytes);
return new String( bytes, "US-ASCII" );
}
public short readShortLittleEndian( ) throws Exception
{
int result = readUnsignedByte();
result |= readUnsignedByte()
-
8/12/2019 Code Audio Analyzer
32/52
-
8/12/2019 Code Audio Analyzer
33/52
6/MP3Decoder.java
package com.badlogic.audio.io;
import java.io.BufferedInputStream;
import java.io.InputStream;
import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.Decoder;
import javazoom.jl.decoder.Header;
import javazoom.jl.decoder.SampleBuffer;
/**
* A simple MP3 decoder based on JLayer
* @author mzechner
*
*/public class MP3Decoder
{
/** inverse max short value as float **/
private final float MAX_VALUE = 1.0f / Short.MAX_VALUE;
/** the bit stream **/
private final Bitstream bitStream;
/** the decoder **/private final Decoder decoder;
/** samples left over **/
private float[] leftOverSamples = new float[1024];
/** how many samples are left over **/
private int leftOver = 0;
/*** Constructor, sets the input stream to read the mp3 from
* @param stream The input stream.
* @throws Exception in case something baaaaad happened.
*/
public MP3Decoder( InputStream stream ) throws Exception
{
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/MP3Decoder.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/MP3Decoder.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/MP3Decoder.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/MP3Decoder.java?r=11 -
8/12/2019 Code Audio Analyzer
34/52
bitStream = new Bitstream( new BufferedInputStream( stream, 1024*1024) );
decoder = new Decoder( );
}
/**
* Tries to read in samples.length samples, merging stereo to a mono
* channel by averaging and converting non float formats to float 32-bit.
* Returns the number of samples actually read. Guarantees that samples.length
* samples are read in if there was enough data in the stream.
*
* @param samples The samples array to write the samples to
* @return The number of samples actually read.
*/
public int readSamples( float[] samples )
{
int readSamples = 0;
if( leftOver > 0 )
{
int maxSamples = Math.min( leftOver, samples.length );
for( int i = 0; i < maxSamples; i++, readSamples++ )
samples[i] = leftOverSamples[i];
if( leftOver > samples.length )
{
leftOver = leftOver - samples.length;
System.arraycopy( leftOverSamples, leftOverSamples.length - leftOver,leftOverSamples, 0, leftOver );
return samples.length;
}
else
leftOver = 0;
}
try
{
Header header = bitStream.readFrame();
if( header == null )
-
8/12/2019 Code Audio Analyzer
35/52
return 0;
float frequency = header.frequency();
if( frequency != 44100 )
{bitStream.closeFrame();
return 0;
}
SampleBuffer frame = (SampleBuffer)decoder.decodeFrame( header, bitStream);
if( frame.getBufferLength() > leftOverSamples.length )
leftOverSamples = new float[frame.getBufferLength()];
int channels = frame.getChannelCount();if( channels > 2 )
{
bitStream.closeFrame();
return 0;
}
for( int i = 0; i < frame.getBufferLength(); )
{
float value = frame.getBuffer()[i++] * MAX_VALUE;if( channels == 2 )
{
value += frame.getBuffer()[i++] * MAX_VALUE;
value /= 2;
}
if( readSamples >= samples.length )
leftOverSamples[leftOver++] = value;
else
samples[readSamples++] = value;
}
bitStream.closeFrame();
return readSamples;
} catch (Exception e)
-
8/12/2019 Code Audio Analyzer
36/52
{
return 0;
}
}
}
-
8/12/2019 Code Audio Analyzer
37/52
7.WaveDecoder.java
package com.badlogic.audio.io;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
/**
* A simple class that can read in the PCM data from a
* Wav file, converting the data to signed 32-bit floats
* in the range [-1,1], merging stereo channels to a mono
* channel for processing. This only supports 16-bit signed
* stereo and mono Wav files with a sampling rate of 44100.
*
* @author mzechner
*
*/
public class WaveDecoder
{
/** inverse max short value as float **/
private final float MAX_VALUE = 1.0f / Short.MAX_VALUE;
/** the input stream we read from **/
private final EndianDataInputStream in;
/** number of channels **/
private final int channels;
/** sample rate in Herz**/
private final float sampleRate;
/** **/
/**
* Constructor, sets the input stream to read
* the Wav file from.
*
* @param stream The input stream.
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/WaveDecoder.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/WaveDecoder.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/WaveDecoder.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/io/WaveDecoder.java?r=11 -
8/12/2019 Code Audio Analyzer
38/52
* @throws Exception in case the input stream couldn't be read properly
*/
public WaveDecoder( InputStream stream ) throws Exception
{
if( stream == null )throw new IllegalArgumentException( "Input stream must not be null" );
in = new EndianDataInputStream( new BufferedInputStream( stream, 1024*1024) );
if( !in.read4ByteString().equals( "RIFF" ) )
throw new IllegalArgumentException( "not a wav" );
in.readIntLittleEndian();
if( !in.read4ByteString().equals( "WAVE" ) )
throw new IllegalArgumentException( "expected WAVE tag" );
if( !in.read4ByteString().equals( "fmt " ) )
throw new IllegalArgumentException( "expected fmt tag" );
if( in.readIntLittleEndian() != 16 )
throw new IllegalArgumentException( "expected wave chunk size to be 16" );
if( in.readShortLittleEndian() != 1 )
throw new IllegalArgumentException( "expected format to be 1" );
channels = in.readShortLittleEndian();
sampleRate = in.readIntLittleEndian();
if( sampleRate != 44100 )
throw new IllegalArgumentException( "Not 44100 sampling rate" );
in.readIntLittleEndian();
in.readShortLittleEndian();
int fmt = in.readShortLittleEndian();
if( fmt != 16 )
throw new IllegalArgumentException( "Only 16-bit signed format supported" );
if( !in.read4ByteString().equals( "data" ) )
throw new RuntimeException( "expected data tag" );
-
8/12/2019 Code Audio Analyzer
39/52
in.readIntLittleEndian();
}
/*** Tries to read in samples.length samples, merging stereo to a mono
* channel by averaging and converting non float formats to float 32-bit.
* Returns the number of samples actually read. Guarantees that samples.length
* samples are read in if there was enough data in the stream.
*
* @param samples The samples array to write the samples to
* @return The number of samples actually read.
*/
public int readSamples( float[] samples )
{
int readSamples = 0;
for( int i = 0; i < samples.length; i++ )
{
float sample = 0;
try
{
for( int j = 0; j < channels; j++ )
{
int shortValue = in.readShortLittleEndian( );
sample += (shortValue * MAX_VALUE);
}
sample /= channels;
samples[i] = sample;
readSamples++;
}
catch( Exception ex )
{
break;
}
}
return readSamples;
}
-
8/12/2019 Code Audio Analyzer
40/52
public static void main( String[] args ) throws FileNotFoundException, Exception
{
WaveDecoder decoder = new WaveDecoder( new FileInputStream(
"samples/sample.wav" ) );
float[] samples = new float[1024];
int readSamples = 0;
while( ( readSamples = decoder.readSamples( samples ) ) > 0 )
System.out.println( "read " + readSamples + " samples" );
}
}
-
8/12/2019 Code Audio Analyzer
41/52
8.FourierTransformPlot.java
package com.badlogic.audio.samples;
import java.awt.Color;
import com.badlogic.audio.analysis.FFT;import com.badlogic.audio.io.AudioDevice;
import com.badlogic.audio.visualization.Plot;
/**
* Simple example that generates a 1024 samples sine wave at 440Hz
* and plots the resulting spectrum.
*
* @author mzechner
*
*/public class FourierTransformPlot
{
public static void main( String[] argv )
{
final float frequencyA = 440; // Note A
final float frequencyAOctave = 880; // Note A in the next octave
float incrementA = (float)(2*Math.PI) * frequencyA / 44100;
float incrementAOctave = (float)(2*Math.PI)*frequencyAOctave / 44100;
float angleA = 0;float angleAOctave = 0;
float samples[] = new float[1024];
FFT fft = new FFT( 1024, 44100 );
for( int i = 0; i < samples.length; i++ )
{
samples[i] = ((float)Math.sin( angleA ) + (float)Math.sin( angleAOctave) ) / 2;
angleA += incrementA;
angleAOctave += incrementAOctave;}
fft.forward( samples );
Plot plot = new Plot( "Note A Spectrum", 512, 512);
plot.plot(fft.getSpectrum(), 1, Color.red );
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11 -
8/12/2019 Code Audio Analyzer
42/52
}
-
8/12/2019 Code Audio Analyzer
43/52
9.FourierTransformPlot.java
package com.badlogic.audio.samples;
import java.io.FileInputStream;
import com.badlogic.audio.analysis.FFT;import com.badlogic.audio.io.AudioDevice;
import com.badlogic.audio.io.WaveDecoder;
/**
* A simple example that shows that transforming samples to
* the frequency domain and back to the time domain preserves
* the original signal nearly perfectly.
* @author mzechner
*
*/public class FourierTransformReconstruction
{
public static void main( String[] argv ) throws Exception
{
AudioDevice device = new AudioDevice( );
WaveDecoder decoder = new WaveDecoder( new FileInputStream("samples/sample.wav" ) );
float[] samples = new float[1024];
FFT fft = new FFT( 1024, 44100 );
while( decoder.readSamples( samples ) > 0 )
{
fft.forward( samples );
fft.inverse( samples );
device.writeSamples( samples );
}
}
}
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/FourierTransformPlot.java?r=11 -
8/12/2019 Code Audio Analyzer
44/52
10.MP3Output.java
package com.badlogic.audio.samples;
import java.io.FileInputStream;import java.io.FileNotFoundException;
import com.badlogic.audio.io.AudioDevice;import com.badlogic.audio.io.MP3Decoder;
/*** Simple example that shows how to decode an mp3 file.** @author mzechner**/public class MP3Output{
public static void main( String[] argv ) throwsFileNotFoundException, Exception
{AudioDevice device = new AudioDevice( );MP3Decoder decoder = new MP3Decoder( new FileInputStream(
"samples/sample.mp3" ) );float[] samples = new float[1024];
while( decoder.readSamples( samples ) > 0 ){
device.writeSamples( samples );// System.out.println( "samples" );
}
}}
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/MP3Output.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/MP3Output.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/MP3Output.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/MP3Output.java?r=11 -
8/12/2019 Code Audio Analyzer
45/52
11.NoteGenerator.java
package com.badlogic.audio.samples;
import com.badlogic.audio.io.AudioDevice;
/*** A simple generator that outputs a sinewave at some* frequency (here 440Hz = Note A) in mono to an {@link AudioDevice}.** @author mzechner**/public class NoteGenerator{
public static void main( String[] argv ) throws Exception{
final float frequency = 880; // 440Hz for note Afloat increment = (float)(2*Math.PI) * frequency / 44100; //
angular increment for each sample
float angle = 0;AudioDevice device = new AudioDevice( );float samples[] = new float[1024];
while( true ){
for( int i = 0; i < samples.length; i++ ){
samples[i] = (float)Math.sin( angle );angle += increment;
}
device.writeSamples( samples );
}}}
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/NoteGenerator.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/NoteGenerator.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/NoteGenerator.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/NoteGenerator.java?r=11 -
8/12/2019 Code Audio Analyzer
46/52
14.PlotExample.java
package com.badlogic.audio.samples;
import java.awt.Color;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import com.badlogic.audio.io.WaveDecoder;
import com.badlogic.audio.visualization.Plot;
/**
* A simple example that shows how to use the {@link Plot} class.
* Note that the plots will not be entirely synchronous to the
* music playback. This is just an example, you should not do
* real-time plotting with the Plot class it is just not made for* this.
*
* @author mzechner
*
*/
public class PlotExample
{
public static void main( String[] argv ) throws FileNotFoundException, Exception
{WaveDecoder decoder = new WaveDecoder( new FileInputStream(
"samples/sample.wav" ) );
ArrayList allSamples = new ArrayList( );
float[] samples = new float[1024];
while( decoder.readSamples( samples ) > 0 )
{
for( int i = 0; i < samples.length; i++ )
allSamples.add( samples[i] );
}
samples = new float[allSamples.size()];
for( int i = 0; i < samples.length; i++ )
samples[i] = allSamples.get(i);
Plot plot = new Plot( "Wave Plot", 512, 512 );
plot.plot( samples, 44100 / 100, Color.red );
}
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/PlotExample.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/PlotExample.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/PlotExample.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/PlotExample.java?r=11 -
8/12/2019 Code Audio Analyzer
47/52
}
-
8/12/2019 Code Audio Analyzer
48/52
15.WaveOutput.java
package com.badlogic.audio.samples;
import java.io.FileInputStream;
import com.badlogic.audio.io.AudioDevice;import com.badlogic.audio.io.WaveDecoder;
/*** A simple example how to read in a Wave file via* a {@link WaveDecoder} and output its contents to* an {@link AudioDevice}.* @author mzechner**/public class WaveOutput{
public static void main( String[] argv ) throws Exception{
AudioDevice device = new AudioDevice( );WaveDecoder decoder = new WaveDecoder( new FileInputStream(
"samples/sample.wav" ) );float[] samples = new float[1024];
while( decoder.readSamples( samples ) > 0 ){
device.writeSamples( samples );}
}}
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/WaveOutput.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/WaveOutput.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/WaveOutput.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/samples/WaveOutput.java?r=11 -
8/12/2019 Code Audio Analyzer
49/52
16.Plot.java
package com.badlogic.audio.visualization;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
/*** A simple class that allows to plot float[] arrays
* to a swing window. The first function to plot that
* is given to this class will set the minimum and
* maximum height values. I'm not that good with Swing
* so i might have done a couple of stupid things in here :)
*
* @author mzechner
*
*/public class Plot
{
/** the frame **/
private JFrame frame;
/** the scroll pane **/
private JScrollPane scrollPane;
/** the image gui component **/
private JLabel panel;
/** the image **/
private BufferedImage image;
/** current samples to draw **/
http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/visualization/Plot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/visualization/Plot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/visualization/Plot.java?r=11http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/visualization/Plot.java?r=11 -
8/12/2019 Code Audio Analyzer
50/52
private float[] currentSamples;
/** the last scaling factor to normalize samples **/
private float scalingFactor = 1;
/** wheter the plot was cleared, if true we have to recalculate the scaling factor **/
private boolean cleared = true;
/**
* Creates a new Plot with the given title and dimensions.
*
* @param title The title.
* @param width The width of the plot in pixels.
* @param height The height of the plot in pixels.
*/
public Plot( final String title, final int width, final int height )
{
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run()
{
frame = new JFrame( title );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setPreferredSize( new Dimension( width, height ) );
BufferedImage img = new BufferedImage( width, height,
BufferedImage.TYPE_4BYTE_ABGR );
Graphics2D g = (Graphics2D)img.getGraphics();
g.setColor( Color.black );
g.fillRect( 0, 0, width, height );
g.dispose();
image = img;
panel = new JLabel( new ImageIcon( img ) );
scrollPane = new JScrollPane( panel );
frame.getContentPane().add(scrollPane);
frame.pack();
frame.setVisible( true );
}
-
8/12/2019 Code Audio Analyzer
51/52
});
}
public void clear( )
{SwingUtilities.invokeLater( new Runnable( ) {
@Override
public void run() {
Graphics2D g = image.createGraphics();
g.setColor( Color.black );
g.fillRect( 0, 0, image.getWidth(), image.getHeight() );
g.dispose();
cleared = true;
}});
}
public void plot( float[] samples, final float samplesPerPixel, final Color color )
{
final float[] smps = new float[samples.length];
System.arraycopy( samples, 0, smps, 0, samples.length );
SwingUtilities.invokeLater( new Runnable( )
{@Override
public void run()
{
if( image.getWidth() < smps.length / samplesPerPixel )
{
image = new BufferedImage( (int)(smps.length / samplesPerPixel),frame.getHeight(), BufferedImage.TYPE_4BYTE_ABGR );
Graphics2D g = image.createGraphics();
g.setColor( Color.black );
g.fillRect( 0, 0, image.getWidth(), image.getHeight() );
g.dispose();
}
if( cleared )
{
-
8/12/2019 Code Audio Analyzer
52/52
float min = 0;
float max = 0;
for( int i = 0; i < smps.length; i++ )
{
min = Math.min( smps[i], min );max = Math.max( smps[i], max );
}
scalingFactor = max - min;
cleared = false;
}
Graphics2D g = image.createGraphics();
g.setColor( color );
float lastValue = (smps[0] / scalingFactor) * image.getHeight() / 3 +
image.getHeight() / 2;
for( int i = 1; i < smps.length; i++ )
{
float value = (smps[i] / scalingFactor) * image.getHeight() / 3 +image.getHeight() / 2;
g.drawLine( (int)((i-1) / samplesPerPixel), image.getHeight() -
(int)lastValue, (int)(i / samplesPerPixel), image.getHeight() - (int)value );
lastValue = value;
}
g.dispose();
panel.setIcon( new ImageIcon( image ) );
}
});
}
}