annotate src/BTrack.cpp @ 97:6a4dd7478954

Replaced C-style arrays with std vectors and modernised some code
author Adam Stark <adamstark.uk@gmail.com>
date Fri, 11 Aug 2017 18:18:33 +0100
parents 4aa362058011
children 3b24b01fbe15
rev   line source
adamstark@46 1 //=======================================================================
adamstark@46 2 /** @file BTrack.cpp
adamstark@47 3 * @brief BTrack - a real-time beat tracker
adamstark@46 4 * @author Adam Stark
adamstark@46 5 * @copyright Copyright (C) 2008-2014 Queen Mary University of London
adamstark@46 6 *
adamstark@46 7 * This program is free software: you can redistribute it and/or modify
adamstark@46 8 * it under the terms of the GNU General Public License as published by
adamstark@46 9 * the Free Software Foundation, either version 3 of the License, or
adamstark@46 10 * (at your option) any later version.
adamstark@46 11 *
adamstark@46 12 * This program is distributed in the hope that it will be useful,
adamstark@46 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
adamstark@46 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
adamstark@46 15 * GNU General Public License for more details.
adamstark@46 16 *
adamstark@46 17 * You should have received a copy of the GNU General Public License
adamstark@46 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
adamstark@46 19 */
adamstark@46 20 //=======================================================================
adamstark@46 21
adamstark@46 22 #include <cmath>
adamstark@52 23 #include <algorithm>
adamstark@97 24 #include <numeric>
adamstark@46 25 #include "BTrack.h"
adamstark@46 26 #include "samplerate.h"
adamstark@89 27 #include <iostream>
adamstark@46 28
adamstark@55 29 //=======================================================================
adamstark@91 30 BTrack::BTrack()
adamstark@91 31 : odf (512, 1024, ComplexSpectralDifferenceHWR, HanningWindow)
adamstark@55 32 {
adamstark@93 33 initialise (512, 1024);
adamstark@55 34 }
adamstark@46 35
adamstark@51 36 //=======================================================================
adamstark@91 37 BTrack::BTrack (int hopSize_)
adamstark@97 38 : odf (hopSize_, 2 * hopSize_, ComplexSpectralDifferenceHWR, HanningWindow)
adamstark@46 39 {
adamstark@97 40 initialise (hopSize_, 2 * hopSize_);
adamstark@55 41 }
adamstark@55 42
adamstark@55 43 //=======================================================================
adamstark@91 44 BTrack::BTrack (int hopSize_, int frameSize_)
adamstark@91 45 : odf (hopSize_, frameSize_, ComplexSpectralDifferenceHWR, HanningWindow)
adamstark@55 46 {
adamstark@91 47 initialise (hopSize_, frameSize_);
adamstark@55 48 }
adamstark@55 49
adamstark@55 50 //=======================================================================
adamstark@88 51 BTrack::~BTrack()
adamstark@88 52 {
adamstark@93 53 #ifdef USE_FFTW
adamstark@88 54 // destroy fft plan
adamstark@91 55 fftw_destroy_plan (acfForwardFFT);
adamstark@91 56 fftw_destroy_plan (acfBackwardFFT);
adamstark@91 57 fftw_free (complexIn);
adamstark@91 58 fftw_free (complexOut);
adamstark@93 59 #endif
adamstark@93 60
adamstark@93 61 #ifdef USE_KISS_FFT
adamstark@93 62 free (cfgForwards);
adamstark@93 63 free (cfgBackwards);
adamstark@93 64 delete [] fftIn;
adamstark@93 65 delete [] fftOut;
adamstark@93 66 #endif
adamstark@88 67 }
adamstark@88 68
adamstark@88 69 //=======================================================================
adamstark@91 70 double BTrack::getBeatTimeInSeconds (long frameNumber, int hopSize, int fs)
adamstark@55 71 {
adamstark@55 72 double hop = (double) hopSize;
adamstark@55 73 double samplingFrequency = (double) fs;
adamstark@55 74 double frameNum = (double) frameNumber;
adamstark@55 75
adamstark@55 76 return ((hop / samplingFrequency) * frameNum);
adamstark@55 77 }
adamstark@55 78
adamstark@55 79 //=======================================================================
adamstark@91 80 double BTrack::getBeatTimeInSeconds (int frameNumber, int hopSize, int fs)
adamstark@55 81 {
adamstark@55 82 long frameNum = (long) frameNumber;
adamstark@55 83
adamstark@91 84 return getBeatTimeInSeconds (frameNum, hopSize, fs);
adamstark@55 85 }
adamstark@55 86
adamstark@55 87
adamstark@55 88
adamstark@55 89 //=======================================================================
adamstark@91 90 void BTrack::initialise (int hopSize_, int frameSize_)
adamstark@55 91 {
adamstark@97 92 // set vector sizes
adamstark@97 93 resampledOnsetDF.resize (512);
adamstark@97 94 acf.resize (512);
adamstark@97 95 weightingVector.resize (128);
adamstark@97 96 combFilterBankOutput.resize (128);
adamstark@97 97 tempoObservationVector.resize (41);
adamstark@97 98 delta.resize (41);
adamstark@97 99 prevDelta.resize (41);
adamstark@97 100 prevDeltaFixed.resize (41);
adamstark@97 101
adamstark@55 102 double rayparam = 43;
adamstark@54 103 double pi = 3.14159265;
adamstark@46 104
adamstark@46 105
adamstark@46 106 // initialise parameters
adamstark@46 107 tightness = 5;
adamstark@46 108 alpha = 0.9;
adamstark@46 109 tempo = 120;
adamstark@58 110 estimatedTempo = 120.0;
adamstark@59 111 tempoToLagFactor = 60.*44100./512.;
adamstark@46 112
adamstark@46 113 m0 = 10;
adamstark@58 114 beatCounter = -1;
adamstark@46 115
adamstark@57 116 beatDueInFrame = false;
adamstark@46 117
adamstark@58 118
adamstark@46 119 // create rayleigh weighting vector
adamstark@91 120 for (int n = 0; n < 128; n++)
adamstark@46 121 {
adamstark@58 122 weightingVector[n] = ((double) n / pow(rayparam,2)) * exp((-1*pow((double)-n,2)) / (2*pow(rayparam,2)));
adamstark@46 123 }
adamstark@46 124
adamstark@97 125 // initialise prev_delta
adamstark@97 126 std::fill (prevDelta.begin(), prevDelta.end(), 1);
adamstark@97 127
adamstark@54 128 double t_mu = 41/2;
adamstark@54 129 double m_sig;
adamstark@54 130 double x;
adamstark@46 131 // create tempo transition matrix
adamstark@46 132 m_sig = 41/8;
adamstark@46 133 for (int i = 0;i < 41;i++)
adamstark@46 134 {
adamstark@46 135 for (int j = 0;j < 41;j++)
adamstark@46 136 {
adamstark@46 137 x = j+1;
adamstark@46 138 t_mu = i+1;
adamstark@58 139 tempoTransitionMatrix[i][j] = (1 / (m_sig * sqrt(2*pi))) * exp( (-1*pow((x-t_mu),2)) / (2*pow(m_sig,2)) );
adamstark@46 140 }
adamstark@55 141 }
adamstark@46 142
adamstark@46 143 // tempo is not fixed
adamstark@58 144 tempoFixed = false;
adamstark@58 145
adamstark@58 146 // initialise latest cumulative score value
adamstark@58 147 // in case it is requested before any processing takes place
adamstark@58 148 latestCumulativeScoreValue = 0;
adamstark@55 149
adamstark@55 150 // initialise algorithm given the hopsize
adamstark@57 151 setHopSize(hopSize_);
adamstark@88 152
adamstark@88 153
adamstark@88 154 // Set up FFT for calculating the auto-correlation function
adamstark@88 155 FFTLengthForACFCalculation = 1024;
adamstark@88 156
adamstark@93 157 #ifdef USE_FFTW
adamstark@91 158 complexIn = (fftw_complex*) fftw_malloc (sizeof(fftw_complex) * FFTLengthForACFCalculation); // complex array to hold fft data
adamstark@91 159 complexOut = (fftw_complex*) fftw_malloc (sizeof(fftw_complex) * FFTLengthForACFCalculation); // complex array to hold fft data
adamstark@88 160
adamstark@91 161 acfForwardFFT = fftw_plan_dft_1d (FFTLengthForACFCalculation, complexIn, complexOut, FFTW_FORWARD, FFTW_ESTIMATE); // FFT plan initialisation
adamstark@91 162 acfBackwardFFT = fftw_plan_dft_1d (FFTLengthForACFCalculation, complexOut, complexIn, FFTW_BACKWARD, FFTW_ESTIMATE); // FFT plan initialisation
adamstark@93 163 #endif
adamstark@93 164
adamstark@93 165 #ifdef USE_KISS_FFT
adamstark@93 166 fftIn = new kiss_fft_cpx[FFTLengthForACFCalculation];
adamstark@93 167 fftOut = new kiss_fft_cpx[FFTLengthForACFCalculation];
adamstark@93 168 cfgForwards = kiss_fft_alloc (FFTLengthForACFCalculation, 0, 0, 0);
adamstark@93 169 cfgBackwards = kiss_fft_alloc (FFTLengthForACFCalculation, 1, 0, 0);
adamstark@93 170 #endif
adamstark@46 171 }
adamstark@46 172
adamstark@51 173 //=======================================================================
adamstark@91 174 void BTrack::setHopSize (int hopSize_)
adamstark@46 175 {
adamstark@57 176 hopSize = hopSize_;
adamstark@97 177 onsetDFBufferSize = (512 * 512) / hopSize; // calculate df buffer size
adamstark@46 178
adamstark@57 179 beatPeriod = round(60/((((double) hopSize)/44100)*tempo));
adamstark@63 180
adamstark@63 181 // set size of onset detection function buffer
adamstark@91 182 onsetDF.resize (onsetDFBufferSize);
adamstark@63 183
adamstark@63 184 // set size of cumulative score buffer
adamstark@91 185 cumulativeScore.resize (onsetDFBufferSize);
adamstark@46 186
adamstark@46 187 // initialise df_buffer to zeros
adamstark@91 188 for (int i = 0; i < onsetDFBufferSize; i++)
adamstark@46 189 {
adamstark@58 190 onsetDF[i] = 0;
adamstark@58 191 cumulativeScore[i] = 0;
adamstark@46 192
adamstark@57 193 if ((i % ((int) round(beatPeriod))) == 0)
adamstark@46 194 {
adamstark@58 195 onsetDF[i] = 1;
adamstark@46 196 }
adamstark@46 197 }
adamstark@46 198 }
adamstark@46 199
adamstark@51 200 //=======================================================================
adamstark@91 201 void BTrack::updateHopAndFrameSize (int hopSize_, int frameSize_)
adamstark@65 202 {
adamstark@65 203 // update the onset detection function object
adamstark@91 204 odf.initialise (hopSize_, frameSize_);
adamstark@65 205
adamstark@65 206 // update the hop size being used by the beat tracker
adamstark@91 207 setHopSize (hopSize_);
adamstark@65 208 }
adamstark@65 209
adamstark@65 210 //=======================================================================
adamstark@57 211 bool BTrack::beatDueInCurrentFrame()
adamstark@57 212 {
adamstark@57 213 return beatDueInFrame;
adamstark@57 214 }
adamstark@57 215
adamstark@57 216 //=======================================================================
adamstark@78 217 double BTrack::getCurrentTempoEstimate()
adamstark@78 218 {
adamstark@78 219 return estimatedTempo;
adamstark@78 220 }
adamstark@78 221
adamstark@78 222 //=======================================================================
adamstark@57 223 int BTrack::getHopSize()
adamstark@57 224 {
adamstark@57 225 return hopSize;
adamstark@57 226 }
adamstark@57 227
adamstark@57 228 //=======================================================================
adamstark@58 229 double BTrack::getLatestCumulativeScoreValue()
adamstark@58 230 {
adamstark@58 231 return latestCumulativeScoreValue;
adamstark@58 232 }
adamstark@58 233
adamstark@58 234 //=======================================================================
adamstark@91 235 void BTrack::processAudioFrame (double* frame)
adamstark@55 236 {
adamstark@55 237 // calculate the onset detection function sample for the frame
adamstark@91 238 double sample = odf.calculateOnsetDetectionFunctionSample (frame);
adamstark@55 239
adamstark@55 240 // process the new onset detection function sample in the beat tracking algorithm
adamstark@91 241 processOnsetDetectionFunctionSample (sample);
adamstark@55 242 }
adamstark@55 243
adamstark@55 244 //=======================================================================
adamstark@91 245 void BTrack::processOnsetDetectionFunctionSample (double newSample)
adamstark@56 246 {
adamstark@56 247 // we need to ensure that the onset
adamstark@56 248 // detection function sample is positive
adamstark@91 249 newSample = fabs (newSample);
adamstark@56 250
adamstark@56 251 // add a tiny constant to the sample to stop it from ever going
adamstark@56 252 // to zero. this is to avoid problems further down the line
adamstark@56 253 newSample = newSample + 0.0001;
adamstark@56 254
adamstark@46 255 m0--;
adamstark@58 256 beatCounter--;
adamstark@57 257 beatDueInFrame = false;
adamstark@90 258
adamstark@46 259 // add new sample at the end
adamstark@91 260 onsetDF.addSampleToEnd (newSample);
adamstark@46 261
adamstark@46 262 // update cumulative score
adamstark@91 263 updateCumulativeScore (newSample);
adamstark@46 264
adamstark@97 265 // if we are halfway between beats, predict a beat
adamstark@46 266 if (m0 == 0)
adamstark@97 267 predictBeat();
adamstark@46 268
adamstark@46 269 // if we are at a beat
adamstark@58 270 if (beatCounter == 0)
adamstark@46 271 {
adamstark@57 272 beatDueInFrame = true; // indicate a beat should be output
adamstark@46 273
adamstark@46 274 // recalculate the tempo
adamstark@57 275 resampleOnsetDetectionFunction();
adamstark@57 276 calculateTempo();
adamstark@46 277 }
adamstark@46 278 }
adamstark@46 279
adamstark@51 280 //=======================================================================
adamstark@91 281 void BTrack::setTempo (double tempo)
adamstark@97 282 {
adamstark@46 283 /////////// TEMPO INDICATION RESET //////////////////
adamstark@46 284
adamstark@46 285 // firstly make sure tempo is between 80 and 160 bpm..
adamstark@46 286 while (tempo > 160)
adamstark@97 287 tempo = tempo / 2;
adamstark@46 288
adamstark@46 289 while (tempo < 80)
adamstark@97 290 tempo = tempo * 2;
adamstark@46 291
adamstark@46 292 // convert tempo from bpm value to integer index of tempo probability
adamstark@46 293 int tempo_index = (int) round((tempo - 80)/2);
adamstark@46 294
adamstark@97 295 // now set previous tempo observations to zero and set desired tempo index to 1
adamstark@97 296 std::fill (prevDelta.begin(), prevDelta.end(), 0);
adamstark@58 297 prevDelta[tempo_index] = 1;
adamstark@46 298
adamstark@46 299 /////////// CUMULATIVE SCORE ARTIFICAL TEMPO UPDATE //////////////////
adamstark@46 300
adamstark@46 301 // calculate new beat period
adamstark@97 302 int newBeatPeriod = (int) round (60 / ((((double) hopSize) / 44100) * tempo));
adamstark@46 303
adamstark@97 304 int k = 1;
adamstark@97 305
adamstark@97 306 // initialise onset detection function with delta functions spaced
adamstark@97 307 // at the new beat period
adamstark@97 308 for (int i = onsetDFBufferSize - 1; i >= 0; i--)
adamstark@46 309 {
adamstark@97 310 if (k == 1)
adamstark@46 311 {
adamstark@58 312 cumulativeScore[i] = 150;
adamstark@58 313 onsetDF[i] = 150;
adamstark@46 314 }
adamstark@46 315 else
adamstark@46 316 {
adamstark@58 317 cumulativeScore[i] = 10;
adamstark@58 318 onsetDF[i] = 10;
adamstark@46 319 }
adamstark@46 320
adamstark@97 321 k++;
adamstark@46 322
adamstark@97 323 if (k > newBeatPeriod)
adamstark@46 324 {
adamstark@97 325 k = 1;
adamstark@46 326 }
adamstark@46 327 }
adamstark@46 328
adamstark@46 329 /////////// INDICATE THAT THIS IS A BEAT //////////////////
adamstark@46 330
adamstark@46 331 // beat is now
adamstark@58 332 beatCounter = 0;
adamstark@46 333
adamstark@46 334 // offbeat is half of new beat period away
adamstark@97 335 m0 = (int) round (((double) newBeatPeriod) / 2);
adamstark@46 336 }
adamstark@46 337
adamstark@51 338 //=======================================================================
adamstark@91 339 void BTrack::fixTempo (double tempo)
adamstark@46 340 {
adamstark@46 341 // firstly make sure tempo is between 80 and 160 bpm..
adamstark@46 342 while (tempo > 160)
adamstark@46 343 {
adamstark@46 344 tempo = tempo/2;
adamstark@46 345 }
adamstark@46 346
adamstark@46 347 while (tempo < 80)
adamstark@46 348 {
adamstark@46 349 tempo = tempo * 2;
adamstark@46 350 }
adamstark@46 351
adamstark@46 352 // convert tempo from bpm value to integer index of tempo probability
adamstark@46 353 int tempo_index = (int) round((tempo - 80)/2);
adamstark@46 354
adamstark@46 355 // now set previous fixed previous tempo observation values to zero
adamstark@46 356 for (int i=0;i < 41;i++)
adamstark@46 357 {
adamstark@58 358 prevDeltaFixed[i] = 0;
adamstark@46 359 }
adamstark@46 360
adamstark@46 361 // set desired tempo index to 1
adamstark@58 362 prevDeltaFixed[tempo_index] = 1;
adamstark@46 363
adamstark@46 364 // set the tempo fix flag
adamstark@58 365 tempoFixed = true;
adamstark@46 366 }
adamstark@46 367
adamstark@51 368 //=======================================================================
adamstark@57 369 void BTrack::doNotFixTempo()
adamstark@46 370 {
adamstark@46 371 // set the tempo fix flag
adamstark@58 372 tempoFixed = false;
adamstark@46 373 }
adamstark@46 374
adamstark@51 375 //=======================================================================
adamstark@57 376 void BTrack::resampleOnsetDetectionFunction()
adamstark@46 377 {
adamstark@46 378 float output[512];
adamstark@58 379 float input[onsetDFBufferSize];
adamstark@54 380
adamstark@58 381 for (int i = 0;i < onsetDFBufferSize;i++)
adamstark@58 382 input[i] = (float) onsetDF[i];
adamstark@89 383
adamstark@97 384 double ratio = 512.0 / ((double) onsetDFBufferSize);
adamstark@97 385 int bufferLength = onsetDFBufferSize;
adamstark@97 386 int outputLength = 512;
adamstark@89 387
adamstark@97 388 SRC_DATA src_data;
adamstark@89 389 src_data.data_in = input;
adamstark@97 390 src_data.input_frames = bufferLength;
adamstark@97 391 src_data.src_ratio = ratio;
adamstark@89 392 src_data.data_out = output;
adamstark@97 393 src_data.output_frames = outputLength;
adamstark@89 394
adamstark@89 395 src_simple (&src_data, SRC_SINC_BEST_QUALITY, 1);
adamstark@89 396
adamstark@97 397 for (int i = 0; i < outputLength; i++)
adamstark@89 398 resampledOnsetDF[i] = (double) src_data.data_out[i];
adamstark@46 399 }
adamstark@46 400
adamstark@51 401 //=======================================================================
adamstark@57 402 void BTrack::calculateTempo()
adamstark@46 403 {
adamstark@46 404 // adaptive threshold on input
adamstark@97 405 adaptiveThreshold (resampledOnsetDF, 512);
adamstark@46 406
adamstark@46 407 // calculate auto-correlation function of detection function
adamstark@97 408 calculateBalancedACF (&resampledOnsetDF[0]);
adamstark@46 409
adamstark@46 410 // calculate output of comb filterbank
adamstark@57 411 calculateOutputOfCombFilterBank();
adamstark@46 412
adamstark@46 413 // adaptive threshold on rcf
adamstark@97 414 adaptiveThreshold (combFilterBankOutput, 128);
adamstark@46 415
adamstark@46 416
adamstark@46 417 int t_index;
adamstark@46 418 int t_index2;
adamstark@59 419 // calculate tempo observation vector from beat period observation vector
adamstark@46 420 for (int i = 0;i < 41;i++)
adamstark@46 421 {
adamstark@91 422 t_index = (int) round (tempoToLagFactor / ((double) ((2*i)+80)));
adamstark@91 423 t_index2 = (int) round (tempoToLagFactor / ((double) ((4*i)+160)));
adamstark@46 424
adamstark@46 425
adamstark@58 426 tempoObservationVector[i] = combFilterBankOutput[t_index-1] + combFilterBankOutput[t_index2-1];
adamstark@46 427 }
adamstark@46 428
adamstark@46 429
adamstark@54 430 double maxval;
adamstark@54 431 double maxind;
adamstark@54 432 double curval;
adamstark@46 433
adamstark@46 434 // if tempo is fixed then always use a fixed set of tempi as the previous observation probability function
adamstark@58 435 if (tempoFixed)
adamstark@46 436 {
adamstark@46 437 for (int k = 0;k < 41;k++)
adamstark@46 438 {
adamstark@58 439 prevDelta[k] = prevDeltaFixed[k];
adamstark@46 440 }
adamstark@46 441 }
adamstark@46 442
adamstark@46 443 for (int j=0;j < 41;j++)
adamstark@46 444 {
adamstark@46 445 maxval = -1;
adamstark@46 446 for (int i = 0;i < 41;i++)
adamstark@46 447 {
adamstark@91 448 curval = prevDelta[i] * tempoTransitionMatrix[i][j];
adamstark@46 449
adamstark@46 450 if (curval > maxval)
adamstark@46 451 {
adamstark@46 452 maxval = curval;
adamstark@46 453 }
adamstark@46 454 }
adamstark@46 455
adamstark@91 456 delta[j] = maxval * tempoObservationVector[j];
adamstark@46 457 }
adamstark@46 458
adamstark@46 459
adamstark@97 460 normaliseArray (delta);
adamstark@46 461
adamstark@46 462 maxind = -1;
adamstark@46 463 maxval = -1;
adamstark@46 464
adamstark@46 465 for (int j=0;j < 41;j++)
adamstark@46 466 {
adamstark@46 467 if (delta[j] > maxval)
adamstark@46 468 {
adamstark@46 469 maxval = delta[j];
adamstark@46 470 maxind = j;
adamstark@46 471 }
adamstark@46 472
adamstark@58 473 prevDelta[j] = delta[j];
adamstark@46 474 }
adamstark@46 475
adamstark@91 476 beatPeriod = round ((60.0*44100.0)/(((2*maxind)+80)*((double) hopSize)));
adamstark@46 477
adamstark@57 478 if (beatPeriod > 0)
adamstark@46 479 {
adamstark@91 480 estimatedTempo = 60.0/((((double) hopSize) / 44100.0) * beatPeriod);
adamstark@46 481 }
adamstark@46 482 }
adamstark@46 483
adamstark@51 484 //=======================================================================
adamstark@97 485 void BTrack::adaptiveThreshold (std::vector<double>& x, int N)
adamstark@46 486 {
adamstark@46 487 int i = 0;
adamstark@46 488 int k,t = 0;
adamstark@54 489 double x_thresh[N];
adamstark@46 490
adamstark@46 491 int p_post = 7;
adamstark@46 492 int p_pre = 8;
adamstark@46 493
adamstark@52 494 t = std::min(N,p_post); // what is smaller, p_post of df size. This is to avoid accessing outside of arrays
adamstark@46 495
adamstark@46 496 // find threshold for first 't' samples, where a full average cannot be computed yet
adamstark@97 497 for (i = 0; i <= t; i++)
adamstark@46 498 {
adamstark@97 499 k = std::min ((i + p_pre), N);
adamstark@97 500 x_thresh[i] = calculateMeanOfArray (x, 1, k);
adamstark@46 501 }
adamstark@46 502 // find threshold for bulk of samples across a moving average from [i-p_pre,i+p_post]
adamstark@97 503 for (i = t + 1; i < N - p_post; i++)
adamstark@46 504 {
adamstark@97 505 x_thresh[i] = calculateMeanOfArray (x, i - p_pre, i + p_post);
adamstark@46 506 }
adamstark@46 507 // for last few samples calculate threshold, again, not enough samples to do as above
adamstark@97 508 for (i = N - p_post; i < N; i++)
adamstark@46 509 {
adamstark@97 510 k = std::max ((i - p_post), 1);
adamstark@97 511 x_thresh[i] = calculateMeanOfArray (x, k, N);
adamstark@46 512 }
adamstark@46 513
adamstark@46 514 // subtract the threshold from the detection function and check that it is not less than 0
adamstark@91 515 for (i = 0; i < N; i++)
adamstark@46 516 {
adamstark@46 517 x[i] = x[i] - x_thresh[i];
adamstark@46 518 if (x[i] < 0)
adamstark@46 519 {
adamstark@46 520 x[i] = 0;
adamstark@46 521 }
adamstark@46 522 }
adamstark@46 523 }
adamstark@46 524
adamstark@51 525 //=======================================================================
adamstark@57 526 void BTrack::calculateOutputOfCombFilterBank()
adamstark@46 527 {
adamstark@46 528 int numelem;
adamstark@46 529
adamstark@46 530 for (int i = 0;i < 128;i++)
adamstark@46 531 {
adamstark@58 532 combFilterBankOutput[i] = 0;
adamstark@46 533 }
adamstark@46 534
adamstark@46 535 numelem = 4;
adamstark@46 536
adamstark@91 537 for (int i = 2; i <= 127; i++) // max beat period
adamstark@46 538 {
adamstark@91 539 for (int a = 1; a <= numelem; a++) // number of comb elements
adamstark@46 540 {
adamstark@91 541 for (int b = 1-a; b <= a-1; b++) // general state using normalisation of comb elements
adamstark@46 542 {
adamstark@58 543 combFilterBankOutput[i-1] = combFilterBankOutput[i-1] + (acf[(a*i+b)-1]*weightingVector[i-1])/(2*a-1); // calculate value for comb filter row
adamstark@46 544 }
adamstark@46 545 }
adamstark@46 546 }
adamstark@46 547 }
adamstark@46 548
adamstark@51 549 //=======================================================================
adamstark@91 550 void BTrack::calculateBalancedACF (double* onsetDetectionFunction)
adamstark@46 551 {
adamstark@88 552 int onsetDetectionFunctionLength = 512;
adamstark@88 553
adamstark@93 554 #ifdef USE_FFTW
adamstark@88 555 // copy into complex array and zero pad
adamstark@88 556 for (int i = 0;i < FFTLengthForACFCalculation;i++)
adamstark@88 557 {
adamstark@88 558 if (i < onsetDetectionFunctionLength)
adamstark@88 559 {
adamstark@88 560 complexIn[i][0] = onsetDetectionFunction[i];
adamstark@88 561 complexIn[i][1] = 0.0;
adamstark@88 562 }
adamstark@88 563 else
adamstark@88 564 {
adamstark@88 565 complexIn[i][0] = 0.0;
adamstark@88 566 complexIn[i][1] = 0.0;
adamstark@88 567 }
adamstark@88 568 }
adamstark@88 569
adamstark@88 570 // perform the fft
adamstark@91 571 fftw_execute (acfForwardFFT);
adamstark@88 572
adamstark@88 573 // multiply by complex conjugate
adamstark@88 574 for (int i = 0;i < FFTLengthForACFCalculation;i++)
adamstark@88 575 {
adamstark@88 576 complexOut[i][0] = complexOut[i][0]*complexOut[i][0] + complexOut[i][1]*complexOut[i][1];
adamstark@88 577 complexOut[i][1] = 0.0;
adamstark@88 578 }
adamstark@88 579
adamstark@88 580 // perform the ifft
adamstark@91 581 fftw_execute (acfBackwardFFT);
adamstark@88 582
adamstark@93 583 #endif
adamstark@93 584
adamstark@93 585 #ifdef USE_KISS_FFT
adamstark@93 586 // copy into complex array and zero pad
adamstark@93 587 for (int i = 0;i < FFTLengthForACFCalculation;i++)
adamstark@93 588 {
adamstark@93 589 if (i < onsetDetectionFunctionLength)
adamstark@93 590 {
adamstark@93 591 fftIn[i].r = onsetDetectionFunction[i];
adamstark@93 592 fftIn[i].i = 0.0;
adamstark@93 593 }
adamstark@93 594 else
adamstark@93 595 {
adamstark@93 596 fftIn[i].r = 0.0;
adamstark@93 597 fftIn[i].i = 0.0;
adamstark@93 598 }
adamstark@93 599 }
adamstark@93 600
adamstark@93 601 // execute kiss fft
adamstark@93 602 kiss_fft (cfgForwards, fftIn, fftOut);
adamstark@93 603
adamstark@93 604 // multiply by complex conjugate
adamstark@93 605 for (int i = 0;i < FFTLengthForACFCalculation;i++)
adamstark@93 606 {
adamstark@93 607 fftOut[i].r = fftOut[i].r * fftOut[i].r + fftOut[i].i * fftOut[i].i;
adamstark@93 608 fftOut[i].i = 0.0;
adamstark@93 609 }
adamstark@93 610
adamstark@93 611 // perform the ifft
adamstark@93 612 kiss_fft (cfgBackwards, fftOut, fftIn);
adamstark@93 613
adamstark@93 614 #endif
adamstark@88 615
adamstark@88 616 double lag = 512;
adamstark@88 617
adamstark@91 618 for (int i = 0; i < 512; i++)
adamstark@88 619 {
adamstark@93 620 #ifdef USE_FFTW
adamstark@88 621 // calculate absolute value of result
adamstark@91 622 double absValue = sqrt (complexIn[i][0]*complexIn[i][0] + complexIn[i][1]*complexIn[i][1]);
adamstark@93 623 #endif
adamstark@88 624
adamstark@93 625 #ifdef USE_KISS_FFT
adamstark@93 626 // calculate absolute value of result
adamstark@93 627 double absValue = sqrt (fftIn[i].r * fftIn[i].r + fftIn[i].i * fftIn[i].i);
adamstark@93 628 #endif
adamstark@88 629 // divide by inverse lad to deal with scale bias towards small lags
adamstark@88 630 acf[i] = absValue / lag;
adamstark@88 631
adamstark@88 632 // this division by 1024 is technically unnecessary but it ensures the algorithm produces
adamstark@88 633 // exactly the same ACF output as the old time domain implementation. The time difference is
adamstark@88 634 // minimal so I decided to keep it
adamstark@88 635 acf[i] = acf[i] / 1024.;
adamstark@88 636
adamstark@88 637 lag = lag - 1.;
adamstark@88 638 }
adamstark@46 639 }
adamstark@46 640
adamstark@51 641 //=======================================================================
adamstark@97 642 double BTrack::calculateMeanOfArray (std::vector<double>& array, int startIndex, int endIndex)
adamstark@46 643 {
adamstark@97 644 int length = endIndex - startIndex;
adamstark@97 645 double sum = std::accumulate (array.begin() + startIndex, array.begin() + endIndex, 0.0);
adamstark@47 646
adamstark@47 647 if (length > 0)
adamstark@97 648 return sum / static_cast<double> (length); // average and return
adamstark@47 649 else
adamstark@47 650 return 0;
adamstark@46 651 }
adamstark@46 652
adamstark@51 653 //=======================================================================
adamstark@97 654 void BTrack::normaliseArray (std::vector<double>& array)
adamstark@46 655 {
adamstark@97 656 double sum = std::accumulate (array.begin(), array.end(), 0.0);
adamstark@46 657
adamstark@46 658 if (sum > 0)
adamstark@97 659 {
adamstark@97 660 for (int i = 0; i < array.size(); i++)
adamstark@97 661 array[i] = array[i] / sum;
adamstark@97 662 }
adamstark@46 663 }
adamstark@46 664
adamstark@51 665 //=======================================================================
adamstark@91 666 void BTrack::updateCumulativeScore (double odfSample)
adamstark@46 667 {
adamstark@46 668 int start, end, winsize;
adamstark@54 669 double max;
adamstark@46 670
adamstark@97 671 start = onsetDFBufferSize - round (2. * beatPeriod);
adamstark@97 672 end = onsetDFBufferSize - round (beatPeriod / 2.);
adamstark@97 673 winsize = end - start + 1;
adamstark@46 674
adamstark@54 675 double w1[winsize];
adamstark@97 676 double v = -2. * beatPeriod;
adamstark@54 677 double wcumscore;
adamstark@46 678
adamstark@46 679 // create window
adamstark@91 680 for (int i = 0; i < winsize; i++)
adamstark@46 681 {
adamstark@93 682 w1[i] = exp((-1 * pow (tightness * log (-v / beatPeriod), 2)) / 2);
adamstark@97 683 v = v + 1;
adamstark@46 684 }
adamstark@46 685
adamstark@46 686 // calculate new cumulative score value
adamstark@46 687 max = 0;
adamstark@46 688 int n = 0;
adamstark@91 689 for (int i=start; i <= end; i++)
adamstark@46 690 {
adamstark@58 691 wcumscore = cumulativeScore[i]*w1[n];
adamstark@46 692
adamstark@46 693 if (wcumscore > max)
adamstark@46 694 {
adamstark@46 695 max = wcumscore;
adamstark@46 696 }
adamstark@46 697 n++;
adamstark@46 698 }
adamstark@46 699
adamstark@93 700 latestCumulativeScoreValue = ((1 - alpha) * odfSample) + (alpha * max);
adamstark@89 701
adamstark@91 702 cumulativeScore.addSampleToEnd (latestCumulativeScoreValue);
adamstark@46 703 }
adamstark@46 704
adamstark@51 705 //=======================================================================
adamstark@57 706 void BTrack::predictBeat()
adamstark@46 707 {
adamstark@58 708 int windowSize = (int) beatPeriod;
adamstark@58 709 double futureCumulativeScore[onsetDFBufferSize + windowSize];
adamstark@58 710 double w2[windowSize];
adamstark@93 711
adamstark@46 712 // copy cumscore to first part of fcumscore
adamstark@58 713 for (int i = 0;i < onsetDFBufferSize;i++)
adamstark@46 714 {
adamstark@58 715 futureCumulativeScore[i] = cumulativeScore[i];
adamstark@46 716 }
adamstark@46 717
adamstark@46 718 // create future window
adamstark@54 719 double v = 1;
adamstark@91 720 for (int i = 0; i < windowSize; i++)
adamstark@46 721 {
adamstark@57 722 w2[i] = exp((-1*pow((v - (beatPeriod/2)),2)) / (2*pow((beatPeriod/2) ,2)));
adamstark@46 723 v++;
adamstark@46 724 }
adamstark@46 725
adamstark@46 726 // create past window
adamstark@57 727 v = -2*beatPeriod;
adamstark@58 728 int start = onsetDFBufferSize - round(2*beatPeriod);
adamstark@58 729 int end = onsetDFBufferSize - round(beatPeriod/2);
adamstark@46 730 int pastwinsize = end-start+1;
adamstark@54 731 double w1[pastwinsize];
adamstark@46 732
adamstark@46 733 for (int i = 0;i < pastwinsize;i++)
adamstark@46 734 {
adamstark@57 735 w1[i] = exp((-1*pow(tightness*log(-v/beatPeriod),2))/2);
adamstark@46 736 v = v+1;
adamstark@46 737 }
adamstark@46 738
adamstark@46 739 // calculate future cumulative score
adamstark@54 740 double max;
adamstark@46 741 int n;
adamstark@54 742 double wcumscore;
adamstark@91 743 for (int i = onsetDFBufferSize; i < (onsetDFBufferSize + windowSize); i++)
adamstark@46 744 {
adamstark@91 745 start = i - round (2*beatPeriod);
adamstark@91 746 end = i - round (beatPeriod/2);
adamstark@46 747
adamstark@46 748 max = 0;
adamstark@46 749 n = 0;
adamstark@46 750 for (int k=start;k <= end;k++)
adamstark@46 751 {
adamstark@58 752 wcumscore = futureCumulativeScore[k]*w1[n];
adamstark@46 753
adamstark@46 754 if (wcumscore > max)
adamstark@46 755 {
adamstark@46 756 max = wcumscore;
adamstark@46 757 }
adamstark@46 758 n++;
adamstark@46 759 }
adamstark@46 760
adamstark@58 761 futureCumulativeScore[i] = max;
adamstark@46 762 }
adamstark@46 763
adamstark@46 764 // predict beat
adamstark@46 765 max = 0;
adamstark@46 766 n = 0;
adamstark@46 767
adamstark@91 768 for (int i = onsetDFBufferSize; i < (onsetDFBufferSize + windowSize); i++)
adamstark@46 769 {
adamstark@58 770 wcumscore = futureCumulativeScore[i]*w2[n];
adamstark@46 771
adamstark@46 772 if (wcumscore > max)
adamstark@46 773 {
adamstark@46 774 max = wcumscore;
adamstark@58 775 beatCounter = n;
adamstark@46 776 }
adamstark@46 777
adamstark@46 778 n++;
adamstark@46 779 }
adamstark@46 780
adamstark@46 781 // set next prediction time
adamstark@91 782 m0 = beatCounter + round (beatPeriod / 2);
adamstark@97 783 }