be.tarsos.dsp.onsets.ComplexOnsetDetector Maven / Gradle / Ivy
/*
* _______ _____ _____ _____
* |__ __| | __ \ / ____| __ \
* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
*
* -------------------------------------------------------------
*
* TarsosDSP is developed by Joren Six at IPEM, University Ghent
*
* -------------------------------------------------------------
*
* Info: http://0110.be/tag/TarsosDSP
* Github: https://github.com/JorenSix/TarsosDSP
* Releases: http://0110.be/releases/TarsosDSP/
*
* TarsosDSP includes modified source code by various authors,
* for credits and info, see README.
*
*/
package be.tarsos.dsp.onsets;
import be.tarsos.dsp.AudioEvent;
import be.tarsos.dsp.AudioProcessor;
import be.tarsos.dsp.util.PeakPicker;
import be.tarsos.dsp.util.fft.FFT;
import be.tarsos.dsp.util.fft.HannWindow;
/**
* A complex Domain Method onset detection function
*
* Christopher Duxbury, Mike E. Davies, and Mark B. Sandler. Complex domain
* onset detection for musical signals. In Proceedings of the Digital Audio
* Effects Conference, DAFx-03, pages 90-93, London, UK, 2003
*
* The implementation is a translation of onset.c from Aubio, Copyright (C)
* 2003-2009 Paul Brossier
*
* @author Joren Six
* @author Paul Brossiers
*/
public class ComplexOnsetDetector implements AudioProcessor, OnsetDetector{
/**
* The threshold to define silence, in dbSPL.
*/
private final double silenceThreshold;
/**
* The minimum IOI (inter onset interval), in seconds.
*/
private final double minimumInterOnsetInterval;
/**
* The last detected onset, in seconds.
*/
private double lastOnset;
/**
* The last detected onset value.
*/
private double lastOnsetValue;
private final PeakPicker peakPicker;
private OnsetHandler handler;
/**
* To calculate the FFT.
*/
private final FFT fft;
/**
* Previous phase vector, one frame behind
*/
private final float[] theta1;
/**
* Previous phase vector, two frames behind
*/
private final float[] theta2;
/**
* Previous norm (power, magnitude) vector
*/
private final float[] oldmag;
/**
* Current onset detection measure vector
*/
private final float[] dev1;
/**
*
* @param fftSize The size of the fft to take (e.g. 512)
* @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.
* @param silenceThreshold The threshold that defines when a buffer is silent. Default is -70dBSPL. -90 is also used.
* @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.
*/
public ComplexOnsetDetector(int fftSize,double peakThreshold,double minimumInterOnsetInterval,double silenceThreshold){
fft = new FFT(fftSize,new HannWindow());
this.silenceThreshold = silenceThreshold;
this.minimumInterOnsetInterval = minimumInterOnsetInterval;
peakPicker = new PeakPicker(peakThreshold);
int rsize = fftSize/2+1;
oldmag = new float[rsize];
dev1 = new float[rsize];
theta1 = new float[rsize];
theta2 = new float[rsize];
handler = new PrintOnsetHandler();
}
public ComplexOnsetDetector(int fftSize){
this(fftSize,0.3);
}
public ComplexOnsetDetector(int fftSize,double peakThreshold){
this(fftSize,peakThreshold,0.03);
}
public ComplexOnsetDetector(int fftSize,double peakThreshold,double minimumInterOnsetInterval){
this(fftSize,peakThreshold,minimumInterOnsetInterval,-70.0);
}
@Override
public boolean process(AudioEvent audioEvent) {
onsetDetection(audioEvent);
return true;
}
private void onsetDetection(AudioEvent audioEvent){
//calculate the complex fft (the magnitude and phase)
float[] data = audioEvent.getFloatBuffer().clone();
float[] power = new float[data.length/2];
float[] phase = new float[data.length/2];
fft.powerPhaseFFT(data, power, phase);
float onsetValue = 0;
for(int j = 0 ; j < power.length ; j++){
//int imgIndex = (power.length - 1) * 2 - j;
// compute the predicted phase
dev1[j] = 2.f * theta1[j] - theta2[j];
// compute the euclidean distance in the complex domain
// sqrt ( r_1^2 + r_2^2 - 2 * r_1 * r_2 * \cos ( \phi_1 - \phi_2 ) )
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])));
/* swap old phase data (need to remember 2 frames behind)*/
theta2[j] = theta1[j];
theta1[j] = phase[j];
/* swap old magnitude data (1 frame is enough) */
oldmag[j]= power[j];
}
lastOnsetValue = onsetValue;
boolean isOnset = peakPicker.pickPeak(onsetValue);
if(isOnset){
if(audioEvent.isSilence(silenceThreshold)){
isOnset = false;
} else {
double delay = ((audioEvent.getOverlap() * 4.3 ))/ audioEvent.getSampleRate();
double onsetTime = audioEvent.getTimeStamp() - delay;
if(onsetTime - lastOnset > minimumInterOnsetInterval){
handler.handleOnset(onsetTime,peakPicker.getLastPeekValue());
lastOnset = onsetTime;
}
}
}
}
public void setHandler(OnsetHandler handler) {
this.handler = handler;
}
public void setThreshold(double threshold){
this.peakPicker.setThreshold(threshold);
}
@Override
public void processingFinished() {
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy