Main Page   Packages   Class Hierarchy   Compound List   File List   Compound Members  

BeatOnsetDetector.java

00001 /*
00002  *  Copyright 2006-2007 Columbia University.
00003  *
00004  *  This file is part of MEAPsoft.
00005  *
00006  *  MEAPsoft is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License version 2 as
00008  *  published by the Free Software Foundation.
00009  *
00010  *  MEAPsoft is distributed in the hope that it will be useful, but
00011  *  WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  *  General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with MEAPsoft; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00018  *  02110-1301 USA
00019  *
00020  *  See the file "COPYING" for the text of the license.
00021  */
00022 
00023 
00024 package com.meapsoft.disgraced;
00025 
00026 import com.meapsoft.STFT;
00027 import com.meapsoft.FFT;
00028 
00037 /*
00038 public class BeatOnsetDetector extends com.meapsoft.OnsetDetector {
00039 
00040   double bpmMin = 60;
00041   double bpmMax = 150;
00042   int minDelay = 10;
00043 //   int minOnset = 10;
00044   double entThresh = 10;
00045 
00046   double framesPerSecond;
00047   double[] real, imag;
00048   FFT fft;
00049   long nextOnset;
00050   int zeroFrames;
00051 
00052   public BeatOnsetDetector(STFT stft, double bandThresh, double bandFrac) {
00053     super(stft, bandThresh, bandFrac);
00054 
00055     // calculate frames per second
00056     //...
00057 
00058     real = new double[onsets.length];
00059     imag = new double[onsets.length];
00060     fft = new FFT(onsets.length);
00061 
00062     zeroFrames = 0;
00063     nextOnset = 256;
00064   }
00065 
00066   // Takes the FFT of the number of onsets per frame for the
00067   // last however many frames (256), finds the peak magnitude between
00068   // 60 and 150 bpm, uses the phase to predict where the next onset
00069   // will be.  If that onset is between lastSeen and newestFrame,
00070   // trigger an onset there.  Otherwise, keep quiet.  Need some way to
00071   // make sure predictions don't change too drastically from one call
00072   // to the next...
00073 
00074   // Assumption: this will be called more often than once per beat, so
00075   // we can safely assume that there will only be at most one beat
00076   // present per call.
00077   public void checkOnsets(long lastSeen, long newestFrame) {
00078 
00079 //     // Update calculation of how many frames of silence we've heard
00080 //     for(long fr=lastSeen+1; fr <= newestFrame; fr++) {
00081 //       if(onsets[(int)(fr % onsets.length)] < minOnset) {
00082 //      zeroFrames++;
00083 //       } else {
00084 //      zeroFrames = 0;
00085 //       }
00086 //     }
00087 
00088     if(nextOnset > lastSeen && nextOnset <= newestFrame) {
00089       // If the next beat is here, trigger an onset
00090 //      System.out.print("*");
00091 //       System.out.print("*"+zeroFrames+" ");
00092       notifyListeners(nextOnset, zeroFrames);
00093 
00094     } else if(nextOnset > newestFrame) {
00095       // haven't gotten to the beat yet
00096 
00097     } else if(nextOnset <= lastSeen) {
00098       // need to find the next beat
00099 
00100       // Copy onset array into FFT buffers, unwrap the circular array
00101       for(int i=0; i<onsets.length; i++) {
00102         real[i] = onsets[(int)((newestFrame+1+i) % onsets.length)];
00103         imag[i] = 0;
00104       }
00105       
00106       fft.fft(real, imag);
00107       
00108       double maxMag = 0, mag, meanMag = 0;
00109       int argMaxMag = 0;
00110  
00111       // ignore DC component
00112       for(int i=1; i<real.length/2; i++) {
00113         mag = real[i]*real[i] + imag[i]*imag[i];
00114         meanMag += mag;
00115         if(mag > maxMag) {
00116           maxMag = mag;
00117           argMaxMag = i;
00118         }
00119       }
00120       meanMag /= real.length/2.0 - 1;
00121 
00122       // Use a parabolic model to get a better idea of where the
00123       // frequency peak actually falls.  This is the offset added to
00124       // the integral frequency peak location.
00125       double magP = real[argMaxMag+1]*real[argMaxMag+1] 
00126         + imag[argMaxMag+1]*imag[argMaxMag+1];
00127       double mag0 = real[argMaxMag]*real[argMaxMag] 
00128         + imag[argMaxMag]*imag[argMaxMag];
00129       double magN = real[argMaxMag-1]*real[argMaxMag-1] 
00130         + imag[argMaxMag-1]*imag[argMaxMag-1];
00131       double rem = -(magP - magN) / (magP - 2.0*mag0 + magN);
00132       
00133 //       System.out.print((argMaxMag+rem) + " ");
00134       int period = (int)((double)real.length / (rem + argMaxMag));
00135       double angle = Math.atan2(imag[argMaxMag], real[argMaxMag]);
00136 
00137       int delay = (int)(-angle * period/(2*Math.PI));
00138       delay = (delay+period) % period;
00139       if(delay < minDelay)
00140         delay = delay + period;
00141 
00142 //       System.out.print(period + ":" + delay + " ");
00143       
00144       // Should be an onset when angle = 2pi n = omega t = 2pi k/N t
00145       nextOnset = newestFrame + delay;
00146 
00147 //       System.out.print(newestFrame + ":" + nextOnset + " ");
00148 
00149       // If the max doesn't stand out enough, assume the signal is
00150       // silence
00151 //       System.out.print((maxMag/meanMag) + " ");
00152       if(maxMag < entThresh*meanMag)
00153         zeroFrames += delay;
00154       else
00155         zeroFrames = 0;
00156     }
00157   }
00158 }
00159 */

Generated on Tue Feb 6 19:02:24 2007 for MEAPsoft by doxygen1.2.18