summaryrefslogtreecommitdiff
path: root/src/pitch/pitchmcomb.c
blob: f9ec61078b27c4e50b28a1ff7901fbb38367bcee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
/*
  Copyright (C) 2003-2009 Paul Brossier <piem@aubio.org>

  This file is part of aubio.

  aubio is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  aubio is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with aubio.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "aubio_priv.h"
#include "fvec.h"
#include "cvec.h"
#include "mathutils.h"
#include "pitch/pitchmcomb.h"

#define CAND_SWAP(a,b) { register aubio_spectralcandidate_t *t=(a);(a)=(b);(b)=t; }

typedef struct _aubio_spectralpeak_t aubio_spectralpeak_t;
typedef struct _aubio_spectralcandidate_t aubio_spectralcandidate_t;
uint_t aubio_pitchmcomb_get_root_peak (aubio_spectralpeak_t * peaks,
    uint_t length);
uint_t aubio_pitchmcomb_quadpick (aubio_spectralpeak_t * spectral_peaks,
    const fvec_t * X);
void aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, const fvec_t * oldmag);
void aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, const fvec_t * newmag);
/* not used but useful : sort by amplitudes (or anything else)
 * sort_pitchpeak(peaks, length);
 */
/** spectral_peak comparison function (must return signed int) */
static sint_t aubio_pitchmcomb_sort_peak_comp (const void *x, const void *y);
/** sort spectral_peak against their mag */
void aubio_pitchmcomb_sort_peak (aubio_spectralpeak_t * peaks, uint_t nbins);
/** select the best candidates */
uint_t aubio_pitch_cands (aubio_pitchmcomb_t * p, const cvec_t * fftgrain,
    smpl_t * cands);

/** sort spectral_candidate against their comb ene */
void aubio_pitchmcomb_sort_cand_ene (aubio_spectralcandidate_t ** candidates,
    uint_t nbins);
/** sort spectral_candidate against their frequency */
void aubio_pitchmcomb_sort_cand_freq (aubio_spectralcandidate_t ** candidates,
    uint_t nbins);

struct _aubio_pitchmcomb_t
{
  smpl_t threshold;                        /**< offset threshold [0.033 or 0.01]     */
  smpl_t alpha;                            /**< normalisation exponent [9]           */
  smpl_t cutoff;                           /**< low-pass filter cutoff [0.34, 1]     */
  smpl_t tol;                              /**< tolerance [0.05]                     */
  // smpl_t tau;                              /**< frequency precision [44100/4096]     */
  uint_t win_post;                         /**< median filter window length          */
  uint_t win_pre;                          /**< median filter window                 */
  uint_t ncand;                            /**< maximum number of candidates (combs) */
  uint_t npartials;                        /**< maximum number of partials per combs */
  uint_t count;                            /**< picked picks                         */
  uint_t goodcandidate;                    /**< best candidate                       */
  uint_t spec_partition;                   /**< spectrum partition to consider       */
  aubio_spectralpeak_t *peaks;             /**< up to length win/spec_partition      */
  aubio_spectralcandidate_t **candidates;  /** up to five candidates                 */
  /* some scratch pads */
  /** \bug  (unnecessary copied from fftgrain?) */
  fvec_t *newmag;                          /**< vec to store mag                     */
  fvec_t *scratch;                         /**< vec to store modified mag            */
  fvec_t *scratch2;                        /**< vec to compute moving median         */
  fvec_t *theta;                          /**< vec to store phase                     */
  smpl_t phasediff;
  smpl_t phasefreq;
  /** threshfn: name or handle of fn for computing adaptive threshold [median] */
  /** aubio_thresholdfn_t thresholdfn; */
  /** picker: name or handle of fn for picking event times [quadpick] */
  /** aubio_pickerfn_t pickerfn; */
};

/** spectral peak object */
struct _aubio_spectralpeak_t
{
  uint_t bin;     /**< bin [0-(length-1)] */
  smpl_t ebin;    /**< estimated bin */
  smpl_t mag;     /**< peak magnitude */
};

/** spectral candidates array object */
struct _aubio_spectralcandidate_t
{
  smpl_t ebin;    /**< interpolated bin */
  smpl_t *ecomb;  /**< comb */
  smpl_t ene;     /**< candidate energy */
  smpl_t len;     /**< length */
};


void
aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, const cvec_t * fftgrain, fvec_t * output)
{
  uint_t j;
  smpl_t instfreq;
  fvec_t *newmag = (fvec_t *) p->newmag;
  //smpl_t hfc; //fe=instfreq(theta1,theta,ops); //theta1=theta;
  /* copy incoming grain to newmag */
  for (j = 0; j < newmag->length; j++)
    newmag->data[j] = fftgrain->norm[j];
  /* detect only if local energy > 10. */
  //if (aubio_level_lin (newmag) * newmag->length > 10.) {
  //hfc = fvec_local_hfc(newmag); //not used
  aubio_pitchmcomb_spectral_pp (p, newmag);
  aubio_pitchmcomb_combdet (p, newmag);
  //aubio_pitchmcomb_sort_cand_freq(p->candidates,p->ncand);
  //return p->candidates[p->goodcandidate]->ebin;
  j = (uint_t) FLOOR (p->candidates[p->goodcandidate]->ebin + .5);
  instfreq = aubio_unwrap2pi (fftgrain->phas[j]
      - p->theta->data[j] - j * p->phasediff);
  instfreq *= p->phasefreq;
  /* store phase for next run */
  for (j = 0; j < p->theta->length; j++) {
    p->theta->data[j] = fftgrain->phas[j];
  }
  //return p->candidates[p->goodcandidate]->ebin;
  output->data[0] =
      FLOOR (p->candidates[p->goodcandidate]->ebin + .5) + instfreq;
  /*} else {
     return -1.;
     } */
}

uint_t
aubio_pitch_cands (aubio_pitchmcomb_t * p, const cvec_t * fftgrain, smpl_t * cands)
{
  uint_t j;
  uint_t k;
  fvec_t *newmag = (fvec_t *) p->newmag;
  aubio_spectralcandidate_t **scands =
      (aubio_spectralcandidate_t **) (p->candidates);
  //smpl_t hfc; //fe=instfreq(theta1,theta,ops); //theta1=theta;
  /* copy incoming grain to newmag */
  for (j = 0; j < newmag->length; j++)
    newmag->data[j] = fftgrain->norm[j];
  /* detect only if local energy > 10. */
  if (aubio_level_lin (newmag) * newmag->length > 10.) {
    /* hfc = fvec_local_hfc(newmag); do not use */
    aubio_pitchmcomb_spectral_pp (p, newmag);
    aubio_pitchmcomb_combdet (p, newmag);
    aubio_pitchmcomb_sort_cand_freq (scands, p->ncand);
    /* store ncand comb energies in cands[1:ncand] */
    for (k = 0; k < p->ncand; k++)
      cands[k] = p->candidates[k]->ene;
    /* store ncand[end] freq in cands[end] */
    cands[p->ncand] = p->candidates[p->ncand - 1]->ebin;
    return 1;
  } else {
    for (k = 0; k < p->ncand; k++)
      cands[k] = 0;
    return 0;
  }
}

void
aubio_pitchmcomb_spectral_pp (aubio_pitchmcomb_t * p, const fvec_t * newmag)
{
  fvec_t *mag = (fvec_t *) p->scratch;
  fvec_t *tmp = (fvec_t *) p->scratch2;
  uint_t j;
  uint_t length = mag->length;
  /* copy newmag to mag (scracth) */
  for (j = 0; j < length; j++) {
    mag->data[j] = newmag->data[j];
  }
  fvec_min_removal (mag);       /* min removal          */
  fvec_alpha_normalise (mag, p->alpha); /* alpha normalisation  */
  /* skipped *//* low pass filtering   */
  /** \bug fvec_moving_thres may write out of bounds */
  fvec_adapt_thres (mag, tmp, p->win_post, p->win_pre);      /* adaptative threshold */
  fvec_add (mag, -p->threshold);        /* fixed threshold      */
  {
    aubio_spectralpeak_t *peaks = (aubio_spectralpeak_t *) p->peaks;
    uint_t count;
    /*  return bin and ebin */
    count = aubio_pitchmcomb_quadpick (peaks, mag);
    for (j = 0; j < count; j++)
      peaks[j].mag = newmag->data[peaks[j].bin];
    /* reset non peaks */
    for (j = count; j < length; j++)
      peaks[j].mag = 0.;
    p->peaks = peaks;
    p->count = count;
  }
}

void
aubio_pitchmcomb_combdet (aubio_pitchmcomb_t * p, const fvec_t * newmag)
{
  aubio_spectralpeak_t *peaks = (aubio_spectralpeak_t *) p->peaks;
  aubio_spectralcandidate_t **candidate =
      (aubio_spectralcandidate_t **) p->candidates;

  /* parms */
  uint_t N = p->npartials;      /* maximum number of partials to be considered 10 */
  uint_t M = p->ncand;          /* maximum number of combs to be considered 5 */
  uint_t length = newmag->length;
  uint_t count = p->count;
  uint_t k;
  uint_t l;
  uint_t d;
  uint_t curlen = 0;

  smpl_t delta2;
  smpl_t xx;
  uint_t position = 0;

  uint_t root_peak = 0;
  uint_t tmpl = 0;
  smpl_t tmpene = 0.;

  /* get the biggest peak in the spectrum */
  root_peak = aubio_pitchmcomb_get_root_peak (peaks, count);
  /* not enough partials in highest notes, could be forced */
  //if (peaks[root_peak].ebin >= aubio_miditofreq(85.)/p->tau) N=2;
  //if (peaks[root_peak].ebin >= aubio_miditofreq(90.)/p->tau) N=1;
  /* now calculate the energy of each of the 5 combs */
  for (l = 0; l < M; l++) {
    smpl_t scaler = (1. / (l + 1.));
    candidate[l]->ene = 0.;     /* reset ene and len sums */
    candidate[l]->len = 0.;
    candidate[l]->ebin = scaler * peaks[root_peak].ebin;
    /* if less than N peaks available, curlen < N */
    if (candidate[l]->ebin != 0.)
      curlen = (uint_t) FLOOR (length / (candidate[l]->ebin));
    curlen = (N < curlen) ? N : curlen;
    /* fill candidate[l]->ecomb[k] with (k+1)*candidate[l]->ebin */
    for (k = 0; k < curlen; k++)
      candidate[l]->ecomb[k] = (candidate[l]->ebin) * (k + 1.);
    for (k = curlen; k < length; k++)
      candidate[l]->ecomb[k] = 0.;
    /* for each in candidate[l]->ecomb[k] */
    for (k = 0; k < curlen; k++) {
      xx = 100000.;
      /** get the candidate->ecomb the closer to peaks.ebin
       * (to cope with the inharmonicity)*/
      for (d = 0; d < count; d++) {
        delta2 = ABS (candidate[l]->ecomb[k] - peaks[d].ebin);
        if (delta2 <= xx) {
          position = d;
          xx = delta2;
        }
      }
      /* for a Q factor of 17, maintaining "constant Q filtering",
       * and sum energy and length over non null combs */
      if (17. * xx < candidate[l]->ecomb[k]) {
        candidate[l]->ecomb[k] = peaks[position].ebin;
        candidate[l]->ene +=    /* ecomb rounded to nearest int */
            POW (newmag->data[(uint_t) FLOOR (candidate[l]->ecomb[k] + .5)],
            0.25);
        candidate[l]->len += 1. / curlen;
      } else
        candidate[l]->ecomb[k] = 0.;
    }
    /* punishment */
    /*if (candidate[l]->len<0.6)
       candidate[l]->ene=0.; */
    /* remember best candidate energy (in polyphonic, could check for
     * tmpene*1.1 < candidate->ene to reduce jumps towards low frequencies) */
    if (tmpene < candidate[l]->ene) {
      tmpl = l;
      tmpene = candidate[l]->ene;
    }
  }
  //p->candidates=candidate;
  //p->peaks=peaks;
  p->goodcandidate = tmpl;
}

/** T=quadpick(X): return indices of elements of X which are peaks and positive
 * exact peak positions are retrieved by quadratic interpolation
 *
 * \bug peak-picking too picky, sometimes counts too many peaks ?
 */
uint_t
aubio_pitchmcomb_quadpick (aubio_spectralpeak_t * spectral_peaks, const fvec_t * X)
{
  uint_t j, ispeak, count = 0;
  for (j = 1; j < X->length - 1; j++) {
    ispeak = fvec_peakpick (X, j);
    if (ispeak) {
      count += ispeak;
      spectral_peaks[count - 1].bin = j;
      spectral_peaks[count - 1].ebin = fvec_quadratic_peak_pos (X, j);
    }
  }
  return count;
}

/* get predominant partial */
uint_t
aubio_pitchmcomb_get_root_peak (aubio_spectralpeak_t * peaks, uint_t length)
{
  uint_t i, pos = 0;
  smpl_t tmp = 0.;
  for (i = 0; i < length; i++)
    if (tmp <= peaks[i].mag) {
      pos = i;
      tmp = peaks[i].mag;
    }
  return pos;
}

void
aubio_pitchmcomb_sort_peak (aubio_spectralpeak_t * peaks, uint_t nbins)
{
  qsort (peaks, nbins, sizeof (aubio_spectralpeak_t),
      aubio_pitchmcomb_sort_peak_comp);
}

static sint_t
aubio_pitchmcomb_sort_peak_comp (const void *x, const void *y)
{
  return (((aubio_spectralpeak_t *) y)->mag -
      ((aubio_spectralpeak_t *) x)->mag);
}


void
aubio_pitchmcomb_sort_cand_ene (aubio_spectralcandidate_t ** candidates,
    uint_t nbins)
{
  uint_t cur = 0;
  uint_t run = 0;
  for (cur = 0; cur < nbins; cur++) {
    for (run = cur + 1; run < nbins; run++) {
      if (candidates[run]->ene > candidates[cur]->ene)
        CAND_SWAP (candidates[run], candidates[cur]);
    }
  }
}


void
aubio_pitchmcomb_sort_cand_freq (aubio_spectralcandidate_t ** candidates,
    uint_t nbins)
{
  uint_t cur = 0;
  uint_t run = 0;
  for (cur = 0; cur < nbins; cur++) {
    for (run = cur + 1; run < nbins; run++) {
      if (candidates[run]->ebin < candidates[cur]->ebin)
        CAND_SWAP (candidates[run], candidates[cur]);
    }
  }
}

aubio_pitchmcomb_t *
new_aubio_pitchmcomb (uint_t bufsize, uint_t hopsize)
{
  aubio_pitchmcomb_t *p = AUBIO_NEW (aubio_pitchmcomb_t);
  /* bug: should check if size / 8 > post+pre+1 */
  uint_t i, j;
  uint_t spec_size;
  p->spec_partition = 2;
  p->ncand = 5;
  p->npartials = 5;
  p->cutoff = 1.;
  p->threshold = 0.01;
  p->win_post = 8;
  p->win_pre = 7;
  // p->tau              = samplerate/bufsize;
  p->alpha = 9.;
  p->goodcandidate = 0;
  p->phasefreq = bufsize / hopsize / TWO_PI;
  p->phasediff = TWO_PI * hopsize / bufsize;
  spec_size = bufsize / p->spec_partition + 1;
  //p->pickerfn = quadpick;
  //p->biquad = new_biquad(0.1600,0.3200,0.1600, -0.5949, 0.2348);
  /* allocate temp memory */
  p->newmag = new_fvec (spec_size);
  /* array for median */
  p->scratch = new_fvec (spec_size);
  /* array for phase */
  p->theta = new_fvec (spec_size);
  /* array for adaptative threshold */
  p->scratch2 = new_fvec (p->win_post + p->win_pre + 1);
  /* array of spectral peaks */
  p->peaks = AUBIO_ARRAY (aubio_spectralpeak_t, spec_size);
  for (i = 0; i < spec_size; i++) {
    p->peaks[i].bin = 0.;
    p->peaks[i].ebin = 0.;
    p->peaks[i].mag = 0.;
  }
  /* array of pointers to spectral candidates */
  p->candidates = AUBIO_ARRAY (aubio_spectralcandidate_t *, p->ncand);
  for (i = 0; i < p->ncand; i++) {
    p->candidates[i] = AUBIO_NEW (aubio_spectralcandidate_t);
    p->candidates[i]->ecomb = AUBIO_ARRAY (smpl_t, spec_size);
    for (j = 0; j < spec_size; j++) {
      p->candidates[i]->ecomb[j] = 0.;
    }
    p->candidates[i]->ene = 0.;
    p->candidates[i]->ebin = 0.;
    p->candidates[i]->len = 0.;
  }
  return p;
}


void
del_aubio_pitchmcomb (aubio_pitchmcomb_t * p)
{
  uint_t i;
  del_fvec (p->newmag);
  del_fvec (p->scratch);
  del_fvec (p->theta);
  del_fvec (p->scratch2);
  AUBIO_FREE (p->peaks);
  for (i = 0; i < p->ncand; i++) {
    AUBIO_FREE (p->candidates[i]->ecomb);
    AUBIO_FREE (p->candidates[i]);
  }
  AUBIO_FREE (p->candidates);
  AUBIO_FREE (p);
}