To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
The primary repository for this project is hosted at svn://svn.code.sf.net/p/onsetsds/code/ .
This repository is a read-only copy which is updated automatically every hour.
root / src / onsetsds.c @ 0:a7c2bda0dfd9
History | View | Annotate | Download (15.6 KB)
| 1 |
/*
|
|---|---|
| 2 |
OnsetsDS - real time musical onset detection library.
|
| 3 |
Copyright (c) 2007 Dan Stowell. All rights reserved.
|
| 4 |
|
| 5 |
This program is free software; you can redistribute it and/or modify
|
| 6 |
it under the terms of the GNU General Public License as published by
|
| 7 |
the Free Software Foundation; either version 2 of the License, or
|
| 8 |
(at your option) any later version.
|
| 9 |
|
| 10 |
This program is distributed in the hope that it will be useful,
|
| 11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
GNU General Public License for more details.
|
| 14 |
|
| 15 |
You should have received a copy of the GNU General Public License
|
| 16 |
along with this program; if not, write to the Free Software
|
| 17 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
| 18 |
*/
|
| 19 |
|
| 20 |
|
| 21 |
#include "onsetsds.h" |
| 22 |
|
| 23 |
|
| 24 |
#define ODS_DEBUG_POST_CSV 0 |
| 25 |
|
| 26 |
// Inline
|
| 27 |
inline float onsetsds_phase_rewrap(float phase); |
| 28 |
inline float onsetsds_phase_rewrap(float phase){ |
| 29 |
return (phase>MINUSPI && phase<PI) ? phase : phase + TWOPI * (1.f + floorf((MINUSPI - phase) * INV_TWOPI)); |
| 30 |
} |
| 31 |
|
| 32 |
|
| 33 |
size_t onsetsds_memneeded (int odftype, size_t fftsize, unsigned int medspan){ |
| 34 |
|
| 35 |
/*
|
| 36 |
Need memory for:
|
| 37 |
- median calculation (2 * medspan floats)
|
| 38 |
- storing old values (whether as OdsPolarBuf or as weirder float lists)
|
| 39 |
- storing the OdsPolarBuf (size is NOT sizeof(OdsPolarBuf) but is fftsize)
|
| 40 |
- storing the PSP (numbins + 2 values)
|
| 41 |
All these are floats.
|
| 42 |
*/
|
| 43 |
|
| 44 |
int numbins = (fftsize >> 1) - 1; // No of bins, not counting DC/nyq |
| 45 |
|
| 46 |
switch(odftype){
|
| 47 |
case ODS_ODF_POWER:
|
| 48 |
case ODS_ODF_MAGSUM:
|
| 49 |
|
| 50 |
// No old FFT frames needed, easy:
|
| 51 |
return (medspan+medspan + fftsize + numbins + 2) * sizeof(float); |
| 52 |
|
| 53 |
case ODS_ODF_COMPLEX:
|
| 54 |
case ODS_ODF_RCOMPLEX:
|
| 55 |
|
| 56 |
return (medspan+medspan + fftsize + numbins + 2 |
| 57 |
// For each bin (NOT dc/nyq) we store mag, phase and d_phase
|
| 58 |
+ numbins + numbins + numbins |
| 59 |
) * sizeof(float); |
| 60 |
|
| 61 |
case ODS_ODF_PHASE:
|
| 62 |
case ODS_ODF_WPHASE:
|
| 63 |
|
| 64 |
return (medspan+medspan + fftsize + numbins + 2 |
| 65 |
// For each bin (NOT dc/nyq) we store phase and d_phase
|
| 66 |
+ numbins + numbins |
| 67 |
) * sizeof(float); |
| 68 |
|
| 69 |
case ODS_ODF_MKL:
|
| 70 |
|
| 71 |
return (medspan+medspan + fftsize + numbins + 2 |
| 72 |
// For each bin (NOT dc/nyq) we store mag
|
| 73 |
+ numbins |
| 74 |
) * sizeof(float); |
| 75 |
|
| 76 |
|
| 77 |
break;
|
| 78 |
|
| 79 |
} |
| 80 |
return -1; //bleh |
| 81 |
} |
| 82 |
|
| 83 |
|
| 84 |
void onsetsds_init(OnsetsDS *ods, float *odsdata, int fftformat, |
| 85 |
int odftype, size_t fftsize, unsigned int medspan, float srate){ |
| 86 |
|
| 87 |
// The main pointer to the processing area - other pointers will indicate areas within this
|
| 88 |
ods->data = odsdata; |
| 89 |
// Set all vals in processing area to zero
|
| 90 |
memset(odsdata, 0, onsetsds_memneeded(odftype, fftsize, medspan));
|
| 91 |
|
| 92 |
ods->srate = srate; |
| 93 |
|
| 94 |
int numbins = (fftsize >> 1) - 1; // No of bins, not counting DC/nyq |
| 95 |
int realnumbins = numbins + 2; |
| 96 |
|
| 97 |
// Also point the other pointers to the right places
|
| 98 |
ods->curr = (OdsPolarBuf*) odsdata; |
| 99 |
ods->psp = odsdata + fftsize; |
| 100 |
ods->odfvals = odsdata + fftsize + realnumbins; |
| 101 |
ods->sortbuf = odsdata + fftsize + realnumbins + medspan; |
| 102 |
ods->other = odsdata + fftsize + realnumbins + medspan + medspan; |
| 103 |
|
| 104 |
// Default settings for Adaptive Whitening, user can set own values after init
|
| 105 |
onsetsds_setrelax(ods, 1.f, fftsize>>1); |
| 106 |
ods->floor = 0.1; |
| 107 |
|
| 108 |
switch(odftype){
|
| 109 |
case ODS_ODF_POWER:
|
| 110 |
ods->odfparam = 0.01; // "powthresh" in SC code |
| 111 |
ods->normfactor = 2560.f / (realnumbins * fftsize); |
| 112 |
break;
|
| 113 |
case ODS_ODF_MAGSUM:
|
| 114 |
ods->odfparam = 0.01; // "powthresh" in SC code |
| 115 |
ods->normfactor = 113.137085f / (realnumbins * sqrt(fftsize)); |
| 116 |
break;
|
| 117 |
case ODS_ODF_COMPLEX:
|
| 118 |
ods->odfparam = 0.01; // "powthresh" in SC code |
| 119 |
ods->normfactor = 231.70475f / pow(fftsize, 1.5);// / fftsize; |
| 120 |
break;
|
| 121 |
case ODS_ODF_RCOMPLEX:
|
| 122 |
ods->odfparam = 0.01; // "powthresh" in SC code |
| 123 |
ods->normfactor = 231.70475f / pow(fftsize, 1.5);// / fftsize; |
| 124 |
break;
|
| 125 |
case ODS_ODF_PHASE:
|
| 126 |
ods->odfparam = 0.01; // "powthresh" in SC code |
| 127 |
ods->normfactor = 5.12f / fftsize;// / fftsize; |
| 128 |
break;
|
| 129 |
case ODS_ODF_WPHASE:
|
| 130 |
ods->odfparam = 0.0001; // "powthresh" in SC code. For WPHASE it's kind of superfluous. |
| 131 |
ods->normfactor = 115.852375f / pow(fftsize, 1.5);// / fftsize; |
| 132 |
break;
|
| 133 |
case ODS_ODF_MKL:
|
| 134 |
ods->odfparam = 0.01; // EPSILON parameter. Brossier recommends 1e-6 but I (ICMC 2007) found larger vals (e.g 0.01) to work better |
| 135 |
ods->normfactor = 7.68f * 0.25f / fftsize; |
| 136 |
break;
|
| 137 |
default:
|
| 138 |
printf("onsetsds_init ERROR: \"odftype\" is not a recognised value\n");
|
| 139 |
} |
| 140 |
|
| 141 |
ods->odfvalpost = 0.f;
|
| 142 |
ods->odfvalpostprev = 0.f;
|
| 143 |
ods->thresh = 0.5f; |
| 144 |
ods->logmags = false;
|
| 145 |
|
| 146 |
ods->odftype = odftype; |
| 147 |
ods->whtype = ODS_WH_ADAPT_MAX1; |
| 148 |
ods->fftformat = fftformat; |
| 149 |
|
| 150 |
ods->whiten = (odftype != ODS_ODF_MKL); // Deactivate whitening for MKL by default
|
| 151 |
ods->detected = false;
|
| 152 |
ods->med_odd = (medspan & 1) != 0; |
| 153 |
|
| 154 |
ods->medspan = medspan; |
| 155 |
|
| 156 |
ods->mingap = 0;
|
| 157 |
ods->gapleft = 0;
|
| 158 |
|
| 159 |
ods->fftsize = fftsize; |
| 160 |
ods->numbins = numbins; |
| 161 |
|
| 162 |
//printf("End of _init: normfactor is %g\n", ods->normfactor);
|
| 163 |
|
| 164 |
} |
| 165 |
|
| 166 |
bool onsetsds_process(OnsetsDS* ods, float* fftbuf){ |
| 167 |
onsetsds_loadframe(ods, fftbuf); |
| 168 |
|
| 169 |
onsetsds_whiten(ods); |
| 170 |
onsetsds_odf(ods); |
| 171 |
onsetsds_detect(ods); |
| 172 |
|
| 173 |
return ods->detected;
|
| 174 |
} |
| 175 |
|
| 176 |
|
| 177 |
void onsetsds_setrelax(OnsetsDS* ods, float time, size_t hopsize){ |
| 178 |
ods->relaxtime = time; |
| 179 |
ods->relaxcoef = (time == 0.0f) ? 0.0f : exp((ods_log1 * hopsize)/(time * ods->srate)); |
| 180 |
} |
| 181 |
|
| 182 |
|
| 183 |
|
| 184 |
void onsetsds_loadframe(OnsetsDS* ods, float* fftbuf){ |
| 185 |
|
| 186 |
float *pos, *pos2, imag, real;
|
| 187 |
int i;
|
| 188 |
|
| 189 |
switch(ods->fftformat){
|
| 190 |
case ODS_FFT_SC3_POLAR:
|
| 191 |
// The format is the same! dc, nyq, mag[1], phase[1], ...
|
| 192 |
memcpy(ods->curr, fftbuf, ods->fftsize * sizeof(float)); |
| 193 |
break;
|
| 194 |
|
| 195 |
case ODS_FFT_SC3_COMPLEX:
|
| 196 |
|
| 197 |
ods->curr->dc = fftbuf[0];
|
| 198 |
ods->curr->nyq = fftbuf[1];
|
| 199 |
|
| 200 |
// Then convert cartesian to polar:
|
| 201 |
pos = fftbuf + 2;
|
| 202 |
for(i=0; i< (ods->numbins << 1); i += 2){ |
| 203 |
real = pos[i]; |
| 204 |
imag = pos[i+1]; // Plus 1 rather than increment; seems to avoid LSU reject on my PPC |
| 205 |
ods->curr->bin[i].mag = hypotf(imag, real); |
| 206 |
ods->curr->bin[i].phase = atan2f(imag, real); |
| 207 |
} |
| 208 |
break;
|
| 209 |
|
| 210 |
case ODS_FFT_FFTW3_HC:
|
| 211 |
|
| 212 |
ods->curr->dc = fftbuf[0];
|
| 213 |
ods->curr->nyq = fftbuf[ods->fftsize>>1];
|
| 214 |
|
| 215 |
// Then convert cartesian to polar:
|
| 216 |
// (Starting positions: real and imag for bin 1)
|
| 217 |
pos = fftbuf + 1;
|
| 218 |
pos2 = fftbuf + ods->fftsize - 1;
|
| 219 |
for(i=0; i<ods->numbins; i++){ |
| 220 |
real = *(pos++); |
| 221 |
imag = *(pos2--); |
| 222 |
ods->curr->bin[i].mag = hypotf(imag, real); |
| 223 |
ods->curr->bin[i].phase = atan2f(imag, real); |
| 224 |
} |
| 225 |
break;
|
| 226 |
|
| 227 |
case ODS_FFT_FFTW3_R2C:
|
| 228 |
|
| 229 |
ods->curr->dc = fftbuf[0];
|
| 230 |
ods->curr->nyq = fftbuf[ods->fftsize]; |
| 231 |
|
| 232 |
// Then convert cartesian to polar:
|
| 233 |
pos = fftbuf + 2;
|
| 234 |
for(i=0; i<ods->numbins; i++){ |
| 235 |
real = *(pos++); |
| 236 |
imag = *(pos++); |
| 237 |
ods->curr->bin[i].mag = hypotf(imag, real); |
| 238 |
ods->curr->bin[i].phase = atan2f(imag, real); |
| 239 |
} |
| 240 |
break;
|
| 241 |
|
| 242 |
} |
| 243 |
|
| 244 |
// Conversion to log-domain magnitudes, including re-scaling to aim back at the zero-to-one range.
|
| 245 |
// Not well tested yet.
|
| 246 |
if(ods->logmags){
|
| 247 |
for(i=0; i<ods->numbins; i++){ |
| 248 |
ods->curr->bin[i].mag = |
| 249 |
(log(ods_max(ods->curr->bin[i].mag, ODS_LOG_LOWER_LIMIT)) - ODS_LOGOF_LOG_LOWER_LIMIT) * ODS_ABSINVOF_LOGOF_LOG_LOWER_LIMIT; |
| 250 |
} |
| 251 |
ods->curr->dc = |
| 252 |
(log(ods_max(ods_abs(ods->curr->dc ), ODS_LOG_LOWER_LIMIT)) - ODS_LOGOF_LOG_LOWER_LIMIT) * ODS_ABSINVOF_LOGOF_LOG_LOWER_LIMIT; |
| 253 |
ods->curr->nyq = |
| 254 |
(log(ods_max(ods_abs(ods->curr->nyq), ODS_LOG_LOWER_LIMIT)) - ODS_LOGOF_LOG_LOWER_LIMIT) * ODS_ABSINVOF_LOGOF_LOG_LOWER_LIMIT; |
| 255 |
} |
| 256 |
|
| 257 |
} |
| 258 |
|
| 259 |
void onsetsds_whiten(OnsetsDS* ods){
|
| 260 |
|
| 261 |
if(ods->whtype == ODS_WH_NONE){
|
| 262 |
//printf("onsetsds_whiten(): ODS_WH_NONE, skipping\n");
|
| 263 |
return;
|
| 264 |
} |
| 265 |
|
| 266 |
// NB: Apart from the above, ods->whtype is currently IGNORED and only one mode is used.
|
| 267 |
|
| 268 |
|
| 269 |
float val,oldval, relaxcoef, floor;
|
| 270 |
int numbins, i;
|
| 271 |
OdsPolarBuf *curr; |
| 272 |
float *psp;
|
| 273 |
float *pspp1; // Offset by 1, avoids quite a lot of "+1"s in the following code |
| 274 |
|
| 275 |
relaxcoef = ods->relaxcoef; |
| 276 |
numbins = ods->numbins; |
| 277 |
curr = ods->curr; |
| 278 |
psp = ods->psp; |
| 279 |
pspp1 = psp + 1;
|
| 280 |
floor = ods->floor; |
| 281 |
|
| 282 |
//printf("onsetsds_whiten: relaxcoef=%g, relaxtime=%g, floor=%g\n", relaxcoef, ods->relaxtime, floor);
|
| 283 |
|
| 284 |
////////////////////// For each bin, update the record of the peak value /////////////////////
|
| 285 |
|
| 286 |
val = fabs(curr->dc); // Grab current magnitude
|
| 287 |
oldval = psp[0];
|
| 288 |
// If it beats the amplitude stored then that's our new amplitude;
|
| 289 |
// otherwise our new amplitude is a decayed version of the old one
|
| 290 |
if(val < oldval) {
|
| 291 |
val = val + (oldval - val) * relaxcoef; |
| 292 |
} |
| 293 |
psp[0] = val; // Store the "amplitude trace" back |
| 294 |
|
| 295 |
val = fabs(curr->nyq); |
| 296 |
oldval = pspp1[numbins]; |
| 297 |
if(val < oldval) {
|
| 298 |
val = val + (oldval - val) * relaxcoef; |
| 299 |
} |
| 300 |
pspp1[numbins] = val; |
| 301 |
|
| 302 |
for(i=0; i<numbins; ++i){ |
| 303 |
val = fabs(curr->bin[i].mag); |
| 304 |
oldval = pspp1[i]; |
| 305 |
if(val < oldval) {
|
| 306 |
val = val + (oldval - val) * relaxcoef; |
| 307 |
} |
| 308 |
pspp1[i] = val; |
| 309 |
} |
| 310 |
|
| 311 |
//////////////////////////// Now for each bin, rescale the current magnitude ////////////////////////////
|
| 312 |
curr->dc /= ods_max(floor, psp[0]);
|
| 313 |
curr->nyq /= ods_max(floor, pspp1[numbins]); |
| 314 |
for(i=0; i<numbins; ++i){ |
| 315 |
curr->bin[i].mag /= ods_max(floor, pspp1[i]); |
| 316 |
} |
| 317 |
} |
| 318 |
|
| 319 |
void onsetsds_odf(OnsetsDS* ods){
|
| 320 |
|
| 321 |
int numbins = ods->numbins;
|
| 322 |
OdsPolarBuf *curr = ods->curr; |
| 323 |
float* val = ods->odfvals;
|
| 324 |
int i, tbpointer;
|
| 325 |
float deviation, diff, curmag;
|
| 326 |
double totdev;
|
| 327 |
|
| 328 |
bool rectify = true; |
| 329 |
|
| 330 |
// Here we shunt the "old" ODF values down one place
|
| 331 |
memcpy(val + 1, val, (ods->medspan - 1)*sizeof(float)); |
| 332 |
|
| 333 |
// Now calculate a new value and store in ods->odfvals[0]
|
| 334 |
switch(ods->odftype){
|
| 335 |
case ODS_ODF_POWER:
|
| 336 |
|
| 337 |
*val = (curr->nyq * curr->nyq) + (curr->dc * curr->dc); |
| 338 |
for(i=0; i<numbins; i++){ |
| 339 |
*val += curr->bin[i].mag * curr->bin[i].mag; |
| 340 |
} |
| 341 |
break;
|
| 342 |
|
| 343 |
case ODS_ODF_MAGSUM:
|
| 344 |
|
| 345 |
*val = ods_abs(curr->nyq) + ods_abs(curr->dc); |
| 346 |
|
| 347 |
for(i=0; i<numbins; i++){ |
| 348 |
*val += ods_abs(curr->bin[i].mag); |
| 349 |
} |
| 350 |
break;
|
| 351 |
|
| 352 |
case ODS_ODF_COMPLEX:
|
| 353 |
rectify = false;
|
| 354 |
// ...and then drop through to:
|
| 355 |
case ODS_ODF_RCOMPLEX:
|
| 356 |
|
| 357 |
// Note: "other" buf is stored in this format: mag[0],phase[0],d_phase[0],mag[1],phase[1],d_phase[1], ...
|
| 358 |
|
| 359 |
// Iterate through, calculating the deviation from expected value.
|
| 360 |
totdev = 0.0; |
| 361 |
tbpointer = 0;
|
| 362 |
float predmag, predphase, yesterphase, yesterphasediff;
|
| 363 |
for (i=0; i<numbins; ++i) { |
| 364 |
curmag = ods_abs(curr->bin[i].mag); |
| 365 |
|
| 366 |
// Predict mag as yestermag
|
| 367 |
predmag = ods->other[tbpointer++]; |
| 368 |
yesterphase = ods->other[tbpointer++]; |
| 369 |
yesterphasediff = ods->other[tbpointer++]; |
| 370 |
|
| 371 |
// Thresholding as Brossier did - discard (ignore) bin's deviation if bin's power is minimal
|
| 372 |
if(curmag > ods->odfparam) {
|
| 373 |
// If rectifying, ignore decreasing bins
|
| 374 |
if((!rectify) || !(curmag < predmag)){
|
| 375 |
|
| 376 |
// Predict phase as yesterval + yesterfirstdiff
|
| 377 |
predphase = yesterphase + yesterphasediff; |
| 378 |
|
| 379 |
// Here temporarily using the "deviation" var to store the phase difference
|
| 380 |
// so that the rewrap macro can use it more efficiently
|
| 381 |
deviation = predphase - curr->bin[i].phase; |
| 382 |
|
| 383 |
// Deviation is Euclidean distance between predicted and actual.
|
| 384 |
// In polar coords: sqrt(r1^2 + r2^2 - r1r2 cos (theta1 - theta2))
|
| 385 |
deviation = sqrtf(predmag * predmag + curmag * curmag |
| 386 |
- predmag * curmag * cosf(onsetsds_phase_rewrap(deviation)) |
| 387 |
); |
| 388 |
|
| 389 |
totdev += deviation; |
| 390 |
} |
| 391 |
} |
| 392 |
} |
| 393 |
|
| 394 |
// totdev will be the output, but first we need to fill tempbuf with today's values, ready for tomorrow.
|
| 395 |
tbpointer = 0;
|
| 396 |
for (i=0; i<numbins; ++i) { |
| 397 |
ods->other[tbpointer++] = ods_abs(curr->bin[i].mag); // Storing mag
|
| 398 |
diff = curr->bin[i].phase - ods->other[tbpointer]; // Retrieving yesterphase from buf
|
| 399 |
ods->other[tbpointer++] = curr->bin[i].phase; // Storing phase
|
| 400 |
// Wrap onto +-PI range
|
| 401 |
diff = onsetsds_phase_rewrap(diff); |
| 402 |
|
| 403 |
ods->other[tbpointer++] = diff; // Storing first diff to buf
|
| 404 |
|
| 405 |
} |
| 406 |
*val = (float)totdev;
|
| 407 |
|
| 408 |
break;
|
| 409 |
|
| 410 |
|
| 411 |
case ODS_ODF_PHASE:
|
| 412 |
rectify = false; // So, actually, "rectify" means "useweighting" in this context |
| 413 |
// ...and then drop through to:
|
| 414 |
case ODS_ODF_WPHASE:
|
| 415 |
|
| 416 |
// Note: "other" buf is stored in this format: phase[0],d_phase[0],phase[1],d_phase[1], ...
|
| 417 |
|
| 418 |
// Iterate through, calculating the deviation from expected value.
|
| 419 |
totdev = 0.0; |
| 420 |
tbpointer = 0;
|
| 421 |
for (i=0; i<numbins; ++i) { |
| 422 |
// Thresholding as Brossier did - discard (ignore) bin's phase deviation if bin's power is low
|
| 423 |
if(ods_abs(curr->bin[i].mag) > ods->odfparam) {
|
| 424 |
|
| 425 |
// Deviation is the *second difference* of the phase, which is calc'ed as curval - yesterval - yesterfirstdiff
|
| 426 |
deviation = curr->bin[i].phase - ods->other[tbpointer++] - ods->other[tbpointer++]; |
| 427 |
// Wrap onto +-PI range
|
| 428 |
deviation = onsetsds_phase_rewrap(deviation); |
| 429 |
|
| 430 |
if(rectify){ // "rectify" meaning "useweighting"... |
| 431 |
totdev += fabs(deviation * ods_abs(curr->bin[i].mag)); |
| 432 |
} else {
|
| 433 |
totdev += fabs(deviation); |
| 434 |
} |
| 435 |
} |
| 436 |
} |
| 437 |
|
| 438 |
// totdev will be the output, but first we need to fill tempbuf with today's values, ready for tomorrow.
|
| 439 |
tbpointer = 0;
|
| 440 |
for (i=0; i<numbins; ++i) { |
| 441 |
diff = curr->bin[i].phase - ods->other[tbpointer]; // Retrieving yesterphase from buf
|
| 442 |
ods->other[tbpointer++] = curr->bin[i].phase; // Storing phase
|
| 443 |
// Wrap onto +-PI range
|
| 444 |
diff = onsetsds_phase_rewrap(diff); |
| 445 |
|
| 446 |
ods->other[tbpointer++] = diff; // Storing first diff to buf
|
| 447 |
|
| 448 |
} |
| 449 |
*val = (float)totdev;
|
| 450 |
break;
|
| 451 |
|
| 452 |
|
| 453 |
case ODS_ODF_MKL:
|
| 454 |
|
| 455 |
// Iterate through, calculating the Modified Kullback-Liebler distance
|
| 456 |
totdev = 0.0; |
| 457 |
tbpointer = 0;
|
| 458 |
float yestermag;
|
| 459 |
for (i=0; i<numbins; ++i) { |
| 460 |
curmag = ods_abs(curr->bin[i].mag); |
| 461 |
yestermag = ods->other[tbpointer]; |
| 462 |
|
| 463 |
// Here's the main implementation of Brossier's MKL eq'n (eqn 2.9 from his thesis):
|
| 464 |
deviation = ods_abs(curmag) / (ods_abs(yestermag) + ods->odfparam); |
| 465 |
totdev += log(1.f + deviation);
|
| 466 |
|
| 467 |
// Store the mag as yestermag
|
| 468 |
ods->other[tbpointer++] = curmag; |
| 469 |
} |
| 470 |
*val = (float)totdev;
|
| 471 |
break;
|
| 472 |
|
| 473 |
} |
| 474 |
|
| 475 |
#if ODS_DEBUG_POST_CSV
|
| 476 |
printf("%g,", *val);
|
| 477 |
printf("%g,", ods->odfvals[0] * ods->normfactor); |
| 478 |
#endif
|
| 479 |
|
| 480 |
ods->odfvals[0] *= ods->normfactor;
|
| 481 |
} |
| 482 |
// End of ODF function
|
| 483 |
|
| 484 |
void SelectionSort(float *array, int length); |
| 485 |
void SelectionSort(float *array, int length) |
| 486 |
{
|
| 487 |
// Algo is simply based on http://en.wikibooks.org/wiki/Algorithm_implementation/Sorting/Selection_sort
|
| 488 |
int max, i;
|
| 489 |
float temp;
|
| 490 |
while(length > 0) |
| 491 |
{
|
| 492 |
max = 0;
|
| 493 |
for(i = 1; i < length; i++) |
| 494 |
if(array[i] > array[max])
|
| 495 |
max = i; |
| 496 |
temp = array[length-1];
|
| 497 |
array[length-1] = array[max];
|
| 498 |
array[max] = temp; |
| 499 |
length--; |
| 500 |
} |
| 501 |
} |
| 502 |
|
| 503 |
|
| 504 |
void onsetsds_detect(OnsetsDS* ods){
|
| 505 |
|
| 506 |
// Shift the yesterval to its rightful place
|
| 507 |
ods->odfvalpostprev = ods->odfvalpost; |
| 508 |
|
| 509 |
///////// MEDIAN REMOVAL ////////////
|
| 510 |
|
| 511 |
float* sortbuf = ods->sortbuf;
|
| 512 |
int medspan = ods->medspan;
|
| 513 |
|
| 514 |
// Copy odfvals to sortbuf
|
| 515 |
memcpy(sortbuf, ods->odfvals, medspan * sizeof(float)); |
| 516 |
|
| 517 |
// Sort sortbuf
|
| 518 |
SelectionSort(sortbuf, medspan); |
| 519 |
|
| 520 |
// Subtract the middlest value === the median
|
| 521 |
if(ods->med_odd){
|
| 522 |
ods->odfvalpost = ods->odfvals[0]
|
| 523 |
- sortbuf[(medspan - 1) >> 1]; |
| 524 |
}else{
|
| 525 |
ods->odfvalpost = ods->odfvals[0]
|
| 526 |
- ((sortbuf[medspan >> 1]
|
| 527 |
+ sortbuf[(medspan >> 1) - 1]) * 0.5f); |
| 528 |
|
| 529 |
} |
| 530 |
|
| 531 |
// Detection not allowed if we're too close to a previous detection.
|
| 532 |
if(ods->gapleft != 0) { |
| 533 |
ods->gapleft--; |
| 534 |
ods->detected = false;
|
| 535 |
} else {
|
| 536 |
// Now do the detection.
|
| 537 |
ods->detected = (ods->odfvalpost > ods->thresh) && (ods->odfvalpostprev <= ods->thresh); |
| 538 |
if(ods->detected){
|
| 539 |
ods->gapleft = ods->mingap; |
| 540 |
} |
| 541 |
} |
| 542 |
#if ODS_DEBUG_POST_CSV
|
| 543 |
printf("%g\n", ods->odfvalpost);
|
| 544 |
#endif
|
| 545 |
} |
| 546 |
|
| 547 |
|