annotate vamp/CQVamp.cpp @ 90:bfc7cf71f2ef

Rearrange classes so the basic ConstantQ produces a complex output, and CQSpectrogram (formerly CQInterpolated) is required if you want a real output
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 09 May 2014 10:05:16 +0100
parents f4fb0ac6120a
children fa1709ed4a3c
rev   line source
c@35 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@69 2 /*
c@69 3 Constant-Q library
c@69 4 Copyright (c) 2013-2014 Queen Mary, University of London
c@69 5
c@69 6 Permission is hereby granted, free of charge, to any person
c@69 7 obtaining a copy of this software and associated documentation
c@69 8 files (the "Software"), to deal in the Software without
c@69 9 restriction, including without limitation the rights to use, copy,
c@69 10 modify, merge, publish, distribute, sublicense, and/or sell copies
c@69 11 of the Software, and to permit persons to whom the Software is
c@69 12 furnished to do so, subject to the following conditions:
c@69 13
c@69 14 The above copyright notice and this permission notice shall be
c@69 15 included in all copies or substantial portions of the Software.
c@69 16
c@69 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@69 18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@69 19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@69 20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
c@69 21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@69 22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@69 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@69 24
c@69 25 Except as contained in this notice, the names of the Centre for
c@69 26 Digital Music; Queen Mary, University of London; and Chris Cannam
c@69 27 shall not be used in advertising or otherwise to promote the sale,
c@69 28 use or other dealings in this Software without prior written
c@69 29 authorization.
c@69 30 */
c@35 31
c@35 32 #include "CQVamp.h"
c@35 33
c@56 34 #include "base/Pitch.h"
c@55 35
c@58 36 #include <algorithm>
c@58 37 #include <cstdio>
c@58 38
c@35 39 using std::string;
c@35 40 using std::vector;
c@35 41 using std::cerr;
c@35 42 using std::endl;
c@35 43
c@35 44 CQVamp::CQVamp(float inputSampleRate) :
c@35 45 Vamp::Plugin(inputSampleRate),
c@55 46 m_minMIDIPitch(36),
c@55 47 m_maxMIDIPitch(84),
c@55 48 m_tuningFrequency(440),
c@55 49 m_bpo(24),
c@90 50 m_interpolation(CQSpectrogram::InterpolateLinear),
c@35 51 m_cq(0),
c@35 52 m_maxFrequency(inputSampleRate/2),
c@35 53 m_minFrequency(46),
c@53 54 m_haveStartTime(false),
c@53 55 m_columnCount(0)
c@35 56 {
c@35 57 }
c@35 58
c@35 59 CQVamp::~CQVamp()
c@35 60 {
c@35 61 delete m_cq;
c@35 62 }
c@35 63
c@35 64 string
c@35 65 CQVamp::getIdentifier() const
c@35 66 {
c@35 67 return "cqvamp";
c@35 68 }
c@35 69
c@35 70 string
c@35 71 CQVamp::getName() const
c@35 72 {
c@35 73 return "Constant-Q Spectrogram";
c@35 74 }
c@35 75
c@35 76 string
c@35 77 CQVamp::getDescription() const
c@35 78 {
c@35 79 return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio";
c@35 80 }
c@35 81
c@35 82 string
c@35 83 CQVamp::getMaker() const
c@35 84 {
c@35 85 return "Queen Mary, University of London";
c@35 86 }
c@35 87
c@35 88 int
c@35 89 CQVamp::getPluginVersion() const
c@35 90 {
c@35 91 return 1;
c@35 92 }
c@35 93
c@35 94 string
c@35 95 CQVamp::getCopyright() const
c@35 96 {
c@35 97 return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2013 QMUL";
c@35 98 }
c@35 99
c@35 100 CQVamp::ParameterList
c@35 101 CQVamp::getParameterDescriptors() const
c@35 102 {
c@35 103 ParameterList list;
c@35 104
c@55 105 ParameterDescriptor desc;
c@55 106 desc.identifier = "minpitch";
c@55 107 desc.name = "Minimum Pitch";
c@55 108 desc.unit = "MIDI units";
c@55 109 desc.description = "MIDI pitch corresponding to the lowest frequency to be included in the constant-Q transform";
c@55 110 desc.minValue = 0;
c@55 111 desc.maxValue = 127;
c@55 112 desc.defaultValue = 36;
c@55 113 desc.isQuantized = true;
c@55 114 desc.quantizeStep = 1;
c@55 115 list.push_back(desc);
c@55 116
c@55 117 desc.identifier = "maxpitch";
c@55 118 desc.name = "Maximum Pitch";
c@55 119 desc.unit = "MIDI units";
c@55 120 desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform";
c@55 121 desc.minValue = 0;
c@55 122 desc.maxValue = 127;
c@55 123 desc.defaultValue = 84;
c@55 124 desc.isQuantized = true;
c@55 125 desc.quantizeStep = 1;
c@55 126 list.push_back(desc);
c@55 127
c@55 128 desc.identifier = "tuning";
c@55 129 desc.name = "Tuning Frequency";
c@55 130 desc.unit = "Hz";
c@55 131 desc.description = "Frequency of concert A";
c@55 132 desc.minValue = 360;
c@55 133 desc.maxValue = 500;
c@55 134 desc.defaultValue = 440;
c@55 135 desc.isQuantized = false;
c@55 136 list.push_back(desc);
c@35 137
c@35 138 desc.identifier = "bpo";
c@35 139 desc.name = "Bins per Octave";
c@35 140 desc.unit = "bins";
c@35 141 desc.description = "Number of constant-Q transform bins per octave";
c@35 142 desc.minValue = 2;
c@35 143 desc.maxValue = 480;
c@35 144 desc.defaultValue = 24;
c@35 145 desc.isQuantized = true;
c@35 146 desc.quantizeStep = 1;
c@35 147 list.push_back(desc);
c@35 148
c@75 149 desc.identifier = "interpolation";
c@75 150 desc.name = "Interpolation";
c@75 151 desc.unit = "";
c@75 152 desc.description = "Interpolation method used to fill empty cells in lower octaves";
c@75 153 desc.minValue = 0;
c@75 154 desc.maxValue = 2;
c@75 155 desc.defaultValue = 2;
c@75 156 desc.isQuantized = true;
c@75 157 desc.quantizeStep = 1;
c@90 158 desc.valueNames.push_back("None, leave as zero");
c@75 159 desc.valueNames.push_back("None, repeat prior value");
c@75 160 desc.valueNames.push_back("Linear interpolation");
c@75 161 list.push_back(desc);
c@75 162
c@35 163 return list;
c@35 164 }
c@35 165
c@35 166 float
c@35 167 CQVamp::getParameter(std::string param) const
c@35 168 {
c@55 169 if (param == "minpitch") {
c@55 170 return m_minMIDIPitch;
c@55 171 }
c@55 172 if (param == "maxpitch") {
c@55 173 return m_maxMIDIPitch;
c@55 174 }
c@55 175 if (param == "tuning") {
c@55 176 return m_tuningFrequency;
c@55 177 }
c@35 178 if (param == "bpo") {
c@35 179 return m_bpo;
c@35 180 }
c@75 181 if (param == "interpolation") {
c@75 182 return (float)m_interpolation;
c@75 183 }
c@35 184 std::cerr << "WARNING: CQVamp::getParameter: unknown parameter \""
c@35 185 << param << "\"" << std::endl;
c@35 186 return 0.0;
c@35 187 }
c@35 188
c@35 189 void
c@35 190 CQVamp::setParameter(std::string param, float value)
c@35 191 {
c@55 192 if (param == "minpitch") {
c@55 193 m_minMIDIPitch = lrintf(value);
c@55 194 } else if (param == "maxpitch") {
c@55 195 m_maxMIDIPitch = lrintf(value);
c@55 196 } else if (param == "tuning") {
c@55 197 m_tuningFrequency = value;
c@75 198 } else if (param == "bpo") {
c@35 199 m_bpo = lrintf(value);
c@75 200 } else if (param == "interpolation") {
c@90 201 m_interpolation = (CQSpectrogram::Interpolation)lrintf(value);
c@35 202 } else {
c@35 203 std::cerr << "WARNING: CQVamp::setParameter: unknown parameter \""
c@35 204 << param << "\"" << std::endl;
c@35 205 }
c@35 206 }
c@35 207
c@35 208 bool
c@35 209 CQVamp::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@35 210 {
c@35 211 if (m_cq) {
c@35 212 delete m_cq;
c@75 213 m_cq = 0;
c@35 214 }
c@35 215
c@35 216 if (channels < getMinChannelCount() ||
c@35 217 channels > getMaxChannelCount()) return false;
c@35 218
c@35 219 m_stepSize = stepSize;
c@35 220 m_blockSize = blockSize;
c@35 221
c@55 222 m_minFrequency = Pitch::getFrequencyForPitch
c@55 223 (m_minMIDIPitch, 0, m_tuningFrequency);
c@55 224 m_maxFrequency = Pitch::getFrequencyForPitch
c@55 225 (m_maxMIDIPitch, 0, m_tuningFrequency);
c@55 226
c@90 227 m_cq = new CQSpectrogram
c@75 228 (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo,
c@75 229 m_interpolation);
c@35 230
c@35 231 return true;
c@35 232 }
c@35 233
c@35 234 void
c@35 235 CQVamp::reset()
c@35 236 {
c@35 237 if (m_cq) {
c@35 238 delete m_cq;
c@90 239 m_cq = new CQSpectrogram
c@75 240 (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo,
c@75 241 m_interpolation);
c@35 242 }
c@36 243 m_prevFeature.clear();
c@53 244 m_haveStartTime = false;
c@53 245 m_columnCount = 0;
c@35 246 }
c@35 247
c@35 248 size_t
c@35 249 CQVamp::getPreferredStepSize() const
c@35 250 {
c@35 251 return 0;
c@35 252 }
c@35 253
c@35 254 size_t
c@35 255 CQVamp::getPreferredBlockSize() const
c@35 256 {
c@35 257 return 0;
c@35 258 }
c@35 259
c@35 260 CQVamp::OutputList
c@35 261 CQVamp::getOutputDescriptors() const
c@35 262 {
c@35 263 OutputList list;
c@35 264
c@35 265 OutputDescriptor d;
c@35 266 d.identifier = "constantq";
c@35 267 d.name = "Constant-Q Spectrogram";
c@35 268 d.unit = "";
c@35 269 d.description = "Output of constant-Q transform, as a single vector per process block";
c@35 270 d.hasFixedBinCount = true;
c@35 271 d.binCount = (m_cq ? m_cq->getTotalBins() : (9 * 24));
c@58 272
c@58 273 if (m_cq) {
c@58 274 char name[20];
c@58 275 for (int i = 0; i < d.binCount; ++i) {
c@58 276 float freq = m_cq->getBinFrequency(i);
c@58 277 sprintf(name, "%.1f Hz", freq);
c@58 278 d.binNames.push_back(name);
c@58 279 }
c@58 280 }
c@58 281
c@35 282 d.hasKnownExtents = false;
c@35 283 d.isQuantized = false;
c@35 284 d.sampleType = OutputDescriptor::FixedSampleRate;
c@35 285 d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256);
c@35 286 list.push_back(d);
c@35 287
c@35 288 return list;
c@35 289 }
c@35 290
c@35 291 CQVamp::FeatureSet
c@35 292 CQVamp::process(const float *const *inputBuffers,
c@53 293 Vamp::RealTime timestamp)
c@35 294 {
c@35 295 if (!m_cq) {
c@35 296 cerr << "ERROR: CQVamp::process: "
c@35 297 << "Plugin has not been initialised"
c@35 298 << endl;
c@35 299 return FeatureSet();
c@35 300 }
c@35 301
c@53 302 if (!m_haveStartTime) {
c@53 303 m_startTime = timestamp;
c@53 304 m_haveStartTime = true;
c@53 305 }
c@53 306
c@35 307 vector<double> data;
c@35 308 for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]);
c@35 309
c@35 310 vector<vector<double> > cqout = m_cq->process(data);
c@36 311 return convertToFeatures(cqout);
c@36 312 }
c@35 313
c@36 314 CQVamp::FeatureSet
c@36 315 CQVamp::getRemainingFeatures()
c@36 316 {
c@90 317 vector<vector<double> > cqout = m_cq->getRemainingOutput();
c@36 318 return convertToFeatures(cqout);
c@36 319 }
c@36 320
c@36 321 CQVamp::FeatureSet
c@36 322 CQVamp::convertToFeatures(const vector<vector<double> > &cqout)
c@36 323 {
c@35 324 FeatureSet returnFeatures;
c@35 325
c@75 326 int width = cqout.size();
c@75 327 int height = m_cq->getTotalBins();
c@35 328
c@75 329 for (int i = 0; i < width; ++i) {
c@36 330
c@75 331 vector<float> column(height, 0.f);
c@75 332 int thisHeight = cqout[i].size();
c@75 333 for (int j = 0; j < thisHeight; ++j) {
c@35 334 column[j] = cqout[i][j];
c@35 335 }
c@36 336
c@58 337 // put low frequencies at the start
c@58 338 std::reverse(column.begin(), column.end());
c@58 339
c@36 340 m_prevFeature = column;
c@35 341
c@35 342 Feature feature;
c@53 343 feature.hasTimestamp = true;
c@53 344 feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime
c@53 345 (m_columnCount * m_cq->getColumnHop() - m_cq->getLatency(),
c@53 346 m_inputSampleRate);
c@35 347 feature.values = column;
c@35 348 feature.label = "";
c@53 349
c@56 350 // cerr << "timestamp = " << feature.timestamp << " (start time = " << m_startTime << ", column count = " << m_columnCount << ", latency = " << m_cq->getLatency() << ", sample rate " << m_inputSampleRate << ")" << endl;
c@53 351
c@53 352 if (feature.timestamp >= m_startTime) {
c@53 353 returnFeatures[0].push_back(feature);
c@53 354 }
c@53 355
c@53 356 ++m_columnCount;
c@35 357 }
c@35 358
c@35 359 return returnFeatures;
c@35 360 }
c@35 361