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