To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/cannam/constant-q-cpp/ .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Revision:

root / vamp / CQVamp.cpp

History | View | Annotate | Download (14.2 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
3
    Constant-Q library
4
    Copyright (c) 2013-2014 Queen Mary, University of London
5

6
    Permission is hereby granted, free of charge, to any person
7
    obtaining a copy of this software and associated documentation
8
    files (the "Software"), to deal in the Software without
9
    restriction, including without limitation the rights to use, copy,
10
    modify, merge, publish, distribute, sublicense, and/or sell copies
11
    of the Software, and to permit persons to whom the Software is
12
    furnished to do so, subject to the following conditions:
13

14
    The above copyright notice and this permission notice shall be
15
    included in all copies or substantial portions of the Software.
16

17
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22
    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24

25
    Except as contained in this notice, the names of the Centre for
26
    Digital Music; Queen Mary, University of London; and Chris Cannam
27
    shall not be used in advertising or otherwise to promote the sale,
28
    use or other dealings in this Software without prior written
29
    authorization.
30
*/
31

    
32
#include "CQVamp.h"
33

    
34
#include "Pitch.h"
35

    
36
#include <algorithm>
37
#include <cstdio>
38

    
39
using std::string;
40
using std::vector;
41
using std::cerr;
42
using std::endl;
43

    
44
// The plugin offers either MIDI pitch or frequency range parameters,
45
// depending on the midiPitchParameters option given to the
46
// constructor. It never offers both. So they can have different
47
// defaults; if we're using MIDI pitch, the min and max frequencies
48
// will come from those rather than from the m_minFrequency and
49
// m_maxFrequency members.
50
static const int defaultMinMIDIPitch = 36;
51
static const int defaultMaxMIDIPitch = 96;
52
static const int defaultBPO = 36;
53
static const float defaultMinFrequency = 110;
54
static const float defaultMaxFrequency = 14700;
55
static const float defaultTuningFrequency = 440.f;
56

    
57
CQVamp::CQVamp(float inputSampleRate, bool midiPitchParameters) :
58
    Vamp::Plugin(inputSampleRate),
59
    m_midiPitchParameters(midiPitchParameters),
60
    m_minMIDIPitch(defaultMinMIDIPitch),
61
    m_maxMIDIPitch(defaultMaxMIDIPitch),
62
    m_tuningFrequency(defaultTuningFrequency),
63
    m_bpo(defaultBPO),
64
    m_atomOverlap(4),
65
    m_useDraftDecimator(false),
66
    m_interpolation(CQSpectrogram::InterpolateLinear),
67
    m_cq(0),
68
    m_maxFrequency(defaultMaxFrequency),
69
    m_minFrequency(defaultMinFrequency),
70
    m_haveStartTime(false),
71
    m_columnCount(0)
72
{
73
}
74

    
75
CQVamp::~CQVamp()
76
{
77
    delete m_cq;
78
}
79

    
80
string
81
CQVamp::getIdentifier() const
82
{
83
    if (m_midiPitchParameters) {
84
        return "cqvampmidi";
85
    } else {
86
        return "cqvamp";
87
    }
88
}
89

    
90
string
91
CQVamp::getName() const
92
{
93
    if (m_midiPitchParameters) {
94
        return "CQ Constant-Q Spectrogram (MIDI pitch range)";
95
    } else {
96
        return "CQ Constant-Q Spectrogram (Hz range)";
97
    }
98
}
99

    
100
string
101
CQVamp::getDescription() const
102
{
103
    if (m_midiPitchParameters) {
104
        return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio, specifying the frequency range in MIDI pitch units.";
105
    } else {
106
        return "Extract a spectrogram with constant ratio of centre frequency to resolution from the input audio, specifying the frequency range in Hz.";
107
    }
108
}
109

    
110
string
111
CQVamp::getMaker() const
112
{
113
    return "Queen Mary, University of London";
114
}
115

    
116
int
117
CQVamp::getPluginVersion() const
118
{
119
    return 3;
120
}
121

    
122
string
123
CQVamp::getCopyright() const
124
{
125
    return "Plugin by Chris Cannam. Method by Christian Schörkhuber and Anssi Klapuri. Copyright (c) 2015-2017 QMUL. BSD/MIT licence.";
126
}
127

    
128
CQVamp::ParameterList
129
CQVamp::getParameterDescriptors() const
130
{
131
    ParameterList list;
132

    
133
    ParameterDescriptor desc;
134

    
135
    if (m_midiPitchParameters) {
136

    
137
        desc.identifier = "minpitch";
138
        desc.name = "Minimum Pitch";
139
        desc.unit = "MIDI units";
140
        desc.description = "MIDI pitch corresponding to the lowest frequency to be included in the constant-Q transform. (The actual minimum frequency may be lower, as the range always covers an integral number of octaves below the highest frequency.)";
141
        desc.minValue = 0;
142
        desc.maxValue = 127;
143
        desc.defaultValue = defaultMinMIDIPitch;
144
        desc.isQuantized = true;
145
        desc.quantizeStep = 1;
146
        list.push_back(desc);
147

    
148
        desc.identifier = "maxpitch";
149
        desc.name = "Maximum Pitch";
150
        desc.unit = "MIDI units";
151
        desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform";
152
        desc.minValue = 0;
153
        desc.maxValue = 127;
154
        desc.defaultValue = defaultMaxMIDIPitch;
155
        desc.isQuantized = true;
156
        desc.quantizeStep = 1;
157
        list.push_back(desc);
158

    
159
        desc.identifier = "tuning";
160
        desc.name = "Tuning Frequency";
161
        desc.unit = "Hz";
162
        desc.description = "Frequency of concert A";
163
        desc.minValue = 360;
164
        desc.maxValue = 500;
165
        desc.defaultValue = defaultTuningFrequency;
166
        desc.isQuantized = false;
167
        list.push_back(desc);
168

    
169
    } else {
170

    
171
        desc.identifier = "minfreq";
172
        desc.name = "Minimum Frequency";
173
        desc.unit = "Hz";
174
        desc.description = "Lowest frequency to be included in the constant-Q transform. (The actual minimum frequency may be lower, as the range always covers an integral number of octaves below the highest frequency.)";
175
        desc.minValue = 1;
176
        desc.maxValue = 22050;
177
        desc.defaultValue = defaultMinFrequency;
178
        desc.isQuantized = false;
179
        list.push_back(desc);
180

    
181
        desc.identifier = "maxfreq";
182
        desc.name = "Maximum Frequency";
183
        desc.unit = "Hz";
184
        desc.description = "MIDI pitch corresponding to the highest frequency to be included in the constant-Q transform";
185
        desc.minValue = 1;
186
        desc.maxValue = 22050;
187
        desc.defaultValue = defaultMaxFrequency;
188
        desc.isQuantized = false;
189
        list.push_back(desc);
190
    }
191
    
192
    desc.identifier = "bpo";
193
    desc.name = "Bins per Octave";
194
    desc.unit = "bins";
195
    desc.description = "Number of constant-Q transform bins per octave";
196
    desc.minValue = 2;
197
    desc.maxValue = 480;
198
    desc.defaultValue = defaultBPO;
199
    desc.isQuantized = true;
200
    desc.quantizeStep = 1;
201
    list.push_back(desc);
202

    
203
    desc.identifier = "atomoverlap";
204
    desc.name = "Overlap";
205
    desc.unit = "";
206
    desc.description = "Overlap factor for CQ kernel atoms (higher = more output values per unit time)";
207
    desc.minValue = 1;
208
    desc.maxValue = 8;
209
    desc.defaultValue = 4;
210
    desc.isQuantized = true;
211
    desc.quantizeStep = 1;
212
    list.push_back(desc);
213

    
214
    desc.identifier = "draftdecimator";
215
    desc.name = "Use Draft Decimator";
216
    desc.unit = "";
217
    desc.description = "Trade off some decimator quality for faster speed";
218
    desc.minValue = 0;
219
    desc.maxValue = 1;
220
    desc.defaultValue = 0;
221
    desc.isQuantized = true;
222
    desc.quantizeStep = 1;
223
    list.push_back(desc);
224
    
225
    desc.identifier = "interpolation";
226
    desc.name = "Interpolation";
227
    desc.unit = "";
228
    desc.description = "Interpolation method used to fill empty cells in lower octaves";
229
    desc.minValue = 0;
230
    desc.maxValue = 2;
231
    desc.defaultValue = 2;
232
    desc.isQuantized = true;
233
    desc.quantizeStep = 1;
234
    desc.valueNames.push_back("None, leave as zero");
235
    desc.valueNames.push_back("None, repeat prior value");
236
    desc.valueNames.push_back("Linear interpolation");
237
    list.push_back(desc);
238

    
239
    return list;
240
}
241

    
242
float
243
CQVamp::getParameter(std::string param) const
244
{
245
    if (param == "minpitch" && m_midiPitchParameters) {
246
        return m_minMIDIPitch;
247
    }
248
    if (param == "maxpitch" && m_midiPitchParameters) {
249
        return m_maxMIDIPitch;
250
    }
251
    if (param == "tuning" && m_midiPitchParameters) {
252
        return m_tuningFrequency;
253
    }
254
    if (param == "bpo") {
255
        return m_bpo;
256
    }
257
    if (param == "interpolation") {
258
        return (float)m_interpolation;
259
    }
260
    if (param == "minfreq" && !m_midiPitchParameters) {
261
        return m_minFrequency;
262
    }
263
    if (param == "maxfreq" && !m_midiPitchParameters) {
264
        return m_maxFrequency;
265
    }
266
    if (param == "atomoverlap") {
267
        return m_atomOverlap;
268
    }
269
    if (param == "draftdecimator") {
270
        return m_useDraftDecimator ? 1.f : 0.f;
271
    }
272
    std::cerr << "WARNING: CQVamp::getParameter: unknown parameter \""
273
              << param << "\"" << std::endl;
274
    return 0.0;
275
}
276

    
277
void
278
CQVamp::setParameter(std::string param, float value)
279
{
280
    if (param == "minpitch" && m_midiPitchParameters) {
281
        m_minMIDIPitch = int(value + 0.5f);
282
    } else if (param == "maxpitch" && m_midiPitchParameters) {
283
        m_maxMIDIPitch = int(value + 0.5f);
284
    } else if (param == "tuning" && m_midiPitchParameters) {
285
        m_tuningFrequency = value;
286
    } else if (param == "bpo") {
287
        m_bpo = int(value + 0.5f);
288
    } else if (param == "interpolation") {
289
        m_interpolation = (CQSpectrogram::Interpolation)int(value + 0.5f);
290
    } else if (param == "minfreq" && !m_midiPitchParameters) {
291
        m_minFrequency = value;
292
    } else if (param == "maxfreq" && !m_midiPitchParameters) {
293
        m_maxFrequency = value;
294
    } else if (param == "atomoverlap") {
295
        m_atomOverlap = int(value + 0.5f);
296
    } else if (param == "draftdecimator") {
297
        m_useDraftDecimator = (value > 0.5f);
298
    } else {
299
        std::cerr << "WARNING: CQVamp::setParameter: unknown parameter \""
300
                  << param << "\"" << std::endl;
301
    }
302
}
303

    
304
bool
305
CQVamp::initialise(size_t channels, size_t stepSize, size_t blockSize)
306
{
307
    if (m_cq) {
308
        delete m_cq;
309
        m_cq = 0;
310
    }
311

    
312
    if (channels < getMinChannelCount() ||
313
        channels > getMaxChannelCount()) return false;
314

    
315
    m_stepSize = stepSize;
316
    m_blockSize = blockSize;
317

    
318
    if (m_midiPitchParameters) {
319
        m_minFrequency = Pitch::getFrequencyForPitch
320
            (m_minMIDIPitch, 0, m_tuningFrequency);
321
        m_maxFrequency = Pitch::getFrequencyForPitch
322
            (m_maxMIDIPitch, 0, m_tuningFrequency);
323
    }
324

    
325
    reset();
326

    
327
    if (!m_cq || !m_cq->isValid()) {
328
        cerr << "CQVamp::initialise: Constant-Q parameters not valid! Not initialising" << endl;
329
        return false;
330
    }
331

    
332
    return true;
333
}
334

    
335
void
336
CQVamp::reset()
337
{
338
    delete m_cq;
339
    CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
340
    p.atomHopFactor = 1.0 / double(m_atomOverlap);
341
    p.decimator = (m_useDraftDecimator ?
342
                   CQParameters::FasterDecimator :
343
                   CQParameters::BetterDecimator);
344
    m_cq = new CQSpectrogram(p, m_interpolation);
345
    m_haveStartTime = false;
346
    m_columnCount = 0;
347
}
348

    
349
size_t
350
CQVamp::getPreferredStepSize() const
351
{
352
    return 0;
353
}
354

    
355
size_t
356
CQVamp::getPreferredBlockSize() const
357
{
358
    return 0;
359
}
360

    
361
std::string
362
CQVamp::noteName(int i) const
363
{
364
    static const char *names[] = {
365
        "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
366
    };
367

    
368
    const char *n = names[i % 12];
369
    int oct = i / 12 - 1;
370
    char buf[20];
371
    sprintf(buf, "%d %s%d", i, n, oct);
372

    
373
    return buf;
374
}
375

    
376
CQVamp::OutputList
377
CQVamp::getOutputDescriptors() const
378
{
379
    OutputList list;
380

    
381
    OutputDescriptor d;
382
    d.identifier = "constantq";
383
    d.name = "Constant-Q Spectrogram";
384
    d.unit = "";
385
    d.description = "Output of constant-Q transform, as a single vector per process block";
386
    d.hasFixedBinCount = true;
387
    d.binCount = (m_cq ? m_cq->getTotalBins() : (9 * 24));
388

    
389
    if (m_cq) {
390
        char name[20];
391
        for (int i = 0; i < (int)d.binCount; ++i) {
392
            float freq = m_cq->getBinFrequency(d.binCount - i - 1);
393
            sprintf(name, "%.1f Hz", freq);
394
            int note = Pitch::getPitchForFrequency(freq, 0, m_tuningFrequency);
395
            float nearestFreq =
396
                Pitch::getFrequencyForPitch(note, 0, m_tuningFrequency);
397
            if (fabs(freq - nearestFreq) < 0.01) {
398
                d.binNames.push_back(name + std::string(" ") + noteName(note));
399
            } else {
400
                d.binNames.push_back(name);
401
            }
402
        }
403
    }
404

    
405
    d.hasKnownExtents = false;
406
    d.isQuantized = false;
407
    d.sampleType = OutputDescriptor::FixedSampleRate;
408
    d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256);
409
    list.push_back(d);
410

    
411
    return list;
412
}
413

    
414
CQVamp::FeatureSet
415
CQVamp::process(const float *const *inputBuffers,
416
                Vamp::RealTime timestamp)
417
{
418
    if (!m_cq) {
419
        cerr << "ERROR: CQVamp::process: "
420
             << "Plugin has not been initialised"
421
             << endl;
422
        return FeatureSet();
423
    }
424

    
425
    if (!m_haveStartTime) {
426
        m_startTime = timestamp;
427
        m_haveStartTime = true;
428
    }
429

    
430
    vector<double> data;
431
    for (int i = 0; i < m_blockSize; ++i) data.push_back(inputBuffers[0][i]);
432
    
433
    vector<vector<double> > cqout = m_cq->process(data);
434
    return convertToFeatures(cqout);
435
}
436

    
437
CQVamp::FeatureSet
438
CQVamp::getRemainingFeatures()
439
{
440
    vector<vector<double> > cqout = m_cq->getRemainingOutput();
441
    return convertToFeatures(cqout);
442
}
443

    
444
CQVamp::FeatureSet
445
CQVamp::convertToFeatures(const vector<vector<double> > &cqout)
446
{
447
    FeatureSet returnFeatures;
448

    
449
    int width = cqout.size();
450
    int height = m_cq->getTotalBins();
451

    
452
    for (int i = 0; i < width; ++i) {
453

    
454
        vector<float> column(height, 0.f);
455
        int thisHeight = cqout[i].size();
456
        for (int j = 0; j < thisHeight; ++j) {
457
            column[j] = cqout[i][j];
458
        }
459

    
460
        // put low frequencies at the start
461
        std::reverse(column.begin(), column.end());
462

    
463
        Feature feature;
464
        feature.hasTimestamp = true;
465
        feature.timestamp = m_startTime + Vamp::RealTime::frame2RealTime
466
            (m_columnCount * m_cq->getColumnHop() - m_cq->getLatency(),
467
             m_inputSampleRate);
468
        feature.values = column;
469
        feature.label = "";
470

    
471
//        cerr << "timestamp = " << feature.timestamp << " (start time = " << m_startTime << ", column count = " << m_columnCount << ", latency = " << m_cq->getLatency() << ", sample rate " << m_inputSampleRate << ")" << endl;
472

    
473
        if (feature.timestamp >= m_startTime) {
474
            returnFeatures[0].push_back(feature);
475
        }
476

    
477
        ++m_columnCount;
478
    }
479

    
480
    return returnFeatures;
481
}
482