annotate examples/PercussionOnsetDetector.cpp @ 415:1522e2f6d700

Fix handling of output sample rate in buffering adapter in case where SampleType is Fixed but no sample rate provided (which is invalid behaviour from the plugin, but we might as well do the right thing with it)
author Chris Cannam
date Fri, 04 Sep 2015 13:48:28 +0100
parents 06cfedd040c7
children
rev   line source
cannam@35 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@35 2
cannam@35 3 /*
cannam@35 4 Vamp
cannam@35 5
cannam@35 6 An API for audio analysis and feature extraction plugins.
cannam@35 7
cannam@35 8 Centre for Digital Music, Queen Mary, University of London.
cannam@35 9 Copyright 2006 Chris Cannam.
cannam@35 10
cannam@35 11 Permission is hereby granted, free of charge, to any person
cannam@35 12 obtaining a copy of this software and associated documentation
cannam@35 13 files (the "Software"), to deal in the Software without
cannam@35 14 restriction, including without limitation the rights to use, copy,
cannam@35 15 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@35 16 of the Software, and to permit persons to whom the Software is
cannam@35 17 furnished to do so, subject to the following conditions:
cannam@35 18
cannam@35 19 The above copyright notice and this permission notice shall be
cannam@35 20 included in all copies or substantial portions of the Software.
cannam@35 21
cannam@35 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@35 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@35 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@35 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@35 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@35 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@35 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@35 29
cannam@35 30 Except as contained in this notice, the names of the Centre for
cannam@35 31 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@35 32 shall not be used in advertising or otherwise to promote the sale,
cannam@35 33 use or other dealings in this Software without prior written
cannam@35 34 authorization.
cannam@35 35 */
cannam@35 36
cannam@35 37 #include "PercussionOnsetDetector.h"
cannam@35 38
cannam@35 39 using std::string;
cannam@35 40 using std::vector;
cannam@35 41 using std::cerr;
cannam@35 42 using std::endl;
cannam@35 43
cannam@35 44 #include <cmath>
cannam@35 45
cannam@35 46
cannam@35 47 PercussionOnsetDetector::PercussionOnsetDetector(float inputSampleRate) :
cannam@35 48 Plugin(inputSampleRate),
cannam@35 49 m_stepSize(0),
cannam@35 50 m_blockSize(0),
cannam@35 51 m_threshold(3),
cannam@35 52 m_sensitivity(40),
cannam@35 53 m_priorMagnitudes(0),
cannam@35 54 m_dfMinus1(0),
cannam@35 55 m_dfMinus2(0)
cannam@35 56 {
cannam@35 57 }
cannam@35 58
cannam@35 59 PercussionOnsetDetector::~PercussionOnsetDetector()
cannam@35 60 {
cannam@35 61 delete[] m_priorMagnitudes;
cannam@35 62 }
cannam@35 63
cannam@35 64 string
cannam@49 65 PercussionOnsetDetector::getIdentifier() const
cannam@35 66 {
cannam@35 67 return "percussiononsets";
cannam@35 68 }
cannam@35 69
cannam@35 70 string
cannam@49 71 PercussionOnsetDetector::getName() const
cannam@49 72 {
cannam@49 73 return "Simple Percussion Onset Detector";
cannam@49 74 }
cannam@49 75
cannam@49 76 string
cannam@35 77 PercussionOnsetDetector::getDescription() const
cannam@35 78 {
cannam@49 79 return "Detect percussive note onsets by identifying broadband energy rises";
cannam@35 80 }
cannam@35 81
cannam@35 82 string
cannam@35 83 PercussionOnsetDetector::getMaker() const
cannam@35 84 {
cannam@43 85 return "Vamp SDK Example Plugins";
cannam@35 86 }
cannam@35 87
cannam@35 88 int
cannam@35 89 PercussionOnsetDetector::getPluginVersion() const
cannam@35 90 {
cannam@35 91 return 2;
cannam@35 92 }
cannam@35 93
cannam@35 94 string
cannam@35 95 PercussionOnsetDetector::getCopyright() const
cannam@35 96 {
cannam@35 97 return "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005. Freely redistributable (BSD license)";
cannam@35 98 }
cannam@35 99
cannam@35 100 size_t
cannam@35 101 PercussionOnsetDetector::getPreferredStepSize() const
cannam@35 102 {
cannam@35 103 return 0;
cannam@35 104 }
cannam@35 105
cannam@35 106 size_t
cannam@35 107 PercussionOnsetDetector::getPreferredBlockSize() const
cannam@35 108 {
cannam@35 109 return 1024;
cannam@35 110 }
cannam@35 111
cannam@35 112 bool
cannam@35 113 PercussionOnsetDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@35 114 {
cannam@35 115 if (channels < getMinChannelCount() ||
cannam@35 116 channels > getMaxChannelCount()) return false;
cannam@35 117
cannam@35 118 m_stepSize = stepSize;
cannam@35 119 m_blockSize = blockSize;
cannam@35 120
cannam@35 121 m_priorMagnitudes = new float[m_blockSize/2];
cannam@35 122
cannam@35 123 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@35 124 m_priorMagnitudes[i] = 0.f;
cannam@35 125 }
cannam@35 126
cannam@35 127 m_dfMinus1 = 0.f;
cannam@35 128 m_dfMinus2 = 0.f;
cannam@35 129
cannam@35 130 return true;
cannam@35 131 }
cannam@35 132
cannam@35 133 void
cannam@35 134 PercussionOnsetDetector::reset()
cannam@35 135 {
cannam@35 136 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@35 137 m_priorMagnitudes[i] = 0.f;
cannam@35 138 }
cannam@35 139
cannam@35 140 m_dfMinus1 = 0.f;
cannam@35 141 m_dfMinus2 = 0.f;
cannam@35 142 }
cannam@35 143
cannam@35 144 PercussionOnsetDetector::ParameterList
cannam@35 145 PercussionOnsetDetector::getParameterDescriptors() const
cannam@35 146 {
cannam@35 147 ParameterList list;
cannam@35 148
cannam@35 149 ParameterDescriptor d;
cannam@49 150 d.identifier = "threshold";
cannam@49 151 d.name = "Energy rise threshold";
cannam@49 152 d.description = "Energy rise within a frequency bin necessary to count toward broadband total";
cannam@35 153 d.unit = "dB";
cannam@35 154 d.minValue = 0;
cannam@35 155 d.maxValue = 20;
cannam@35 156 d.defaultValue = 3;
cannam@35 157 d.isQuantized = false;
cannam@35 158 list.push_back(d);
cannam@35 159
cannam@49 160 d.identifier = "sensitivity";
cannam@49 161 d.name = "Sensitivity";
cannam@49 162 d.description = "Sensitivity of peak detector applied to broadband detection function";
cannam@35 163 d.unit = "%";
cannam@35 164 d.minValue = 0;
cannam@35 165 d.maxValue = 100;
cannam@35 166 d.defaultValue = 40;
cannam@35 167 d.isQuantized = false;
cannam@35 168 list.push_back(d);
cannam@35 169
cannam@35 170 return list;
cannam@35 171 }
cannam@35 172
cannam@35 173 float
cannam@49 174 PercussionOnsetDetector::getParameter(std::string id) const
cannam@35 175 {
cannam@49 176 if (id == "threshold") return m_threshold;
cannam@49 177 if (id == "sensitivity") return m_sensitivity;
cannam@35 178 return 0.f;
cannam@35 179 }
cannam@35 180
cannam@35 181 void
cannam@49 182 PercussionOnsetDetector::setParameter(std::string id, float value)
cannam@35 183 {
cannam@49 184 if (id == "threshold") {
cannam@35 185 if (value < 0) value = 0;
cannam@35 186 if (value > 20) value = 20;
cannam@35 187 m_threshold = value;
cannam@49 188 } else if (id == "sensitivity") {
cannam@35 189 if (value < 0) value = 0;
cannam@35 190 if (value > 100) value = 100;
cannam@35 191 m_sensitivity = value;
cannam@35 192 }
cannam@35 193 }
cannam@35 194
cannam@35 195 PercussionOnsetDetector::OutputList
cannam@35 196 PercussionOnsetDetector::getOutputDescriptors() const
cannam@35 197 {
cannam@35 198 OutputList list;
cannam@35 199
cannam@35 200 OutputDescriptor d;
cannam@49 201 d.identifier = "onsets";
cannam@49 202 d.name = "Onsets";
cannam@49 203 d.description = "Percussive note onset locations";
cannam@35 204 d.unit = "";
cannam@35 205 d.hasFixedBinCount = true;
cannam@35 206 d.binCount = 0;
cannam@35 207 d.hasKnownExtents = false;
cannam@35 208 d.isQuantized = false;
cannam@35 209 d.sampleType = OutputDescriptor::VariableSampleRate;
cannam@35 210 d.sampleRate = m_inputSampleRate;
cannam@35 211 list.push_back(d);
cannam@35 212
cannam@49 213 d.identifier = "detectionfunction";
cannam@49 214 d.name = "Detection Function";
cannam@49 215 d.description = "Broadband energy rise detection function";
cannam@35 216 d.binCount = 1;
cannam@35 217 d.isQuantized = true;
cannam@35 218 d.quantizeStep = 1.0;
cannam@35 219 d.sampleType = OutputDescriptor::OneSamplePerStep;
cannam@35 220 list.push_back(d);
cannam@35 221
cannam@35 222 return list;
cannam@35 223 }
cannam@35 224
cannam@35 225 PercussionOnsetDetector::FeatureSet
cannam@47 226 PercussionOnsetDetector::process(const float *const *inputBuffers,
cannam@47 227 Vamp::RealTime ts)
cannam@35 228 {
cannam@35 229 if (m_stepSize == 0) {
cannam@35 230 cerr << "ERROR: PercussionOnsetDetector::process: "
cannam@35 231 << "PercussionOnsetDetector has not been initialised"
cannam@35 232 << endl;
cannam@35 233 return FeatureSet();
cannam@35 234 }
cannam@35 235
cannam@35 236 int count = 0;
cannam@35 237
cannam@35 238 for (size_t i = 1; i < m_blockSize/2; ++i) {
cannam@35 239
cannam@35 240 float real = inputBuffers[0][i*2];
cannam@35 241 float imag = inputBuffers[0][i*2 + 1];
cannam@82 242
cannam@35 243 float sqrmag = real * real + imag * imag;
cannam@35 244
cannam@35 245 if (m_priorMagnitudes[i] > 0.f) {
cannam@35 246 float diff = 10.f * log10f(sqrmag / m_priorMagnitudes[i]);
cannam@35 247
cannam@270 248 // std::cout << "i=" << i << ", sqrmag=" << sqrmag << ", prior=" << m_priorMagnitudes[i] << ", diff=" << diff << ", threshold=" << m_threshold << " " << (diff >= m_threshold ? "[*]" : "") << std::endl;
cannam@35 249
cannam@35 250 if (diff >= m_threshold) ++count;
cannam@35 251 }
cannam@35 252
cannam@35 253 m_priorMagnitudes[i] = sqrmag;
cannam@35 254 }
cannam@35 255
cannam@35 256 FeatureSet returnFeatures;
cannam@35 257
cannam@35 258 Feature detectionFunction;
cannam@35 259 detectionFunction.hasTimestamp = false;
cannam@35 260 detectionFunction.values.push_back(count);
cannam@35 261 returnFeatures[1].push_back(detectionFunction);
cannam@35 262
cannam@35 263 if (m_dfMinus2 < m_dfMinus1 &&
cannam@35 264 m_dfMinus1 >= count &&
cannam@37 265 m_dfMinus1 > ((100 - m_sensitivity) * m_blockSize) / 200) {
cannam@35 266
cannam@270 267 //std::cout << "result at " << ts << "! (count == " << count << ", prev == " << m_dfMinus1 << ")" << std::endl;
cannam@270 268
cannam@35 269 Feature onset;
cannam@35 270 onset.hasTimestamp = true;
cannam@35 271 onset.timestamp = ts - Vamp::RealTime::frame2RealTime
cannam@210 272 (m_stepSize, int(m_inputSampleRate + 0.5));
cannam@35 273 returnFeatures[0].push_back(onset);
cannam@35 274 }
cannam@35 275
cannam@35 276 m_dfMinus2 = m_dfMinus1;
cannam@35 277 m_dfMinus1 = count;
cannam@35 278
cannam@35 279 return returnFeatures;
cannam@35 280 }
cannam@35 281
cannam@35 282 PercussionOnsetDetector::FeatureSet
cannam@35 283 PercussionOnsetDetector::getRemainingFeatures()
cannam@35 284 {
cannam@35 285 return FeatureSet();
cannam@35 286 }
cannam@35 287