summaryrefslogtreecommitdiff
path: root/src/pitch/pitch.c
blob: bee0ea3379395cd84a421363f0b117c407bac7e6 (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
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
/*
  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 "lvec.h"
#include "mathutils.h"
#include "musicutils.h"
#include "spectral/phasevoc.h"
#include "temporal/filter.h"
#include "temporal/c_weighting.h"
#include "pitch/pitchmcomb.h"
#include "pitch/pitchyin.h"
#include "pitch/pitchfcomb.h"
#include "pitch/pitchschmitt.h"
#include "pitch/pitchyinfft.h"
#include "pitch/pitchspecacf.h"
#include "pitch/pitch.h"

#define DEFAULT_PITCH_SILENCE -50.

/** pitch detection algorithms */
typedef enum
{
  aubio_pitcht_yin,        /**< `yin`, YIN algorithm */
  aubio_pitcht_mcomb,      /**< `mcomb`, Multi-comb filter */
  aubio_pitcht_schmitt,    /**< `schmitt`, Schmitt trigger */
  aubio_pitcht_fcomb,      /**< `fcomb`, Fast comb filter */
  aubio_pitcht_yinfft,     /**< `yinfft`, Spectral YIN */
  aubio_pitcht_specacf,    /**< `specacf`, Spectral autocorrelation */
  aubio_pitcht_default
    = aubio_pitcht_yinfft, /**< `default` */
} aubio_pitch_type;

/** pitch detection output modes */
typedef enum
{
  aubio_pitchm_freq,   /**< Frequency (Hz) */
  aubio_pitchm_midi,   /**< MIDI note (0.,127) */
  aubio_pitchm_cent,   /**< Cent */
  aubio_pitchm_bin,    /**< Frequency bin (0,bufsize) */
  aubio_pitchm_default = aubio_pitchm_freq, /**< the one used when "default" is asked */
} aubio_pitch_mode;

/** callback to get pitch candidate, defined below */
typedef void (*aubio_pitch_detect_t) (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);

/** callback to convert pitch from one unit to another, defined below */
typedef smpl_t(*aubio_pitch_convert_t) (smpl_t value, uint_t samplerate, uint_t bufsize);

/** callback to fetch the confidence of the algorithm */
typedef smpl_t (*aubio_pitch_get_conf_t) (void * p);

/** generic pitch detection structure */
struct _aubio_pitch_t
{
  aubio_pitch_type type;          /**< pitch detection mode */
  aubio_pitch_mode mode;          /**< pitch detection output mode */
  uint_t samplerate;              /**< samplerate */
  uint_t bufsize;                 /**< buffer size */
  void *p_object;                 /**< pointer to pitch object */
  aubio_filter_t *filter;         /**< filter */
  fvec_t *filtered;               /**< filtered input */
  aubio_pvoc_t *pv;               /**< phase vocoder for mcomb */
  cvec_t *fftgrain;               /**< spectral frame for mcomb */
  fvec_t *buf;                    /**< temporary buffer for yin */
  aubio_pitch_detect_t detect_cb; /**< callback to get the pitch candidates */
  aubio_pitch_convert_t conv_cb;  /**< callback to convert it to the desired unit */
  aubio_pitch_get_conf_t conf_cb; /**< pointer to the current confidence callback */
  smpl_t silence;                 /**< silence threshold */
};

/* callback functions for pitch detection */
static void aubio_pitch_do_mcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);
static void aubio_pitch_do_yin (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);
static void aubio_pitch_do_schmitt (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);
static void aubio_pitch_do_fcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);
static void aubio_pitch_do_yinfft (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);
static void aubio_pitch_do_specacf (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf);

/* conversion functions for frequency conversions */
smpl_t freqconvbin (smpl_t f, uint_t samplerate, uint_t bufsize);
smpl_t freqconvmidi (smpl_t f, uint_t samplerate, uint_t bufsize);
smpl_t freqconvpass (smpl_t f, uint_t samplerate, uint_t bufsize);

/* adapter to stack ibuf new samples at the end of buf, and trim `buf` to `bufsize` */
void aubio_pitch_slideblock (aubio_pitch_t * p, const fvec_t * ibuf);


aubio_pitch_t *
new_aubio_pitch (const char_t * pitch_mode,
    uint_t bufsize, uint_t hopsize, uint_t samplerate)
{
  aubio_pitch_t *p = AUBIO_NEW (aubio_pitch_t);
  aubio_pitch_type pitch_type;
  if (strcmp (pitch_mode, "mcomb") == 0)
    pitch_type = aubio_pitcht_mcomb;
  else if (strcmp (pitch_mode, "yinfft") == 0)
    pitch_type = aubio_pitcht_yinfft;
  else if (strcmp (pitch_mode, "yin") == 0)
    pitch_type = aubio_pitcht_yin;
  else if (strcmp (pitch_mode, "schmitt") == 0)
    pitch_type = aubio_pitcht_schmitt;
  else if (strcmp (pitch_mode, "fcomb") == 0)
    pitch_type = aubio_pitcht_fcomb;
  else if (strcmp (pitch_mode, "specacf") == 0)
    pitch_type = aubio_pitcht_specacf;
  else if (strcmp (pitch_mode, "default") == 0)
    pitch_type = aubio_pitcht_default;
  else {
    AUBIO_ERR ("unknown pitch detection method %s, using default.\n",
        pitch_mode);
    pitch_type = aubio_pitcht_default;
  }

  // check parameters are valid
  if ((sint_t)hopsize < 1) {
    AUBIO_ERR("pitch: got hopsize %d, but can not be < 1\n", hopsize);
    goto beach;
  } else if ((sint_t)bufsize < 1) {
    AUBIO_ERR("pitch: got buffer_size %d, but can not be < 1\n", bufsize);
    goto beach;
  } else if (bufsize < hopsize) {
    AUBIO_ERR("pitch: hop size (%d) is larger than win size (%d)\n", bufsize, hopsize);
    goto beach;
  } else if ((sint_t)samplerate < 1) {
    AUBIO_ERR("pitch: samplerate (%d) can not be < 1\n", samplerate);
    goto beach;
  }

  p->samplerate = samplerate;
  p->type = pitch_type;
  aubio_pitch_set_unit (p, "default");
  p->bufsize = bufsize;
  p->silence = DEFAULT_PITCH_SILENCE;
  p->conf_cb = NULL;
  switch (p->type) {
    case aubio_pitcht_yin:
      p->buf = new_fvec (bufsize);
      p->p_object = new_aubio_pitchyin (bufsize);
      p->detect_cb = aubio_pitch_do_yin;
      p->conf_cb = (aubio_pitch_get_conf_t)aubio_pitchyin_get_confidence;
      aubio_pitchyin_set_tolerance (p->p_object, 0.15);
      break;
    case aubio_pitcht_mcomb:
      p->filtered = new_fvec (hopsize);
      p->pv = new_aubio_pvoc (bufsize, hopsize);
      p->fftgrain = new_cvec (bufsize);
      p->p_object = new_aubio_pitchmcomb (bufsize, hopsize);
      p->filter = new_aubio_filter_c_weighting (samplerate);
      p->detect_cb = aubio_pitch_do_mcomb;
      break;
    case aubio_pitcht_fcomb:
      p->buf = new_fvec (bufsize);
      p->p_object = new_aubio_pitchfcomb (bufsize, hopsize);
      p->detect_cb = aubio_pitch_do_fcomb;
      break;
    case aubio_pitcht_schmitt:
      p->buf = new_fvec (bufsize);
      p->p_object = new_aubio_pitchschmitt (bufsize);
      p->detect_cb = aubio_pitch_do_schmitt;
      break;
    case aubio_pitcht_yinfft:
      p->buf = new_fvec (bufsize);
      p->p_object = new_aubio_pitchyinfft (samplerate, bufsize);
      p->detect_cb = aubio_pitch_do_yinfft;
      p->conf_cb = (aubio_pitch_get_conf_t)aubio_pitchyinfft_get_confidence;
      aubio_pitchyinfft_set_tolerance (p->p_object, 0.85);
      break;
    case aubio_pitcht_specacf:
      p->buf = new_fvec (bufsize);
      p->p_object = new_aubio_pitchspecacf (bufsize);
      p->detect_cb = aubio_pitch_do_specacf;
      p->conf_cb = (aubio_pitch_get_conf_t)aubio_pitchspecacf_get_tolerance;
      aubio_pitchspecacf_set_tolerance (p->p_object, 0.85);
      break;
    default:
      break;
  }
  return p;

beach:
  AUBIO_FREE(p);
  return NULL;
}

void
del_aubio_pitch (aubio_pitch_t * p)
{
  switch (p->type) {
    case aubio_pitcht_yin:
      del_fvec (p->buf);
      del_aubio_pitchyin (p->p_object);
      break;
    case aubio_pitcht_mcomb:
      del_fvec (p->filtered);
      del_aubio_pvoc (p->pv);
      del_cvec (p->fftgrain);
      del_aubio_filter (p->filter);
      del_aubio_pitchmcomb (p->p_object);
      break;
    case aubio_pitcht_schmitt:
      del_fvec (p->buf);
      del_aubio_pitchschmitt (p->p_object);
      break;
    case aubio_pitcht_fcomb:
      del_fvec (p->buf);
      del_aubio_pitchfcomb (p->p_object);
      break;
    case aubio_pitcht_yinfft:
      del_fvec (p->buf);
      del_aubio_pitchyinfft (p->p_object);
      break;
    case aubio_pitcht_specacf:
      del_fvec (p->buf);
      del_aubio_pitchspecacf (p->p_object);
      break;
    default:
      break;
  }
  AUBIO_FREE (p);
}

void
aubio_pitch_slideblock (aubio_pitch_t * p, const fvec_t * ibuf)
{
  uint_t overlap_size = p->buf->length - ibuf->length;
#if 1 //!HAVE_MEMCPY_HACKS
  uint_t j;
  for (j = 0; j < overlap_size; j++) {
    p->buf->data[j] = p->buf->data[j + ibuf->length];
  }
  for (j = 0; j < ibuf->length; j++) {
    p->buf->data[j + overlap_size] = ibuf->data[j];
  }
#else
  smpl_t *data = p->buf->data;
  smpl_t *newdata = ibuf->data;
  memmove(data, data + ibuf->length, overlap_size);
  memcpy(data + overlap_size, newdata, ibuf->length);
#endif
}

uint_t
aubio_pitch_set_unit (aubio_pitch_t * p, const char_t * pitch_unit)
{
  uint_t err = AUBIO_OK;
  aubio_pitch_mode pitch_mode;
  if (strcmp (pitch_unit, "freq") == 0)
    pitch_mode = aubio_pitchm_freq;
  else if (strcmp (pitch_unit, "hertz") == 0)
    pitch_mode = aubio_pitchm_freq;
  else if (strcmp (pitch_unit, "Hertz") == 0)
    pitch_mode = aubio_pitchm_freq;
  else if (strcmp (pitch_unit, "Hz") == 0)
    pitch_mode = aubio_pitchm_freq;
  else if (strcmp (pitch_unit, "f0") == 0)
    pitch_mode = aubio_pitchm_freq;
  else if (strcmp (pitch_unit, "midi") == 0)
    pitch_mode = aubio_pitchm_midi;
  else if (strcmp (pitch_unit, "cent") == 0)
    pitch_mode = aubio_pitchm_cent;
  else if (strcmp (pitch_unit, "bin") == 0)
    pitch_mode = aubio_pitchm_bin;
  else if (strcmp (pitch_unit, "default") == 0)
    pitch_mode = aubio_pitchm_default;
  else {
    AUBIO_ERR ("unknown pitch detection unit %s, using default\n", pitch_unit);
    pitch_mode = aubio_pitchm_default;
    err = AUBIO_FAIL;
  }
  p->mode = pitch_mode;
  switch (p->mode) {
    case aubio_pitchm_freq:
      p->conv_cb = freqconvpass;
      break;
    case aubio_pitchm_midi:
      p->conv_cb = freqconvmidi;
      break;
    case aubio_pitchm_cent:
      /* bug: not implemented */
      p->conv_cb = freqconvmidi;
      break;
    case aubio_pitchm_bin:
      p->conv_cb = freqconvbin;
      break;
    default:
      break;
  }
  return err;
}

uint_t
aubio_pitch_set_tolerance (aubio_pitch_t * p, smpl_t tol)
{
  switch (p->type) {
    case aubio_pitcht_yin:
      aubio_pitchyin_set_tolerance (p->p_object, tol);
      break;
    case aubio_pitcht_yinfft:
      aubio_pitchyinfft_set_tolerance (p->p_object, tol);
      break;
    default:
      break;
  }
  return AUBIO_OK;
}

uint_t
aubio_pitch_set_silence (aubio_pitch_t * p, smpl_t silence)
{
  if (silence <= 0 && silence >= -200) {
    p->silence = silence;
    return AUBIO_OK;
  } else {
    AUBIO_ERR("pitch: could not set silence to %.2f", silence);
    return AUBIO_FAIL;
  }
}

smpl_t
aubio_pitch_get_silence (aubio_pitch_t * p)
{
  return p->silence;
}


/* do method, calling the detection callback, then the conversion callback */
void
aubio_pitch_do (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf)
{
  p->detect_cb (p, ibuf, obuf);
  if (aubio_silence_detection(ibuf, p->silence) == 1) {
    obuf->data[0] = 0.;
  }
  obuf->data[0] = p->conv_cb (obuf->data[0], p->samplerate, p->bufsize);
}

/* do method for each algorithm */
void
aubio_pitch_do_mcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf)
{
  aubio_filter_do_outplace (p->filter, ibuf, p->filtered);
  aubio_pvoc_do (p->pv, ibuf, p->fftgrain);
  aubio_pitchmcomb_do (p->p_object, p->fftgrain, obuf);
  obuf->data[0] = aubio_bintofreq (obuf->data[0], p->samplerate, p->bufsize);
}

void
aubio_pitch_do_yin (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf)
{
  smpl_t pitch = 0.;
  aubio_pitch_slideblock (p, ibuf);
  aubio_pitchyin_do (p->p_object, p->buf, obuf);
  pitch = obuf->data[0];
  if (pitch > 0) {
    pitch = p->samplerate / (pitch + 0.);
  } else {
    pitch = 0.;
  }
  obuf->data[0] = pitch;
}


void
aubio_pitch_do_yinfft (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * obuf)
{
  smpl_t pitch = 0.;
  aubio_pitch_slideblock (p, ibuf);
  aubio_pitchyinfft_do (p->p_object, p->buf, obuf);
  pitch = obuf->data[0];
  if (pitch > 0) {
    pitch = p->samplerate / (pitch + 0.);
  } else {
    pitch = 0.;
  }
  obuf->data[0] = pitch;
}

void
aubio_pitch_do_specacf (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * out)
{
  smpl_t pitch = 0., period;
  aubio_pitch_slideblock (p, ibuf);
  aubio_pitchspecacf_do (p->p_object, p->buf, out);
  //out->data[0] = aubio_bintofreq (out->data[0], p->samplerate, p->bufsize);
  period = out->data[0];
  if (period > 0) {
    pitch = p->samplerate / period;
  } else {
    pitch = 0.;
  }
  out->data[0] = pitch;
}

void
aubio_pitch_do_fcomb (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * out)
{
  aubio_pitch_slideblock (p, ibuf);
  aubio_pitchfcomb_do (p->p_object, p->buf, out);
  out->data[0] = aubio_bintofreq (out->data[0], p->samplerate, p->bufsize);
}

void
aubio_pitch_do_schmitt (aubio_pitch_t * p, const fvec_t * ibuf, fvec_t * out)
{
  smpl_t period, pitch = 0.;
  aubio_pitch_slideblock (p, ibuf);
  aubio_pitchschmitt_do (p->p_object, p->buf, out);
  period = out->data[0];
  if (period > 0) {
    pitch = p->samplerate / period;
  } else {
    pitch = 0.;
  }
  out->data[0] = pitch;
}

/* conversion callbacks */
smpl_t
freqconvbin(smpl_t f, uint_t samplerate, uint_t bufsize)
{
  return aubio_freqtobin(f, samplerate, bufsize);
}

smpl_t
freqconvmidi (smpl_t f, uint_t samplerate UNUSED, uint_t bufsize UNUSED)
{
  return aubio_freqtomidi (f);
}

smpl_t
freqconvpass (smpl_t f, uint_t samplerate UNUSED, uint_t bufsize UNUSED)
{
  return f;
}

/* confidence callbacks */
smpl_t
aubio_pitch_get_confidence (aubio_pitch_t * p)
{
  if (p->conf_cb) {
    return p->conf_cb(p->p_object);
  }
  return 0.;
}