diff options
Diffstat (limited to 'src/spectral/specdesc.c')
-rw-r--r-- | src/spectral/specdesc.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/src/spectral/specdesc.c b/src/spectral/specdesc.c new file mode 100644 index 0000000..fb9b2f7 --- /dev/null +++ b/src/spectral/specdesc.c @@ -0,0 +1,399 @@ +/* + 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 "spectral/fft.h" +#include "spectral/specdesc.h" +#include "mathutils.h" +#include "utils/hist.h" + +void aubio_specdesc_energy(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_hfc(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_complex(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_phase(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_specdiff(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_kl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_mkl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_specflux(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); + +extern void aubio_specdesc_centroid (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_spread (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_skewness (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_kurtosis (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_slope (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_decrease (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_rolloff (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); + +/** onsetdetection types */ +typedef enum { + aubio_onset_energy, /**< energy based */ + aubio_onset_specdiff, /**< spectral diff */ + aubio_onset_hfc, /**< high frequency content */ + aubio_onset_complex, /**< complex domain */ + aubio_onset_phase, /**< phase fast */ + aubio_onset_kl, /**< Kullback Liebler */ + aubio_onset_mkl, /**< modified Kullback Liebler */ + aubio_onset_specflux, /**< spectral flux */ + aubio_specmethod_centroid, /**< spectral centroid */ + aubio_specmethod_spread, /**< spectral spread */ + aubio_specmethod_skewness, /**< spectral skewness */ + aubio_specmethod_kurtosis, /**< spectral kurtosis */ + aubio_specmethod_slope, /**< spectral kurtosis */ + aubio_specmethod_decrease, /**< spectral decrease */ + aubio_specmethod_rolloff, /**< spectral rolloff */ + aubio_onset_default = aubio_onset_hfc, /**< default mode, set to hfc */ +} aubio_specdesc_type; + +/** structure to store object state */ +struct _aubio_specdesc_t { + aubio_specdesc_type onset_type; /**< onset detection type */ + /** Pointer to aubio_specdesc_<type> function */ + void (*funcpointer)(aubio_specdesc_t *o, + const cvec_t * fftgrain, fvec_t * onset); + smpl_t threshold; /**< minimum norm threshold for phase and specdiff */ + fvec_t *oldmag; /**< previous norm vector */ + fvec_t *dev1 ; /**< current onset detection measure vector */ + fvec_t *theta1; /**< previous phase vector, one frame behind */ + fvec_t *theta2; /**< previous phase vector, two frames behind */ + aubio_hist_t * histog; /**< histogram */ +}; + + +/* Energy based onset detection function */ +void aubio_specdesc_energy (aubio_specdesc_t *o UNUSED, + const cvec_t * fftgrain, fvec_t * onset) { + uint_t j; + onset->data[0] = 0.; + for (j=0;j<fftgrain->length;j++) { + onset->data[0] += SQR(fftgrain->norm[j]); + } +} + +/* High Frequency Content onset detection function */ +void aubio_specdesc_hfc(aubio_specdesc_t *o UNUSED, + const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;j<fftgrain->length;j++) { + onset->data[0] += (j+1)*fftgrain->norm[j]; + } +} + + +/* Complex Domain Method onset detection function */ +void aubio_specdesc_complex (aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset) { + uint_t j; + uint_t nbins = fftgrain->length; + onset->data[0] = 0.; + for (j=0;j<nbins; j++) { + // compute the predicted phase + o->dev1->data[j] = 2. * o->theta1->data[j] - o->theta2->data[j]; + // compute the euclidean distance in the complex domain + // sqrt ( r_1^2 + r_2^2 - 2 * r_1 * r_2 * \cos ( \phi_1 - \phi_2 ) ) + onset->data[0] += + SQRT (ABS (SQR (o->oldmag->data[j]) + SQR (fftgrain->norm[j]) + - 2 * o->oldmag->data[j] * fftgrain->norm[j] + * COS (o->dev1->data[j] - fftgrain->phas[j]))); + /* swap old phase data (need to remember 2 frames behind)*/ + o->theta2->data[j] = o->theta1->data[j]; + o->theta1->data[j] = fftgrain->phas[j]; + /* swap old magnitude data (1 frame is enough) */ + o->oldmag->data[j] = fftgrain->norm[j]; + } +} + + +/* Phase Based Method onset detection function */ +void aubio_specdesc_phase(aubio_specdesc_t *o, + const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + uint_t nbins = fftgrain->length; + onset->data[0] = 0.0; + o->dev1->data[0]=0.; + for ( j=0;j<nbins; j++ ) { + o->dev1->data[j] = + aubio_unwrap2pi( + fftgrain->phas[j] + -2.0*o->theta1->data[j] + +o->theta2->data[j]); + if ( o->threshold < fftgrain->norm[j] ) + o->dev1->data[j] = ABS(o->dev1->data[j]); + else + o->dev1->data[j] = 0.0; + /* keep a track of the past frames */ + o->theta2->data[j] = o->theta1->data[j]; + o->theta1->data[j] = fftgrain->phas[j]; + } + /* apply o->histogram */ + aubio_hist_dyn_notnull(o->histog,o->dev1); + /* weight it */ + aubio_hist_weight(o->histog); + /* its mean is the result */ + onset->data[0] = aubio_hist_mean(o->histog); + //onset->data[0] = fvec_mean(o->dev1); +} + +/* Spectral difference method onset detection function */ +void aubio_specdesc_specdiff(aubio_specdesc_t *o, + const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + uint_t nbins = fftgrain->length; + onset->data[0] = 0.0; + for (j=0;j<nbins; j++) { + o->dev1->data[j] = SQRT( + ABS(SQR( fftgrain->norm[j]) + - SQR(o->oldmag->data[j]))); + if (o->threshold < fftgrain->norm[j] ) + o->dev1->data[j] = ABS(o->dev1->data[j]); + else + o->dev1->data[j] = 0.0; + o->oldmag->data[j] = fftgrain->norm[j]; + } + + /* apply o->histogram (act somewhat as a low pass on the + * overall function)*/ + aubio_hist_dyn_notnull(o->histog,o->dev1); + /* weight it */ + aubio_hist_weight(o->histog); + /* its mean is the result */ + onset->data[0] = aubio_hist_mean(o->histog); +} + +/* Kullback Liebler onset detection function + * note we use ln(1+Xn/(Xn-1+0.0001)) to avoid + * negative (1.+) and infinite values (+1.e-10) */ +void aubio_specdesc_kl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;j<fftgrain->length;j++) { + onset->data[0] += fftgrain->norm[j] + *LOG(1.+fftgrain->norm[j]/(o->oldmag->data[j]+1.e-1)); + o->oldmag->data[j] = fftgrain->norm[j]; + } + if (isnan(onset->data[0])) onset->data[0] = 0.; +} + +/* Modified Kullback Liebler onset detection function + * note we use ln(1+Xn/(Xn-1+0.0001)) to avoid + * negative (1.+) and infinite values (+1.e-10) */ +void aubio_specdesc_mkl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;j<fftgrain->length;j++) { + onset->data[0] += LOG(1.+fftgrain->norm[j]/(o->oldmag->data[j]+1.e-1)); + o->oldmag->data[j] = fftgrain->norm[j]; + } + if (isnan(onset->data[0])) onset->data[0] = 0.; +} + +/* Spectral flux */ +void aubio_specdesc_specflux(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;j<fftgrain->length;j++) { + if (fftgrain->norm[j] > o->oldmag->data[j]) + onset->data[0] += fftgrain->norm[j] - o->oldmag->data[j]; + o->oldmag->data[j] = fftgrain->norm[j]; + } +} + +/* Generic function pointing to the choosen one */ +void +aubio_specdesc_do (aubio_specdesc_t *o, const cvec_t * fftgrain, + fvec_t * onset) { + o->funcpointer(o,fftgrain,onset); +} + +/* Allocate memory for an onset detection + * depending on the choosen type, allocate memory as needed + */ +aubio_specdesc_t * +new_aubio_specdesc (const char_t * onset_mode, uint_t size){ + aubio_specdesc_t * o = AUBIO_NEW(aubio_specdesc_t); + uint_t rsize = size/2+1; + aubio_specdesc_type onset_type; + if (strcmp (onset_mode, "energy") == 0) + onset_type = aubio_onset_energy; + else if (strcmp (onset_mode, "specdiff") == 0) + onset_type = aubio_onset_specdiff; + else if (strcmp (onset_mode, "hfc") == 0) + onset_type = aubio_onset_hfc; + else if (strcmp (onset_mode, "complexdomain") == 0) + onset_type = aubio_onset_complex; + else if (strcmp (onset_mode, "complex") == 0) + onset_type = aubio_onset_complex; + else if (strcmp (onset_mode, "phase") == 0) + onset_type = aubio_onset_phase; + else if (strcmp (onset_mode, "mkl") == 0) + onset_type = aubio_onset_mkl; + else if (strcmp (onset_mode, "kl") == 0) + onset_type = aubio_onset_kl; + else if (strcmp (onset_mode, "specflux") == 0) + onset_type = aubio_onset_specflux; + else if (strcmp (onset_mode, "centroid") == 0) + onset_type = aubio_specmethod_centroid; + else if (strcmp (onset_mode, "spread") == 0) + onset_type = aubio_specmethod_spread; + else if (strcmp (onset_mode, "skewness") == 0) + onset_type = aubio_specmethod_skewness; + else if (strcmp (onset_mode, "kurtosis") == 0) + onset_type = aubio_specmethod_kurtosis; + else if (strcmp (onset_mode, "slope") == 0) + onset_type = aubio_specmethod_slope; + else if (strcmp (onset_mode, "decrease") == 0) + onset_type = aubio_specmethod_decrease; + else if (strcmp (onset_mode, "rolloff") == 0) + onset_type = aubio_specmethod_rolloff; + else if (strcmp (onset_mode, "default") == 0) + onset_type = aubio_onset_default; + else { + AUBIO_ERR("unknown spectral descriptor type %s, using default.\n", onset_mode); + onset_type = aubio_onset_default; + } + switch(onset_type) { + /* for both energy and hfc, only fftgrain->norm is required */ + case aubio_onset_energy: + break; + case aubio_onset_hfc: + break; + /* the other approaches will need some more memory spaces */ + case aubio_onset_complex: + o->oldmag = new_fvec(rsize); + o->dev1 = new_fvec(rsize); + o->theta1 = new_fvec(rsize); + o->theta2 = new_fvec(rsize); + break; + case aubio_onset_phase: + o->dev1 = new_fvec(rsize); + o->theta1 = new_fvec(rsize); + o->theta2 = new_fvec(rsize); + o->histog = new_aubio_hist(0.0, PI, 10); + o->threshold = 0.1; + break; + case aubio_onset_specdiff: + o->oldmag = new_fvec(rsize); + o->dev1 = new_fvec(rsize); + o->histog = new_aubio_hist(0.0, PI, 10); + o->threshold = 0.1; + break; + case aubio_onset_kl: + case aubio_onset_mkl: + case aubio_onset_specflux: + o->oldmag = new_fvec(rsize); + break; + default: + break; + } + + switch(onset_type) { + case aubio_onset_energy: + o->funcpointer = aubio_specdesc_energy; + break; + case aubio_onset_hfc: + o->funcpointer = aubio_specdesc_hfc; + break; + case aubio_onset_complex: + o->funcpointer = aubio_specdesc_complex; + break; + case aubio_onset_phase: + o->funcpointer = aubio_specdesc_phase; + break; + case aubio_onset_specdiff: + o->funcpointer = aubio_specdesc_specdiff; + break; + case aubio_onset_kl: + o->funcpointer = aubio_specdesc_kl; + break; + case aubio_onset_mkl: + o->funcpointer = aubio_specdesc_mkl; + break; + case aubio_onset_specflux: + o->funcpointer = aubio_specdesc_specflux; + break; + case aubio_specmethod_centroid: + o->funcpointer = aubio_specdesc_centroid; + break; + case aubio_specmethod_spread: + o->funcpointer = aubio_specdesc_spread; + break; + case aubio_specmethod_skewness: + o->funcpointer = aubio_specdesc_skewness; + break; + case aubio_specmethod_kurtosis: + o->funcpointer = aubio_specdesc_kurtosis; + break; + case aubio_specmethod_slope: + o->funcpointer = aubio_specdesc_slope; + break; + case aubio_specmethod_decrease: + o->funcpointer = aubio_specdesc_decrease; + break; + case aubio_specmethod_rolloff: + o->funcpointer = aubio_specdesc_rolloff; + break; + default: + break; + } + o->onset_type = onset_type; + return o; +} + +void del_aubio_specdesc (aubio_specdesc_t *o){ + switch(o->onset_type) { + case aubio_onset_energy: + break; + case aubio_onset_hfc: + break; + case aubio_onset_complex: + del_fvec(o->oldmag); + del_fvec(o->dev1); + del_fvec(o->theta1); + del_fvec(o->theta2); + break; + case aubio_onset_phase: + del_fvec(o->dev1); + del_fvec(o->theta1); + del_fvec(o->theta2); + del_aubio_hist(o->histog); + break; + case aubio_onset_specdiff: + del_fvec(o->oldmag); + del_fvec(o->dev1); + del_aubio_hist(o->histog); + break; + case aubio_onset_kl: + case aubio_onset_mkl: + case aubio_onset_specflux: + del_fvec(o->oldmag); + break; + default: + break; + } + AUBIO_FREE(o); +} |