diff transform/BeatDetectTransform.cpp @ 0:da6937383da8

initial import
author Chris Cannam
date Tue, 10 Jan 2006 16:33:16 +0000
parents
children d86891498eef
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/BeatDetectTransform.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,203 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#include "BeatDetectTransform.h"
+
+#include "model/DenseTimeValueModel.h"
+#include "model/SparseOneDimensionalModel.h"
+
+#include <iostream>
+#include "dsp/onsets/DetectionFunction.h"
+#include "dsp/tempotracking/TempoTrack.h"
+
+
+BeatDetectTransform::BeatDetectTransform(Model *inputModel) :
+    Transform(inputModel)
+{
+    // Step resolution for the detection function in seconds
+    double stepSecs = 0.01161;
+
+    // Step resolution for the detection function in samples
+    size_t stepSize = (size_t)floor((double)inputModel->getSampleRate() * 
+				    stepSecs); 
+
+
+//    m_w->m_bdf->setResolution(stepSize);
+//    output->setResolution(stepSize);
+
+    std::cerr << "BeatDetectTransform::BeatDetectTransform: input sample rate " << inputModel->getSampleRate() << ", stepSecs " << stepSecs << ", stepSize " << stepSize << ", unrounded stepSize " << double(inputModel->getSampleRate()) * stepSecs << ", output sample rate " << inputModel->getSampleRate() / stepSize << ", unrounded output sample rate " << double(inputModel->getSampleRate()) / double(stepSize) << std::endl;
+
+    m_output = new SparseOneDimensionalModel(inputModel->getSampleRate(), 1);
+}
+
+BeatDetectTransform::~BeatDetectTransform()
+{
+    // parent does it all
+}
+
+TransformName
+BeatDetectTransform::getName()
+{
+    return tr("Beats");
+}
+
+void
+BeatDetectTransform::run()
+{
+    SparseOneDimensionalModel *output = getOutput();
+    DenseTimeValueModel *input = getInput();
+    if (!input) return;
+
+    DFConfig config;
+
+    config.DFType = DF_COMPLEXSD;
+
+    // Step resolution for the detection function in seconds
+    config.stepSecs = 0.01161;
+
+    // Step resolution for the detection function in samples
+    config.stepSize = (unsigned int)floor((double)input->getSampleRate() * 
+					  config.stepSecs ); 
+
+    config.frameLength = 2 * config.stepSize;
+
+    unsigned int stepSize = config.stepSize;
+    unsigned int frameLength = config.frameLength;
+
+//    m_w->m_bdf->setResolution(stepSize);
+    output->setResolution(stepSize);
+
+    //Tempo Tracking Configuration Parameters
+    TTParams ttparams;
+    
+    // Low Pass filter coefficients for detection function smoothing
+    double* aCoeffs = new double[3];
+    double* bCoeffs = new double[3];
+	
+    aCoeffs[ 0 ] = 1;
+    aCoeffs[ 1 ] = -0.5949;
+    aCoeffs[ 2 ] = 0.2348;
+    bCoeffs[ 0 ] = 0.1600;
+    bCoeffs[ 1 ] = 0.3200;
+    bCoeffs[ 2 ] = 0.1600;
+
+    ttparams.winLength = 512;
+    ttparams.lagLength = 128;
+    ttparams.LPOrd = 2;
+    ttparams.LPACoeffs = aCoeffs;
+    ttparams.LPBCoeffs = bCoeffs; 
+    ttparams.alpha = 9;
+    ttparams.WinT.post = 8;
+    ttparams.WinT.pre = 7;
+
+    ////////////////////////////////////////////////////////////
+    // DetectionFunction
+    ////////////////////////////////////////////////////////////
+    // Instantiate and configure detection function object
+
+    DetectionFunction df(config);
+
+    size_t origin = input->getStartFrame();
+    size_t frameCount = input->getEndFrame() - origin;
+    size_t blocks = (frameCount / stepSize);
+    if (blocks * stepSize < frameCount) ++blocks;
+
+    double *buffer = new double[frameLength];
+
+    // DF output with causal extension
+    unsigned int clen = blocks + ttparams.winLength;
+    double *dfOutput = new double[clen];
+
+    std::cerr << "Detecting beats at step size " << stepSize << "..." << std::endl;
+
+    for (size_t i = 0; i < clen; ++i) {
+
+//	std::cerr << "block " << i << "/" << clen << std::endl;
+//	std::cerr << ".";
+
+	if (i < blocks) {
+	    size_t got = input->getValues(-1, //!!! needs to come from parent layer -- which is not supposed to be in scope at this point
+					  origin + i * stepSize,
+					  origin + i * stepSize + frameLength,
+					  buffer);
+	    while (got < frameLength) buffer[got++] = 0.0;
+	    dfOutput[i] = df.process(buffer);
+	} else {
+	    dfOutput[i] = 0.0;
+	}
+
+//	m_w->m_bdf->addPoint(SparseTimeValueModel::Point
+//			     (i * stepSize, dfOutput[i],
+//			      QString("%1").arg(dfOutput[i])));
+//	m_w->m_bdf->setCompletion(i * 99 / clen);
+	output->setCompletion(i * 99 / clen);
+
+	if (m_deleting) {
+	    delete [] buffer;
+	    delete [] dfOutput;
+	    delete [] aCoeffs;
+	    delete [] bCoeffs;
+	    return;
+	}
+    }
+
+//    m_w->m_bdf->setCompletion(100);
+
+    // Tempo Track Object instantiation and configuration
+    TempoTrack tempoTracker(ttparams);
+
+    // Vector of detected onsets
+    vector<int> beats; 
+
+    std::cerr << "Running tempo tracker..." << std::endl;
+
+    beats = tempoTracker.process(dfOutput, blocks);
+
+    delete [] buffer;
+    delete [] dfOutput;
+    delete [] aCoeffs;
+    delete [] bCoeffs;
+
+    for (size_t i = 0; i < beats.size(); ++i) {
+//	std::cerr << "Beat value " << beats[i] << ", multiplying out to " << beats[i] * stepSize << std::endl;
+	float bpm = 0.0;
+	int fdiff = 0;
+	if (i < beats.size() - 1) {
+	    fdiff = (beats[i+1] - beats[i]) * stepSize;
+	    // one beat is fdiff frames, so there are samplerate/fdiff bps,
+	    // so 60*samplerate/fdiff bpm
+	    if (fdiff > 0) {
+		bpm = (60.0 * input->getSampleRate()) / fdiff;
+	    }
+	}
+	output->addPoint(SparseOneDimensionalModel::Point
+			 (origin + beats[i] * stepSize, QString("%1").arg(bpm)));
+	if (m_deleting) return;
+    }
+
+    output->setCompletion(100);
+}
+
+DenseTimeValueModel *
+BeatDetectTransform::getInput()
+{
+    DenseTimeValueModel *dtvm =
+	dynamic_cast<DenseTimeValueModel *>(getInputModel());
+    if (!dtvm) {
+	std::cerr << "BeatDetectTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+    }
+    return dtvm;
+}
+
+SparseOneDimensionalModel *
+BeatDetectTransform::getOutput()
+{
+    return static_cast<SparseOneDimensionalModel *>(getOutputModel());
+}
+