PercussionOnsetDetector.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Vamp
00005 
00006     An API for audio analysis and feature extraction plugins.
00007 
00008     Centre for Digital Music, Queen Mary, University of London.
00009     Copyright 2006 Chris Cannam.
00010   
00011     Permission is hereby granted, free of charge, to any person
00012     obtaining a copy of this software and associated documentation
00013     files (the "Software"), to deal in the Software without
00014     restriction, including without limitation the rights to use, copy,
00015     modify, merge, publish, distribute, sublicense, and/or sell copies
00016     of the Software, and to permit persons to whom the Software is
00017     furnished to do so, subject to the following conditions:
00018 
00019     The above copyright notice and this permission notice shall be
00020     included in all copies or substantial portions of the Software.
00021 
00022     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00023     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00024     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00025     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
00026     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00027     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00028     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00029 
00030     Except as contained in this notice, the names of the Centre for
00031     Digital Music; Queen Mary, University of London; and Chris Cannam
00032     shall not be used in advertising or otherwise to promote the sale,
00033     use or other dealings in this Software without prior written
00034     authorization.
00035 */
00036 
00037 #include "PercussionOnsetDetector.h"
00038 
00039 using std::string;
00040 using std::vector;
00041 using std::cerr;
00042 using std::endl;
00043 
00044 #include <cmath>
00045 
00046 
00047 PercussionOnsetDetector::PercussionOnsetDetector(float inputSampleRate) :
00048     Plugin(inputSampleRate),
00049     m_stepSize(0),
00050     m_blockSize(0),
00051     m_threshold(3),
00052     m_sensitivity(40),
00053     m_priorMagnitudes(0),
00054     m_dfMinus1(0),
00055     m_dfMinus2(0)
00056 {
00057 }
00058 
00059 PercussionOnsetDetector::~PercussionOnsetDetector()
00060 {
00061     delete[] m_priorMagnitudes;
00062 }
00063 
00064 string
00065 PercussionOnsetDetector::getIdentifier() const
00066 {
00067     return "percussiononsets";
00068 }
00069 
00070 string
00071 PercussionOnsetDetector::getName() const
00072 {
00073     return "Simple Percussion Onset Detector";
00074 }
00075 
00076 string
00077 PercussionOnsetDetector::getDescription() const
00078 {
00079     return "Detect percussive note onsets by identifying broadband energy rises";
00080 }
00081 
00082 string
00083 PercussionOnsetDetector::getMaker() const
00084 {
00085     return "Vamp SDK Example Plugins";
00086 }
00087 
00088 int
00089 PercussionOnsetDetector::getPluginVersion() const
00090 {
00091     return 2;
00092 }
00093 
00094 string
00095 PercussionOnsetDetector::getCopyright() const
00096 {
00097     return "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005.  Freely redistributable (BSD license)";
00098 }
00099 
00100 size_t
00101 PercussionOnsetDetector::getPreferredStepSize() const
00102 {
00103     return 0;
00104 }
00105 
00106 size_t
00107 PercussionOnsetDetector::getPreferredBlockSize() const
00108 {
00109     return 1024;
00110 }
00111 
00112 bool
00113 PercussionOnsetDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
00114 {
00115     if (channels < getMinChannelCount() ||
00116         channels > getMaxChannelCount()) return false;
00117 
00118     m_stepSize = stepSize;
00119     m_blockSize = blockSize;
00120 
00121     m_priorMagnitudes = new float[m_blockSize/2];
00122 
00123     for (size_t i = 0; i < m_blockSize/2; ++i) {
00124         m_priorMagnitudes[i] = 0.f;
00125     }
00126 
00127     m_dfMinus1 = 0.f;
00128     m_dfMinus2 = 0.f;
00129 
00130     return true;
00131 }
00132 
00133 void
00134 PercussionOnsetDetector::reset()
00135 {
00136     for (size_t i = 0; i < m_blockSize/2; ++i) {
00137         m_priorMagnitudes[i] = 0.f;
00138     }
00139 
00140     m_dfMinus1 = 0.f;
00141     m_dfMinus2 = 0.f;
00142 }
00143 
00144 PercussionOnsetDetector::ParameterList
00145 PercussionOnsetDetector::getParameterDescriptors() const
00146 {
00147     ParameterList list;
00148 
00149     ParameterDescriptor d;
00150     d.identifier = "threshold";
00151     d.name = "Energy rise threshold";
00152     d.description = "Energy rise within a frequency bin necessary to count toward broadband total";
00153     d.unit = "dB";
00154     d.minValue = 0;
00155     d.maxValue = 20;
00156     d.defaultValue = 3;
00157     d.isQuantized = false;
00158     list.push_back(d);
00159 
00160     d.identifier = "sensitivity";
00161     d.name = "Sensitivity";
00162     d.description = "Sensitivity of peak detector applied to broadband detection function";
00163     d.unit = "%";
00164     d.minValue = 0;
00165     d.maxValue = 100;
00166     d.defaultValue = 40;
00167     d.isQuantized = false;
00168     list.push_back(d);
00169 
00170     return list;
00171 }
00172 
00173 float
00174 PercussionOnsetDetector::getParameter(std::string id) const
00175 {
00176     if (id == "threshold") return m_threshold;
00177     if (id == "sensitivity") return m_sensitivity;
00178     return 0.f;
00179 }
00180 
00181 void
00182 PercussionOnsetDetector::setParameter(std::string id, float value)
00183 {
00184     if (id == "threshold") {
00185         if (value < 0) value = 0;
00186         if (value > 20) value = 20;
00187         m_threshold = value;
00188     } else if (id == "sensitivity") {
00189         if (value < 0) value = 0;
00190         if (value > 100) value = 100;
00191         m_sensitivity = value;
00192     }
00193 }
00194 
00195 PercussionOnsetDetector::OutputList
00196 PercussionOnsetDetector::getOutputDescriptors() const
00197 {
00198     OutputList list;
00199 
00200     OutputDescriptor d;
00201     d.identifier = "onsets";
00202     d.name = "Onsets";
00203     d.description = "Percussive note onset locations";
00204     d.unit = "";
00205     d.hasFixedBinCount = true;
00206     d.binCount = 0;
00207     d.hasKnownExtents = false;
00208     d.isQuantized = false;
00209     d.sampleType = OutputDescriptor::VariableSampleRate;
00210     d.sampleRate = m_inputSampleRate;
00211     list.push_back(d);
00212 
00213     d.identifier = "detectionfunction";
00214     d.name = "Detection Function";
00215     d.description = "Broadband energy rise detection function";
00216     d.binCount = 1;
00217     d.isQuantized = true;
00218     d.quantizeStep = 1.0;
00219     d.sampleType = OutputDescriptor::OneSamplePerStep;
00220     list.push_back(d);
00221 
00222     return list;
00223 }
00224 
00225 PercussionOnsetDetector::FeatureSet
00226 PercussionOnsetDetector::process(const float *const *inputBuffers,
00227                                  Vamp::RealTime ts)
00228 {
00229     if (m_stepSize == 0) {
00230         cerr << "ERROR: PercussionOnsetDetector::process: "
00231              << "PercussionOnsetDetector has not been initialised"
00232              << endl;
00233         return FeatureSet();
00234     }
00235 
00236     int count = 0;
00237 
00238     for (size_t i = 1; i < m_blockSize/2; ++i) {
00239 
00240         float real = inputBuffers[0][i*2];
00241         float imag = inputBuffers[0][i*2 + 1];
00242 
00243         float sqrmag = real * real + imag * imag;
00244 
00245         if (m_priorMagnitudes[i] > 0.f) {
00246             float diff = 10.f * log10f(sqrmag / m_priorMagnitudes[i]);
00247 
00248 //        std::cout << "i=" << i << ", sqrmag=" << sqrmag << ", prior=" << m_priorMagnitudes[i] << ", diff=" << diff << ", threshold=" << m_threshold << " " << (diff >= m_threshold ? "[*]" : "") << std::endl;
00249 
00250             if (diff >= m_threshold) ++count;
00251         }
00252 
00253         m_priorMagnitudes[i] = sqrmag;
00254     }
00255 
00256     FeatureSet returnFeatures;
00257 
00258     Feature detectionFunction;
00259     detectionFunction.hasTimestamp = false;
00260     detectionFunction.values.push_back(count);
00261     returnFeatures[1].push_back(detectionFunction);
00262 
00263     if (m_dfMinus2 < m_dfMinus1 &&
00264         m_dfMinus1 >= count &&
00265         m_dfMinus1 > ((100 - m_sensitivity) * m_blockSize) / 200) {
00266 
00267 //std::cout << "result at " << ts << "! (count == " << count << ", prev == " << m_dfMinus1 << ")" << std::endl;
00268 
00269         Feature onset;
00270         onset.hasTimestamp = true;
00271         onset.timestamp = ts - Vamp::RealTime::frame2RealTime
00272             (m_stepSize, int(m_inputSampleRate + 0.5));
00273         returnFeatures[0].push_back(onset);
00274     }
00275 
00276     m_dfMinus2 = m_dfMinus1;
00277     m_dfMinus1 = count;
00278 
00279     return returnFeatures;
00280 }
00281 
00282 PercussionOnsetDetector::FeatureSet
00283 PercussionOnsetDetector::getRemainingFeatures()
00284 {
00285     return FeatureSet();
00286 }
00287