annotate src/BTrack.cpp @ 93:4aa362058011

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