Main Page   Packages   Class Hierarchy   Compound List   File List   Compound Members  

OnsetDetector.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 package com.meapsoft;
00024 
00025 import java.util.ArrayList;
00026 
00027 /*
00028  * Base class for all onset detectors.  Looks at an STFT and
00029  * determines if there is anything that looks like an onset in it.
00030  *
00031  * @author Mike Mandel ([email protected])
00032  */
00033 
00034 public class OnsetDetector implements FrameListener {
00035   STFT stft;
00036   long lastSeen;
00037   int histLen = 4, onsLen = 512;
00038   double[][] history;
00039   double[] onsets;
00040 
00041   ArrayList listeners;
00042 
00043   // Keep track of the mean of each band with a leaky integrator
00044   double[] state;
00045 
00046   // Controls the length over which the mean is calculated for each
00047   // band.
00048   double adaptation = 0.01;
00049 
00050   // The threshold for an onset, as a fraction of the mean for each
00051   // band
00052   double bandThresh = 2;
00053 
00054   // The fraction of band onsets required to trigger a global onset
00055   double bandFrac = 0.3;
00056 
00057   public OnsetDetector(STFT stft, double bandThresh, double bandFrac) {
00058     this.stft = stft;
00059     this.bandThresh = bandThresh;
00060     this.bandFrac = bandFrac;
00061 
00062     this.listeners = new ArrayList();
00063     this.history = new double[histLen+1][];
00064     this.onsets = new double[onsLen];
00065 
00066     state = new double[stft.getRows()];
00067     for(int i=0; i<state.length; i++)
00068       state[i] = 0.5/adaptation;
00069 
00070     stft.addFrameListener(this);
00071   }
00072 
00073   public void addOnsetListener(OnsetListener listener) {
00074     listeners.add(listener);
00075   }
00076   public void removeOnsetListener(OnsetListener listener) {
00077     listeners.remove(listener);
00078   }
00079   protected void notifyListeners(long frAddr, int zeroFrames) {
00080     for(int i=0; i<listeners.size(); i++)
00081       ((OnsetListener)listeners.get(i)).newOnset(frAddr, zeroFrames);
00082   }
00083 
00084 
00085   // Determine if there has been an onset since the last time this was
00086   // called.  If there was, notify our listeners.
00087   public void newFrame(STFT stft, long newestFrame) {
00088     // Get the history for the first column to be analyzed
00089     for(int i=0; i<histLen+1; i++) {
00090       history[i] = stft.getFrame(lastSeen+1-histLen+i);
00091       if(history[i] == null)
00092         history[i] = new double[stft.getRows()];
00093     }
00094 
00095     // Analyze each column in turn, bucket-brigade the history
00096     for(long fr = lastSeen+1; fr <= newestFrame; fr++) {
00097       double result;
00098       int curOns = (int)(fr % onsets.length);
00099       onsets[curOns] = 0;
00100 
00101       for(int band=0; band<history[0].length; band++) {
00102         result = 0;
00103         for(int i=0; i<histLen; i++)
00104           result += Math.abs(history[i][band] - history[histLen][band]);
00105 
00106         // See if this band is an onset
00107         if(result > bandThresh*state[band])
00108           ++onsets[curOns];
00109         
00110         // update running mean
00111         state[band] = adaptation*result + (1-adaptation)*state[band];
00112       }
00113 
00114       // get the next history
00115       for(int i=0; i<histLen; i++)
00116         history[i] = history[i+1];
00117       history[histLen] = stft.getFrame(fr);
00118     }
00119 
00120     checkOnsets(lastSeen, newestFrame);
00121 
00122     // No onsets found
00123     lastSeen = newestFrame;
00124   }
00125 
00126   public void checkOnsets(long lastSeen, long newestFrame) {
00127     // Simple onset detector: if enough bands have onsetted, trigger
00128     // a global onset
00129     for(long fr = lastSeen+1; fr <= newestFrame; fr++) {
00130       int curOns = (int)(fr % onsets.length);
00131 
00132       if(onsets[curOns] > bandFrac*state.length) {
00133 //      System.out.print((int)(onsets[curOns]/10));
00134 //      System.out.print("*");
00135         notifyListeners(fr, 0);
00136       }
00137     }
00138   }
00139 }

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