c@110: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@110: /* c@110: Constant-Q library c@110: Copyright (c) 2013-2014 Queen Mary, University of London c@110: c@110: Permission is hereby granted, free of charge, to any person c@110: obtaining a copy of this software and associated documentation c@110: files (the "Software"), to deal in the Software without c@110: restriction, including without limitation the rights to use, copy, c@110: modify, merge, publish, distribute, sublicense, and/or sell copies c@110: of the Software, and to permit persons to whom the Software is c@110: furnished to do so, subject to the following conditions: c@110: c@110: The above copyright notice and this permission notice shall be c@110: included in all copies or substantial portions of the Software. c@110: c@110: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, c@110: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF c@110: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND c@110: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY c@110: CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF c@110: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION c@110: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. c@110: c@110: Except as contained in this notice, the names of the Centre for c@110: Digital Music; Queen Mary, University of London; and Chris Cannam c@110: shall not be used in advertising or otherwise to promote the sale, c@110: use or other dealings in this Software without prior written c@110: authorization. c@110: */ c@110: c@110: #include "CQChromaVamp.h" c@110: c@170: #include "cq/Chromagram.h" c@110: c@110: #include c@110: c@110: using std::string; c@110: using std::vector; c@110: using std::cerr; c@110: using std::endl; c@110: c@113: static const int defaultLowestOctave = 0; c@113: static const int defaultOctaveCount = 7; c@110: static const int defaultBPO = 36; c@110: static const float defaultTuningFrequency = 440.f; c@110: c@110: CQChromaVamp::CQChromaVamp(float inputSampleRate) : c@110: Vamp::Plugin(inputSampleRate), c@110: m_lowestOctave(defaultLowestOctave), c@110: m_octaveCount(defaultOctaveCount), c@110: m_tuningFrequency(defaultTuningFrequency), c@110: m_bpo(defaultBPO), c@170: m_chroma(0), c@110: m_haveStartTime(false), c@110: m_columnCount(0) c@110: { c@110: } c@110: c@110: CQChromaVamp::~CQChromaVamp() c@110: { c@170: delete m_chroma; c@110: } c@110: c@110: string c@110: CQChromaVamp::getIdentifier() const c@110: { c@110: return "cqchromavamp"; c@110: } c@110: c@110: string c@110: CQChromaVamp::getName() const c@110: { c@169: return "CQ Chromagram"; c@110: } c@110: c@110: string c@110: CQChromaVamp::getDescription() const c@110: { c@155: return "Extract a Constant-Q spectrogram with constant ratio of centre frequency to resolution from the audio, then wrap it around into a single-octave chromagram."; c@110: } c@110: c@110: string c@110: CQChromaVamp::getMaker() const c@110: { c@110: return "Queen Mary, University of London"; c@110: } c@110: c@110: int c@110: CQChromaVamp::getPluginVersion() const c@110: { c@182: return 2; c@110: } c@110: c@110: string c@110: CQChromaVamp::getCopyright() const c@110: { c@182: return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2015 QMUL. BSD/MIT licence."; c@110: } c@110: c@110: CQChromaVamp::ParameterList c@110: CQChromaVamp::getParameterDescriptors() const c@110: { c@110: ParameterList list; c@110: c@110: ParameterDescriptor desc; c@110: c@110: desc.identifier = "lowestoct"; c@110: desc.name = "Lowest Contributing Octave"; c@110: desc.unit = ""; c@110: desc.description = "Octave number of the lowest octave to include in the chromagram. Octave numbering is ASA standard, with -1 as the first octave in the MIDI range and middle-C being C4. The octave starts at C."; c@110: desc.minValue = -1; c@110: desc.maxValue = 12; c@110: desc.defaultValue = defaultLowestOctave; c@110: desc.isQuantized = true; c@110: desc.quantizeStep = 1; c@110: list.push_back(desc); c@110: c@110: desc.identifier = "octaves"; c@110: desc.name = "Contributing Octave Count"; c@110: desc.unit = "octaves"; c@110: desc.description = "Number of octaves to use when generating the Constant-Q transform. All octaves are wrapped around and summed to produce a single octave chromagram as output."; c@110: desc.minValue = 1; c@110: desc.maxValue = 12; c@110: desc.defaultValue = defaultOctaveCount; c@110: desc.isQuantized = true; c@110: desc.quantizeStep = 1; c@110: list.push_back(desc); c@110: c@110: desc.identifier = "tuning"; c@110: desc.name = "Tuning Frequency"; c@110: desc.unit = "Hz"; c@110: desc.description = "Frequency of concert A"; c@110: desc.minValue = 360; c@110: desc.maxValue = 500; c@151: desc.defaultValue = defaultTuningFrequency; c@110: desc.isQuantized = false; c@110: list.push_back(desc); c@110: c@110: desc.identifier = "bpo"; c@110: desc.name = "Bins per Octave"; c@110: desc.unit = "bins"; c@110: desc.description = "Number of constant-Q transform bins per octave"; c@110: desc.minValue = 2; c@110: desc.maxValue = 480; c@110: desc.defaultValue = defaultBPO; c@110: desc.isQuantized = true; c@110: desc.quantizeStep = 1; c@110: list.push_back(desc); c@110: c@110: return list; c@110: } c@110: c@110: float c@110: CQChromaVamp::getParameter(std::string param) const c@110: { c@110: if (param == "lowestoct") { c@110: return m_lowestOctave; c@110: } c@110: if (param == "octaves") { c@110: return m_octaveCount; c@110: } c@110: if (param == "tuning") { c@110: return m_tuningFrequency; c@110: } c@110: if (param == "bpo") { c@110: return m_bpo; c@110: } c@110: std::cerr << "WARNING: CQChromaVamp::getParameter: unknown parameter \"" c@110: << param << "\"" << std::endl; c@110: return 0.0; c@110: } c@110: c@110: void c@110: CQChromaVamp::setParameter(std::string param, float value) c@110: { c@110: if (param == "lowestoct") { c@164: m_lowestOctave = int(value + 0.5f); c@110: } else if (param == "octaves") { c@164: m_octaveCount = int(value + 0.5f); c@110: } else if (param == "tuning") { c@110: m_tuningFrequency = value; c@110: } else if (param == "bpo") { c@164: m_bpo = int(value + 0.5f); c@110: } else { c@110: std::cerr << "WARNING: CQChromaVamp::setParameter: unknown parameter \"" c@110: << param << "\"" << std::endl; c@110: } c@110: } c@110: c@110: bool c@110: CQChromaVamp::initialise(size_t channels, size_t stepSize, size_t blockSize) c@110: { c@170: if (m_chroma) { c@170: delete m_chroma; c@170: m_chroma = 0; c@110: } c@110: c@110: if (channels < getMinChannelCount() || c@110: channels > getMaxChannelCount()) return false; c@110: c@110: m_stepSize = stepSize; c@110: m_blockSize = blockSize; c@110: c@147: reset(); c@147: c@170: if (!m_chroma || !m_chroma->isValid()) { c@147: cerr << "CQVamp::initialise: Constant-Q parameters not valid! Not initialising" << endl; c@147: return false; c@147: } c@110: c@110: return true; c@110: } c@110: c@110: void c@110: CQChromaVamp::reset() c@110: { c@170: delete m_chroma; c@170: Chromagram::Parameters p(m_inputSampleRate); c@170: p.lowestOctave = m_lowestOctave; c@171: p.octaveCount = m_octaveCount; c@171: p.binsPerOctave = m_bpo; c@170: p.tuningFrequency = m_tuningFrequency; c@170: c@170: m_chroma = new Chromagram(p); c@147: c@110: m_haveStartTime = false; c@147: m_startTime = Vamp::RealTime::zeroTime; c@110: m_columnCount = 0; c@110: } c@110: c@110: size_t c@110: CQChromaVamp::getPreferredStepSize() const c@110: { c@110: return 0; c@110: } c@110: c@110: size_t c@110: CQChromaVamp::getPreferredBlockSize() const c@110: { c@110: return 0; c@110: } c@110: c@110: CQChromaVamp::OutputList c@110: CQChromaVamp::getOutputDescriptors() const c@110: { c@110: OutputList list; c@110: c@110: OutputDescriptor d; c@110: d.identifier = "chromagram"; c@110: d.name = "Chromagram"; c@110: d.unit = ""; c@110: d.description = "Chromagram obtained from output of constant-Q transform, folding over each process block into a single-octave vector"; c@110: d.hasFixedBinCount = true; c@110: d.binCount = m_bpo; c@110: c@170: if (m_chroma) { c@110: for (int i = 0; i < (int)d.binCount; ++i) { c@170: d.binNames.push_back(m_chroma->getBinName(i)); c@110: } c@110: } c@110: c@110: d.hasKnownExtents = false; c@110: d.isQuantized = false; c@110: d.sampleType = OutputDescriptor::FixedSampleRate; c@170: d.sampleRate = m_inputSampleRate / (m_chroma ? m_chroma->getColumnHop() : 256); c@110: list.push_back(d); c@110: c@110: return list; c@110: } c@110: c@110: CQChromaVamp::FeatureSet c@110: CQChromaVamp::process(const float *const *inputBuffers, c@110: Vamp::RealTime timestamp) c@110: { c@170: if (!m_chroma) { c@110: cerr << "ERROR: CQChromaVamp::process: " c@110: << "Plugin has not been initialised" c@110: << endl; c@110: return FeatureSet(); c@110: } c@110: c@110: if (!m_haveStartTime) { c@110: m_startTime = timestamp; c@110: m_haveStartTime = true; c@110: } c@110: c@110: vector data; c@110: for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]); c@110: c@170: vector > chromaout = m_chroma->process(data); c@170: return convertToFeatures(chromaout); c@110: } c@110: c@110: CQChromaVamp::FeatureSet c@110: CQChromaVamp::getRemainingFeatures() c@110: { c@170: vector > chromaout = m_chroma->getRemainingOutput(); c@170: return convertToFeatures(chromaout); c@110: } c@110: c@110: CQChromaVamp::FeatureSet c@170: CQChromaVamp::convertToFeatures(const vector > &chromaout) c@110: { c@110: FeatureSet returnFeatures; c@110: c@170: int width = chromaout.size(); c@110: c@110: for (int i = 0; i < width; ++i) { c@110: c@170: vector column(chromaout[i].begin(), chromaout[i].end()); c@110: c@110: Feature feature; c@110: feature.hasTimestamp = true; c@110: feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime c@170: (m_columnCount * m_chroma->getColumnHop() - m_chroma->getLatency(), c@110: m_inputSampleRate); c@110: feature.values = column; c@110: feature.label = ""; c@110: c@110: if (feature.timestamp >= m_startTime) { c@110: returnFeatures[0].push_back(feature); c@110: } c@110: c@110: ++m_columnCount; c@110: } c@110: c@110: return returnFeatures; c@110: } c@110: