shithub: aubio

Download patch

ref: c1ba75b55fb6351ca1f2dcebf89b93aee72d2711
parent: adc6e02ecf2d72a827822e7d09b519434e66376a
parent: 6b84d815b7333e98b3b23ac0b80b9bd40648b93b
author: Paul Brossier <piem@piem.org>
date: Tue Dec 11 11:47:32 EST 2018

Merge branch 'master' into feature/pytest

--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,8 @@
 # gcov generated files
 *.gcno
 *.gcda
+python/lib/aubio/_aubio.*.so
+.coverage
 
 # ignore compiled examples
 RE:examples/[a-z]*
--- /dev/null
+++ b/doc/py_analysis.rst
@@ -1,0 +1,15 @@
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Analysis
+--------
+
+.. members of generated classes are not shown
+
+.. autoclass:: onset
+
+.. autoclass:: pitch
+
+.. autoclass:: tempo
+
+.. autoclass:: notes
--- /dev/null
+++ b/doc/py_spectral.rst
@@ -1,0 +1,34 @@
+.. currentmodule:: aubio
+.. default-domain:: py
+
+.. members of generated classes are not yet documented
+
+Spectral features
+-----------------
+
+This section contains the documentation for:
+
+- :class:`dct`
+- :class:`fft`
+- :class:`filterbank`
+- :class:`mfcc`
+- :class:`pvoc`
+- :class:`specdesc`
+- :class:`tss`
+
+.. autoclass:: dct
+
+.. autoclass:: fft
+  :members:
+
+.. autoclass:: filterbank
+  :members:
+
+.. autoclass:: mfcc
+
+.. autoclass:: pvoc
+  :members:
+
+.. autoclass:: specdesc
+
+.. autoclass:: tss
--- /dev/null
+++ b/doc/py_synth.rst
@@ -1,0 +1,9 @@
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Synthesis
+---------
+
+.. autoclass:: sampler
+
+.. autoclass:: wavetable
--- /dev/null
+++ b/doc/py_temporal.rst
@@ -1,0 +1,8 @@
+.. currentmodule:: aubio
+.. default-domain:: py
+
+Digital filters
+---------------
+
+.. autoclass:: digital_filter
+  :members:
--- a/doc/python.rst
+++ b/doc/python.rst
@@ -30,6 +30,10 @@
 
    py_datatypes
    py_io
+   py_temporal
+   py_spectral
+   py_analysis
+   py_synth
    py_utils
    py_examples
 
--- a/examples/aubiomfcc.c
+++ b/examples/aubiomfcc.c
@@ -68,7 +68,7 @@
     goto beach;
   }
 
-  examples_common_process((aubio_process_func_t)process_block, process_print);
+  examples_common_process(process_block, process_print);
 
   del_aubio_pvoc (pv);
   del_cvec (fftgrain);
--- a/examples/aubionotes.c
+++ b/examples/aubionotes.c
@@ -90,7 +90,7 @@
     }
   }
 
-  examples_common_process((aubio_process_func_t)process_block, process_print);
+  examples_common_process(process_block, process_print);
 
   // send a last note off if required
   if (lastmidi) {
--- a/examples/aubioonset.c
+++ b/examples/aubioonset.c
@@ -86,7 +86,7 @@
   aubio_wavetable_set_freq ( wavetable, 2450.);
   //aubio_sampler_load (sampler, "/archives/sounds/woodblock.aiff");
 
-  examples_common_process((aubio_process_func_t)process_block, process_print);
+  examples_common_process(process_block, process_print);
 
   // send a last note off
   if (usejack) {
--- a/examples/aubiopitch.c
+++ b/examples/aubiopitch.c
@@ -79,7 +79,7 @@
   wavetable = new_aubio_wavetable (samplerate, hop_size);
   aubio_wavetable_play ( wavetable );
 
-  examples_common_process((aubio_process_func_t)process_block,process_print);
+  examples_common_process(process_block, process_print);
 
   del_aubio_pitch (o);
   del_aubio_wavetable (wavetable);
--- a/examples/aubioquiet.c
+++ b/examples/aubioquiet.c
@@ -55,7 +55,7 @@
   verbmsg ("using source: %s at %dHz\n", source_uri, samplerate);
   verbmsg ("buffer_size: %d, ", buffer_size);
   verbmsg ("hop_size: %d\n", hop_size);
-  examples_common_process((aubio_process_func_t)process_block,process_print);
+  examples_common_process(process_block, process_print);
   examples_common_del();
   return 0;
 }
--- a/examples/aubiotrack.c
+++ b/examples/aubiotrack.c
@@ -87,7 +87,7 @@
   aubio_wavetable_set_freq ( wavetable, 2450.);
   //aubio_sampler_load (sampler, "/archives/sounds/woodblock.aiff");
 
-  examples_common_process((aubio_process_func_t)process_block,process_print);
+  examples_common_process(process_block, process_print);
 
   // send a last note off
   if (usejack) {
--- /dev/null
+++ b/python/ext/aubio-docstrings.h
@@ -1,0 +1,143 @@
+#define PYAUBIO_dct_doc \
+    "dct(size=1024)\n"\
+    "\n"\
+    "Compute Discrete Fourier Transorms of Type-II.\n"\
+    "\n"\
+    "Parameters\n"\
+    "----------\n"\
+    "size : int\n"\
+    "    size of the DCT to compute\n"\
+    "\n"\
+    "Example\n"\
+    "-------\n"\
+    ">>> d = aubio.dct(16)\n"\
+    ">>> d.size\n"\
+    "16\n"\
+    ">>> x = aubio.fvec(np.ones(d.size))\n"\
+    ">>> d(x)\n"\
+    "array([4., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n"\
+    "      dtype=float32)\n"\
+    ">>> d.rdo(d(x))\n"\
+    "array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n"\
+    "      dtype=float32)\n"\
+    "\n"\
+    "References\n"\
+    "----------\n"\
+    "`DCT-II in Discrete Cosine Transform\n"\
+    "<https://en.wikipedia.org/wiki/Discrete_cosine_transform#DCT-II>`_\n"\
+    "on Wikipedia.\n"
+
+#define PYAUBIO_mfcc_doc \
+    "mfcc(buf_size=1024, n_filters=40, n_coeffs=13, samplerate=44100)\n"\
+    "\n"\
+    "Compute Mel Frequency Cepstrum Coefficients (MFCC).\n"\
+    "\n"\
+    "`mfcc` creates a callable which takes a `cvec` as input.\n"\
+    "\n"\
+    "If `n_filters = 40`, the filterbank will be initialized with\n"\
+    ":meth:`filterbank.set_mel_coeffs_slaney`. Otherwise, if `n_filters`\n"\
+    "is greater than `0`, it will be initialized with\n"\
+    ":meth:`filterbank.set_mel_coeffs` using `fmin = 0`,\n"\
+    "`fmax = samplerate/`.\n"\
+    "\n"\
+    "Example\n"\
+    "-------\n"\
+    ">>> buf_size = 2048; n_filters = 128; n_coeffs = 13; samplerate = 44100\n"\
+    ">>> mf = aubio.mfcc(buf_size, n_filters, n_coeffs, samplerate)\n"\
+    ">>> fftgrain = aubio.cvec(buf_size)\n"\
+    ">>> mf(fftgrain).shape\n"\
+    "(13,)\n"\
+    ""
+
+#define PYAUBIO_notes_doc \
+    "notes(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+    "\n"\
+    "Note detection\n"
+
+#define PYAUBIO_onset_doc \
+    "onset(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+    "\n"\
+    "Onset detection object. `method` should be one of method supported by\n"\
+    ":class:`specdesc`.\n"
+
+#define PYAUBIO_pitch_doc \
+    "pitch(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+    "\n"\
+    "Pitch detection.\n"\
+    "\n"\
+    "Supported methods: `yinfft`, `yin`, `yinfast`, `fcomb`, `mcomb`,\n"\
+    "`schmitt`, `specacf`, `default` (`yinfft`).\n"
+
+#define PYAUBIO_sampler_doc \
+    "sampler(hop_size=512, samplerate=44100)\n"\
+    "\n"\
+    "Sampler.\n"
+
+#define PYAUBIO_specdesc_doc \
+    "specdesc(method=\"default\", buf_size=1024)\n"\
+    "\n"\
+    "Spectral description functions. Creates a callable that takes a\n"\
+    ":class:`cvec` as input, typically created by :class:`pvoc` for\n"\
+    "overlap and windowing, and returns a single float.\n"\
+    "\n"\
+    "`method` can be any of the values listed below. If `default` is used\n"\
+    "the `hfc` function will be selected.\n"\
+    "\n"\
+    "Onset novelty functions:\n"\
+    "\n"\
+    "- `energy`: local energy,\n"\
+    "- `hfc`: high frequency content,\n"\
+    "- `complex`: complex domain,\n"\
+    "- `phase`: phase-based method,\n"\
+    "- `wphase`: weighted phase deviation,\n"\
+    "- `specdiff`: spectral difference,\n"\
+    "- `kl`: Kullback-Liebler,\n"\
+    "- `mkl`: modified Kullback-Liebler,\n"\
+    "- `specflux`: spectral flux.\n"\
+    "\n"\
+    "Spectral shape functions:\n"\
+    "\n"\
+    "- `centroid`: spectral centroid (barycenter of the norm vector),\n"\
+    "- `spread`: variance around centroid,\n"\
+    "- `skewness`: third order moment,\n"\
+    "- `kurtosis`: a measure of the flatness of the spectrum,\n"\
+    "- `slope`: decreasing rate of the amplitude,\n"\
+    "- `decrease`: perceptual based measurement of the decreasing rate,\n"\
+    "- `rolloff`: 95th energy percentile.\n"\
+    "\n"\
+    "Parameters\n"\
+    "----------\n"\
+    "method : str\n"\
+    "    Onset novelty or spectral shape function.\n"\
+    "buf_size : int\n"\
+    "    Length of the input frame.\n"\
+    "\n"\
+    "Example\n"\
+    "-------\n"\
+    ">>> win_s = 1024; hop_s = win_s // 2\n"\
+    ">>> pv = aubio.pvoc(win_s, hop_s)\n"\
+    ">>> sd = aubio.specdesc(\"mkl\", win_s)\n"\
+    ">>> sd(pv(aubio.fvec(hop_s))).shape\n"\
+    "(1,)\n"\
+    "\n"\
+    "References\n"\
+    "----------\n"\
+    "`Detailed description "\
+    "<https://aubio.org/doc/latest/specdesc_8h.html#details>`_ in\n"\
+    "`aubio API documentation <https://aubio.org/doc/latest/index.html>`_.\n"\
+    ""
+
+#define PYAUBIO_tempo_doc \
+    "tempo(method=\"default\", buf_size=1024, hop_size=512, samplerate=44100)\n"\
+    "\n"\
+    "Tempo detection and beat tracking.\n"
+
+#define PYAUBIO_tss_doc \
+    "tss(buf_size=1024, hop_size=512)\n"\
+    "\n"\
+    "Transient/Steady-state separation.\n"
+
+#define PYAUBIO_wavetable_doc \
+    "wavetable(samplerate=44100, hop_size=512)\n"\
+    "\n"\
+    "Wavetable synthesis.\n"
--- a/python/ext/aubio-types.h
+++ b/python/ext/aubio-types.h
@@ -1,6 +1,7 @@
 #include <Python.h>
 #include <structmember.h>
 
+#include "aubio-docstrings.h"
 #include "aubio-generated.h"
 
 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
--- a/python/ext/py-fft.c
+++ b/python/ext/py-fft.c
@@ -1,6 +1,24 @@
 #include "aubio-types.h"
 
-static char Py_fft_doc[] = "fft object";
+static char Py_fft_doc[] = ""
+"fft(size=1024)\n"
+"\n"
+"Compute Fast Fourier Transorms.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"size : int\n"
+"    size of the FFT to compute\n"
+"\n"
+"Example\n"
+"-------\n"
+">>> x = aubio.fvec(512)\n"
+">>> f = aubio.fft(512)\n"
+">>> c = f(x); c\n"
+"aubio cvec of 257 elements\n"
+">>> x2 = f.rdo(c); x2.shape\n"
+"(512,)\n"
+"";
 
 typedef struct
 {
--- a/python/ext/py-filter.c
+++ b/python/ext/py-filter.c
@@ -10,8 +10,59 @@
   fvec_t c_out;
 } Py_filter;
 
-static char Py_filter_doc[] = "filter object";
+static char Py_filter_doc[] = ""
+"digital_filter(order=7)\n"
+"\n"
+"Create a digital filter.\n"
+"";
 
+static char Py_filter_set_c_weighting_doc[] = ""
+"set_c_weighting(samplerate)\n"
+"\n"
+"Set filter coefficients to C-weighting.\n"
+"\n"
+"`samplerate` should be one of 8000, 11025, 16000, 22050, 24000, 32000,\n"
+"44100, 48000, 88200, 96000, or 192000. `order` of the filter should be 5.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : int\n"
+"    Sampling-rate of the input signal, in Hz.\n"
+"";
+
+static char Py_filter_set_a_weighting_doc[] = ""
+"set_a_weighting(samplerate)\n"
+"\n"
+"Set filter coefficients to A-weighting.\n"
+"\n"
+"`samplerate` should be one of 8000, 11025, 16000, 22050, 24000, 32000,\n"
+"44100, 48000, 88200, 96000, or 192000. `order` of the filter should be 7.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : int\n"
+"    Sampling-rate of the input signal.\n"
+"";
+
+static char Py_filter_set_biquad_doc[] = ""
+"set_biquad(b0, b1, b2, a1, a2)\n"
+"\n"
+"Set biquad coefficients. `order` of the filter should be 3.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"b0 : float\n"
+"    Forward filter coefficient.\n"
+"b1 : float\n"
+"    Forward filter coefficient.\n"
+"b2 : float\n"
+"    Forward filter coefficient.\n"
+"a1 : float\n"
+"    Feedback filter coefficient.\n"
+"a2 : float\n"
+"    Feedback filter coefficient.\n"
+"";
+
 static PyObject *
 Py_filter_new (PyTypeObject * type, PyObject * args, PyObject * kwds)
 {
@@ -156,11 +207,11 @@
 
 static PyMethodDef Py_filter_methods[] = {
   {"set_c_weighting", (PyCFunction) Py_filter_set_c_weighting, METH_VARARGS,
-      "set filter coefficients to C-weighting"},
+      Py_filter_set_c_weighting_doc},
   {"set_a_weighting", (PyCFunction) Py_filter_set_a_weighting, METH_VARARGS,
-      "set filter coefficients to A-weighting"},
+      Py_filter_set_a_weighting_doc},
   {"set_biquad", (PyCFunction) Py_filter_set_biquad, METH_VARARGS,
-      "set b0, b1, b2, a1, a2 biquad coefficients"},
+      Py_filter_set_biquad_doc},
   {NULL}
 };
 
--- a/python/ext/py-filterbank.c
+++ b/python/ext/py-filterbank.c
@@ -1,7 +1,189 @@
 #include "aubio-types.h"
 
-static char Py_filterbank_doc[] = "filterbank object";
+static char Py_filterbank_doc[] = ""
+"filterbank(n_filters=40, win_s=1024)\n"
+"\n"
+"Create a bank of spectral filters. Each instance is a callable\n"
+"that holds a matrix of coefficients.\n"
+"\n"
+"See also :meth:`set_mel_coeffs`, :meth:`set_mel_coeffs_htk`,\n"
+":meth:`set_mel_coeffs_slaney`, :meth:`set_triangle_bands`, and\n"
+":meth:`set_coeffs`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"n_filters : int\n"
+"    Number of filters to create.\n"
+"win_s : int\n"
+"    Size of the input spectrum to process.\n"
+"\n"
+"Examples\n"
+"--------\n"
+">>> f = aubio.filterbank(128, 1024)\n"
+">>> f.set_mel_coeffs(44100, 0, 10000)\n"
+">>> c = aubio.cvec(1024)\n"
+">>> f(c).shape\n"
+"(128, )\n"
+"";
 
+static char Py_filterbank_set_triangle_bands_doc[] =""
+"set_triangle_bands(freqs, samplerate)\n"
+"\n"
+"Set triangular bands. The coefficients will be set to triangular\n"
+"overlapping windows using the boundaries specified by `freqs`.\n"
+"\n"
+"`freqs` should contain `n_filters + 2` frequencies in Hz, ordered\n"
+"by value, from smallest to largest. The first element should be greater\n"
+"or equal to zero; the last element should be smaller or equal to\n"
+"`samplerate / 2`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"freqs: fvec\n"
+"    List of frequencies, in Hz.\n"
+"samplerate : float\n"
+"    Sampling-rate of the expected input.\n"
+"\n"
+"Example\n"
+"-------\n"
+">>> fb = aubio.filterbank(n_filters=100, win_s=2048)\n"
+">>> samplerate = 44100; freqs = np.linspace(0, 20200, 102)\n"
+">>> fb.set_triangle_bands(aubio.fvec(freqs), samplerate)\n"
+"";
+
+static char Py_filterbank_set_mel_coeffs_slaney_doc[] = ""
+"set_mel_coeffs_slaney(samplerate)\n"
+"\n"
+"Set coefficients of filterbank to match Slaney's Auditory Toolbox.\n"
+"\n"
+"The filter coefficients will be set as in Malcolm Slaney's\n"
+"implementation. The filterbank should have been created with\n"
+"`n_filters = 40`.\n"
+"\n"
+"This is approximately equivalent to using :meth:`set_mel_coeffs` with\n"
+"`fmin = 400./3., fmax = 6853.84`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : float\n"
+"    Sampling-rate of the expected input.\n"
+"\n"
+"References\n"
+"----------\n"
+"\n"
+"Malcolm Slaney, `Auditory Toolbox Version 2, Technical Report #1998-010\n"
+"<https://engineering.purdue.edu/~malcolm/interval/1998-010/>`_\n"
+"";
+
+static char Py_filterbank_set_mel_coeffs_doc[] = ""
+"set_mel_coeffs(samplerate, fmin, fmax)\n"
+"\n"
+"Set coefficients of filterbank to linearly spaced mel scale.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : float\n"
+"    Sampling-rate of the expected input.\n"
+"fmin : float\n"
+"    Lower frequency boundary of the first filter.\n"
+"fmax : float\n"
+"    Upper frequency boundary of the last filter.\n"
+"\n"
+"See also\n"
+"--------\n"
+"hztomel\n"
+"";
+
+static char Py_filterbank_set_mel_coeffs_htk_doc[] = ""
+"set_mel_coeffs_htk(samplerate, fmin, fmax)\n"
+"\n"
+"Set coefficients of the filters to be linearly spaced in the HTK mel scale.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"samplerate : float\n"
+"    Sampling-rate of the expected input.\n"
+"fmin : float\n"
+"    Lower frequency boundary of the first filter.\n"
+"fmax : float\n"
+"    Upper frequency boundary of the last filter.\n"
+"\n"
+"See also\n"
+"--------\n"
+"hztomel with `htk=True`\n"
+"";
+
+static char Py_filterbank_get_coeffs_doc[] = ""
+"get_coeffs()\n"
+"\n"
+"Get coefficients matrix of filterbank.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"array_like\n"
+"    Array of shape (n_filters, win_s/2+1) containing the coefficients.\n"
+"";
+
+static char Py_filterbank_set_coeffs_doc[] = ""
+"set_coeffs(coeffs)\n"
+"\n"
+"Set coefficients of filterbank.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"coeffs : fmat\n"
+"    Array of shape (n_filters, win_s/2+1) containing the coefficients.\n"
+"";
+
+static char Py_filterbank_set_power_doc[] = ""
+"set_power(power)\n"
+"\n"
+"Set power applied to input spectrum of filterbank.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"power : float\n"
+"    Power to raise input spectrum to before computing the filters.\n"
+"";
+
+static char Py_filterbank_get_power_doc[] = ""
+"get_power()\n"
+"\n"
+"Get power applied to filterbank.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+"    Power parameter.\n"
+"";
+
+static char Py_filterbank_set_norm_doc[] = ""
+"set_norm(norm)\n"
+"\n"
+"Set norm parameter. If set to `0`, the filters will not be normalized.\n"
+"If set to `1`, the filters will be normalized to one. Default to `1`.\n"
+"\n"
+"This function should be called *before* :meth:`set_triangle_bands`,\n"
+":meth:`set_mel_coeffs`, :meth:`set_mel_coeffs_htk`, or\n"
+":meth:`set_mel_coeffs_slaney`.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"norm : int\n"
+"   `0` to disable, `1` to enable\n"
+"";
+
+static char Py_filterbank_get_norm_doc[] = ""
+"get_norm()\n"
+"\n"
+"Get norm parameter of filterbank.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+"    Norm parameter.\n"
+"";
+
 typedef struct
 {
   PyObject_HEAD
@@ -289,6 +471,13 @@
 }
 
 static PyObject *
+Py_filterbank_get_power (Py_filterbank * self, PyObject *unused)
+{
+  smpl_t power = aubio_filterbank_get_power(self->o);
+  return (PyObject *)PyFloat_FromDouble (power);
+}
+
+static PyObject *
 Py_filterbank_set_norm(Py_filterbank *self, PyObject *args)
 {
   smpl_t norm;
@@ -311,23 +500,34 @@
   Py_RETURN_NONE;
 }
 
+static PyObject *
+Py_filterbank_get_norm (Py_filterbank * self, PyObject *unused)
+{
+  smpl_t norm = aubio_filterbank_get_norm(self->o);
+  return (PyObject *)PyFloat_FromDouble (norm);
+}
+
 static PyMethodDef Py_filterbank_methods[] = {
   {"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands,
-    METH_VARARGS, "set coefficients of filterbanks"},
+    METH_VARARGS, Py_filterbank_set_triangle_bands_doc},
   {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney,
-    METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"},
+    METH_VARARGS, Py_filterbank_set_mel_coeffs_slaney_doc},
   {"set_mel_coeffs", (PyCFunction) Py_filterbank_set_mel_coeffs,
-    METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"},
+    METH_VARARGS, Py_filterbank_set_mel_coeffs_doc},
   {"set_mel_coeffs_htk", (PyCFunction) Py_filterbank_set_mel_coeffs_htk,
-    METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"},
+    METH_VARARGS, Py_filterbank_set_mel_coeffs_htk_doc},
   {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs,
-    METH_NOARGS, "get coefficients of filterbank"},
+    METH_NOARGS, Py_filterbank_get_coeffs_doc},
   {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs,
-    METH_VARARGS, "set coefficients of filterbank"},
+    METH_VARARGS, Py_filterbank_set_coeffs_doc},
   {"set_power", (PyCFunction) Py_filterbank_set_power,
-    METH_VARARGS, "set power applied to filterbank input spectrum"},
+    METH_VARARGS, Py_filterbank_set_power_doc},
+  {"get_power", (PyCFunction) Py_filterbank_get_power,
+    METH_NOARGS, Py_filterbank_get_power_doc},
   {"set_norm", (PyCFunction) Py_filterbank_set_norm,
-    METH_VARARGS, "set norm applied to filterbank input spectrum"},
+    METH_VARARGS, Py_filterbank_set_norm_doc},
+  {"get_norm", (PyCFunction) Py_filterbank_get_norm,
+    METH_NOARGS, Py_filterbank_get_norm_doc},
   {NULL}
 };
 
--- a/python/lib/gen_code.py
+++ b/python/lib/gen_code.py
@@ -231,11 +231,21 @@
         return out.format(do_inputs_list = do_inputs_list, **self.__dict__)
 
     def gen_doc(self):
+        sig = []
+        for p in self.input_params:
+            name = p['name']
+            defval = aubiodefvalue[name].replace('"','\\\"')
+            sig.append("{name}={defval}".format(defval=defval, name=name))
         out = """
-// TODO: add documentation
-static char Py_{shortname}_doc[] = \"undefined\";
+#ifndef PYAUBIO_{shortname}_doc
+#define PYAUBIO_{shortname}_doc "{shortname}({sig})"
+#endif /* PYAUBIO_{shortname}_doc */
+
+static char Py_{shortname}_doc[] = ""
+PYAUBIO_{shortname}_doc
+"";
 """
-        return out.format(**self.__dict__)
+        return out.format(sig=', '.join(sig), **self.__dict__)
 
     def gen_new(self):
         out = """
--- a/python/tests/test_mfcc.py
+++ b/python/tests/test_mfcc.py
@@ -111,13 +111,15 @@
     def test_set_scale(self):
         buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
         m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
-        m.set_scale(10.)
+        m.set_scale(10.5)
+        assert m.get_scale() == 10.5
         m(cvec(buf_size))
 
     def test_set_power(self):
         buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
         m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
-        m.set_power(2.)
+        m.set_power(2.5)
+        assert m.get_power() == 2.5
         m(cvec(buf_size))
 
     def test_set_mel_coeffs(self):
--- a/scripts/get_waf.sh
+++ b/scripts/get_waf.sh
@@ -3,7 +3,7 @@
 set -e
 #set -x
 
-WAFVERSION=2.0.12
+WAFVERSION=2.0.13
 WAFTARBALL=waf-$WAFVERSION.tar.bz2
 WAFURL=https://waf.io/$WAFTARBALL
 WAFUPSTREAMKEY=https://gitlab.com/ita1024/waf/raw/master/utils/pubkey.asc
--- a/src/pitch/pitchspecacf.c
+++ b/src/pitch/pitchspecacf.c
@@ -92,6 +92,7 @@
   del_aubio_fft (p->fft);
   del_fvec (p->sqrmag);
   del_fvec (p->fftout);
+  del_fvec (p->acf);
   AUBIO_FREE (p);
 }
 
--- a/src/spectral/awhitening.c
+++ b/src/spectral/awhitening.c
@@ -43,7 +43,8 @@
 aubio_spectral_whitening_do (aubio_spectral_whitening_t * o, cvec_t * fftgrain)
 {
   uint_t i = 0;
-  for (i = 0; i < o->peak_values->length; i++) {
+  uint_t length = MIN(fftgrain->length, o->peak_values->length);
+  for (i = 0; i < length; i++) {
     smpl_t tmp = MAX(o->r_decay * o->peak_values->data[i], o->floor);
     o->peak_values->data[i] = MAX(fftgrain->norm[i], tmp);
     fftgrain->norm[i] /= o->peak_values->data[i];
--- a/src/spectral/filterbank.c
+++ b/src/spectral/filterbank.c
@@ -130,5 +130,5 @@
 smpl_t
 aubio_filterbank_get_power (aubio_filterbank_t *f)
 {
-  return f->norm;
+  return f->power;
 }
--- a/src/spectral/mfcc.c
+++ b/src/spectral/mfcc.c
@@ -143,7 +143,7 @@
   return aubio_filterbank_set_power(mf->fb, power);
 }
 
-uint_t aubio_mfcc_get_power (aubio_mfcc_t *mf)
+smpl_t aubio_mfcc_get_power (aubio_mfcc_t *mf)
 {
   return aubio_filterbank_get_power(mf->fb);
 }
@@ -154,7 +154,7 @@
   return AUBIO_OK;
 }
 
-uint_t aubio_mfcc_get_scale (aubio_mfcc_t *mf)
+smpl_t aubio_mfcc_get_scale (aubio_mfcc_t *mf)
 {
   return mf->scale;
 }
--- a/src/spectral/mfcc.h
+++ b/src/spectral/mfcc.h
@@ -92,7 +92,7 @@
   See aubio_filterbank_get_power().
 
  */
-uint_t aubio_mfcc_get_power (aubio_mfcc_t *mf);
+smpl_t aubio_mfcc_get_power (aubio_mfcc_t *mf);
 
 /** set scaling parameter
 
@@ -111,7 +111,7 @@
   \return current scaling parameter. Defaults to `1`.
 
  */
-uint_t aubio_mfcc_get_scale (aubio_mfcc_t *mf);
+smpl_t aubio_mfcc_get_scale (aubio_mfcc_t *mf);
 
 /** Mel filterbank initialization
 
--- a/src/utils/hist.c
+++ b/src/utils/hist.c
@@ -44,6 +44,7 @@
   smpl_t accum = step;
   uint_t i;
   if ((sint_t)nelems <= 0) {
+    AUBIO_FREE(s);
     return NULL;
   }
   s->nelems = nelems;
--- a/tests/src/spectral/test-awhitening.c
+++ b/tests/src/spectral/test-awhitening.c
@@ -41,9 +41,11 @@
   if (!o) { err = 1; goto beach_sink; }
 
   aubio_pvoc_t *pv = new_aubio_pvoc(win_size, hop_size);
+  if (!pv) { err = 1; goto beach_pvoc; }
 
   aubio_spectral_whitening_t *awhitening =
     new_aubio_spectral_whitening (win_size, hop_size, samplerate);
+  if (!awhitening) { err = 1; goto beach_awhitening; }
 
   aubio_spectral_whitening_set_relax_time(awhitening, 20.);
   fvec_set_all(scale, 3.);
@@ -71,11 +73,18 @@
       n_frames, samplerate, n_frames / hop_size,
       source_path, sink_path);
 
+  del_aubio_spectral_whitening(awhitening);
+beach_awhitening:
+  del_aubio_pvoc(pv);
+beach_pvoc:
   del_aubio_sink(o);
 beach_sink:
   del_aubio_source(i);
 beach_source:
   del_fvec(vec);
+  del_fvec(out);
+  del_fvec(scale);
+  del_cvec(fftgrain);
 beach_fvec:
   return err;
 }
--- a/tests/src/spectral/test-mfcc.c
+++ b/tests/src/spectral/test-mfcc.c
@@ -1,13 +1,89 @@
 #include <aubio.h>
+#include "utils_tests.h"
 
-int main (void)
+int test_wrong_params(void);
+
+int main (int argc, char** argv)
 {
+  sint_t err = 0;
+
+  if (argc < 2) {
+    err = 2;
+    PRINT_WRN("no arguments, running tests\n");
+    err = test_wrong_params();
+    PRINT_MSG("usage: %s <input_path> [samplerate] [hop_size]\n", argv[0]);
+    return err;
+  }
+
+  uint_t win_s; // fft size
+  uint_t hop_s = 256; // block size
+  uint_t samplerate = 0; // samplerate
+  uint_t n_filters = 40; // number of filters
+  uint_t n_coeffs = 13; // number of coefficients
+  uint_t read = 0;
+
+  char_t *source_path = argv[1];
+
+  if ( argc >= 3 ) samplerate = atoi(argv[2]);
+  if ( argc >= 4 ) hop_s = atoi(argv[3]);
+
+  win_s = 2 * hop_s;
+
+  aubio_source_t *source = 0;
+  aubio_pvoc_t *pv = 0;
+  aubio_mfcc_t *mfcc = 0;
+
+  fvec_t *in = new_fvec (win_s); // input buffer
+  cvec_t *fftgrain = new_cvec (win_s); // input buffer
+  fvec_t *out = new_fvec (n_coeffs); // output coefficients
+
+  if (!in || !fftgrain || !out) { err = 1; goto failure; }
+
+  // source
+  source = new_aubio_source(source_path, samplerate, hop_s);
+  if (!source) { err = 1; goto failure; }
+  if (samplerate == 0) samplerate = aubio_source_get_samplerate(source);
+
+  // phase vocoder
+  pv = new_aubio_pvoc(win_s, hop_s);
+  if (!pv) { err = 1; goto failure; }
+
+  // mfcc object
+  mfcc = new_aubio_mfcc (win_s, n_filters, n_coeffs, samplerate);
+  if (!mfcc) { err = 1; goto failure; }
+
+  // processing loop
+  do {
+    aubio_source_do(source, in, &read);
+    aubio_pvoc_do(pv, in, fftgrain);
+    aubio_mfcc_do(mfcc, fftgrain, out);
+    fvec_print(out);
+  } while (read == hop_s);
+
+failure:
+
+  if (mfcc)
+    del_aubio_mfcc(mfcc);
+  if (pv)
+    del_aubio_pvoc(pv);
+  if (source)
+    del_aubio_source(source);
+  if (in)
+    del_fvec(in);
+  if (fftgrain)
+    del_cvec(fftgrain);
+  if (out)
+    del_fvec(out);
+  aubio_cleanup();
+  return err;
+}
+
+int test_wrong_params()
+{
   uint_t win_s = 512; // fft size
   uint_t n_filters = 40; // number of filters
   uint_t n_coeffs = 13; // number of coefficients
   smpl_t samplerate = 16000.; // samplerate
-  cvec_t *in = new_cvec (win_s); // input buffer
-  fvec_t *out = new_fvec (n_coeffs); // output coefficients
 
   if (new_aubio_mfcc(    0, n_filters, n_coeffs, samplerate)) return 1;
   if (new_aubio_mfcc(win_s,         0, n_coeffs, samplerate)) return 1;
@@ -14,22 +90,5 @@
   if (new_aubio_mfcc(win_s, n_filters,        0, samplerate)) return 1;
   if (new_aubio_mfcc(win_s, n_filters, n_coeffs,          0)) return 1;
 
-  // create mfcc object
-  aubio_mfcc_t *o = new_aubio_mfcc (win_s, n_filters, n_coeffs, samplerate);
-
-  cvec_norm_set_all (in, 1.);
-  aubio_mfcc_do (o, in, out);
-  fvec_print (out);
-
-  cvec_norm_set_all (in, .5);
-  aubio_mfcc_do (o, in, out);
-  fvec_print (out);
-
-  // clean up
-  del_aubio_mfcc (o);
-  del_cvec (in);
-  del_fvec (out);
-  aubio_cleanup ();
-
-  return 0;
+  return run_on_default_source(main);
 }
--- a/tests/src/test-mathutils.c
+++ b/tests/src/test-mathutils.c
@@ -100,6 +100,7 @@
   window = new_fvec(window_size);
   fvec_set_window(window, "rectangle");
   fvec_print(window);
+  del_fvec(window);
 
   window_size /= 2.;
   window = new_aubio_window("parzen", window_size);
--- a/wscript
+++ b/wscript
@@ -546,32 +546,43 @@
     # build documentation from source files using doxygen
     if bld.env['DOXYGEN']:
         bld.env.VERSION = VERSION
-        rule = '( cat ${SRC} && echo PROJECT_NUMBER=${VERSION}; )'
+        rule = '( cat ${SRC[0]} && echo PROJECT_NUMBER=${VERSION}'
+        rule += ' && echo OUTPUT_DIRECTORY=%s && echo HTML_OUTPUT=%s )'
         rule += ' | doxygen - > /dev/null'
+        rule %= (os.path.abspath(out), 'api')
         bld( name = 'doxygen', rule = rule,
-                source = 'doc/web.cfg',
-                target = '../doc/web/html/index.html',
-                cwd = 'doc')
-        bld.install_files( '${DATAROOTDIR}' + '/doc/libaubio-doc',
-                bld.path.ant_glob('doc/web/html/**'),
-                cwd = bld.path.find_dir ('doc/web'),
-                relative_trick = True)
+                source = ['doc/web.cfg']
+                    + bld.path.find_dir('src').ant_glob('**/*.h'),
+                target = bld.path.find_or_declare('api/index.html'),
+                cwd = bld.path.find_dir('doc'))
+        bld.install_files('${DATAROOTDIR}/doc/libaubio-doc/api',
+                bld.path.find_or_declare('api').ant_glob('**/*'),
+                cwd=bld.path.find_or_declare('api'),
+                relative_trick=True)
 
 def sphinx(bld):
-    # build documentation from source files using sphinx-build note: build in
-    # ../doc/_build/html, otherwise waf wont install unsigned files
-    if bld.env['SPHINX']:
+    # build documentation from source files using sphinx-build
+    try:
+        import aubio
+        has_aubio = True
+    except ImportError:
+        from waflib import Logs
+        Logs.pprint('YELLOW', "Sphinx manual: install aubio first")
+        has_aubio = False
+    if bld.env['SPHINX'] and has_aubio:
         bld.env.VERSION = VERSION
-        bld( name = 'sphinx',
-                rule = '${SPHINX} -b html -D release=${VERSION}' \
-                        ' -D version=${VERSION} -a -q' \
-                        ' `dirname ${SRC}` `dirname ${TGT}`',
-                source = 'doc/conf.py',
-                target = '../doc/_build/html/index.html')
-        bld.install_files( '${DATAROOTDIR}' + '/doc/libaubio-doc/sphinx',
-                bld.path.ant_glob('doc/_build/html/**'),
-                cwd = bld.path.find_dir('doc/_build/html'),
-                relative_trick = True)
+        rule = '${SPHINX} -b html -D release=${VERSION}' \
+                ' -D version=${VERSION} -W -a -q' \
+                ' -d %s ' % os.path.join(os.path.abspath(out), 'doctrees')
+        rule += ' . %s' % os.path.join(os.path.abspath(out), 'manual')
+        bld( name = 'sphinx', rule = rule,
+                cwd = bld.path.find_dir('doc'),
+                source = bld.path.find_dir('doc').ant_glob('*.rst'),
+                target = bld.path.find_or_declare('manual/index.html'))
+        bld.install_files('${DATAROOTDIR}/doc/libaubio-doc/manual',
+                bld.path.find_or_declare('manual').ant_glob('**/*'),
+                cwd=bld.path.find_or_declare('manual'),
+                relative_trick=True)
 
 # register the previous rules as build rules
 from waflib.Build import BuildContext