/* * Created on Mar 20, 2007 * * Copyright (c) 2006-2007 P.J.Leonard * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Frinika 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.audio.analysis.dft; import com.frinika.audio.analysis.FFTMagnitude; import com.frinika.audio.analysis.FFTWorker; import java.awt.Dimension; import java.util.Vector; import com.frinika.audio.analysis.gui.CyclicSpectrogramDataListener; import uk.org.toot.audio.core.AudioBuffer; import uk.org.toot.audio.core.AudioProcess; /** * Creates a spectrogram from a DoubleDataSource * * Observers are notified when data changes (during build) * * SizeObserver are notify when the number of frequency bins is changed. * * @author pjl * */ public class CyclicBufferFFTSpectrogramDataBuilder implements CyclicSpectrumDataBuilder { Vector<CyclicSpectrogramDataListener> sizeObservers = new Vector<CyclicSpectrogramDataListener>(); private AudioProcess reader; FFTWorker fftWorker; private float [][] data; private float[][] magnArray; double fftBuffer[][]; //private float[][] smoothMagnArray; //private float[][] phaseArray; //private float[][] dPhaseFreqHz; int chunkPtr = 0; private int sizeInChunks; double Fs; //int nFrame; int chunksize; int fftsize; double dt; Dimension size; private int chunkStartInSamples; private int totalFramesRendered; //private double[] dPhaRef; private double[] input; // private double logMagn[]; // private double twoPI; private AudioBuffer buffer; boolean abortFlag=false; private boolean running=false; private Thread runThread=null; private Thread abortWaiter; private FFTMagnitude fftMagn; /** * * @param minF * @param nOctave * @param binsPerOctave */ public CyclicBufferFFTSpectrogramDataBuilder(AudioProcess reader, int bufferSize,double Fs) { this.reader = reader; this.sizeInChunks = bufferSize; // twoPI = 2 * Math.PI; fftWorker=new FFTWorker(Fs,true); fftMagn=new FFTMagnitude(); data=new float[1][]; } synchronized public void setParameters(int chunksize, int fftsize,float Fs) { System.err.println(" AAAA "); this.Fs=Fs; if (chunksize == this.chunksize && fftsize == this.fftsize) return; System.out.println(" ABORT REQUEST "+System.currentTimeMillis()); abortFlag=true; abortWaiter=Thread.currentThread(); while(running) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } abortWaiter=null; System.out.println(" ABORT DONE "+System.currentTimeMillis()); buffer = new AudioBuffer("TEMP", 1, chunksize,Fs); this.chunksize = chunksize; this.fftsize = fftsize; dt = chunksize / Fs; System.out.println(" RESIZE FFT REQUEST "+System.currentTimeMillis()); resize(); System.out.println(" RESIZE FFT DONE "+System.currentTimeMillis()); Runnable runner= new Runnable() { public void run() { doWork(); runThread=null; } }; runThread = new Thread(runner); runThread.start(); } public void abortConstruction() { } public void addSizeObserver(CyclicSpectrogramDataListener o) { sizeObservers.add(o); } void notifyMoreDataObservers(float buff[]) { for (CyclicSpectrogramDataListener o : sizeObservers) o.notifyMoreDataReady(buff); } public int getSizeInChunks() { return sizeInChunks; } public int getChunkRenderedCount() { return totalFramesRendered; } // public int getBinCount() { // return nBin; // } // public float[][] getMagnitude() { // return magnArray; // } // // // public float[][] getSmoothMagnitude() { // return smoothMagnArray; // } synchronized void resize() { fftWorker.resize(fftsize); /* * Here the size of arrays changes any user should synchronize with me * here */ int nBin=fftWorker.getSizeInBins(); for (int i = 0; i < nBin; i++) { // freq[i] = (i * Fs / nBin); // freqArray[i] = (float) freq[i]; // System.out.println(" fftsize/chunkSIze = " + fftsize + "/" // + chunksize); size = new Dimension(sizeInChunks, nBin); // dPhaseFreqHz = new float[sizeInChunks][nBin]; magnArray = new float[sizeInChunks][nBin]; fftBuffer = new double[sizeInChunks][fftsize]; // smoothMagnArray = new float[sizeInChunks][nBin]; // logMagn = new double[nBin*2]; // phaseArray = new float[sizeInChunks][nBin]; } // Phase change (radians) of each bin due to chunksize translation // dPhaRef = new double[nBin]; // for (int i = 0; i < nBin; i++) { // dPhaRef[i] = (twoPI * freq[i] * dt); // >0 // } input = new double[fftsize]; System.out.println(" Resized " + fftsize); } protected void doWork() { running =true; abortFlag=false; chunkPtr = 0; // make invalid // int nRead = 0; chunkStartInSamples = 0; // float phaLast[] = phaseArray[0]; totalFramesRendered = 0; // notifySizeObservers(); do { if (abortFlag) break; if (fftsize != chunksize) { for (int i = 0; i < fftsize - chunksize; i++) input[i] = input[i + chunksize]; } buffer.makeSilence(); reader.processAudio(buffer); float left[] = buffer.getChannel(0); // System.out.println(" mm =" + left[0]); for (int i = fftsize - chunksize, j = 0; i < fftsize; i++, j++) { // if (ch == 2) // input[i] = buffer[2 * j + 1]; // else input[i] = left[j]; } if (chunkPtr < 0) { chunkPtr++; chunkStartInSamples += chunksize; continue; } fftWorker.process(input,fftBuffer[chunkPtr]); data[0]=magnArray[chunkPtr]; fftMagn.getData(data, fftBuffer[chunkPtr]); // System.out.println(" maqxV " + maxV); notifyMoreDataObservers(magnArray[chunkPtr]); chunkPtr++; totalFramesRendered++; if (chunkPtr >= sizeInChunks) chunkPtr = 0; // System.out.println(vertPtr[0]); // bar.setValue(pix); // if (chunkPtr % 50 == 0) { // } // nFrame = (int) reader.getLengthInFrames(); // } while (!reader.eof() && chunkPtr < sizeInChunks); } while (true); running=false; abortFlag=false; if (abortWaiter != null) abortWaiter.interrupt(); System.out.println(" ABORTED "); } public float[] getFreqArray() { return fftWorker.getFreqArray(); } public float[] getMagnitudeAt(long chunkPtr) { if (magnArray == null) return null; // int pix = (int) (framePtr / chunksize); if (chunkPtr >= magnArray.length || chunkPtr < 0) return null; return magnArray[(int) chunkPtr]; } // // public float[] getPhaseAt(long chunkPtr) { // if (phaseArray == null) // return null; // // // int pix = (int) (framePtr / chunksize); // if (chunkPtr >= phaseArray.length || chunkPtr < 0) // return null; // return phaseArray[(int) chunkPtr]; // } // // public float[] getPhaseFreqAt(long chunkPtr) { // if (dPhaseFreqHz == null) // return null; // // // int pix = (int) (framePtr / chunksize); // if (chunkPtr >= dPhaseFreqHz.length) // return null; // return dPhaseFreqHz[(int) chunkPtr]; // } // public long getLengthInFrames() { // return reader.getLengthInFrames(); // } public long chunkStartInSamples(long chunkPtr) { return chunkStartInSamples + chunkPtr * chunksize; } public int getChunkAtFrame(long framePtr) { int chunkPtr1 = (int) ((framePtr - chunkStartInSamples) / chunksize); return chunkPtr1; } public boolean validAt(long chunkPtr2) { return chunkPtr2 >= 0 && chunkPtr2 < this.chunkPtr; } public double getSampleRate() { // TODO Auto-generated method stub return Fs; } public int getBinCount() { return fftWorker.getSizeInBins(); //throw new UnsupportedOperationException("Not supported yet."); } public float[][] getMagnitude() { throw new UnsupportedOperationException("Not supported yet."); } }