#include "MixxxBpmDetection.h" MixxxBpmDetection::MixxxBpmDetection(float inputSampleRate): Vamp::Plugin(inputSampleRate), m_pDetector(NULL), m_iSampleRate(inputSampleRate), m_iBlockSize(4096), m_fNumCycles(0), m_fMinBpm(50), m_fMaxBpm(150), m_bAllowAboveRange(0) { m_fPhase=0; } MixxxBpmDetection::~MixxxBpmDetection() { } string MixxxBpmDetection::getIdentifier() const { return "mixxxbpmdetection"; } string MixxxBpmDetection::getName() const { return "SoundTouch BPM Detector (Legacy)"; } string MixxxBpmDetection::getDescription() const { // Return something helpful here! return "Port of Mixxx BPM Analyser"; } string MixxxBpmDetection::getMaker() const { return "Olli Parviainen"; } int MixxxBpmDetection::getPluginVersion() const { // Increment this each time you release a version that behaves // differently from the previous one return 1; } string MixxxBpmDetection::getCopyright() const { // This function is not ideally named. It does not necessarily // need to say who made the plugin -- getMaker does that -- but it // should indicate the terms under which it is distributed. For // example, "Copyright (year). All Rights Reserved", or "GPL" return "LGPL v2.1"; } MixxxBpmDetection::InputDomain MixxxBpmDetection::getInputDomain() const { return TimeDomain; } size_t MixxxBpmDetection::getPreferredBlockSize() const { return 4096; // 0 means "I can handle any block size" } size_t MixxxBpmDetection::getPreferredStepSize() const { return 0; // 0 means "anything sensible"; in practice this // means the same as the block size for TimeDomain // plugins, or half of it for FrequencyDomain plugins } size_t MixxxBpmDetection::getMinChannelCount() const { return 1; } size_t MixxxBpmDetection::getMaxChannelCount() const { return 1; } MixxxBpmDetection::ParameterList MixxxBpmDetection::getParameterDescriptors() const { ParameterList list; // If the plugin has no adjustable parameters, return an empty // list here (and there's no need to provide implementations of // getParameter and setParameter in that case either). // Note that it is your responsibility to make sure the parameters // start off having their default values (e.g. in the constructor // above). The host needs to know the default value so it can do // things like provide a "reset to default" function, but it will // not explicitly set your parameters to their defaults for you if // they have not changed in the mean time. ParameterDescriptor d; d.identifier = "minbpm"; d.name = "BPM min"; d.description = "Minimum detected BPM"; d.unit = "bpm"; d.minValue = 0; d.maxValue = 220; d.defaultValue = 50; d.isQuantized = true; d.quantizeStep = 1.0f; list.push_back(d); d.identifier = "maxbpm"; d.name = "BPM max"; d.description = "Maximum BPM"; d.unit = "bpm"; d.minValue = 0; d.maxValue = 220; d.defaultValue = 150; d.isQuantized = true; d.quantizeStep = 1.0f; list.push_back(d); d.identifier = "bpmaboverange"; d.name = "BPM above range"; d.description = "Allow BPM above range"; d.unit = ""; d.minValue = 0; d.maxValue = 1.0f; d.defaultValue = 0; d.isQuantized = true; d.quantizeStep = 1.0f; list.push_back(d); d.identifier = "phase"; d.name = "phase"; d.description = "Phase ( expressed as a fraction of beat length )"; d.unit = ""; d.minValue = 0; d.maxValue = 1.0f; d.defaultValue = 0; d.isQuantized = false; list.push_back(d); return list; } float MixxxBpmDetection::getParameter(string identifier) const { if (identifier == "minbpm") { return m_fMinBpm; // return the ACTUAL current value of your parameter here! } if (identifier == "maxbpm") { return m_fMaxBpm; } if (identifier == "bpmaboverange") { return m_bAllowAboveRange ? 1.0 : 0.0; } if (identifier == "phase") { return m_fPhase; } return 0; } void MixxxBpmDetection::setParameter(string identifier, float value) { if (identifier == "minbpm") { m_fMinBpm = value; // return the ACTUAL current value of your parameter here! } if (identifier == "maxbpm") { m_fMaxBpm = value; } if (identifier == "bpmaboverange") { m_bAllowAboveRange = (value > 0.5); } if (identifier == "phase") { m_fPhase = value; } } MixxxBpmDetection::ProgramList MixxxBpmDetection::getPrograms() const { ProgramList list; // If you have no programs, return an empty list (or simply don't // implement this function or getCurrentProgram/selectProgram) return list; } string MixxxBpmDetection::getCurrentProgram() const { return ""; // no programs } void MixxxBpmDetection::selectProgram(string name) { } MixxxBpmDetection::OutputList MixxxBpmDetection::getOutputDescriptors() const { OutputList list; // See OutputDescriptor documentation for the possibilities here. // Every plugin must have at least one output. OutputDescriptor d; d.identifier = "Beat"; d.name = "Beat location"; d.description = "Estimated Beat location"; d.unit = ""; d.hasFixedBinCount = true; d.binCount = 0; d.sampleType = OutputDescriptor::FixedSampleRate; d.hasDuration = false; list.push_back(d); return list; } bool MixxxBpmDetection::initialise(size_t channels, size_t stepSize, size_t blockSize) { if (channels < getMinChannelCount() || channels > getMaxChannelCount()) return false; m_pDetector = new soundtouch::BPMDetect(channels, m_iSampleRate); m_iBlockSize = blockSize; return true; } void MixxxBpmDetection::reset() { m_fNumCycles = 0; // Clear buffers, reset stored values, etc } MixxxBpmDetection::FeatureSet MixxxBpmDetection::process(const float *const *inputBuffers, Vamp::RealTime timestamp) { if(m_pDetector != NULL) { m_pDetector->inputSamples(inputBuffers[0], m_iBlockSize); } m_fNumCycles++; return FeatureSet(); } MixxxBpmDetection::FeatureSet MixxxBpmDetection::getRemainingFeatures() { FeatureSet returnfs; if(m_pDetector != NULL) { float bpm = m_pDetector->getBpm(); if(bpm != 0) { // Shift it by 2's until it is in the desired range float newbpm = correctBPM(bpm, m_fMinBpm, m_fMaxBpm, m_bAllowAboveRange); float beatlen = (60.0 * m_iSampleRate / newbpm); float beatpos = m_fPhase * beatlen; while (beatpos max ) BPM /= 2; } while ( BPM < min ) BPM *= 2; return BPM; }