/* * Created on Jan 16, 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.sequencer.model.audio; import java.io.IOException; import javax.swing.JFrame; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import com.frinika.audio.analysis.constantq.FFTConstantQ; import com.frinika.audio.analysis.constantq.FreqToBin; import com.frinika.audio.analysis.gui.SpectrogramPanelOLD; import com.frinika.global.FrinikaConfig; import com.frinika.sequencer.FrinikaSequencer; import com.frinika.sequencer.model.AudioPart; import com.frinika.sequencer.model.MidiPart; import com.frinika.sequencer.model.audio.DoubleDataSource; public class AudioPartToMidi { public static MidiPart process(AudioPart part) throws IOException { double dt=.01; double Fs=FrinikaConfig.sampleRate; MidiPart midiPart = new MidiPart(); int maxpixels = 1000; JFrame frame=new JFrame(); JProgressBar bar=new JProgressBar(0,maxpixels); frame.setContentPane(bar); frame.pack(); frame.setVisible(true); double minF=40.0; double maxF=Math.pow(2,7)*minF; int binsPerOctave=12; double thresh=0.01; int chunksize = (int)(Fs * dt); dt=chunksize/Fs; // rasmus.interpreter.sampled.util.FFTConstantQ fftCQ = new rasmus.interpreter.sampled.util.FFTConstantQ(Fs, minF, maxF, binsPerOctave,thresh); FFTConstantQ fftCQ = new FFTConstantQ(Fs,minF , maxF, binsPerOctave,thresh,1.0); // DeltaFFTCQ fftCQ = new DeltaFFTCQ(Fs,minF , maxF, binsPerOctave,thresh,chunksize); int fftsize = fftCQ.getFFTSize(); double freq[]=fftCQ.getFreqs(); System.out.println(" fftsize/chunkSIze = " + fftsize +"/"+ chunksize); int nBin = fftCQ.getNumberOfOutputBands(); double twoPI=2*Math.PI; // Phase change (radians) of each bin due to chunksize translation double dPhaRef[]=new double[nBin]; for (int i=0;i<nBin;i++) { dPhaRef[i]=(twoPI*freq[i]*dt); // >0 } double gbuffer[][] = new double[maxpixels][nBin]; double dgbuffer[][] = new double[maxpixels][nBin]; double pFreq[][] = new double[maxpixels][nBin]; double fftOut[] = new double[nBin * 2]; double fftIn[] = new double[fftsize]; double input[] = new double[fftsize]; DoubleDataSource reader=null; FrinikaSequencer seq=part.getLane().getProject().getSequencer(); long startFrame=(long)((seq.getMicrosecondPosition()*Fs/1000000.0)); System.out.println("start ="+startFrame); // reader= part.createClipReader(); reader.seekFrame(startFrame); double testF=minF*2; //reader = new SInDoubleSource(testF,Fs); int ch = reader.getChannels(); double buffer[] = new double[chunksize*ch]; int pix=0; double realmaxdB=-10000; double maxdB=-40; double rangeDb=40; double satdB=20; double peak=0.0; double phase[]=new double[nBin]; do { if (fftsize != chunksize) { for (int i = 0; i < fftsize-chunksize; i++) input[i] = input[i + chunksize]; } reader.readNextDouble(buffer, 0, chunksize); // System.out.println(reader.getCurrentFrame()); for (int i = fftsize - chunksize,j=0; i < fftsize; i++,j++) { if (ch == 2) input[i] = buffer[2 * j+1]; else input[i] = buffer[j]; if (Math.abs(input[i])> peak) peak=Math.abs(input[i]); } for(int i=0;i< fftsize;i++) { fftIn[i]=input[i]; } fftCQ.calc(fftIn, fftOut); // fftCQ.calcShifted(fftIn, fftOut); //System.out.print("."); double vertPtr[] = gbuffer[pix]; double pfPtr[] = pFreq[pix]; for (int i = 0; i < nBin; i++) { double real=fftOut[2*i]; double imag=fftOut[2*i+1]; double magn = Math.sqrt(real*real+imag*imag); double pha = Math.atan2(imag,real); // -PI PI double dpha = pha - phase[i]; // + dPhaRef[i]; vertPtr[i]=magn; phase[i]=pha; // make it in the range [-PI PI] dpha = -( (dPhaRef[i]- dpha+ Math.PI+twoPI)%twoPI - Math.PI); //Math.rint(dpha/twoPI)*twoPI; // if (i == 12) System.out.println(freq[12] + " " + dpha + " " + dPhaRef[i]); pfPtr[i]= freq[i] + dpha/twoPI/dt; } pix++; // System.out.println(vertPtr[0]); bar.setValue(pix); } while (!reader.endOfFile() && pix < maxpixels); // Do this before or after log ? double dMax=0.0; double dMin=0.0; double state[]=new double[nBin]; // 100 Hz resolution double halfLife=.01; double halfLifeInChunks=halfLife*Fs/chunksize; // damp^halfLifeInChunksize =0.5 // log(damp) = log(0.5)/hlins // damp = e^(log(0.5)/hlins) double damp= Math.exp(Math.log(0.5)/halfLifeInChunks); double damp2=1.0-damp; for (int i = 0; i < maxpixels-1; i++) { for (int j = 0; j <nBin; j++) { double stateNew=state[j]*damp + gbuffer[i][j]*damp2; dgbuffer[i][j]=stateNew-state[j]; // dMax= Math.max(dgbuffer[i][j],dMax); dMin= Math.min(dgbuffer[i][j],dMin); state[j]=stateNew; } } for (int i = 0; i < maxpixels; i++) { for (int j = 0; j < nBin; j++) { dgbuffer[i][j] =(dgbuffer[i][j]-dMin)/(dMax-dMin); } } for (int i = 0; i < maxpixels; i++) { for (int j = 0; j < nBin; j++) { double magn=gbuffer[i][j]; double dB=20*Math.log10(4*magn +1e-15); gbuffer[i][j]= dB; if (dB> realmaxdB ) realmaxdB=dB; } } System.out.println(" real max dB= " + realmaxdB + " peak value =" + peak); realmaxdB -= satdB; for (int i = 0; i < maxpixels; i++) { for (int j = 0; j < nBin; j++) { gbuffer[i][j]=(gbuffer[i][j]+rangeDb-realmaxdB)/rangeDb; } } SpectrogramPanelOLD panel=new SpectrogramPanelOLD(); panel.setData(gbuffer,pFreq,freq,dt,new FreqToBin(minF,binsPerOctave),0.3,dgbuffer); JScrollPane scroll=new JScrollPane(panel); frame.setContentPane(scroll); frame.pack(); // frame.setSize(scroll.getPreferredSize()); // frame.setVisible(true); return midiPart; } }