plectrum

Plectrum: instrument tuner for Android
Log | Files | Refs | README | LICENSE

ComplexOnsetDetector.java (5679B)


      1 /*
      2 *      _______                       _____   _____ _____  
      3 *     |__   __|                     |  __ \ / ____|  __ \ 
      4 *        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
      5 *        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
      6 *        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
      7 *        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
      8 *                                                         
      9 * -------------------------------------------------------------
     10 *
     11 * TarsosDSP is developed by Joren Six at IPEM, University Ghent
     12 *  
     13 * -------------------------------------------------------------
     14 *
     15 *  Info: http://0110.be/tag/TarsosDSP
     16 *  Github: https://github.com/JorenSix/TarsosDSP
     17 *  Releases: http://0110.be/releases/TarsosDSP/
     18 *  
     19 *  TarsosDSP includes modified source code by various authors,
     20 *  for credits and info, see README.
     21 * 
     22 */
     23 
     24 package be.tarsos.dsp.onsets;
     25 
     26 import be.tarsos.dsp.AudioEvent;
     27 import be.tarsos.dsp.AudioProcessor;
     28 import be.tarsos.dsp.util.PeakPicker;
     29 import be.tarsos.dsp.util.fft.FFT;
     30 import be.tarsos.dsp.util.fft.HannWindow;
     31 
     32 /**
     33  * A complex Domain Method onset detection function
     34  * 
     35  * Christopher Duxbury, Mike E. Davies, and Mark B. Sandler. Complex domain
     36  * onset detection for musical signals. In Proceedings of the Digital Audio
     37  * Effects Conference, DAFx-03, pages 90-93, London, UK, 2003
     38  * 
     39  * The implementation is a translation of onset.c from Aubio, Copyright (C)
     40  * 2003-2009 Paul Brossier <piem@aubio.org>
     41  * 
     42  * @author Joren Six
     43  * @author Paul Brossiers
     44  */
     45 public class ComplexOnsetDetector implements AudioProcessor, OnsetDetector{
     46 	
     47 	
     48 	/**
     49 	 * The threshold to define silence, in dbSPL.
     50 	 */
     51 	private final double silenceThreshold;
     52 	
     53 	/**
     54 	 * The minimum IOI (inter onset interval), in seconds.
     55 	 */
     56 	private final double minimumInterOnsetInterval;
     57 	
     58 	/**
     59 	 * The last detected onset, in seconds.
     60 	 */
     61 	private double lastOnset;
     62 	
     63 	/**
     64 	 * The last detected onset value.
     65 	 */
     66 	private double lastOnsetValue;
     67 	
     68 	private final PeakPicker peakPicker;
     69 	
     70 	private OnsetHandler handler;
     71 	
     72 	
     73 	/**
     74 	 * To calculate the FFT.
     75 	 */
     76 	private final FFT fft;
     77 	
     78 	/**
     79 	 * Previous phase vector, one frame behind
     80 	 */
     81 	private final float[] theta1;
     82 	/**
     83 	 * Previous phase vector, two frames behind
     84 	 */
     85 	private final float[] theta2;
     86 	
     87 	/**
     88 	 * Previous norm (power, magnitude) vector
     89 	 */
     90 	private final float[] oldmag;
     91 	
     92 	/**
     93 	 * Current onset detection measure vector 
     94 	 */
     95 	private final float[] dev1;
     96 	
     97 	/**
     98 	 * 
     99 	 * @param fftSize The size of the fft to take (e.g. 512)
    100 	 * @param peakThreshold A threshold used for peak picking. Values between 0.1 and 0.8. Default is 0.3, if too many onsets are detected adjust to 0.4 or 0.5.
    101 	 * @param silenceThreshold The threshold that defines when a buffer is silent. Default is -70dBSPL. -90 is also used.
    102 	 * @param minimumInterOnsetInterval The minimum inter-onset-interval in seconds. When two onsets are detected within this interval the last one does not count. Default is 0.004 seconds.
    103 	 */
    104 	public ComplexOnsetDetector(int fftSize,double peakThreshold,double minimumInterOnsetInterval,double silenceThreshold){
    105 		fft = new FFT(fftSize,new HannWindow());
    106 		this.silenceThreshold = silenceThreshold;
    107 		this.minimumInterOnsetInterval = minimumInterOnsetInterval;
    108 		
    109 		peakPicker = new PeakPicker(peakThreshold);
    110 		
    111 		int rsize = fftSize/2+1;
    112 		oldmag = new float[rsize];
    113 		dev1 = new float[rsize];
    114 		theta1 = new float[rsize];
    115 		theta2 = new float[rsize];
    116 		
    117 		handler = new PrintOnsetHandler();
    118 	}
    119 	
    120 	public ComplexOnsetDetector(int fftSize){
    121 		this(fftSize,0.3);
    122 	}
    123 	
    124 	public ComplexOnsetDetector(int fftSize,double peakThreshold){
    125 		this(fftSize,peakThreshold,0.03);
    126 	}
    127 	
    128 	public ComplexOnsetDetector(int fftSize,double peakThreshold,double minimumInterOnsetInterval){
    129 		this(fftSize,peakThreshold,minimumInterOnsetInterval,-70.0);
    130 	}
    131 
    132 	@Override
    133 	public boolean process(AudioEvent audioEvent) {
    134 		onsetDetection(audioEvent);
    135 		return true;
    136 	}
    137 	
    138 	
    139 	private void onsetDetection(AudioEvent audioEvent){
    140 		//calculate the complex fft (the magnitude and phase)
    141 		float[] data = audioEvent.getFloatBuffer().clone();
    142 		float[] power = new float[data.length/2];
    143 		float[] phase = new float[data.length/2];
    144 		fft.powerPhaseFFT(data, power, phase);
    145 		
    146 		float onsetValue = 0;
    147 		
    148 		for(int j = 0 ; j < power.length ; j++){
    149 			//int imgIndex = (power.length - 1) * 2 - j;
    150 			
    151 			 // compute the predicted phase
    152 			dev1[j] = 2.f * theta1[j] - theta2[j];
    153 			
    154 			// compute the euclidean distance in the complex domain
    155 		    // sqrt ( r_1^2 + r_2^2 - 2 * r_1 * r_2 * \cos ( \phi_1 - \phi_2 ) )
    156 			onsetValue += Math.sqrt(Math.abs(Math.pow(oldmag[j],2) + Math.pow(power[j],2) - 2. * oldmag[j] *power[j] * Math.cos(dev1[j] - phase[j])));
    157 					
    158 			/* swap old phase data (need to remember 2 frames behind)*/
    159 			theta2[j] = theta1[j];
    160 			theta1[j] = phase[j];
    161 			
    162 			/* swap old magnitude data (1 frame is enough) */
    163 			oldmag[j]= power[j];
    164 		}
    165 		
    166 		lastOnsetValue = onsetValue;
    167 		
    168 		
    169 		boolean isOnset = peakPicker.pickPeak(onsetValue);
    170 		if(isOnset){
    171 			if(audioEvent.isSilence(silenceThreshold)){
    172 				isOnset = false;
    173 			} else {				
    174 				double delay = ((audioEvent.getOverlap()  * 4.3 ))/ audioEvent.getSampleRate(); 
    175 				double onsetTime = audioEvent.getTimeStamp() - delay;
    176 				if(onsetTime - lastOnset > minimumInterOnsetInterval){
    177 					handler.handleOnset(onsetTime,peakPicker.getLastPeekValue());
    178 					lastOnset = onsetTime;
    179 				}
    180 			}
    181 		}
    182 	}
    183 	
    184 	public void setHandler(OnsetHandler handler) {
    185 		this.handler = handler;
    186 	}	
    187 	
    188 	public void setThreshold(double threshold){
    189 		this.peakPicker.setThreshold(threshold);
    190 	}
    191 
    192 	@Override
    193 	public void processingFinished() {
    194 		
    195 	}
    196 }