annotate vamp/CQVamp.cpp @ 55:2a21b4506d7f

Use MIDI pitch values, as in QM Vamp Plugins implementation
author Chris Cannam <c.cannam@qmul.ac.uk>
date Thu, 28 Nov 2013 17:03:57 +0000
parents a25abb7a21c0
children e2b7f7462618
rev   line source
c@35 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@35 2
c@35 3 #include "CQVamp.h"
c@35 4
c@35 5 #include "../cpp-qm-dsp/ConstantQ.h"
c@35 6
c@55 7 #include "qm-dsp/base/Pitch.h"
c@55 8
c@35 9 using std::string;
c@35 10 using std::vector;
c@35 11 using std::cerr;
c@35 12 using std::endl;
c@35 13
c@35 14 CQVamp::CQVamp(float inputSampleRate) :
c@35 15 Vamp::Plugin(inputSampleRate),
c@55 16 m_minMIDIPitch(36),
c@55 17 m_maxMIDIPitch(84),
c@55 18 m_tuningFrequency(440),
c@55 19 m_bpo(24),
c@35 20 m_cq(0),
c@35 21 m_maxFrequency(inputSampleRate/2),
c@35 22 m_minFrequency(46),
c@53 23 m_haveStartTime(false),
c@53 24 m_columnCount(0)
c@35 25 {
c@35 26 }
c@35 27
c@35 28 CQVamp::~CQVamp()
c@35 29 {
c@35 30 delete m_cq;
c@35 31 }
c@35 32
c@35 33 string
c@35 34 CQVamp::getIdentifier() const
c@35 35 {
c@35 36 return "cqvamp";
c@35 37 }
c@35 38
c@35 39 string
c@35 40 CQVamp::getName() const
c@35 41 {
c@35 42 return "Constant-Q Spectrogram";
c@35 43 }
c@35 44
c@35 45 string
c@35 46 CQVamp::getDescription() const
c@35 47 {
c@35 48 return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio";
c@35 49 }
c@35 50
c@35 51 string
c@35 52 CQVamp::getMaker() const
c@35 53 {
c@35 54 return "Queen Mary, University of London";
c@35 55 }
c@35 56
c@35 57 int
c@35 58 CQVamp::getPluginVersion() const
c@35 59 {
c@35 60 return 1;
c@35 61 }
c@35 62
c@35 63 string
c@35 64 CQVamp::getCopyright() const
c@35 65 {
c@35 66 return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2013 QMUL";
c@35 67 }
c@35 68
c@35 69 CQVamp::ParameterList
c@35 70 CQVamp::getParameterDescriptors() const
c@35 71 {
c@35 72 ParameterList list;
c@55 73 /*
c@35 74 ParameterDescriptor desc;
c@35 75 desc.identifier = "minfreq";
c@35 76 desc.name = "Minimum Frequency";
c@35 77 desc.unit = "Hz";
c@35 78 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 79 desc.minValue = 10;
c@35 80 desc.maxValue = m_inputSampleRate/2;
c@35 81 desc.defaultValue = 46;
c@35 82 desc.isQuantized = false;
c@35 83 list.push_back(desc);
c@35 84
c@35 85 desc.identifier = "maxfreq";
c@35 86 desc.name = "Maximum Frequency";
c@35 87 desc.unit = "Hz";
c@35 88 desc.description = "Highest frequency to be included in the constant-Q transform";
c@35 89 desc.minValue = 10;
c@35 90 desc.maxValue = m_inputSampleRate/2;
c@35 91 desc.defaultValue = m_inputSampleRate/2;
c@35 92 desc.isQuantized = false;
c@35 93 list.push_back(desc);
c@55 94 */
c@55 95 ParameterDescriptor desc;
c@55 96 desc.identifier = "minpitch";
c@55 97 desc.name = "Minimum Pitch";
c@55 98 desc.unit = "MIDI units";
c@55 99 desc.description = "MIDI pitch corresponding to the lowest frequency to be included in the constant-Q transform";
c@55 100 desc.minValue = 0;
c@55 101 desc.maxValue = 127;
c@55 102 desc.defaultValue = 36;
c@55 103 desc.isQuantized = true;
c@55 104 desc.quantizeStep = 1;
c@55 105 list.push_back(desc);
c@55 106
c@55 107 desc.identifier = "maxpitch";
c@55 108 desc.name = "Maximum Pitch";
c@55 109 desc.unit = "MIDI units";
c@55 110 desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform";
c@55 111 desc.minValue = 0;
c@55 112 desc.maxValue = 127;
c@55 113 desc.defaultValue = 84;
c@55 114 desc.isQuantized = true;
c@55 115 desc.quantizeStep = 1;
c@55 116 list.push_back(desc);
c@55 117
c@55 118 desc.identifier = "tuning";
c@55 119 desc.name = "Tuning Frequency";
c@55 120 desc.unit = "Hz";
c@55 121 desc.description = "Frequency of concert A";
c@55 122 desc.minValue = 360;
c@55 123 desc.maxValue = 500;
c@55 124 desc.defaultValue = 440;
c@55 125 desc.isQuantized = false;
c@55 126 list.push_back(desc);
c@35 127
c@35 128 desc.identifier = "bpo";
c@35 129 desc.name = "Bins per Octave";
c@35 130 desc.unit = "bins";
c@35 131 desc.description = "Number of constant-Q transform bins per octave";
c@35 132 desc.minValue = 2;
c@35 133 desc.maxValue = 480;
c@35 134 desc.defaultValue = 24;
c@35 135 desc.isQuantized = true;
c@35 136 desc.quantizeStep = 1;
c@35 137 list.push_back(desc);
c@35 138
c@35 139 return list;
c@35 140 }
c@35 141
c@35 142 float
c@35 143 CQVamp::getParameter(std::string param) const
c@35 144 {
c@55 145 if (param == "minpitch") {
c@55 146 return m_minMIDIPitch;
c@55 147 }
c@55 148 if (param == "maxpitch") {
c@55 149 return m_maxMIDIPitch;
c@55 150 }
c@55 151 if (param == "tuning") {
c@55 152 return m_tuningFrequency;
c@55 153 }
c@55 154 /*
c@35 155 if (param == "minfreq") {
c@35 156 return m_minFrequency;
c@35 157 }
c@35 158 if (param == "maxfreq") {
c@35 159 return m_maxFrequency;
c@35 160 }
c@55 161 */
c@35 162 if (param == "bpo") {
c@35 163 return m_bpo;
c@35 164 }
c@35 165 std::cerr << "WARNING: CQVamp::getParameter: unknown parameter \""
c@35 166 << param << "\"" << std::endl;
c@35 167 return 0.0;
c@35 168 }
c@35 169
c@35 170 void
c@35 171 CQVamp::setParameter(std::string param, float value)
c@35 172 {
c@55 173 if (param == "minpitch") {
c@55 174 m_minMIDIPitch = lrintf(value);
c@55 175 } else if (param == "maxpitch") {
c@55 176 m_maxMIDIPitch = lrintf(value);
c@55 177 } else if (param == "tuning") {
c@55 178 m_tuningFrequency = value;
c@55 179 /* if (param == "minfreq") {
c@35 180 m_minFrequency = value;
c@35 181 } else if (param == "maxfreq") {
c@35 182 m_maxFrequency = value;
c@55 183 */
c@55 184 } else if (param == "bpo") {
c@35 185 m_bpo = lrintf(value);
c@35 186 } else {
c@35 187 std::cerr << "WARNING: CQVamp::setParameter: unknown parameter \""
c@35 188 << param << "\"" << std::endl;
c@35 189 }
c@35 190 }
c@35 191
c@35 192 bool
c@35 193 CQVamp::initialise(size_t channels, size_t stepSize, size_t blockSize)
c@35 194 {
c@35 195 if (m_cq) {
c@35 196 delete m_cq;
c@35 197 m_cq = 0;
c@35 198 }
c@35 199
c@35 200 if (channels < getMinChannelCount() ||
c@35 201 channels > getMaxChannelCount()) return false;
c@35 202
c@35 203 m_stepSize = stepSize;
c@35 204 m_blockSize = blockSize;
c@35 205
c@55 206 m_minFrequency = Pitch::getFrequencyForPitch
c@55 207 (m_minMIDIPitch, 0, m_tuningFrequency);
c@55 208 m_maxFrequency = Pitch::getFrequencyForPitch
c@55 209 (m_maxMIDIPitch, 0, m_tuningFrequency);
c@55 210
c@35 211 m_cq = new ConstantQ
c@35 212 (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
c@35 213
c@35 214 return true;
c@35 215 }
c@35 216
c@35 217 void
c@35 218 CQVamp::reset()
c@35 219 {
c@35 220 if (m_cq) {
c@35 221 delete m_cq;
c@35 222 m_cq = new ConstantQ
c@35 223 (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
c@35 224 }
c@36 225 m_prevFeature.clear();
c@53 226 m_haveStartTime = false;
c@53 227 m_columnCount = 0;
c@35 228 }
c@35 229
c@35 230 size_t
c@35 231 CQVamp::getPreferredStepSize() const
c@35 232 {
c@35 233 return 0;
c@35 234 }
c@35 235
c@35 236 size_t
c@35 237 CQVamp::getPreferredBlockSize() const
c@35 238 {
c@35 239 return 0;
c@35 240 }
c@35 241
c@35 242 CQVamp::OutputList
c@35 243 CQVamp::getOutputDescriptors() const
c@35 244 {
c@35 245 OutputList list;
c@35 246
c@35 247 OutputDescriptor d;
c@35 248 d.identifier = "constantq";
c@35 249 d.name = "Constant-Q Spectrogram";
c@35 250 d.unit = "";
c@35 251 d.description = "Output of constant-Q transform, as a single vector per process block";
c@35 252 d.hasFixedBinCount = true;
c@35 253 d.binCount = (m_cq ? m_cq->getTotalBins() : (9 * 24));
c@35 254 d.hasKnownExtents = false;
c@35 255 d.isQuantized = false;
c@35 256 d.sampleType = OutputDescriptor::FixedSampleRate;
c@35 257 d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256);
c@35 258 list.push_back(d);
c@35 259
c@35 260 return list;
c@35 261 }
c@35 262
c@35 263 CQVamp::FeatureSet
c@35 264 CQVamp::process(const float *const *inputBuffers,
c@53 265 Vamp::RealTime timestamp)
c@35 266 {
c@35 267 if (!m_cq) {
c@35 268 cerr << "ERROR: CQVamp::process: "
c@35 269 << "Plugin has not been initialised"
c@35 270 << endl;
c@35 271 return FeatureSet();
c@35 272 }
c@35 273
c@53 274 if (!m_haveStartTime) {
c@53 275 m_startTime = timestamp;
c@53 276 m_haveStartTime = true;
c@53 277 }
c@53 278
c@35 279 vector<double> data;
c@35 280 for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]);
c@35 281
c@35 282 vector<vector<double> > cqout = m_cq->process(data);
c@36 283 return convertToFeatures(cqout);
c@36 284 }
c@35 285
c@36 286 CQVamp::FeatureSet
c@36 287 CQVamp::getRemainingFeatures()
c@36 288 {
c@36 289 vector<vector<double> > cqout = m_cq->getRemainingBlocks();
c@36 290 return convertToFeatures(cqout);
c@36 291 }
c@36 292
c@36 293 CQVamp::FeatureSet
c@36 294 CQVamp::convertToFeatures(const vector<vector<double> > &cqout)
c@36 295 {
c@35 296 FeatureSet returnFeatures;
c@35 297
c@36 298 for (int i = 0; i < (int)cqout.size(); ++i) {
c@35 299
c@35 300 vector<float> column(m_cq->getTotalBins(), 0.f);
c@36 301
c@36 302 for (int j = 0; j < (int)cqout[i].size(); ++j) {
c@35 303 column[j] = cqout[i][j];
c@35 304 }
c@36 305 for (int j = cqout[i].size(); j < m_cq->getTotalBins(); ++j) {
c@36 306 if (j < (int)m_prevFeature.size()) {
c@36 307 column[j] = m_prevFeature[j];
c@36 308 }
c@36 309 }
c@36 310
c@36 311 m_prevFeature = column;
c@35 312
c@35 313 Feature feature;
c@53 314 feature.hasTimestamp = true;
c@53 315 feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime
c@53 316 (m_columnCount * m_cq->getColumnHop() - m_cq->getLatency(),
c@53 317 m_inputSampleRate);
c@35 318 feature.values = column;
c@35 319 feature.label = "";
c@53 320
c@55 321 // cerr << "timestamp = " << feature.timestamp << " (latency = " << m_cq->getLatency() << ", sample rate " << m_inputSampleRate << ")" << endl;
c@53 322
c@53 323 if (feature.timestamp >= m_startTime) {
c@53 324 returnFeatures[0].push_back(feature);
c@53 325 }
c@53 326
c@53 327 ++m_columnCount;
c@35 328 }
c@35 329
c@35 330 return returnFeatures;
c@35 331 }
c@35 332