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 }