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 }