annotate vamp/CQVamp.cpp @ 58:daf7c92058da

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