Mercurial > hg > svcore
view transform/BeatDetectTransform.cpp @ 14:b101cc2ae1ab
* Introduce potentially-separate read and write ring buffers, so we can swap
in a new set when something changes -- thus allowing us to respond quickly
when something changes during playback, without losing the long buffers
* Some fixes for display & editing
author | Chris Cannam |
---|---|
date | Fri, 27 Jan 2006 18:04:07 +0000 |
parents | d86891498eef |
children | 39ae3dee27b9 |
line wrap: on
line source
/* -*- 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-2006 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()); }