Chris@1: Chris@1: Chris@1:
Chris@1: Chris@3: Chris@1:
Chris@1: VampPluginSDK
Chris@3: 2.4
Chris@1:
Chris@1:
Chris@1: |
Chris@1:
Chris@1:
Chris@1:
Chris@1:
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1: 00002 Chris@1: 00003 /* Chris@1: 00004 Vamp Chris@1: 00005 Chris@1: 00006 An API for audio analysis and feature extraction plugins. Chris@1: 00007 Chris@1: 00008 Centre for Digital Music, Queen Mary, University of London. Chris@1: 00009 Copyright 2006-2009 Chris Cannam and QMUL. Chris@1: 00010 Chris@1: 00011 Permission is hereby granted, free of charge, to any person Chris@1: 00012 obtaining a copy of this software and associated documentation Chris@1: 00013 files (the "Software"), to deal in the Software without Chris@1: 00014 restriction, including without limitation the rights to use, copy, Chris@1: 00015 modify, merge, publish, distribute, sublicense, and/or sell copies Chris@1: 00016 of the Software, and to permit persons to whom the Software is Chris@1: 00017 furnished to do so, subject to the following conditions: Chris@1: 00018 Chris@1: 00019 The above copyright notice and this permission notice shall be Chris@1: 00020 included in all copies or substantial portions of the Software. Chris@1: 00021 Chris@1: 00022 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@1: 00023 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@1: 00024 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@1: 00025 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@1: 00026 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@1: 00027 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@1: 00028 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@1: 00029 Chris@1: 00030 Except as contained in this notice, the names of the Centre for Chris@1: 00031 Digital Music; Queen Mary, University of London; and Chris Cannam Chris@1: 00032 shall not be used in advertising or otherwise to promote the sale, Chris@1: 00033 use or other dealings in this Software without prior written Chris@1: 00034 authorization. Chris@1: 00035 */ Chris@1: 00036 Chris@1: 00037 #include "FixedTempoEstimator.h" Chris@1: 00038 Chris@1: 00039 using std::string; Chris@1: 00040 using std::vector; Chris@1: 00041 using std::cerr; Chris@1: 00042 using std::endl; Chris@1: 00043 Chris@1: 00044 using Vamp::RealTime; Chris@1: 00045 Chris@1: 00046 #include <cmath> Chris@1: 00047 #include <cstdio> Chris@1: 00048 Chris@1: 00049 Chris@1: 00050 class FixedTempoEstimator::D Chris@1: 00051 // this class just avoids us having to declare any data members in the header Chris@1: 00052 { Chris@1: 00053 public: Chris@1: 00054 D(float inputSampleRate); Chris@1: 00055 ~D(); Chris@1: 00056 Chris@1: 00057 size_t getPreferredStepSize() const { return 64; } Chris@1: 00058 size_t getPreferredBlockSize() const { return 256; } Chris@1: 00059 Chris@1: 00060 ParameterList getParameterDescriptors() const; Chris@1: 00061 float getParameter(string id) const; Chris@1: 00062 void setParameter(string id, float value); Chris@1: 00063 Chris@1: 00064 OutputList getOutputDescriptors() const; Chris@1: 00065 Chris@1: 00066 bool initialise(size_t channels, size_t stepSize, size_t blockSize); Chris@1: 00067 void reset(); Chris@1: 00068 FeatureSet process(const float *const *, RealTime); Chris@1: 00069 FeatureSet getRemainingFeatures(); Chris@1: 00070 Chris@1: 00071 private: Chris@1: 00072 void calculate(); Chris@1: 00073 FeatureSet assembleFeatures(); Chris@1: 00074 Chris@1: 00075 float lag2tempo(int); Chris@1: 00076 int tempo2lag(float); Chris@1: 00077 Chris@1: 00078 float m_inputSampleRate; Chris@1: 00079 size_t m_stepSize; Chris@1: 00080 size_t m_blockSize; Chris@1: 00081 Chris@1: 00082 float m_minbpm; Chris@1: 00083 float m_maxbpm; Chris@1: 00084 float m_maxdflen; Chris@1: 00085 Chris@1: 00086 float *m_priorMagnitudes; Chris@1: 00087 Chris@1: 00088 size_t m_dfsize; Chris@1: 00089 float *m_df; Chris@1: 00090 float *m_r; Chris@1: 00091 float *m_fr; Chris@1: 00092 float *m_t; Chris@1: 00093 size_t m_n; Chris@1: 00094 Chris@1: 00095 Vamp::RealTime m_start; Chris@1: 00096 Vamp::RealTime m_lasttime; Chris@1: 00097 }; Chris@1: 00098 Chris@1: 00099 FixedTempoEstimator::D::D(float inputSampleRate) : Chris@1: 00100 m_inputSampleRate(inputSampleRate), Chris@1: 00101 m_stepSize(0), Chris@1: 00102 m_blockSize(0), Chris@1: 00103 m_minbpm(50), Chris@1: 00104 m_maxbpm(190), Chris@1: 00105 m_maxdflen(10), Chris@1: 00106 m_priorMagnitudes(0), Chris@1: 00107 m_df(0), Chris@1: 00108 m_r(0), Chris@1: 00109 m_fr(0), Chris@1: 00110 m_t(0), Chris@1: 00111 m_n(0) Chris@1: 00112 { Chris@1: 00113 } Chris@1: 00114 Chris@1: 00115 FixedTempoEstimator::D::~D() Chris@1: 00116 { Chris@1: 00117 delete[] m_priorMagnitudes; Chris@1: 00118 delete[] m_df; Chris@1: 00119 delete[] m_r; Chris@1: 00120 delete[] m_fr; Chris@1: 00121 delete[] m_t; Chris@1: 00122 } Chris@1: 00123 Chris@1: 00124 FixedTempoEstimator::ParameterList Chris@1: 00125 FixedTempoEstimator::D::getParameterDescriptors() const Chris@1: 00126 { Chris@1: 00127 ParameterList list; Chris@1: 00128 Chris@1: 00129 ParameterDescriptor d; Chris@1: 00130 d.identifier = "minbpm"; Chris@1: 00131 d.name = "Minimum estimated tempo"; Chris@1: 00132 d.description = "Minimum beat-per-minute value which the tempo estimator is able to return"; Chris@1: 00133 d.unit = "bpm"; Chris@1: 00134 d.minValue = 10; Chris@1: 00135 d.maxValue = 360; Chris@1: 00136 d.defaultValue = 50; Chris@1: 00137 d.isQuantized = false; Chris@1: 00138 list.push_back(d); Chris@1: 00139 Chris@1: 00140 d.identifier = "maxbpm"; Chris@1: 00141 d.name = "Maximum estimated tempo"; Chris@1: 00142 d.description = "Maximum beat-per-minute value which the tempo estimator is able to return"; Chris@1: 00143 d.defaultValue = 190; Chris@1: 00144 list.push_back(d); Chris@1: 00145 Chris@1: 00146 d.identifier = "maxdflen"; Chris@1: 00147 d.name = "Input duration to study"; Chris@1: 00148 d.description = "Length of audio input, in seconds, which should be taken into account when estimating tempo. There is no need to supply the plugin with any further input once this time has elapsed since the start of the audio. The tempo estimator may use only the first part of this, up to eight times the slowest beat duration: increasing this value further than that is unlikely to improve results."; Chris@1: 00149 d.unit = "s"; Chris@1: 00150 d.minValue = 2; Chris@1: 00151 d.maxValue = 40; Chris@1: 00152 d.defaultValue = 10; Chris@1: 00153 list.push_back(d); Chris@1: 00154 Chris@1: 00155 return list; Chris@1: 00156 } Chris@1: 00157 Chris@1: 00158 float Chris@1: 00159 FixedTempoEstimator::D::getParameter(string id) const Chris@1: 00160 { Chris@1: 00161 if (id == "minbpm") { Chris@1: 00162 return m_minbpm; Chris@1: 00163 } else if (id == "maxbpm") { Chris@1: 00164 return m_maxbpm; Chris@1: 00165 } else if (id == "maxdflen") { Chris@1: 00166 return m_maxdflen; Chris@1: 00167 } Chris@1: 00168 return 0.f; Chris@1: 00169 } Chris@1: 00170 Chris@1: 00171 void Chris@1: 00172 FixedTempoEstimator::D::setParameter(string id, float value) Chris@1: 00173 { Chris@1: 00174 if (id == "minbpm") { Chris@1: 00175 m_minbpm = value; Chris@1: 00176 } else if (id == "maxbpm") { Chris@1: 00177 m_maxbpm = value; Chris@1: 00178 } else if (id == "maxdflen") { Chris@1: 00179 m_maxdflen = value; Chris@1: 00180 } Chris@1: 00181 } Chris@1: 00182 Chris@1: 00183 static int TempoOutput = 0; Chris@1: 00184 static int CandidatesOutput = 1; Chris@1: 00185 static int DFOutput = 2; Chris@1: 00186 static int ACFOutput = 3; Chris@1: 00187 static int FilteredACFOutput = 4; Chris@1: 00188 Chris@1: 00189 FixedTempoEstimator::OutputList Chris@1: 00190 FixedTempoEstimator::D::getOutputDescriptors() const Chris@1: 00191 { Chris@1: 00192 OutputList list; Chris@1: 00193 Chris@1: 00194 OutputDescriptor d; Chris@1: 00195 d.identifier = "tempo"; Chris@1: 00196 d.name = "Tempo"; Chris@1: 00197 d.description = "Estimated tempo"; Chris@1: 00198 d.unit = "bpm"; Chris@1: 00199 d.hasFixedBinCount = true; Chris@1: 00200 d.binCount = 1; Chris@1: 00201 d.hasKnownExtents = false; Chris@1: 00202 d.isQuantized = false; Chris@1: 00203 d.sampleType = OutputDescriptor::VariableSampleRate; Chris@1: 00204 d.sampleRate = m_inputSampleRate; Chris@1: 00205 d.hasDuration = true; // our returned tempo spans a certain range Chris@1: 00206 list.push_back(d); Chris@1: 00207 Chris@1: 00208 d.identifier = "candidates"; Chris@1: 00209 d.name = "Tempo candidates"; Chris@1: 00210 d.description = "Possible tempo estimates, one per bin with the most likely in the first bin"; Chris@1: 00211 d.unit = "bpm"; Chris@1: 00212 d.hasFixedBinCount = false; Chris@1: 00213 list.push_back(d); Chris@1: 00214 Chris@1: 00215 d.identifier = "detectionfunction"; Chris@1: 00216 d.name = "Detection Function"; Chris@1: 00217 d.description = "Onset detection function"; Chris@1: 00218 d.unit = ""; Chris@1: 00219 d.hasFixedBinCount = 1; Chris@1: 00220 d.binCount = 1; Chris@1: 00221 d.hasKnownExtents = true; Chris@1: 00222 d.minValue = 0.0; Chris@1: 00223 d.maxValue = 1.0; Chris@1: 00224 d.isQuantized = false; Chris@1: 00225 d.quantizeStep = 0.0; Chris@1: 00226 d.sampleType = OutputDescriptor::FixedSampleRate; Chris@1: 00227 if (m_stepSize) { Chris@1: 00228 d.sampleRate = m_inputSampleRate / m_stepSize; Chris@1: 00229 } else { Chris@1: 00230 d.sampleRate = m_inputSampleRate / (getPreferredBlockSize()/2); Chris@1: 00231 } Chris@1: 00232 d.hasDuration = false; Chris@1: 00233 list.push_back(d); Chris@1: 00234 Chris@1: 00235 d.identifier = "acf"; Chris@1: 00236 d.name = "Autocorrelation Function"; Chris@1: 00237 d.description = "Autocorrelation of onset detection function"; Chris@1: 00238 d.hasKnownExtents = false; Chris@1: 00239 d.unit = "r"; Chris@1: 00240 list.push_back(d); Chris@1: 00241 Chris@1: 00242 d.identifier = "filtered_acf"; Chris@1: 00243 d.name = "Filtered Autocorrelation"; Chris@1: 00244 d.description = "Filtered autocorrelation of onset detection function"; Chris@1: 00245 d.unit = "r"; Chris@1: 00246 list.push_back(d); Chris@1: 00247 Chris@1: 00248 return list; Chris@1: 00249 } Chris@1: 00250 Chris@1: 00251 bool Chris@1: 00252 FixedTempoEstimator::D::initialise(size_t, size_t stepSize, size_t blockSize) Chris@1: 00253 { Chris@1: 00254 m_stepSize = stepSize; Chris@1: 00255 m_blockSize = blockSize; Chris@1: 00256 Chris@1: 00257 float dfLengthSecs = m_maxdflen; Chris@1: 00258 m_dfsize = (dfLengthSecs * m_inputSampleRate) / m_stepSize; Chris@1: 00259 Chris@1: 00260 m_priorMagnitudes = new float[m_blockSize/2]; Chris@1: 00261 m_df = new float[m_dfsize]; Chris@1: 00262 Chris@1: 00263 for (size_t i = 0; i < m_blockSize/2; ++i) { Chris@1: 00264 m_priorMagnitudes[i] = 0.f; Chris@1: 00265 } Chris@1: 00266 for (size_t i = 0; i < m_dfsize; ++i) { Chris@1: 00267 m_df[i] = 0.f; Chris@1: 00268 } Chris@1: 00269 Chris@1: 00270 m_n = 0; Chris@1: 00271 Chris@1: 00272 return true; Chris@1: 00273 } Chris@1: 00274 Chris@1: 00275 void Chris@1: 00276 FixedTempoEstimator::D::reset() Chris@1: 00277 { Chris@1: 00278 if (!m_priorMagnitudes) return; Chris@1: 00279 Chris@1: 00280 for (size_t i = 0; i < m_blockSize/2; ++i) { Chris@1: 00281 m_priorMagnitudes[i] = 0.f; Chris@1: 00282 } Chris@1: 00283 for (size_t i = 0; i < m_dfsize; ++i) { Chris@1: 00284 m_df[i] = 0.f; Chris@1: 00285 } Chris@1: 00286 Chris@1: 00287 delete[] m_r; Chris@1: 00288 m_r = 0; Chris@1: 00289 Chris@1: 00290 delete[] m_fr; Chris@1: 00291 m_fr = 0; Chris@1: 00292 Chris@1: 00293 delete[] m_t; Chris@1: 00294 m_t = 0; Chris@1: 00295 Chris@1: 00296 m_n = 0; Chris@1: 00297 Chris@1: 00298 m_start = RealTime::zeroTime; Chris@1: 00299 m_lasttime = RealTime::zeroTime; Chris@1: 00300 } Chris@1: 00301 Chris@1: 00302 FixedTempoEstimator::FeatureSet Chris@1: 00303 FixedTempoEstimator::D::process(const float *const *inputBuffers, RealTime ts) Chris@1: 00304 { Chris@1: 00305 FeatureSet fs; Chris@1: 00306 Chris@1: 00307 if (m_stepSize == 0) { Chris@1: 00308 cerr << "ERROR: FixedTempoEstimator::process: " Chris@1: 00309 << "FixedTempoEstimator has not been initialised" Chris@1: 00310 << endl; Chris@1: 00311 return fs; Chris@1: 00312 } Chris@1: 00313 Chris@1: 00314 if (m_n == 0) m_start = ts; Chris@1: 00315 m_lasttime = ts; Chris@1: 00316 Chris@1: 00317 if (m_n == m_dfsize) { Chris@1: 00318 // If we have seen enough input, do the estimation and return Chris@1: 00319 calculate(); Chris@1: 00320 fs = assembleFeatures(); Chris@1: 00321 ++m_n; Chris@1: 00322 return fs; Chris@1: 00323 } Chris@1: 00324 Chris@1: 00325 // If we have seen more than enough, just discard and return! Chris@1: 00326 if (m_n > m_dfsize) return FeatureSet(); Chris@1: 00327 Chris@1: 00328 float value = 0.f; Chris@1: 00329 Chris@1: 00330 // m_df will contain an onset detection function based on the rise Chris@1: 00331 // in overall power from one spectral frame to the next -- Chris@1: 00332 // simplistic but reasonably effective for our purposes. Chris@1: 00333 Chris@1: 00334 for (size_t i = 1; i < m_blockSize/2; ++i) { Chris@1: 00335 Chris@1: 00336 float real = inputBuffers[0][i*2]; Chris@1: 00337 float imag = inputBuffers[0][i*2 + 1]; Chris@1: 00338 Chris@1: 00339 float sqrmag = real * real + imag * imag; Chris@1: 00340 value += fabsf(sqrmag - m_priorMagnitudes[i]); Chris@1: 00341 Chris@1: 00342 m_priorMagnitudes[i] = sqrmag; Chris@1: 00343 } Chris@1: 00344 Chris@1: 00345 m_df[m_n] = value; Chris@1: 00346 Chris@1: 00347 ++m_n; Chris@1: 00348 return fs; Chris@1: 00349 } Chris@1: 00350 Chris@1: 00351 FixedTempoEstimator::FeatureSet Chris@1: 00352 FixedTempoEstimator::D::getRemainingFeatures() Chris@1: 00353 { Chris@1: 00354 FeatureSet fs; Chris@1: 00355 if (m_n > m_dfsize) return fs; Chris@1: 00356 calculate(); Chris@1: 00357 fs = assembleFeatures(); Chris@1: 00358 ++m_n; Chris@1: 00359 return fs; Chris@1: 00360 } Chris@1: 00361 Chris@1: 00362 float Chris@1: 00363 FixedTempoEstimator::D::lag2tempo(int lag) Chris@1: 00364 { Chris@1: 00365 return 60.f / ((lag * m_stepSize) / m_inputSampleRate); Chris@1: 00366 } Chris@1: 00367 Chris@1: 00368 int Chris@1: 00369 FixedTempoEstimator::D::tempo2lag(float tempo) Chris@1: 00370 { Chris@1: 00371 return ((60.f / tempo) * m_inputSampleRate) / m_stepSize; Chris@1: 00372 } Chris@1: 00373 Chris@1: 00374 void Chris@1: 00375 FixedTempoEstimator::D::calculate() Chris@1: 00376 { Chris@1: 00377 if (m_r) { Chris@1: 00378 cerr << "FixedTempoEstimator::calculate: calculation already happened?" << endl; Chris@1: 00379 return; Chris@1: 00380 } Chris@1: 00381 Chris@1: 00382 if (m_n < m_dfsize / 9 && Chris@1: 00383 m_n < (1.0 * m_inputSampleRate) / m_stepSize) { // 1 second Chris@1: 00384 cerr << "FixedTempoEstimator::calculate: Input is too short" << endl; Chris@1: 00385 return; Chris@1: 00386 } Chris@1: 00387 Chris@1: 00388 // This function takes m_df (the detection function array filled Chris@1: 00389 // out in process()) and calculates m_r (the raw autocorrelation) Chris@1: 00390 // and m_fr (the filtered autocorrelation from whose peaks tempo Chris@1: 00391 // estimates will be taken). Chris@1: 00392 Chris@1: 00393 int n = m_n; // length of actual df array (m_dfsize is the theoretical max) Chris@1: 00394 Chris@1: 00395 m_r = new float[n/2]; // raw autocorrelation Chris@1: 00396 m_fr = new float[n/2]; // filtered autocorrelation Chris@1: 00397 m_t = new float[n/2]; // averaged tempo estimate for each lag value Chris@1: 00398 Chris@1: 00399 for (int i = 0; i < n/2; ++i) { Chris@1: 00400 m_r[i] = 0.f; Chris@1: 00401 m_fr[i] = 0.f; Chris@1: 00402 m_t[i] = lag2tempo(i); Chris@1: 00403 } Chris@1: 00404 Chris@1: 00405 // Calculate the raw autocorrelation of the detection function Chris@1: 00406 Chris@1: 00407 for (int i = 0; i < n/2; ++i) { Chris@1: 00408 Chris@1: 00409 for (int j = i; j < n; ++j) { Chris@1: 00410 m_r[i] += m_df[j] * m_df[j - i]; Chris@1: 00411 } Chris@1: 00412 Chris@1: 00413 m_r[i] /= n - i - 1; Chris@1: 00414 } Chris@1: 00415 Chris@1: 00416 // Filter the autocorrelation and average out the tempo estimates Chris@1: 00417 Chris@1: 00418 float related[] = { 0.5, 2, 4, 8 }; Chris@1: 00419 Chris@1: 00420 for (int i = 1; i < n/2-1; ++i) { Chris@1: 00421 Chris@1: 00422 m_fr[i] = m_r[i]; Chris@1: 00423 Chris@1: 00424 int div = 1; Chris@1: 00425 Chris@1: 00426 for (int j = 0; j < int(sizeof(related)/sizeof(related[0])); ++j) { Chris@1: 00427 Chris@1: 00428 // Check for an obvious peak at each metrically related lag Chris@1: 00429 Chris@1: 00430 int k0 = int(i * related[j] + 0.5); Chris@1: 00431 Chris@1: 00432 if (k0 >= 0 && k0 < int(n/2)) { Chris@1: 00433 Chris@1: 00434 int kmax = 0, kmin = 0; Chris@1: 00435 float kvmax = 0, kvmin = 0; Chris@1: 00436 bool have = false; Chris@1: 00437 Chris@1: 00438 for (int k = k0 - 1; k <= k0 + 1; ++k) { Chris@1: 00439 Chris@1: 00440 if (k < 0 || k >= n/2) continue; Chris@1: 00441 Chris@1: 00442 if (!have || (m_r[k] > kvmax)) { kmax = k; kvmax = m_r[k]; } Chris@1: 00443 if (!have || (m_r[k] < kvmin)) { kmin = k; kvmin = m_r[k]; } Chris@1: 00444 Chris@1: 00445 have = true; Chris@1: 00446 } Chris@1: 00447 Chris@1: 00448 // Boost the original lag according to the strongest Chris@1: 00449 // value found close to this related lag Chris@1: 00450 Chris@1: 00451 m_fr[i] += m_r[kmax] / 5; Chris@1: 00452 Chris@1: 00453 if ((kmax == 0 || m_r[kmax] > m_r[kmax-1]) && Chris@1: 00454 (kmax == n/2-1 || m_r[kmax] > m_r[kmax+1]) && Chris@1: 00455 kvmax > kvmin * 1.05) { Chris@1: 00456 Chris@1: 00457 // The strongest value close to the related lag is Chris@1: 00458 // also a pretty good looking peak, so use it to Chris@1: 00459 // improve our tempo estimate for the original lag Chris@1: 00460 Chris@1: 00461 m_t[i] = m_t[i] + lag2tempo(kmax) * related[j]; Chris@1: 00462 ++div; Chris@1: 00463 } Chris@1: 00464 } Chris@1: 00465 } Chris@1: 00466 Chris@1: 00467 m_t[i] /= div; Chris@1: 00468 Chris@1: 00469 // Finally apply a primitive perceptual weighting (to prefer Chris@1: 00470 // tempi of around 120-130) Chris@1: 00471 Chris@1: 00472 float weight = 1.f - fabsf(128.f - lag2tempo(i)) * 0.005; Chris@1: 00473 if (weight < 0.f) weight = 0.f; Chris@1: 00474 weight = weight * weight * weight; Chris@1: 00475 Chris@1: 00476 m_fr[i] += m_fr[i] * (weight / 3); Chris@1: 00477 } Chris@1: 00478 } Chris@1: 00479 Chris@1: 00480 FixedTempoEstimator::FeatureSet Chris@1: 00481 FixedTempoEstimator::D::assembleFeatures() Chris@1: 00482 { Chris@1: 00483 FeatureSet fs; Chris@1: 00484 if (!m_r) return fs; // No autocorrelation: no results Chris@1: 00485 Chris@1: 00486 Feature feature; Chris@1: 00487 feature.hasTimestamp = true; Chris@1: 00488 feature.hasDuration = false; Chris@1: 00489 feature.label = ""; Chris@1: 00490 feature.values.clear(); Chris@1: 00491 feature.values.push_back(0.f); Chris@1: 00492 Chris@1: 00493 char buffer[40]; Chris@1: 00494 Chris@1: 00495 int n = m_n; Chris@1: 00496 Chris@1: 00497 for (int i = 0; i < n; ++i) { Chris@1: 00498 Chris@1: 00499 // Return the detection function in the DF output Chris@1: 00500 Chris@1: 00501 feature.timestamp = m_start + Chris@1: 00502 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate); Chris@1: 00503 feature.values[0] = m_df[i]; Chris@1: 00504 feature.label = ""; Chris@1: 00505 fs[DFOutput].push_back(feature); Chris@1: 00506 } Chris@1: 00507 Chris@1: 00508 for (int i = 1; i < n/2; ++i) { Chris@1: 00509 Chris@1: 00510 // Return the raw autocorrelation in the ACF output, each Chris@1: 00511 // value labelled according to its corresponding tempo Chris@1: 00512 Chris@1: 00513 feature.timestamp = m_start + Chris@1: 00514 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate); Chris@1: 00515 feature.values[0] = m_r[i]; Chris@1: 00516 sprintf(buffer, "%.1f bpm", lag2tempo(i)); Chris@1: 00517 if (i == n/2-1) feature.label = ""; Chris@1: 00518 else feature.label = buffer; Chris@1: 00519 fs[ACFOutput].push_back(feature); Chris@1: 00520 } Chris@1: 00521 Chris@1: 00522 float t0 = m_minbpm; // our minimum detected tempo Chris@1: 00523 float t1 = m_maxbpm; // our maximum detected tempo Chris@1: 00524 Chris@1: 00525 int p0 = tempo2lag(t1); Chris@1: 00526 int p1 = tempo2lag(t0); Chris@1: 00527 Chris@1: 00528 std::map<float, int> candidates; Chris@1: 00529 Chris@1: 00530 for (int i = p0; i <= p1 && i+1 < n/2; ++i) { Chris@1: 00531 Chris@1: 00532 if (m_fr[i] > m_fr[i-1] && Chris@1: 00533 m_fr[i] > m_fr[i+1]) { Chris@1: 00534 Chris@1: 00535 // This is a peak in the filtered autocorrelation: stick Chris@1: 00536 // it into the map from filtered autocorrelation to lag Chris@1: 00537 // index -- this sorts our peaks by filtered acf value Chris@1: 00538 Chris@1: 00539 candidates[m_fr[i]] = i; Chris@1: 00540 } Chris@1: 00541 Chris@1: 00542 // Also return the filtered autocorrelation in its own output Chris@1: 00543 Chris@1: 00544 feature.timestamp = m_start + Chris@1: 00545 RealTime::frame2RealTime(i * m_stepSize, m_inputSampleRate); Chris@1: 00546 feature.values[0] = m_fr[i]; Chris@1: 00547 sprintf(buffer, "%.1f bpm", lag2tempo(i)); Chris@1: 00548 if (i == p1 || i == n/2-2) feature.label = ""; Chris@1: 00549 else feature.label = buffer; Chris@1: 00550 fs[FilteredACFOutput].push_back(feature); Chris@1: 00551 } Chris@1: 00552 Chris@1: 00553 if (candidates.empty()) { Chris@1: 00554 cerr << "No tempo candidates!" << endl; Chris@1: 00555 return fs; Chris@1: 00556 } Chris@1: 00557 Chris@1: 00558 feature.hasTimestamp = true; Chris@1: 00559 feature.timestamp = m_start; Chris@1: 00560 Chris@1: 00561 feature.hasDuration = true; Chris@1: 00562 feature.duration = m_lasttime - m_start; Chris@1: 00563 Chris@1: 00564 // The map contains only peaks and is sorted by filtered acf Chris@1: 00565 // value, so the final element in it is our "best" tempo guess Chris@1: 00566 Chris@1: 00567 std::map<float, int>::const_iterator ci = candidates.end(); Chris@1: 00568 --ci; Chris@1: 00569 int maxpi = ci->second; Chris@1: 00570 Chris@1: 00571 if (m_t[maxpi] > 0) { Chris@1: 00572 Chris@1: 00573 // This lag has an adjusted tempo from the averaging process: Chris@1: 00574 // use it Chris@1: 00575 Chris@1: 00576 feature.values[0] = m_t[maxpi]; Chris@1: 00577 Chris@1: 00578 } else { Chris@1: 00579 Chris@1: 00580 // shouldn't happen -- it would imply that this high value was Chris@1: 00581 // not a peak! Chris@1: 00582 Chris@1: 00583 feature.values[0] = lag2tempo(maxpi); Chris@1: 00584 cerr << "WARNING: No stored tempo for index " << maxpi << endl; Chris@1: 00585 } Chris@1: 00586 Chris@1: 00587 sprintf(buffer, "%.1f bpm", feature.values[0]); Chris@1: 00588 feature.label = buffer; Chris@1: 00589 Chris@1: 00590 // Return the best tempo in the main output Chris@1: 00591 Chris@1: 00592 fs[TempoOutput].push_back(feature); Chris@1: 00593 Chris@1: 00594 // And return the other estimates (up to the arbitrarily chosen Chris@1: 00595 // number of 10 of them) in the candidates output Chris@1: 00596 Chris@1: 00597 feature.values.clear(); Chris@1: 00598 feature.label = ""; Chris@1: 00599 Chris@1: 00600 while (feature.values.size() < 10) { Chris@1: 00601 if (m_t[ci->second] > 0) { Chris@1: 00602 feature.values.push_back(m_t[ci->second]); Chris@1: 00603 } else { Chris@1: 00604 feature.values.push_back(lag2tempo(ci->second)); Chris@1: 00605 } Chris@1: 00606 if (ci == candidates.begin()) break; Chris@1: 00607 --ci; Chris@1: 00608 } Chris@1: 00609 Chris@1: 00610 fs[CandidatesOutput].push_back(feature); Chris@1: 00611 Chris@1: 00612 return fs; Chris@1: 00613 } Chris@1: 00614 Chris@1: 00615 Chris@1: 00616 Chris@1: 00617 FixedTempoEstimator::FixedTempoEstimator(float inputSampleRate) : Chris@1: 00618 Plugin(inputSampleRate), Chris@1: 00619 m_d(new D(inputSampleRate)) Chris@1: 00620 { Chris@1: 00621 } Chris@1: 00622 Chris@1: 00623 FixedTempoEstimator::~FixedTempoEstimator() Chris@1: 00624 { Chris@1: 00625 delete m_d; Chris@1: 00626 } Chris@1: 00627 Chris@1: 00628 string Chris@1: 00629 FixedTempoEstimator::getIdentifier() const Chris@1: 00630 { Chris@1: 00631 return "fixedtempo"; Chris@1: 00632 } Chris@1: 00633 Chris@1: 00634 string Chris@1: 00635 FixedTempoEstimator::getName() const Chris@1: 00636 { Chris@1: 00637 return "Simple Fixed Tempo Estimator"; Chris@1: 00638 } Chris@1: 00639 Chris@1: 00640 string Chris@1: 00641 FixedTempoEstimator::getDescription() const Chris@1: 00642 { Chris@1: 00643 return "Study a short section of audio and estimate its tempo, assuming the tempo is constant"; Chris@1: 00644 } Chris@1: 00645 Chris@1: 00646 string Chris@1: 00647 FixedTempoEstimator::getMaker() const Chris@1: 00648 { Chris@1: 00649 return "Vamp SDK Example Plugins"; Chris@1: 00650 } Chris@1: 00651 Chris@1: 00652 int Chris@1: 00653 FixedTempoEstimator::getPluginVersion() const Chris@1: 00654 { Chris@1: 00655 return 1; Chris@1: 00656 } Chris@1: 00657 Chris@1: 00658 string Chris@1: 00659 FixedTempoEstimator::getCopyright() const Chris@1: 00660 { Chris@1: 00661 return "Code copyright 2008 Queen Mary, University of London. Freely redistributable (BSD license)"; Chris@1: 00662 } Chris@1: 00663 Chris@1: 00664 size_t Chris@1: 00665 FixedTempoEstimator::getPreferredStepSize() const Chris@1: 00666 { Chris@1: 00667 return m_d->getPreferredStepSize(); Chris@1: 00668 } Chris@1: 00669 Chris@1: 00670 size_t Chris@1: 00671 FixedTempoEstimator::getPreferredBlockSize() const Chris@1: 00672 { Chris@1: 00673 return m_d->getPreferredBlockSize(); Chris@1: 00674 } Chris@1: 00675 Chris@1: 00676 bool Chris@1: 00677 FixedTempoEstimator::initialise(size_t channels, size_t stepSize, size_t blockSize) Chris@1: 00678 { Chris@1: 00679 if (channels < getMinChannelCount() || Chris@1: 00680 channels > getMaxChannelCount()) return false; Chris@1: 00681 Chris@1: 00682 return m_d->initialise(channels, stepSize, blockSize); Chris@1: 00683 } Chris@1: 00684 Chris@1: 00685 void Chris@1: 00686 FixedTempoEstimator::reset() Chris@1: 00687 { Chris@1: 00688 return m_d->reset(); Chris@1: 00689 } Chris@1: 00690 Chris@1: 00691 FixedTempoEstimator::ParameterList Chris@1: 00692 FixedTempoEstimator::getParameterDescriptors() const Chris@1: 00693 { Chris@1: 00694 return m_d->getParameterDescriptors(); Chris@1: 00695 } Chris@1: 00696 Chris@1: 00697 float Chris@1: 00698 FixedTempoEstimator::getParameter(std::string id) const Chris@1: 00699 { Chris@1: 00700 return m_d->getParameter(id); Chris@1: 00701 } Chris@1: 00702 Chris@1: 00703 void Chris@1: 00704 FixedTempoEstimator::setParameter(std::string id, float value) Chris@1: 00705 { Chris@1: 00706 m_d->setParameter(id, value); Chris@1: 00707 } Chris@1: 00708 Chris@1: 00709 FixedTempoEstimator::OutputList Chris@1: 00710 FixedTempoEstimator::getOutputDescriptors() const Chris@1: 00711 { Chris@1: 00712 return m_d->getOutputDescriptors(); Chris@1: 00713 } Chris@1: 00714 Chris@1: 00715 FixedTempoEstimator::FeatureSet Chris@1: 00716 FixedTempoEstimator::process(const float *const *inputBuffers, RealTime ts) Chris@1: 00717 { Chris@1: 00718 return m_d->process(inputBuffers, ts); Chris@1: 00719 } Chris@1: 00720 Chris@1: 00721 FixedTempoEstimator::FeatureSet Chris@1: 00722 FixedTempoEstimator::getRemainingFeatures() Chris@1: 00723 { Chris@1: 00724 return m_d->getRemainingFeatures(); Chris@1: 00725 } Chris@3: