plectrum

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

PercussionOnsetDetector.java (6581B)


      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 
     25 package be.tarsos.dsp.onsets;
     26 
     27 import be.tarsos.dsp.AudioEvent;
     28 import be.tarsos.dsp.AudioProcessor;
     29 import be.tarsos.dsp.util.fft.FFT;
     30 
     31 /**
     32  * <p>
     33  * Estimates the locations of percussive onsets using a simple method described
     34  * in <a
     35  * href="http://arrow.dit.ie/cgi/viewcontent.cgi?article=1018&context=argcon"
     36  * >"Drum Source Separation using Percussive Feature Detection and Spectral
     37  * Modulation"</a> by Dan Barry, Derry Fitzgerald, Eugene Coyle and Bob Lawlor,
     38  * ISSC 2005.
     39  * </p>
     40  * <p>
     41  * Implementation based on a <a href=
     42  * "http://vamp-plugins.org/code-doc/PercussionOnsetDetector_8cpp-source.html"
     43  * >VAMP plugin example</a> by Chris Cannam at Queen Mary, London:
     44  * 
     45  * <pre>
     46  *  Centre for Digital Music, Queen Mary, University of London.
     47  *  Copyright 2006 Chris Cannam.
     48  *    
     49  *  Permission is hereby granted, free of charge, to any person
     50  *  obtaining a copy of this software and associated documentation
     51  *  files (the "Software"), to deal in the Software without
     52  *  restriction, including without limitation the rights to use, copy,
     53  *  modify, merge, publish, distribute, sublicense, and/or sell copies
     54  *  of the Software, and to permit persons to whom the Software is
     55  *  furnished to do so, subject to the following conditions:
     56  *  
     57  *  The above copyright notice and this permission notice shall be
     58  *  included in all copies or substantial portions of the Software.
     59  *  
     60  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     61  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     62  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     63  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
     64  *  ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     65  *  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     66  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     67  *  
     68  *  Except as contained in this notice, the names of the Centre for
     69  *  Digital Music; Queen Mary, University of London; and Chris Cannam
     70  *  shall not be used in advertising or otherwise to promote the sale,
     71  *  use or other dealings in this Software without prior written
     72  *  authorization.
     73  * </pre>
     74  * 
     75  * </p>
     76  * 
     77  * @author Joren Six
     78  * @author Chris Cannam
     79  */
     80 public class PercussionOnsetDetector implements AudioProcessor, OnsetDetector {
     81 
     82 	public static final double DEFAULT_THRESHOLD = 8;
     83 	
     84 	public static final double DEFAULT_SENSITIVITY = 20;
     85 
     86 	private final FFT fft;
     87 
     88 	private final float[] priorMagnitudes;
     89 	private final float[] currentMagnitudes;
     90 
     91 	private float dfMinus1, dfMinus2;
     92 
     93 	private OnsetHandler handler;
     94 
     95 	private final float sampleRate;//samples per second (Hz)
     96 	private long processedSamples;//in samples
     97 	
     98 	/**
     99 	 * Sensitivity of peak detector applied to broadband detection function (%).
    100 	 * In [0-100].
    101 	 */
    102 	private final double sensitivity;
    103 	
    104 	/**
    105 	 * Energy rise within a frequency bin necessary to count toward broadband
    106 	 * total (dB). In [0-20].
    107 	 * 
    108 	 */
    109 	private final double threshold;
    110 
    111 	/**
    112 	 * Create a new percussion onset detector. With a default sensitivity and threshold.
    113 	 * 
    114 	 * @param sampleRate
    115 	 *            The sample rate in Hz (used to calculate timestamps)
    116 	 * @param bufferSize
    117 	 *            The size of the buffer in samples.
    118 	 * @param bufferOverlap
    119 	 *            The overlap of buffers in samples.
    120 	 * @param handler
    121 	 *            An interface implementor to handle percussion onset events.
    122 	 */
    123 	public PercussionOnsetDetector(float sampleRate, int bufferSize,
    124 			int bufferOverlap, OnsetHandler handler) {
    125 		this(sampleRate, bufferSize, handler,
    126 				DEFAULT_SENSITIVITY, DEFAULT_THRESHOLD);
    127 	}
    128 
    129 	/**
    130 	 * Create a new percussion onset detector.
    131 	 * 
    132 	 * @param sampleRate
    133 	 *            The sample rate in Hz (used to calculate timestamps)
    134 	 * @param bufferSize
    135 	 *            The size of the buffer in samples.
    136 	 * @param handler
    137 	 *            An interface implementor to handle percussion onset events.
    138 	 * @param sensitivity
    139 	 *            Sensitivity of the peak detector applied to broadband
    140 	 *            detection function (%). In [0-100].
    141 	 * @param threshold
    142 	 *            Energy rise within a frequency bin necessary to count toward
    143 	 *            broadband total (dB). In [0-20].
    144 	 */
    145 	public PercussionOnsetDetector(float sampleRate, int bufferSize, OnsetHandler handler, double sensitivity, double threshold) {
    146 		fft = new FFT(bufferSize / 2);
    147 		this.threshold = threshold;
    148 		this.sensitivity = sensitivity;
    149 		priorMagnitudes = new float[bufferSize / 2];
    150 		currentMagnitudes = new float[bufferSize / 2];
    151 		this.handler = handler;
    152 		this.sampleRate = sampleRate;
    153 		
    154 	}
    155 	
    156 	@Override
    157 	public boolean process(AudioEvent audioEvent) {
    158 		float[] audioFloatBuffer = audioEvent.getFloatBuffer();
    159 		this.processedSamples += audioFloatBuffer.length;
    160 		this.processedSamples -= audioEvent.getOverlap();
    161 
    162 		fft.forwardTransform(audioFloatBuffer);
    163 		fft.modulus(audioFloatBuffer, currentMagnitudes);
    164 		int binsOverThreshold = 0;
    165 		for (int i = 0; i < currentMagnitudes.length; i++) {
    166 			if (priorMagnitudes[i] > 0.f) {
    167 				double diff = 10 * Math.log10(currentMagnitudes[i]
    168 						/ priorMagnitudes[i]);
    169 				if (diff >= threshold) {
    170 					binsOverThreshold++;
    171 				}
    172 			}
    173 			priorMagnitudes[i] = currentMagnitudes[i];
    174 		}
    175 
    176 		if (dfMinus2 < dfMinus1
    177 				&& dfMinus1 >= binsOverThreshold
    178 				&& dfMinus1 > ((100 - sensitivity) * audioFloatBuffer.length) / 200) {
    179 			float timeStamp = processedSamples / sampleRate;
    180 			handler.handleOnset(timeStamp,-1);
    181 		}
    182 
    183 		dfMinus2 = dfMinus1;
    184 		dfMinus1 = binsOverThreshold;
    185 
    186 		return true;
    187 	}
    188 
    189 	@Override
    190 	public void processingFinished() {
    191 	}
    192 
    193 	@Override
    194 	public void setHandler(OnsetHandler handler) {
    195 		this.handler = handler;
    196 	}
    197 }