c@35: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@35: c@35: #include "CQVamp.h" c@35: c@56: #include "cpp-qm-dsp/ConstantQ.h" c@35: c@56: #include "base/Pitch.h" c@55: c@35: using std::string; c@35: using std::vector; c@35: using std::cerr; c@35: using std::endl; c@35: c@35: CQVamp::CQVamp(float inputSampleRate) : c@35: Vamp::Plugin(inputSampleRate), c@55: m_minMIDIPitch(36), c@55: m_maxMIDIPitch(84), c@55: m_tuningFrequency(440), c@55: m_bpo(24), c@35: m_cq(0), c@35: m_maxFrequency(inputSampleRate/2), c@35: m_minFrequency(46), c@53: m_haveStartTime(false), c@53: m_columnCount(0) c@35: { c@35: } c@35: c@35: CQVamp::~CQVamp() c@35: { c@35: delete m_cq; c@35: } c@35: c@35: string c@35: CQVamp::getIdentifier() const c@35: { c@35: return "cqvamp"; c@35: } c@35: c@35: string c@35: CQVamp::getName() const c@35: { c@35: return "Constant-Q Spectrogram"; c@35: } c@35: c@35: string c@35: CQVamp::getDescription() const c@35: { c@35: return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio"; c@35: } c@35: c@35: string c@35: CQVamp::getMaker() const c@35: { c@35: return "Queen Mary, University of London"; c@35: } c@35: c@35: int c@35: CQVamp::getPluginVersion() const c@35: { c@35: return 1; c@35: } c@35: c@35: string c@35: CQVamp::getCopyright() const c@35: { c@35: return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2013 QMUL"; c@35: } c@35: c@35: CQVamp::ParameterList c@35: CQVamp::getParameterDescriptors() const c@35: { c@35: ParameterList list; c@55: /* c@35: ParameterDescriptor desc; c@35: desc.identifier = "minfreq"; c@35: desc.name = "Minimum Frequency"; c@35: desc.unit = "Hz"; c@35: desc.description = "Hint for the lowest frequency to be included in the constant-Q transform. The actual frequency range will be an integral number of octaves ending at the highest frequency specified"; c@35: desc.minValue = 10; c@35: desc.maxValue = m_inputSampleRate/2; c@35: desc.defaultValue = 46; c@35: desc.isQuantized = false; c@35: list.push_back(desc); c@35: c@35: desc.identifier = "maxfreq"; c@35: desc.name = "Maximum Frequency"; c@35: desc.unit = "Hz"; c@35: desc.description = "Highest frequency to be included in the constant-Q transform"; c@35: desc.minValue = 10; c@35: desc.maxValue = m_inputSampleRate/2; c@35: desc.defaultValue = m_inputSampleRate/2; c@35: desc.isQuantized = false; c@35: list.push_back(desc); c@55: */ c@55: ParameterDescriptor desc; c@55: desc.identifier = "minpitch"; c@55: desc.name = "Minimum Pitch"; c@55: desc.unit = "MIDI units"; c@55: desc.description = "MIDI pitch corresponding to the lowest frequency to be included in the constant-Q transform"; c@55: desc.minValue = 0; c@55: desc.maxValue = 127; c@55: desc.defaultValue = 36; c@55: desc.isQuantized = true; c@55: desc.quantizeStep = 1; c@55: list.push_back(desc); c@55: c@55: desc.identifier = "maxpitch"; c@55: desc.name = "Maximum Pitch"; c@55: desc.unit = "MIDI units"; c@55: desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform"; c@55: desc.minValue = 0; c@55: desc.maxValue = 127; c@55: desc.defaultValue = 84; c@55: desc.isQuantized = true; c@55: desc.quantizeStep = 1; c@55: list.push_back(desc); c@55: c@55: desc.identifier = "tuning"; c@55: desc.name = "Tuning Frequency"; c@55: desc.unit = "Hz"; c@55: desc.description = "Frequency of concert A"; c@55: desc.minValue = 360; c@55: desc.maxValue = 500; c@55: desc.defaultValue = 440; c@55: desc.isQuantized = false; c@55: list.push_back(desc); c@35: c@35: desc.identifier = "bpo"; c@35: desc.name = "Bins per Octave"; c@35: desc.unit = "bins"; c@35: desc.description = "Number of constant-Q transform bins per octave"; c@35: desc.minValue = 2; c@35: desc.maxValue = 480; c@35: desc.defaultValue = 24; c@35: desc.isQuantized = true; c@35: desc.quantizeStep = 1; c@35: list.push_back(desc); c@35: c@35: return list; c@35: } c@35: c@35: float c@35: CQVamp::getParameter(std::string param) const c@35: { c@55: if (param == "minpitch") { c@55: return m_minMIDIPitch; c@55: } c@55: if (param == "maxpitch") { c@55: return m_maxMIDIPitch; c@55: } c@55: if (param == "tuning") { c@55: return m_tuningFrequency; c@55: } c@55: /* c@35: if (param == "minfreq") { c@35: return m_minFrequency; c@35: } c@35: if (param == "maxfreq") { c@35: return m_maxFrequency; c@35: } c@55: */ c@35: if (param == "bpo") { c@35: return m_bpo; c@35: } c@35: std::cerr << "WARNING: CQVamp::getParameter: unknown parameter \"" c@35: << param << "\"" << std::endl; c@35: return 0.0; c@35: } c@35: c@35: void c@35: CQVamp::setParameter(std::string param, float value) c@35: { c@55: if (param == "minpitch") { c@55: m_minMIDIPitch = lrintf(value); c@55: } else if (param == "maxpitch") { c@55: m_maxMIDIPitch = lrintf(value); c@55: } else if (param == "tuning") { c@55: m_tuningFrequency = value; c@55: /* if (param == "minfreq") { c@35: m_minFrequency = value; c@35: } else if (param == "maxfreq") { c@35: m_maxFrequency = value; c@55: */ c@55: } else if (param == "bpo") { c@35: m_bpo = lrintf(value); c@35: } else { c@35: std::cerr << "WARNING: CQVamp::setParameter: unknown parameter \"" c@35: << param << "\"" << std::endl; c@35: } c@35: } c@35: c@35: bool c@35: CQVamp::initialise(size_t channels, size_t stepSize, size_t blockSize) c@35: { c@35: if (m_cq) { c@35: delete m_cq; c@35: m_cq = 0; c@35: } c@35: c@35: if (channels < getMinChannelCount() || c@35: channels > getMaxChannelCount()) return false; c@35: c@35: m_stepSize = stepSize; c@35: m_blockSize = blockSize; c@35: c@55: m_minFrequency = Pitch::getFrequencyForPitch c@55: (m_minMIDIPitch, 0, m_tuningFrequency); c@55: m_maxFrequency = Pitch::getFrequencyForPitch c@55: (m_maxMIDIPitch, 0, m_tuningFrequency); c@55: c@35: m_cq = new ConstantQ c@35: (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo); c@35: c@35: return true; c@35: } c@35: c@35: void c@35: CQVamp::reset() c@35: { c@35: if (m_cq) { c@35: delete m_cq; c@35: m_cq = new ConstantQ c@35: (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo); c@35: } c@36: m_prevFeature.clear(); c@53: m_haveStartTime = false; c@53: m_columnCount = 0; c@35: } c@35: c@35: size_t c@35: CQVamp::getPreferredStepSize() const c@35: { c@35: return 0; c@35: } c@35: c@35: size_t c@35: CQVamp::getPreferredBlockSize() const c@35: { c@35: return 0; c@35: } c@35: c@35: CQVamp::OutputList c@35: CQVamp::getOutputDescriptors() const c@35: { c@35: OutputList list; c@35: c@35: OutputDescriptor d; c@35: d.identifier = "constantq"; c@35: d.name = "Constant-Q Spectrogram"; c@35: d.unit = ""; c@35: d.description = "Output of constant-Q transform, as a single vector per process block"; c@35: d.hasFixedBinCount = true; c@35: d.binCount = (m_cq ? m_cq->getTotalBins() : (9 * 24)); c@35: d.hasKnownExtents = false; c@35: d.isQuantized = false; c@35: d.sampleType = OutputDescriptor::FixedSampleRate; c@35: d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256); c@35: list.push_back(d); c@35: c@35: return list; c@35: } c@35: c@35: CQVamp::FeatureSet c@35: CQVamp::process(const float *const *inputBuffers, c@53: Vamp::RealTime timestamp) c@35: { c@35: if (!m_cq) { c@35: cerr << "ERROR: CQVamp::process: " c@35: << "Plugin has not been initialised" c@35: << endl; c@35: return FeatureSet(); c@35: } c@35: c@53: if (!m_haveStartTime) { c@53: m_startTime = timestamp; c@53: m_haveStartTime = true; c@53: } c@53: c@35: vector data; c@35: for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]); c@35: c@35: vector > cqout = m_cq->process(data); c@36: return convertToFeatures(cqout); c@36: } c@35: c@36: CQVamp::FeatureSet c@36: CQVamp::getRemainingFeatures() c@36: { c@36: vector > cqout = m_cq->getRemainingBlocks(); c@36: return convertToFeatures(cqout); c@36: } c@36: c@36: CQVamp::FeatureSet c@36: CQVamp::convertToFeatures(const vector > &cqout) c@36: { c@35: FeatureSet returnFeatures; c@35: c@36: for (int i = 0; i < (int)cqout.size(); ++i) { c@35: c@35: vector column(m_cq->getTotalBins(), 0.f); c@36: c@36: for (int j = 0; j < (int)cqout[i].size(); ++j) { c@35: column[j] = cqout[i][j]; c@35: } c@36: for (int j = cqout[i].size(); j < m_cq->getTotalBins(); ++j) { c@36: if (j < (int)m_prevFeature.size()) { c@36: column[j] = m_prevFeature[j]; c@36: } c@36: } c@36: c@36: m_prevFeature = column; c@35: c@35: Feature feature; c@53: feature.hasTimestamp = true; c@53: feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime c@53: (m_columnCount * m_cq->getColumnHop() - m_cq->getLatency(), c@53: m_inputSampleRate); c@35: feature.values = column; c@35: feature.label = ""; c@53: c@56: // cerr << "timestamp = " << feature.timestamp << " (start time = " << m_startTime << ", column count = " << m_columnCount << ", latency = " << m_cq->getLatency() << ", sample rate " << m_inputSampleRate << ")" << endl; c@53: c@53: if (feature.timestamp >= m_startTime) { c@53: returnFeatures[0].push_back(feature); c@53: } c@53: c@53: ++m_columnCount; c@35: } c@35: c@35: return returnFeatures; c@35: } c@35: