Index: .gitignore
===================================================================
--- .gitignore	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ .gitignore	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -49,2 +49,4 @@
 python/tests/sounds
 aubio.egg-info
+.eggs
+.cache
Index: MANIFEST.in
===================================================================
--- MANIFEST.in	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ MANIFEST.in	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -2,7 +2,7 @@
 include python/README.md
 include this_version.py
+include waf
+recursive-include waflib *.py
 include Makefile wscript */wscript_build
-include waf waflib/* waflib/*/*
-exclude waflib/__pycache__/*
 include aubio.pc.in
 include nose2.cfg
@@ -13,13 +13,8 @@
 include tests/*.h tests/*/*.c tests/*/*/*.c
 include python/ext/*.h
-include python/__init__.py
-include python/lib/__init__.py
-include python/lib/moresetuptools.py
-include python/lib/gen_external.py
-include python/lib/gen_code.py
+recursive-include python *.py
+include python/README.md
 include python/tests/run_all_tests
-include python/tests/*.py
 include python/tests/eval_pitch
-include python/demos/*.py
 include python/tests/*.expected
 include doc/*.txt doc/*.rst doc/*.cfg doc/Makefile doc/make.bat doc/conf.py
Index: Makefile
===================================================================
--- Makefile	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ Makefile	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -234,4 +234,9 @@
 	uninstall_python \
 	check_clean_python
+
+coverage_cycle: coverage_zero_counters coverage_report
+
+coverage_zero_counters:
+	lcov --zerocounters --directory .
 
 coverage: export CFLAGS=--coverage
@@ -247,4 +252,6 @@
 	lcov -a build/coverage_python.info -a build/coverage_lib.info -o build/coverage.info
 
+# make sure we don't build the doc, which builds a temporary python module
+coverage_report: export WAFOPTS += --disable-docs
 coverage_report: coverage
 	genhtml build/coverage.info --output-directory lcov_html
Index: doc/py_io.rst
===================================================================
--- doc/py_io.rst	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ doc/py_io.rst	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -40,5 +40,5 @@
     down-mixed to produce the new samples.
 
-    return: A tuple of one array of samples and one integer.
+    :returns: A tuple of one array of samples and one integer.
     :rtype: (array, int)
 
Index: doc/py_utils.rst
===================================================================
--- doc/py_utils.rst	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ doc/py_utils.rst	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -28,4 +28,10 @@
 
 .. autofunction:: miditofreq
+
+.. python/ext/py-musicutils.h
+
+.. autofunction:: meltohz
+
+.. autofunction:: hztomel
 
 .. python/ext/aubiomodule.c
Index: python/demos/demo_filter.py
===================================================================
--- python/demos/demo_filter.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/demos/demo_filter.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -4,4 +4,5 @@
 import os.path
 import aubio
+
 
 def apply_filter(path, target):
@@ -28,5 +29,6 @@
         total_frames += read
         # end of file reached
-        if read < s.hop_size: break
+        if read < s.hop_size:
+            break
 
     # print some info
@@ -34,11 +36,11 @@
     input_str = "input: {:s} ({:.2f} s, {:d} Hz)"
     output_str = "output: {:s}, A-weighting filtered ({:d} frames total)"
-    print (input_str.format(s.uri, duration, samplerate))
-    print (output_str.format(o.uri, total_frames))
+    print(input_str.format(s.uri, duration, samplerate))
+    print(output_str.format(o.uri, total_frames))
 
 if __name__ == '__main__':
     usage = "{:s} <input_file> [output_file]".format(sys.argv[0])
     if not 1 < len(sys.argv) < 4:
-        print (usage)
+        print(usage)
         sys.exit(1)
     if len(sys.argv) < 3:
Index: python/demos/demo_filterbank.py
===================================================================
--- python/demos/demo_filterbank.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/demos/demo_filterbank.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -1,30 +1,44 @@
 #! /usr/bin/env python
 
-from aubio import filterbank, fvec
-from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title
-from numpy import vstack, arange
+"""Create a filterbank from a list of frequencies.
 
+This demo uses `aubio.filterbank.set_triangle_bands` to build a set of
+triangular filters from a list of frequencies.
+
+The filterbank coefficients are then modified before being displayed."""
+
+import aubio
+import numpy as np
+import matplotlib.pyplot as plt
+
+# sampling rate and size of the fft
+samplerate = 48000
 win_s = 2048
-samplerate = 48000
 
+# define a list of custom frequency
 freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
+# number of filters to create
 n_filters = len(freq_list) - 2
 
-f = filterbank(n_filters, win_s)
-freqs = fvec(freq_list)
+# create a new filterbank
+f = aubio.filterbank(n_filters, win_s)
+freqs = aubio.fvec(freq_list)
 f.set_triangle_bands(freqs, samplerate)
 
+# get the coefficients from the filterbank
 coeffs = f.get_coeffs()
-coeffs[4] *= 5.
-
+# apply a gain to fifth band
+coeffs[4] *= 6.
+# load the modified coeffs into the filterbank
 f.set_coeffs(coeffs)
 
-times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
-title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.')
-loglog(times.T, f.get_coeffs().T, '.-')
-xlim([50, samplerate/2])
-ylim([1.0e-6, 2.0e-2])
-xlabel('log frequency (Hz)')
-ylabel('log amplitude')
-
-show()
+# display the band gains in a loglog plot
+freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
+plt.title('filterbank built from a list of frequencies\n'
+          'The 5th band has been amplified by a factor 6.')
+plt.loglog(freqs.T, f.get_coeffs().T, '.-')
+plt.xlim([50, samplerate/2])
+plt.ylim([1.0e-6, 2.0e-2])
+plt.xlabel('log frequency (Hz)')
+plt.ylabel('log amplitude')
+plt.show()
Index: python/demos/demo_source_simple.py
===================================================================
--- python/demos/demo_source_simple.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/demos/demo_source_simple.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -1,16 +1,20 @@
 #! /usr/bin/env python
+
+"""A simple example using aubio.source."""
+
 import sys
 import aubio
 
-samplerate = 0 # use original source samplerate
-hop_size = 256 # number of frames to read in one block
+samplerate = 0  # use original source samplerate
+hop_size = 256  # number of frames to read in one block
 src = aubio.source(sys.argv[1], samplerate, hop_size)
 total_frames = 0
 
 while True:
-    samples, read = src()     # read hop_size new samples from source
-    total_frames += read      # increment total number of frames
-    if read < hop_size: break # end of file reached
+    samples, read = src()  # read hop_size new samples from source
+    total_frames += read   # increment total number of frames
+    if read < hop_size:    # end of file reached
+        break
 
 fmt_string = "read {:d} frames at {:d}Hz from {:s}"
-print (fmt_string.format(total_frames, src.samplerate, src.uri))
+print(fmt_string.format(total_frames, src.samplerate, src.uri))
Index: python/ext/aubiomodule.c
===================================================================
--- python/ext/aubiomodule.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/ext/aubiomodule.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -373,4 +373,8 @@
   {"shift", Py_aubio_shift, METH_VARARGS, Py_aubio_shift_doc},
   {"ishift", Py_aubio_ishift, METH_VARARGS, Py_aubio_ishift_doc},
+  {"hztomel", Py_aubio_hztomel, METH_VARARGS|METH_KEYWORDS, Py_aubio_hztomel_doc},
+  {"meltohz", Py_aubio_meltohz, METH_VARARGS|METH_KEYWORDS, Py_aubio_meltohz_doc},
+  {"hztomel_htk", Py_aubio_hztomel_htk, METH_VARARGS, Py_aubio_hztomel_htk_doc},
+  {"meltohz_htk", Py_aubio_meltohz_htk, METH_VARARGS, Py_aubio_meltohz_htk_doc},
   {NULL, NULL, 0, NULL} /* Sentinel */
 };
Index: python/ext/py-filterbank.c
===================================================================
--- python/ext/py-filterbank.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/ext/py-filterbank.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -139,6 +139,12 @@
       &(self->freqs), samplerate);
   if (err > 0) {
-    PyErr_SetString (PyExc_ValueError,
-        "error when running set_triangle_bands");
+    if (PyErr_Occurred() == NULL) {
+      PyErr_SetString (PyExc_ValueError, "error running set_triangle_bands");
+    } else {
+      // change the RuntimeError into ValueError
+      PyObject *type, *value, *traceback;
+      PyErr_Fetch(&type, &value, &traceback);
+      PyErr_Restore(PyExc_ValueError, value, traceback);
+    }
     return NULL;
   }
@@ -151,6 +157,6 @@
   uint_t err = 0;
 
-  uint_t samplerate;
-  if (!PyArg_ParseTuple (args, "I", &samplerate)) {
+  smpl_t samplerate;
+  if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &samplerate)) {
     return NULL;
   }
@@ -158,6 +164,70 @@
   err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate);
   if (err > 0) {
-    PyErr_SetString (PyExc_ValueError,
-        "error when running set_mel_coeffs_slaney");
+    if (PyErr_Occurred() == NULL) {
+      PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_slaney");
+    } else {
+      // change the RuntimeError into ValueError
+      PyObject *type, *value, *traceback;
+      PyErr_Fetch(&type, &value, &traceback);
+      PyErr_Restore(PyExc_ValueError, value, traceback);
+    }
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_set_mel_coeffs (Py_filterbank * self, PyObject *args)
+{
+  uint_t err = 0;
+
+  smpl_t samplerate;
+  smpl_t freq_min;
+  smpl_t freq_max;
+  if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR
+        AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) {
+    return NULL;
+  }
+
+  err = aubio_filterbank_set_mel_coeffs (self->o, samplerate,
+      freq_min, freq_max);
+  if (err > 0) {
+    if (PyErr_Occurred() == NULL) {
+      PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs");
+    } else {
+      // change the RuntimeError into ValueError
+      PyObject *type, *value, *traceback;
+      PyErr_Fetch(&type, &value, &traceback);
+      PyErr_Restore(PyExc_ValueError, value, traceback);
+    }
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_set_mel_coeffs_htk (Py_filterbank * self, PyObject *args)
+{
+  uint_t err = 0;
+
+  smpl_t samplerate;
+  smpl_t freq_min;
+  smpl_t freq_max;
+  if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR
+        AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) {
+    return NULL;
+  }
+
+  err = aubio_filterbank_set_mel_coeffs_htk (self->o, samplerate,
+      freq_min, freq_max);
+  if (err > 0) {
+    if (PyErr_Occurred() == NULL) {
+      PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_htk");
+    } else {
+      // change the RuntimeError into ValueError
+      PyObject *type, *value, *traceback;
+      PyErr_Fetch(&type, &value, &traceback);
+      PyErr_Restore(PyExc_ValueError, value, traceback);
+    }
     return NULL;
   }
@@ -194,4 +264,50 @@
   return (PyObject *)PyAubio_CFmatToArray(
       aubio_filterbank_get_coeffs (self->o) );
+}
+
+static PyObject *
+Py_filterbank_set_power(Py_filterbank *self, PyObject *args)
+{
+  uint_t power;
+
+  if (!PyArg_ParseTuple (args, "I", &power)) {
+    return NULL;
+  }
+  if(aubio_filterbank_set_power (self->o, power)) {
+    if (PyErr_Occurred() == NULL) {
+      PyErr_SetString (PyExc_ValueError,
+          "error running filterbank.set_power");
+    } else {
+      // change the RuntimeError into ValueError
+      PyObject *type, *value, *traceback;
+      PyErr_Fetch(&type, &value, &traceback);
+      PyErr_Restore(PyExc_ValueError, value, traceback);
+    }
+    return NULL;
+  }
+  Py_RETURN_NONE;
+}
+
+static PyObject *
+Py_filterbank_set_norm(Py_filterbank *self, PyObject *args)
+{
+  uint_t playing;
+
+  if (!PyArg_ParseTuple (args, "I", &playing)) {
+    return NULL;
+  }
+  if(aubio_filterbank_set_norm (self->o, playing)) {
+    if (PyErr_Occurred() == NULL) {
+      PyErr_SetString (PyExc_ValueError,
+          "error running filterbank.set_power");
+    } else {
+      // change the RuntimeError into ValueError
+      PyObject *type, *value, *traceback;
+      PyErr_Fetch(&type, &value, &traceback);
+      PyErr_Restore(PyExc_ValueError, value, traceback);
+    }
+    return NULL;
+  }
+  Py_RETURN_NONE;
 }
 
@@ -201,8 +317,16 @@
   {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney,
     METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"},
+  {"set_mel_coeffs", (PyCFunction) Py_filterbank_set_mel_coeffs,
+    METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"},
+  {"set_mel_coeffs_htk", (PyCFunction) Py_filterbank_set_mel_coeffs_htk,
+    METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"},
   {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs,
     METH_NOARGS, "get coefficients of filterbank"},
   {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs,
     METH_VARARGS, "set coefficients of filterbank"},
+  {"set_power", (PyCFunction) Py_filterbank_set_power,
+    METH_VARARGS, "set power applied to filterbank input spectrum"},
+  {"set_norm", (PyCFunction) Py_filterbank_set_norm,
+    METH_VARARGS, "set norm applied to filterbank input spectrum"},
   {NULL}
 };
Index: python/ext/py-musicutils.c
===================================================================
--- python/ext/py-musicutils.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/ext/py-musicutils.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -182,2 +182,56 @@
   return (PyObject *) PyAubio_CFvecToArray(&vec);
 }
+
+PyObject*
+Py_aubio_hztomel(PyObject *self, PyObject *args, PyObject *kwds)
+{
+  smpl_t v;
+  PyObject *htk = NULL;
+  static char *kwlist[] = {"f", "htk", NULL};
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O",
+        kwlist, &v, &htk))
+  {
+    return NULL;
+  }
+  if (htk != NULL && PyObject_IsTrue(htk) == 1)
+    return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel_htk(v));
+  else
+    return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel(v));
+}
+
+PyObject*
+Py_aubio_meltohz(PyObject *self, PyObject *args, PyObject *kwds)
+{
+  smpl_t v;
+  PyObject *htk = NULL;
+  static char *kwlist[] = {"m", "htk", NULL};
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O",
+        kwlist, &v, &htk))
+  {
+    return NULL;
+  }
+  if (htk != NULL && PyObject_IsTrue(htk) == 1)
+    return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz_htk(v));
+  else
+    return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz(v));
+}
+
+PyObject*
+Py_aubio_hztomel_htk(PyObject *self, PyObject *args)
+{
+  smpl_t v;
+  if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) {
+    return NULL;
+  }
+  return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel_htk(v));
+}
+
+PyObject*
+Py_aubio_meltohz_htk(PyObject *self, PyObject *args)
+{
+  smpl_t v;
+  if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) {
+    return NULL;
+  }
+  return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz_htk(v));
+}
Index: python/ext/py-musicutils.h
===================================================================
--- python/ext/py-musicutils.h	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/ext/py-musicutils.h	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -301,3 +301,71 @@
 PyObject * Py_aubio_ishift(PyObject *self, PyObject *args);
 
+static char Py_aubio_hztomel_doc[] = ""
+"hztomel(f, htk=False)\n"
+"\n"
+"Convert a scalar from frequency to mel scale.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"m : float\n"
+"   input frequency, in Hz\n"
+"htk : bool\n"
+"   if `True`, use Htk mel scale instead of Slaney.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+"   output mel\n"
+"\n"
+"See Also\n"
+"--------\n"
+"meltohz\n"
+"";
+PyObject * Py_aubio_hztomel(PyObject *self, PyObject *args);
+
+static char Py_aubio_meltohz_doc[] = ""
+"meltohz(m, htk=False)\n"
+"\n"
+"Convert a scalar from mel scale to frequency.\n"
+"\n"
+"Parameters\n"
+"----------\n"
+"m : float\n"
+"   input mel\n"
+"htk : bool\n"
+"   if `True`, use Htk mel scale instead of Slaney.\n"
+"\n"
+"Returns\n"
+"-------\n"
+"float\n"
+"   output frequency, in Hz\n"
+"\n"
+"See Also\n"
+"--------\n"
+"hztomel\n"
+"";
+PyObject * Py_aubio_meltohz(PyObject *self, PyObject *args);
+
+static char Py_aubio_hztomel_htk_doc[] = ""
+"hztomel_htk(m)\n"
+"\n"
+"Same as `hztomel(m, htk=True)`\n"
+"\n"
+"See Also\n"
+"--------\n"
+"hztomel\n"
+"";
+PyObject * Py_aubio_hztomel_htk(PyObject *self, PyObject *args);
+
+static char Py_aubio_meltohz_htk_doc[] = ""
+"meltohz_htk(m)\n"
+"\n"
+"Same as `meltohz(m, htk=True)`\n"
+"\n"
+"See Also\n"
+"--------\n"
+"meltohz\n"
+"";
+PyObject * Py_aubio_meltohz_htk(PyObject *self, PyObject *args);
+
 #endif /* PY_AUBIO_MUSICUTILS_H */
Index: python/ext/py-sink.c
===================================================================
--- python/ext/py-sink.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/ext/py-sink.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -15,5 +15,5 @@
 "sink(path, samplerate=44100, channels=1)\n"
 "\n"
-"Open `path` to write a WAV file.\n"
+"Write audio samples to file.\n"
 "\n"
 "Parameters\n"
Index: python/ext/py-source.c
===================================================================
--- python/ext/py-source.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/ext/py-source.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -19,5 +19,5 @@
 "source(path, samplerate=0, hop_size=512, channels=0)\n"
 "\n"
-"Create a new source, opening the given pathname for reading.\n"
+"Read audio samples from a media file.\n"
 "\n"
 "`source` open the file specified in `path` and creates a callable\n"
@@ -248,5 +248,5 @@
 "Returns\n"
 "-------\n"
-"samples : numpy.ndarray, shape `(hop_size,)`, dtype aubio.float_type\n"
+"samples : numpy.ndarray\n"
 "    `fvec` of size `hop_size` containing the new samples.\n"
 "read : int\n"
@@ -284,5 +284,5 @@
 "Returns\n"
 "-------\n"
-"samples : np.ndarray([hop_size, channels], dtype=aubio.float_type)\n"
+"samples : numpy.ndarray\n"
 "    NumPy array of shape `(hop_size, channels)` containing the new\n"
 "    audio samples.\n"
Index: python/lib/aubio/__init__.py
===================================================================
--- python/lib/aubio/__init__.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/aubio/__init__.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -29,4 +29,5 @@
 from .midiconv import *
 from .slicing import *
+
 
 class fvec(numpy.ndarray):
Index: python/lib/aubio/cmd.py
===================================================================
--- python/lib/aubio/cmd.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/aubio/cmd.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -12,4 +12,5 @@
 import sys
 import argparse
+import warnings
 import aubio
 
@@ -169,8 +170,8 @@
 
     def add_verbose_help(self):
-        self.add_argument("-v","--verbose",
+        self.add_argument("-v", "--verbose",
                 action="count", dest="verbose", default=1,
                 help="make lots of noise [default]")
-        self.add_argument("-q","--quiet",
+        self.add_argument("-q", "--quiet",
                 action="store_const", dest="verbose", const=0,
                 help="be quiet")
@@ -181,5 +182,5 @@
 
     def add_buf_size(self, buf_size=512):
-        self.add_argument("-B","--bufsize",
+        self.add_argument("-B", "--bufsize",
                 action="store", dest="buf_size", default=buf_size,
                 metavar = "<size>", type=int,
@@ -187,5 +188,5 @@
 
     def add_hop_size(self, hop_size=256):
-        self.add_argument("-H","--hopsize",
+        self.add_argument("-H", "--hopsize",
                 metavar = "<size>", type=int,
                 action="store", dest="hop_size", default=hop_size,
@@ -193,5 +194,5 @@
 
     def add_method(self, method='default', helpstr='method'):
-        self.add_argument("-m","--method",
+        self.add_argument("-m", "--method",
                 metavar = "<method>", type=str,
                 action="store", dest="method", default=method,
@@ -199,5 +200,5 @@
 
     def add_threshold(self, default=None):
-        self.add_argument("-t","--threshold",
+        self.add_argument("-t", "--threshold",
                 metavar = "<threshold>", type=float,
                 action="store", dest="threshold", default=default,
@@ -240,12 +241,14 @@
 
     def add_slicer_options(self):
-        self.add_argument("-o","--output", type = str,
+        self.add_argument("-o", "--output", type = str,
                 metavar = "<outputdir>",
                 action="store", dest="output_directory", default=None,
-                help="specify path where slices of the original file should be created")
+                help="specify path where slices of the original file should"
+                " be created")
         self.add_argument("--cut-until-nsamples", type = int,
                 metavar = "<samples>",
                 action = "store", dest = "cut_until_nsamples", default = None,
-                help="how many extra samples should be added at the end of each slice")
+                help="how many extra samples should be added at the end of"
+                " each slice")
         self.add_argument("--cut-every-nslices", type = int,
                 metavar = "<samples>",
@@ -255,5 +258,6 @@
                 metavar = "<slices>",
                 action = "store", dest = "cut_until_nslices", default = None,
-                help="how many extra slices should be added at the end of each slice")
+                help="how many extra slices should be added at the end of"
+                " each slice")
         self.add_argument("--create-first",
                 action = "store_true", dest = "create_first", default = False,
@@ -289,5 +293,6 @@
         if args.verbose > 2 and hasattr(self, 'options'):
             name = type(self).__name__.split('_')[1]
-            optstr = ' '.join(['running', name, 'with options', repr(self.options), '\n'])
+            optstr = ' '.join(['running', name, 'with options',
+                repr(self.options), '\n'])
             sys.stderr.write(optstr)
     def flush(self, frames_read, samplerate):
@@ -297,5 +302,5 @@
     def parse_options(self, args, valid_opts):
         # get any valid options found in a dictionnary of arguments
-        options = {k :v for k,v in vars(args).items() if k in valid_opts}
+        options = {k: v for k, v in vars(args).items() if k in valid_opts}
         self.options = options
 
@@ -378,5 +383,5 @@
             outstr = "unknown bpm"
         else:
-            bpms = 60./ np.diff(self.beat_locations)
+            bpms = 60. / np.diff(self.beat_locations)
             median_bpm = np.mean(bpms)
             if len(self.beat_locations) < 10:
@@ -399,12 +404,12 @@
         return self.notes(block)
     def repr_res(self, res, frames_read, samplerate):
-        if res[2] != 0: # note off
+        if res[2] != 0:  # note off
             fmt_out = self.time2string(frames_read, samplerate)
             sys.stdout.write(fmt_out + '\n')
-        if res[0] != 0: # note on
+        if res[0] != 0:  # note on
             lastmidi = res[0]
             fmt_out = "%f\t" % lastmidi
             fmt_out += self.time2string(frames_read, samplerate)
-            sys.stdout.write(fmt_out) # + '\t')
+            sys.stdout.write(fmt_out)  # + '\t')
     def flush(self, frames_read, samplerate):
         eof = self.time2string(frames_read, samplerate)
@@ -473,11 +478,11 @@
             if self.wassilence != 1:
                 self.wassilence = 1
-                return 2 # newly found silence
-            return 1 # silence again
+                return 2   # newly found silence
+            return 1       # silence again
         else:
             if self.wassilence != 0:
                 self.wassilence = 0
-                return -1 # newly found noise
-            return 0 # noise again
+                return -1  # newly found noise
+            return 0       # noise again
 
     def repr_res(self, res, frames_read, samplerate):
@@ -499,17 +504,44 @@
     def __call__(self, block):
         ret = super(process_cut, self).__call__(block)
-        if ret: self.slices.append(self.onset.get_last())
+        if ret:
+            self.slices.append(self.onset.get_last())
         return ret
 
     def flush(self, frames_read, samplerate):
-        from aubio.cut import _cut_slice
         _cut_slice(self.options, self.slices)
-        duration = float (frames_read) / float(samplerate)
-        base_info = '%(source_file)s' % {'source_file': self.options.source_uri}
+        duration = float(frames_read) / float(samplerate)
+        base_info = '%(source_file)s' % \
+                    {'source_file': self.options.source_uri}
         base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
-                {'duration': duration, 'samplerate': samplerate}
+                     {'duration': duration, 'samplerate': samplerate}
         info = "created %d slices from " % len(self.slices)
         info += base_info
         sys.stderr.write(info)
+
+def _cut_slice(options, timestamps):
+    # cutting pass
+    nstamps = len(timestamps)
+    if nstamps > 0:
+        # generate output files
+        timestamps_end = None
+        if options.cut_every_nslices:
+            timestamps = timestamps[::options.cut_every_nslices]
+            nstamps = len(timestamps)
+        if options.cut_until_nslices and options.cut_until_nsamples:
+            msg = "using cut_until_nslices, but cut_until_nsamples is set"
+            warnings.warn(msg)
+        if options.cut_until_nsamples:
+            lag = options.cut_until_nsamples
+            timestamps_end = [t + lag for t in timestamps[1:]]
+            timestamps_end += [1e120]
+        if options.cut_until_nslices:
+            slice_lag = options.cut_until_nslices
+            timestamps_end = [t for t in timestamps[1 + slice_lag:]]
+            timestamps_end += [1e120] * (options.cut_until_nslices + 1)
+        aubio.slice_source_at_stamps(options.source_uri,
+                timestamps, timestamps_end = timestamps_end,
+                output_dir = options.output_directory,
+                samplerate = options.samplerate,
+                create_first = options.create_first)
 
 def main():
@@ -526,10 +558,10 @@
                 action="store_true", dest="show_version")
         args, extras = parser_root.parse_known_args()
-        if args.show_version == False: # no -V, forward to parser
+        if not args.show_version:  # no -V, forward to parser
             args = parser.parse_args(extras, namespace=args)
-        elif len(extras) != 0: # -V with other arguments, print help
+        elif len(extras) != 0:     # -V with other arguments, print help
             parser.print_help()
             sys.exit(1)
-    else: # in py3, we can simply use parser directly
+    else:  # in py3, we can simply use parser directly
         args = parser.parse_args()
     if 'show_version' in args and args.show_version:
@@ -538,5 +570,6 @@
     elif 'verbose' in args and args.verbose > 3:
         sys.stderr.write('aubio version ' + aubio.version + '\n')
-    if 'command' not in args or args.command is None or args.command in ['help']:
+    if 'command' not in args or args.command is None \
+            or args.command in ['help']:
         # no command given, print help and return 1
         parser.print_help()
@@ -572,5 +605,6 @@
                 frames_read += read
                 # exit loop at end of file
-                if read < a_source.hop_size: break
+                if read < a_source.hop_size:
+                    break
             # flush the processor if needed
             processor.flush(frames_read, a_source.samplerate)
@@ -580,5 +614,5 @@
                 fmt_string += " from {:s} at {:d}Hz\n"
                 sys.stderr.write(fmt_string.format(
-                        frames_read/float(a_source.samplerate),
+                        frames_read / float(a_source.samplerate),
                         frames_read,
                         frames_read // a_source.hop_size + 1,
Index: python/lib/aubio/cut.py
===================================================================
--- python/lib/aubio/cut.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/aubio/cut.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -6,10 +6,10 @@
 
 import sys
-from aubio.cmd import AubioArgumentParser
+from aubio.cmd import AubioArgumentParser, _cut_slice
 
 def aubio_cut_parser():
     parser = AubioArgumentParser()
     parser.add_input()
-    parser.add_argument("-O","--onset-method",
+    parser.add_argument("-O", "--onset-method",
             action="store", dest="onset_method", default='default',
             metavar = "<onset_method>",
@@ -17,12 +17,12 @@
                     complexdomain|hfc|phase|specdiff|energy|kl|mkl")
     # cutting methods
-    parser.add_argument("-b","--beat",
+    parser.add_argument("-b", "--beat",
             action="store_true", dest="beat", default=False,
             help="slice at beat locations")
     """
-    parser.add_argument("-S","--silencecut",
+    parser.add_argument("-S", "--silencecut",
             action="store_true", dest="silencecut", default=False,
             help="use silence locations")
-    parser.add_argument("-s","--silence",
+    parser.add_argument("-s", "--silence",
             metavar = "<value>",
             action="store", dest="silence", default=-70,
@@ -31,9 +31,9 @@
     # algorithm parameters
     parser.add_buf_hop_size()
-    parser.add_argument("-t","--threshold", "--onset-threshold",
+    parser.add_argument("-t", "--threshold", "--onset-threshold",
             metavar = "<threshold>", type=float,
             action="store", dest="threshold", default=0.3,
             help="onset peak picking threshold [default=0.3]")
-    parser.add_argument("-c","--cut",
+    parser.add_argument("-c", "--cut",
             action="store_true", dest="cut", default=False,
             help="cut input sound file at detected labels")
@@ -41,46 +41,46 @@
 
     """
-    parser.add_argument("-D","--delay",
+    parser.add_argument("-D", "--delay",
             action = "store", dest = "delay", type = float,
             metavar = "<seconds>", default=0,
             help="number of seconds to take back [default=system]\
                     default system delay is 3*hopsize/samplerate")
-    parser.add_argument("-C","--dcthreshold",
+    parser.add_argument("-C", "--dcthreshold",
             metavar = "<value>",
             action="store", dest="dcthreshold", default=1.,
             help="onset peak picking DC component [default=1.]")
-    parser.add_argument("-L","--localmin",
+    parser.add_argument("-L", "--localmin",
             action="store_true", dest="localmin", default=False,
             help="use local minima after peak detection")
-    parser.add_argument("-d","--derivate",
+    parser.add_argument("-d", "--derivate",
             action="store_true", dest="derivate", default=False,
             help="derivate onset detection function")
-    parser.add_argument("-z","--zerocross",
+    parser.add_argument("-z", "--zerocross",
             metavar = "<value>",
             action="store", dest="zerothres", default=0.008,
             help="zero-crossing threshold for slicing [default=0.00008]")
     # plotting functions
-    parser.add_argument("-p","--plot",
+    parser.add_argument("-p", "--plot",
             action="store_true", dest="plot", default=False,
             help="draw plot")
-    parser.add_argument("-x","--xsize",
+    parser.add_argument("-x", "--xsize",
             metavar = "<size>",
             action="store", dest="xsize", default=1.,
             type=float, help="define xsize for plot")
-    parser.add_argument("-y","--ysize",
+    parser.add_argument("-y", "--ysize",
             metavar = "<size>",
             action="store", dest="ysize", default=1.,
             type=float, help="define ysize for plot")
-    parser.add_argument("-f","--function",
+    parser.add_argument("-f", "--function",
             action="store_true", dest="func", default=False,
             help="print detection function")
-    parser.add_argument("-n","--no-onsets",
+    parser.add_argument("-n", "--no-onsets",
             action="store_true", dest="nplot", default=False,
             help="do not plot detected onsets")
-    parser.add_argument("-O","--outplot",
+    parser.add_argument("-O", "--outplot",
             metavar = "<output_image>",
             action="store", dest="outplot", default=None,
             help="save plot to output.{ps,png}")
-    parser.add_argument("-F","--spectrogram",
+    parser.add_argument("-F", "--spectrogram",
             action="store_true", dest="spectro", default=False,
             help="add spectrogram to the plot")
@@ -106,7 +106,9 @@
 
     if options.beat:
-        o = tempo(options.onset_method, bufsize, hopsize, samplerate=samplerate)
+        o = tempo(options.onset_method, bufsize, hopsize,
+                samplerate=samplerate)
     else:
-        o = onset(options.onset_method, bufsize, hopsize, samplerate=samplerate)
+        o = onset(options.onset_method, bufsize, hopsize,
+                samplerate=samplerate)
         if options.minioi:
             if options.minioi.endswith('ms'):
@@ -123,34 +125,12 @@
         samples, read = s()
         if o(samples):
-            timestamps.append (o.get_last())
-            if options.verbose: print ("%.4f" % o.get_last_s())
+            timestamps.append(o.get_last())
+            if options.verbose:
+                print("%.4f" % o.get_last_s())
         total_frames += read
-        if read < hopsize: break
+        if read < hopsize:
+            break
     del s
     return timestamps, total_frames
-
-def _cut_slice(options, timestamps):
-    # cutting pass
-    nstamps = len(timestamps)
-    if nstamps > 0:
-        # generate output files
-        from aubio.slicing import slice_source_at_stamps
-        timestamps_end = None
-        if options.cut_every_nslices:
-            timestamps = timestamps[::options.cut_every_nslices]
-            nstamps = len(timestamps)
-        if options.cut_until_nslices and options.cut_until_nsamples:
-            print ("warning: using cut_until_nslices, but cut_until_nsamples is set")
-        if options.cut_until_nsamples:
-            timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]]
-            timestamps_end += [ 1e120 ]
-        if options.cut_until_nslices:
-            timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]]
-            timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1)
-        slice_source_at_stamps(options.source_uri,
-                timestamps, timestamps_end = timestamps_end,
-                output_dir = options.output_directory,
-                samplerate = options.samplerate,
-                create_first = options.create_first)
 
 def main():
@@ -168,5 +148,5 @@
 
     # print some info
-    duration = float (total_frames) / float(options.samplerate)
+    duration = float(total_frames) / float(options.samplerate)
     base_info = '%(source_uri)s' % {'source_uri': options.source_uri}
     base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
Index: python/lib/aubio/midiconv.py
===================================================================
--- python/lib/aubio/midiconv.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/aubio/midiconv.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -2,8 +2,8 @@
 """ utilities to convert midi note number to and from note names """
 
-__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
-
 import sys
 from ._aubio import freqtomidi, miditofreq
+
+__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
 
 py3 = sys.version_info[0] == 3
@@ -14,4 +14,5 @@
     str_instances = (str, unicode)
     int_instances = (int, long)
+
 
 def note2midi(note):
@@ -56,11 +57,12 @@
     midi2note, freqtomidi, miditofreq
     """
-    _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
+    _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7,
+                        'A': 9, 'B': 11}
     _valid_modifiers = {
-            u'𝄫': -2,                        # double flat
-            u'♭': -1, 'b': -1, '\u266d': -1, # simple flat
-            u'♮': 0, '\u266e': 0, None: 0,   # natural
-            '#': +1, u'♯': +1, '\u266f': +1, # sharp
-            u'𝄪': +2,                        # double sharp
+            u'𝄫': -2,                         # double flat
+            u'♭': -1, 'b': -1, '\u266d': -1,  # simple flat
+            u'♮': 0, '\u266e': 0, None: 0,    # natural
+            '#': +1, u'♯': +1, '\u266f': +1,  # sharp
+            u'𝄪': +2,                         # double sharp
             }
     _valid_octaves = range(-1, 10)
@@ -71,5 +73,5 @@
         msg = "string of 2 to 4 characters expected, got {:d} ({:s})"
         raise ValueError(msg.format(len(note), note))
-    notename, modifier, octave = [None]*3
+    notename, modifier, octave = [None] * 3
 
     if len(note) == 4:
@@ -94,9 +96,10 @@
         raise ValueError("%s is not a valid octave" % octave)
 
-    midi = 12 + octave * 12 + _valid_notenames[notename] \
-            + _valid_modifiers[modifier]
+    midi = (octave + 1) * 12 + _valid_notenames[notename] \
+                             + _valid_modifiers[modifier]
     if midi > 127:
         raise ValueError("%s is outside of the range C-2 to G8" % note)
     return midi
+
 
 def midi2note(midi):
@@ -137,6 +140,7 @@
         raise ValueError(msg.format(midi))
     _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#',
-            'A', 'A#', 'B']
+                        'A', 'A#', 'B']
     return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)
+
 
 def freq2note(freq):
@@ -163,4 +167,5 @@
     return midi2note(nearest_note)
 
+
 def note2freq(note):
     """Convert note name to corresponding frequency, in Hz.
Index: python/lib/aubio/slicing.py
===================================================================
--- python/lib/aubio/slicing.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/aubio/slicing.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -5,4 +5,5 @@
 
 _max_timestamp = 1e120
+
 
 def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
@@ -73,5 +74,5 @@
     """
 
-    if timestamps is None or len(timestamps) == 0:
+    if not timestamps:
         raise ValueError("no timestamps given")
 
@@ -90,5 +91,4 @@
 
     regions = list(zip(timestamps, timestamps_end))
-    #print regions
 
     source_base_name, _ = os.path.splitext(os.path.basename(source_file))
@@ -98,6 +98,6 @@
         source_base_name = os.path.join(output_dir, source_base_name)
 
-    def new_sink_name(source_base_name, timestamp, samplerate):
-        """ create a sink based on a timestamp in samples, converted in seconds """
+    def _new_sink_name(source_base_name, timestamp, samplerate):
+        # create name based on a timestamp in samples, converted in seconds
         timestamp_seconds = timestamp / float(samplerate)
         return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'
@@ -114,14 +114,15 @@
         vec, read = _source.do_multi()
         # if the total number of frames read will exceed the next region start
-        while len(regions) and total_frames + read >= regions[0][0]:
-            #print "getting", regions[0], "at", total_frames
+        while regions and total_frames + read >= regions[0][0]:
             # get next region
             start_stamp, end_stamp = regions.pop(0)
             # create a name for the sink
-            new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate)
+            new_sink_path = _new_sink_name(source_base_name, start_stamp,
+                                           samplerate)
             # create its sink
             _sink = sink(new_sink_path, samplerate, _source.channels)
             # create a dictionary containing all this
-            new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink}
+            new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp,
+                         'sink': _sink}
             # append the dictionary to the current list of slices
             slices.append(new_slice)
@@ -135,5 +136,4 @@
             # number of samples yet to written be until end of region
             remaining = end_stamp - total_frames + 1
-            #print current_slice, remaining, start
             # not enough frames remaining, time to split
             if remaining < read:
@@ -141,5 +141,4 @@
                     # write remaining samples from current region
                     _sink.do_multi(vec[:, start:remaining], remaining - start)
-                    #print("closing region", "remaining", remaining)
                     # close this file
                     _sink.close()
@@ -150,5 +149,5 @@
         # remove old slices
         slices = list(filter(lambda s: s['end_stamp'] > total_frames,
-            slices))
+                             slices))
         if read < hopsize:
             break
Index: python/lib/gen_code.py
===================================================================
--- python/lib/gen_code.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/gen_code.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -465,9 +465,15 @@
 """.format(**self.__dict__)
         for set_param in self.prototypes['set']:
-            params = get_params_types_names(set_param)[1]
-            paramtype = params['type']
+            params = get_params_types_names(set_param)[1:]
+            param = self.shortname.split('_set_')[-1]
+            paramdecls = "".join(["""
+   {0} {1};""".format(p['type'], p['name']) for p in params])
             method_name = get_name(set_param)
             param = method_name.split('aubio_'+self.shortname+'_set_')[-1]
-            pyparamtype = pyargparse_chars[paramtype]
+            refs = ", ".join(["&%s" % p['name'] for p in params])
+            paramlist = ", ".join(["%s" % p['name'] for p in params])
+            if len(params):
+                paramlist = "," + paramlist
+            pyparamtypes = ''.join([pyargparse_chars[p['type']] for p in params])
             out += """
 static PyObject *
@@ -475,10 +481,17 @@
 {{
   uint_t err = 0;
-  {paramtype} {param};
-
-  if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{
+  {paramdecls}
+""".format(param = param, paramdecls = paramdecls, **self.__dict__)
+
+            if len(refs) and len(pyparamtypes):
+                out += """
+
+  if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{
     return NULL;
   }}
-  err = aubio_{shortname}_set_{param} (self->o, {param});
+""".format(pyparamtypes = pyparamtypes, refs = refs)
+
+            out += """
+  err = aubio_{shortname}_set_{param} (self->o {paramlist});
 
   if (err > 0) {{
@@ -495,5 +508,6 @@
   Py_RETURN_NONE;
 }}
-""".format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__)
+""".format(param = param, refs = refs, paramdecls = paramdecls,
+        pyparamtypes = pyparamtypes, paramlist = paramlist, **self.__dict__)
         return out
 
Index: python/lib/gen_external.py
===================================================================
--- python/lib/gen_external.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/gen_external.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -4,4 +4,6 @@
 import subprocess
 import glob
+from distutils.sysconfig import customize_compiler
+from gen_code import MappedObject
 
 header = os.path.join('src', 'aubio.h')
@@ -50,5 +52,4 @@
 def get_preprocessor():
     # findout which compiler to use
-    from distutils.sysconfig import customize_compiler
     compiler_name = distutils.ccompiler.get_default_compiler()
     compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
@@ -263,8 +264,4 @@
 
     sources_list = []
-    try:
-        from .gen_code import MappedObject
-    except (SystemError, ValueError):
-        from gen_code import MappedObject
     for o in lib:
         out = source_header
Index: python/lib/moresetuptools.py
===================================================================
--- python/lib/moresetuptools.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/lib/moresetuptools.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -3,5 +3,5 @@
 import sys, os, glob, subprocess
 import distutils, distutils.command.clean, distutils.dir_util
-from .gen_external import generate_external, header, output_path
+from gen_external import generate_external, header, output_path
 
 from this_version import get_aubio_version
Index: python/tests/test_fft.py
===================================================================
--- python/tests/test_fft.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/tests/test_fft.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -143,4 +143,35 @@
         assert_almost_equal ( r[1:], 0)
 
+class aubio_fft_odd_sizes(TestCase):
+
+    def test_reconstruct_with_odd_size(self):
+        win_s = 29
+        self.recontruct(win_s, 'odd sizes not supported')
+
+    def test_reconstruct_with_radix15(self):
+        win_s = 2 ** 4 * 15
+        self.recontruct(win_s, 'radix 15 supported')
+
+    def test_reconstruct_with_radix5(self):
+        win_s = 2 ** 4 * 5
+        self.recontruct(win_s, 'radix 5 supported')
+
+    def test_reconstruct_with_radix3(self):
+        win_s = 2 ** 4 * 3
+        self.recontruct(win_s, 'radix 3 supported')
+
+    def recontruct(self, win_s, skipMessage):
+        try:
+            f = fft(win_s)
+        except RuntimeError:
+            self.skipTest(skipMessage)
+        input_signal = fvec(win_s)
+        input_signal[win_s//2] = 1
+        c = f(input_signal)
+        output_signal = f.rdo(c)
+        assert_almost_equal(input_signal, output_signal)
+
+class aubio_fft_wrong_params(TestCase):
+
     def test_large_input_timegrain(self):
         win_s = 1024
@@ -170,6 +201,4 @@
         with self.assertRaises(ValueError):
             f.rdo(s)
-
-class aubio_fft_wrong_params(TestCase):
 
     def test_wrong_buf_size(self):
@@ -177,13 +206,4 @@
         with self.assertRaises(ValueError):
             fft(win_s)
-
-    def test_buf_size_not_power_of_two(self):
-        # when compiled with fftw3, aubio supports non power of two fft sizes
-        win_s = 320
-        try:
-            with self.assertRaises(RuntimeError):
-                fft(win_s)
-        except AssertionError:
-            self.skipTest('creating aubio.fft with size %d did not fail' % win_s)
 
     def test_buf_size_too_small(self):
Index: python/tests/test_filter.py
===================================================================
--- python/tests/test_filter.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/tests/test_filter.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -78,4 +78,14 @@
             f.set_biquad(0., 0., 0, 0., 0.)
 
+    def test_all_available_presets(self):
+        f = digital_filter(7)
+        for sr in [8000, 11025, 16000, 22050, 24000, 32000,
+                44100, 48000, 88200, 96000, 192000]:
+            f.set_a_weighting(sr)
+        f = digital_filter(5)
+        for sr in [8000, 11025, 16000, 22050, 24000, 32000,
+                44100, 48000, 88200, 96000, 192000]:
+            f.set_c_weighting(sr)
+
 class aubio_filter_wrong_params(TestCase):
 
Index: python/tests/test_filterbank_mel.py
===================================================================
--- python/tests/test_filterbank_mel.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/tests/test_filterbank_mel.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -4,5 +4,5 @@
 from numpy.testing import TestCase, assert_equal, assert_almost_equal
 
-from aubio import cvec, filterbank, float_type
+from aubio import fvec, cvec, filterbank, float_type
 
 import warnings
@@ -48,4 +48,124 @@
                     0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
 
+    def test_triangle_freqs_with_zeros(self):
+        """make sure set_triangle_bands works when list starts with 0"""
+        freq_list = [0, 40, 80]
+        freqs = np.array(freq_list, dtype = float_type)
+        f = filterbank(len(freqs)-2, 1024)
+        f.set_triangle_bands(freqs, 48000)
+        assert_equal ( f(cvec(1024)), 0)
+        self.assertIsInstance(f.get_coeffs(), np.ndarray)
+
+    def test_triangle_freqs_with_wrong_negative(self):
+        """make sure set_triangle_bands fails when list contains a negative"""
+        freq_list = [-10, 0, 80]
+        f = filterbank(len(freq_list)-2, 1024)
+        with self.assertRaises(ValueError):
+            f.set_triangle_bands(fvec(freq_list), 48000)
+
+    def test_triangle_freqs_with_wrong_ordering(self):
+        """make sure set_triangle_bands fails when list not ordered"""
+        freq_list = [0, 80, 40]
+        f = filterbank(len(freq_list)-2, 1024)
+        with self.assertRaises(ValueError):
+            f.set_triangle_bands(fvec(freq_list), 48000)
+
+    def test_triangle_freqs_with_large_freq(self):
+        """make sure set_triangle_bands warns when freq > nyquist"""
+        samplerate = 22050
+        freq_list = [0, samplerate//4, samplerate // 2 + 1]
+        f = filterbank(len(freq_list)-2, 1024)
+        # TODO add assert_warns
+        f.set_triangle_bands(fvec(freq_list), samplerate)
+
+    def test_triangle_freqs_with_not_enough_filters(self):
+        """make sure set_triangle_bands warns when not enough filters"""
+        samplerate = 22050
+        freq_list = [0, 100, 1000, 4000, 8000, 10000]
+        f = filterbank(len(freq_list)-3, 1024)
+        # TODO add assert_warns
+        f.set_triangle_bands(fvec(freq_list), samplerate)
+
+    def test_triangle_freqs_with_too_many_filters(self):
+        """make sure set_triangle_bands warns when too many filters"""
+        samplerate = 22050
+        freq_list = [0, 100, 1000, 4000, 8000, 10000]
+        f = filterbank(len(freq_list)-1, 1024)
+        # TODO add assert_warns
+        f.set_triangle_bands(fvec(freq_list), samplerate)
+
+    def test_triangle_freqs_with_double_value(self):
+        """make sure set_triangle_bands works with 2 duplicate freqs"""
+        samplerate = 22050
+        freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000]
+        f = filterbank(len(freq_list)-2, 1024)
+        # TODO add assert_warns
+        f.set_triangle_bands(fvec(freq_list), samplerate)
+
+    def test_triangle_freqs_with_triple(self):
+        """make sure set_triangle_bands works with 3 duplicate freqs"""
+        samplerate = 22050
+        freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000]
+        f = filterbank(len(freq_list)-2, 1024)
+        # TODO add assert_warns
+        f.set_triangle_bands(fvec(freq_list), samplerate)
+
+    def test_triangle_freqs_without_norm(self):
+        """make sure set_triangle_bands works without """
+        samplerate = 22050
+        freq_list = fvec([0, 100, 1000, 10000])
+        f = filterbank(len(freq_list) - 2, 1024)
+        f.set_norm(0)
+        f.set_triangle_bands(freq_list, samplerate)
+        expected = f.get_coeffs()
+        f.set_norm(1)
+        f.set_triangle_bands(fvec(freq_list), samplerate)
+        assert_almost_equal(f.get_coeffs().T,
+                expected.T * 2. / (freq_list[2:] - freq_list[:-2]))
+
+    def test_triangle_freqs_wrong_norm(self):
+        f = filterbank(10, 1024)
+        with self.assertRaises(ValueError):
+            f.set_norm(-1)
+
+    def test_triangle_freqs_with_power(self):
+        f = filterbank(9, 1024)
+        freqs = fvec([40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000,
+            24000])
+        f.set_power(2)
+        f.set_triangle_bands(freqs, 48000)
+        spec = cvec(1024)
+        spec.norm[:] = .1
+        expected = fvec([0.02070313, 0.02138672, 0.02127604, 0.02135417,
+            0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
+        expected /= 100.
+        assert_almost_equal(f(spec), expected)
+
+    def test_mel_coeffs(self):
+        f = filterbank(40, 1024)
+        f.set_mel_coeffs(44100, 0, 44100 / 2)
+
+    def test_zero_fmax(self):
+        f = filterbank(40, 1024)
+        f.set_mel_coeffs(44100, 0, 0)
+
+    def test_wrong_mel_coeffs(self):
+        f = filterbank(40, 1024)
+        with self.assertRaises(ValueError):
+            f.set_mel_coeffs_slaney(0)
+        with self.assertRaises(ValueError):
+            f.set_mel_coeffs(44100, 0, -44100 / 2)
+        with self.assertRaises(ValueError):
+            f.set_mel_coeffs(44100, -0.1, 44100 / 2)
+        with self.assertRaises(ValueError):
+            f.set_mel_coeffs(-44100, 0.1, 44100 / 2)
+        with self.assertRaises(ValueError):
+            f.set_mel_coeffs_htk(-1, 0, 0)
+
+    def test_mel_coeffs_htk(self):
+        f = filterbank(40, 1024)
+        f.set_mel_coeffs_htk(44100, 0, 44100 / 2)
+
+
 if __name__ == '__main__':
     import nose2
Index: python/tests/test_hztomel.py
===================================================================
--- python/tests/test_hztomel.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
+++ python/tests/test_hztomel.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -0,0 +1,109 @@
+#! /usr/bin/env python
+
+from unittest import main
+from numpy.testing import TestCase
+from numpy.testing import assert_equal, assert_almost_equal
+import numpy as np
+import aubio
+
+from aubio import hztomel, meltohz
+from aubio import hztomel_htk, meltohz_htk
+
+
+class aubio_hztomel_test_case(TestCase):
+
+    def test_hztomel(self):
+        assert_equal(hztomel(0.), 0.)
+        assert_almost_equal(hztomel(400. / 3.), 2., decimal=5)
+        assert_almost_equal(hztomel(1000. / 3), 5.)
+        assert_equal(hztomel(200.), 3.)
+        assert_almost_equal(hztomel(1000.), 15)
+        assert_almost_equal(hztomel(6400), 42)
+        assert_almost_equal(hztomel(40960), 69)
+
+        for m in np.linspace(0, 1000, 100):
+            assert_almost_equal(hztomel(meltohz(m)) - m, 0, decimal=3)
+
+    def test_meltohz(self):
+        assert_equal(meltohz(0.), 0.)
+        assert_almost_equal(meltohz(2), 400. / 3., decimal=4)
+        assert_equal(meltohz(3.), 200.)
+        assert_almost_equal(meltohz(5), 1000. / 3., decimal=4)
+        assert_almost_equal(meltohz(15), 1000., decimal=4)
+        assert_almost_equal(meltohz(42), 6400., decimal=2)
+        assert_almost_equal(meltohz(69), 40960., decimal=1)
+
+        for f in np.linspace(0, 20000, 1000):
+            assert_almost_equal(meltohz(hztomel(f)) - f, 0, decimal=1)
+
+    def test_meltohz_negative(self):
+        # TODO add assert_warns
+        assert_equal(meltohz(-1), 0)
+
+    def test_hztomel_negative(self):
+        # TODO add assert_warns
+        assert_equal(hztomel(-1), 0)
+
+
+class aubio_hztomel_htk_test_case(TestCase):
+
+    def test_meltohz(self):
+        assert_equal(meltohz(0, htk=True), 0)
+        assert_almost_equal(meltohz(2595, htk=True), 6300., decimal=1)
+
+    def test_hztomel(self):
+        assert_equal(hztomel(0, htk=True), 0)
+        assert_almost_equal(hztomel(3428.7, htk=True), 2000., decimal=1)
+        assert_almost_equal(hztomel(6300, htk=True), 2595., decimal=1)
+
+    def test_meltohz_negative(self):
+        # TODO add assert_warns
+        assert_equal(meltohz(-1, htk=True), 0)
+        assert_almost_equal(meltohz(2000, htk=True), 3428.7, decimal=1)
+        assert_almost_equal(meltohz(1000, htk=True), 1000., decimal=1)
+
+    def test_hztomel_negative(self):
+        # TODO add assert_warns
+        assert_equal(hztomel(-1, htk=True), 0)
+        assert_almost_equal(hztomel(1000, htk=True), 1000., decimal=1)
+
+    def test_hztomel_htk(self):
+        for f in np.linspace(0, 20000, 1000):
+            assert_almost_equal(meltohz_htk(hztomel_htk(f)) - f, 0, decimal=1)
+        for f in np.linspace(0, 20000, 1000):
+            assert_almost_equal(hztomel_htk(meltohz_htk(f)) - f, 0, decimal=1)
+
+
+class aubio_hztomel_wrong_values(TestCase):
+    """ more tests to cover all branches """
+
+    def test_hztomel_wrong_values(self):
+        with self.assertRaises(TypeError):
+            hztomel('s')
+
+    def test_meltohz_wrong_values(self):
+        with self.assertRaises(TypeError):
+            meltohz(bytes('ad'))
+
+    def test_meltohz_no_arg(self):
+        with self.assertRaises(TypeError):
+            meltohz()
+
+    def test_meltohz_htk_no_arg(self):
+        with self.assertRaises(TypeError):
+            meltohz_htk()
+
+    def test_hztomel_htk_wrong_values(self):
+        with self.assertRaises(TypeError):
+            hztomel_htk('0')
+
+    def test_hztomel_htk_false(self):
+        assert hztomel(120, htk=False) == hztomel(120)
+
+    def test_meltohz_htk_false(self):
+        assert meltohz(12, htk=False) == meltohz(12)
+
+
+if __name__ == '__main__':
+    from unittest import main
+    main()
Index: python/tests/test_mfcc.py
===================================================================
--- python/tests/test_mfcc.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/tests/test_mfcc.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -111,4 +111,39 @@
         #print coeffs
 
+
+class aubio_mfcc_fb_params(TestCase):
+
+    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(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(cvec(buf_size))
+
+    def test_set_mel_coeffs(self):
+        buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
+        m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+        m.set_mel_coeffs(0., samplerate/2.)
+        m(cvec(buf_size))
+
+    def test_set_mel_coeffs_htk(self):
+        buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
+        m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+        m.set_mel_coeffs_htk(0., samplerate/2.)
+        m(cvec(buf_size))
+
+    def test_set_mel_coeffs_slaney(self):
+        buf_size, n_filters, n_coeffs, samplerate = 512, 40, 10, 16000
+        m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
+        m.set_mel_coeffs_slaney(samplerate)
+        m(cvec(buf_size))
+        assert m.get_power() == 1
+        assert m.get_scale() == 1
+
 if __name__ == '__main__':
     main()
Index: python/tests/test_onset.py
===================================================================
--- python/tests/test_onset.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ python/tests/test_onset.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -3,5 +3,5 @@
 from unittest import main
 from numpy.testing import TestCase, assert_equal, assert_almost_equal
-from aubio import onset
+from aubio import onset, fvec
 
 class aubio_onset_default(TestCase):
@@ -84,4 +84,36 @@
     samplerate = 8000
 
+class aubio_onset_coverate(TestCase):
+    # extra tests to execute the C routines and improve coverage
+
+    def test_all_methods(self):
+        for method in ['default', 'energy', 'hfc', 'complexdomain', 'complex',
+                'phase', 'wphase', 'mkl', 'kl', 'specflux', 'specdiff',
+                'old_default']:
+            o = onset(method=method, buf_size=512, hop_size=256)
+            o(fvec(256))
+
+    def test_get_methods(self):
+        o = onset(method='default', buf_size=512, hop_size=256)
+
+        assert o.get_silence() == -70
+        o.set_silence(-20)
+        assert_almost_equal(o.get_silence(), -20)
+
+        assert o.get_compression() == 1
+        o.set_compression(.99)
+        assert_almost_equal(o.get_compression(), .99)
+
+        assert o.get_awhitening() == 0
+        o.set_awhitening(1)
+        assert o.get_awhitening() == 1
+
+        o.get_last()
+        o.get_last_ms()
+        o.get_last_s()
+        o.get_descriptor()
+        o.get_thresholded_descriptor()
+
+
 if __name__ == '__main__':
     main()
Index: python/tests/test_source_channels.py
===================================================================
--- python/tests/test_source_channels.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
+++ python/tests/test_source_channels.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -0,0 +1,93 @@
+#! /usr/bin/env python
+
+"""A brute force test using `sink` to create and write samples to a stereo
+file, then `source` to check the correct content is read from the files."""
+
+import os.path
+import unittest
+import aubio
+import numpy as np
+from numpy.testing import assert_equal
+from utils import get_tmp_sink_path
+
+class aubio_source_test_case(unittest.TestCase):
+
+    def test_read_from_mono(self):
+        out = get_tmp_sink_path()
+        samplerate = 44100
+        hop_size = 256
+        blocks = 10
+        channels = 1
+        write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
+        write_samples *= .5
+        self.check_write_and_read(samplerate, channels, hop_size, blocks,
+                write_samples)
+
+    def test_read_from_stereo(self):
+        out = get_tmp_sink_path()
+        samplerate = 44100
+        hop_size = 256
+        blocks = 10
+        channels = 1
+        write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
+        write_samples *= .5
+        self.check_write_and_read(samplerate, channels, hop_size, blocks,
+                write_samples)
+
+    def test_read_from_half_stereo(self):
+        samplerate = 16000
+        channels = 2
+        hop_size = 512
+        blocks = 10
+        write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
+        write_samples *= .5
+        write_samples[1, :] = 0
+        self.check_write_and_read(samplerate, channels, hop_size, blocks,
+                write_samples)
+
+    def test_read_from_cancelling_channels(self):
+        samplerate = 16000
+        channels = 2
+        hop_size = 512
+        blocks = 10
+        write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
+        write_samples *= .5
+        write_samples[1] *= -1
+        self.check_write_and_read(samplerate, channels, hop_size, blocks,
+                write_samples)
+
+    def test_read_from_strange_three_channels(self):
+        samplerate = 8000
+        channels = 3
+        hop_size = 123
+        blocks = 10
+        write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
+        write_samples *= .5
+        write_samples[1, :] = 0
+        self.check_write_and_read(samplerate, channels, hop_size, blocks,
+                write_samples)
+
+    def check_write_and_read(self, samplerate, channels,
+            hop_size, blocks, write_samples):
+        expected_mono = np.sum(write_samples, axis=0)/write_samples.shape[0]
+        out = get_tmp_sink_path()
+        snk = aubio.sink(out, samplerate, channels=channels)
+        for i in range(blocks):
+            snk.do_multi(write_samples, hop_size)
+        # close the sink before reading from it
+        snk.close()
+
+        src = aubio.source(out, samplerate, hop_size)
+        for i in range(blocks):
+            read_samples, read = src.do_multi()
+            assert_equal (read_samples, write_samples)
+            assert_equal (read, hop_size)
+
+        src.seek(0)
+        for i in range(blocks):
+            read_samples, read = src()
+            assert_equal (read, hop_size)
+            assert_equal (read_samples, expected_mono)
+
+if __name__ == '__main__':
+    unittest.main()
Index: setup.py
===================================================================
--- setup.py	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ setup.py	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -1,7 +1,13 @@
 #! /usr/bin/env python
 
-import sys, os.path, glob
+import sys
+import os.path
+import glob
 from setuptools import setup, Extension
-from python.lib.moresetuptools import build_ext, CleanGenerated
+
+# add ./python/lib to current path
+sys.path.append(os.path.join('python', 'lib'))  # noqa
+from moresetuptools import build_ext, CleanGenerated
+
 # function to generate gen/*.{c,h}
 from this_version import get_aubio_version, get_aubio_pyversion
@@ -15,13 +21,14 @@
 extra_link_args = []
 
-include_dirs += [ 'python/ext' ]
+include_dirs += ['python/ext']
 try:
     import numpy
-    include_dirs += [ numpy.get_include() ]
+    include_dirs += [numpy.get_include()]
 except ImportError:
     pass
 
 if sys.platform.startswith('darwin'):
-    extra_link_args += ['-framework','CoreFoundation', '-framework','AudioToolbox']
+    extra_link_args += ['-framework', 'CoreFoundation',
+            '-framework', 'AudioToolbox']
 
 sources = sorted(glob.glob(os.path.join('python', 'ext', '*.c')))
@@ -34,8 +41,9 @@
     define_macros = define_macros)
 
-if os.path.isfile('src/aubio.h'):
-    if not os.path.isdir(os.path.join('build','src')):
-        pass
-        #__version__ += 'a2' # python only version
+# TODO: find a way to track if package is built against libaubio
+# if os.path.isfile('src/aubio.h'):
+#     if not os.path.isdir(os.path.join('build','src')):
+#         pass
+#         #__version__ += 'a2' # python only version
 
 classifiers = [
@@ -51,5 +59,6 @@
     'Programming Language :: C',
     'Programming Language :: Python',
-    'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
+    'License :: OSI Approved :: '
+    'GNU General Public License v3 or later (GPLv3+)',
     ]
 
@@ -57,5 +66,5 @@
     version = __version__,
     packages = ['aubio'],
-    package_dir = {'aubio':'python/lib/aubio'},
+    package_dir = {'aubio': 'python/lib/aubio'},
     ext_modules = [aubio_extension],
     description = 'a collection of tools for music analysis',
Index: src/io/source_avcodec.c
===================================================================
--- src/io/source_avcodec.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/io/source_avcodec.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -90,10 +90,8 @@
   sint_t selected_stream;
   uint_t eof;
-  uint_t multi;
 };
 
 // create or re-create the context when _do or _do_multi is called
-void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s,
-    uint_t multi);
+void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s);
 // actually read a frame
 void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s,
@@ -285,11 +283,9 @@
   s->avFrame = avFrame;
 
-  // default to mono output
-  aubio_source_avcodec_reset_resampler(s, 0);
+  aubio_source_avcodec_reset_resampler(s);
 
   if (s->avr == NULL) goto beach;
 
   s->eof = 0;
-  s->multi = 0;
 
   //av_log_set_level(AV_LOG_QUIET);
@@ -304,19 +300,15 @@
 }
 
-void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s,
-    uint_t multi)
+void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s)
 {
   // create or reset resampler to/from mono/multi-channel
-  if ( (multi != s->multi) || (s->avr == NULL) ) {
+  if ( s->avr == NULL ) {
     int err;
     int64_t input_layout = av_get_default_channel_layout(s->input_channels);
-    uint_t output_channels = multi ? s->input_channels : 1;
-    int64_t output_layout = av_get_default_channel_layout(output_channels);
+    int64_t output_layout = av_get_default_channel_layout(s->input_channels);
 #ifdef HAVE_AVRESAMPLE
     AVAudioResampleContext *avr = avresample_alloc_context();
-    AVAudioResampleContext *oldavr = s->avr;
 #elif defined(HAVE_SWRESAMPLE)
     SwrContext *avr = swr_alloc();
-    SwrContext *oldavr = s->avr;
 #endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
 
@@ -346,14 +338,4 @@
     }
     s->avr = avr;
-    if (oldavr != NULL) {
-#ifdef HAVE_AVRESAMPLE
-      avresample_close( oldavr );
-#elif defined(HAVE_SWRESAMPLE)
-      swr_close ( oldavr );
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
-      av_free ( oldavr );
-      oldavr = NULL;
-    }
-    s->multi = multi;
   }
 }
@@ -496,13 +478,16 @@
 void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_data,
     uint_t * read) {
-  uint_t i;
+  uint_t i, j;
   uint_t end = 0;
   uint_t total_wrote = 0;
-  // switch from multi
-  if (s->multi == 1) aubio_source_avcodec_reset_resampler(s, 0);
   while (total_wrote < s->hop_size) {
     end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
     for (i = 0; i < end; i++) {
-      read_data->data[i + total_wrote] = s->output[i + s->read_index];
+      read_data->data[i + total_wrote] = 0.;
+      for (j = 0; j < s->input_channels; j++) {
+        read_data->data[i + total_wrote] +=
+          s->output[(i + s->read_index) * s->input_channels + j];
+      }
+      read_data->data[i + total_wrote] *= 1./s->input_channels;
     }
     total_wrote += end;
@@ -532,6 +517,4 @@
   uint_t end = 0;
   uint_t total_wrote = 0;
-  // switch from mono
-  if (s->multi == 0) aubio_source_avcodec_reset_resampler(s, 1);
   while (total_wrote < s->hop_size) {
     end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
Index: src/mathutils.c
===================================================================
--- src/mathutils.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/mathutils.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -388,4 +388,13 @@
 }
 
+void
+fvec_mul (fvec_t *o, smpl_t val)
+{
+  uint_t j;
+  for (j = 0; j < o->length; j++) {
+    o->data[j] *= val;
+  }
+}
+
 void fvec_adapt_thres(fvec_t * vec, fvec_t * tmp,
     uint_t post, uint_t pre) {
Index: src/mathutils.h
===================================================================
--- src/mathutils.h	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/mathutils.h	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -193,4 +193,12 @@
 */
 void fvec_add (fvec_t * v, smpl_t c);
+
+/** multiply each elements of a vector by a scalar
+
+  \param v vector to add constant to
+  \param s constant to scale v with
+
+*/
+void fvec_mul (fvec_t * v, smpl_t s);
 
 /** remove the minimum value of the vector to each elements
Index: src/musicutils.c
===================================================================
--- src/musicutils.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
+++ src/musicutils.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 2018 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 "musicutils.h"
+
+smpl_t
+aubio_hztomel (smpl_t freq)
+{
+  const smpl_t lin_space = 3./200.;
+  const smpl_t split_hz = 1000.;
+  const smpl_t split_mel = split_hz * lin_space;
+  const smpl_t log_space = 27./LOG(6400/1000.);
+  if (freq < 0) {
+    AUBIO_WRN("hztomel: input frequency should be >= 0\n");
+    return 0;
+  }
+  if (freq < split_hz)
+  {
+    return freq * lin_space;
+  } else {
+    return split_mel + log_space * LOG (freq / split_hz);
+  }
+
+}
+
+smpl_t
+aubio_meltohz (smpl_t mel)
+{
+  const smpl_t lin_space = 200./3.;
+  const smpl_t split_hz = 1000.;
+  const smpl_t split_mel = split_hz / lin_space;
+  const smpl_t logSpacing = POW(6400/1000., 1/27.);
+  if (mel < 0) {
+    AUBIO_WRN("meltohz: input mel should be >= 0\n");
+    return 0;
+  }
+  if (mel < split_mel) {
+    return lin_space * mel;
+  } else {
+    return split_hz * POW(logSpacing, mel - split_mel);
+  }
+}
+
+smpl_t
+aubio_hztomel_htk (smpl_t freq)
+{
+  const smpl_t split_hz = 700.;
+  const smpl_t log_space = 1127.;
+  if (freq < 0) {
+    AUBIO_WRN("hztomel_htk: input frequency should be >= 0\n");
+    return 0;
+  }
+  return log_space * LOG (1 + freq / split_hz);
+}
+
+smpl_t
+aubio_meltohz_htk (smpl_t mel)
+{
+  const smpl_t split_hz = 700.;
+  const smpl_t log_space = 1./1127.;
+  if (mel < 0) {
+    AUBIO_WRN("meltohz_htk: input frequency should be >= 0\n");
+    return 0;
+  }
+  return split_hz * ( EXP ( mel * log_space) - 1.);
+}
+
Index: src/musicutils.h
===================================================================
--- src/musicutils.h	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/musicutils.h	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -87,4 +87,103 @@
 smpl_t aubio_freqtobin (smpl_t freq, smpl_t samplerate, smpl_t fftsize);
 
+/** convert frequency (Hz) to mel
+
+  \param freq input frequency, in Hz
+
+  \return output mel
+
+  Converts a scalar from the frequency domain to the mel scale using Slaney
+  Auditory Toolbox's implementation:
+
+  If \f$ f < 1000 \f$, \f$ m = 3 f / 200 \f$.
+
+  If \f$ f >= 1000 \f$, \f$ m = 1000 + 27 \frac{{ln}(f) - ln(1000))}
+  {{ln}(6400) - ln(1000)}
+  \f$
+
+  See also
+  --------
+
+  aubio_meltohz(), aubio_hztomel_htk().
+
+*/
+smpl_t aubio_hztomel (smpl_t freq);
+
+/** convert mel to frequency (Hz)
+
+  \param mel input mel
+
+  \return output frequency, in Hz
+
+  Converts a scalar from the mel scale to the frequency domain using Slaney
+  Auditory Toolbox's implementation:
+
+  If \f$ f < 1000 \f$, \f$ f = 200 m/3 \f$.
+
+  If \f$ f \geq 1000 \f$, \f$ f = 1000 + \left(\frac{6400}{1000}\right)
+  ^{\frac{m - 1000}{27}} \f$
+
+  See also
+  --------
+
+  aubio_hztomel(), aubio_meltohz_htk().
+
+  References
+  ----------
+
+  Malcolm Slaney, *Auditory Toolbox Version 2, Technical Report #1998-010*
+  https://engineering.purdue.edu/~malcolm/interval/1998-010/
+
+*/
+smpl_t aubio_meltohz (smpl_t mel);
+
+/** convert frequency (Hz) to mel
+
+  \param freq input frequency, in Hz
+
+  \return output mel
+
+  Converts a scalar from the frequency domain to the mel scale, using the
+  equation defined by O'Shaughnessy, as implemented in the HTK speech
+  recognition toolkit:
+
+  \f$ m = 1127 + ln(1 + \frac{f}{700}) \f$
+
+  See also
+  --------
+
+  aubio_meltohz_htk(), aubio_hztomel().
+
+  References
+  ----------
+
+  Douglas O'Shaughnessy (1987). *Speech communication: human and machine*.
+  Addison-Wesley. p. 150. ISBN 978-0-201-16520-3.
+
+  HTK Speech Recognition Toolkit: http://htk.eng.cam.ac.uk/
+
+ */
+smpl_t aubio_hztomel_htk (smpl_t freq);
+
+/** convert mel to frequency (Hz)
+
+  \param mel input mel
+
+  \return output frequency, in Hz
+
+  Converts a scalar from the mel scale to the frequency domain, using the
+  equation defined by O'Shaughnessy, as implemented in the HTK speech
+  recognition toolkit:
+
+  \f$ f = 700 * {e}^\left(\frac{f}{1127} - 1\right) \f$
+
+  See also
+  --------
+
+  aubio_hztomel_htk(), aubio_meltohz().
+
+*/
+smpl_t aubio_meltohz_htk (smpl_t mel);
+
 /** convert frequency (Hz) to midi value (0-128) */
 smpl_t aubio_freqtomidi (smpl_t freq);
Index: src/spectral/fft.c
===================================================================
--- src/spectral/fft.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/fft.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -90,9 +90,10 @@
 #define aubio_vDSP_vsadd               vDSP_vsadd
 #define aubio_vDSP_vsmul               vDSP_vsmul
-#define aubio_vDSP_create_fftsetup     vDSP_create_fftsetup
-#define aubio_vDSP_destroy_fftsetup    vDSP_destroy_fftsetup
 #define aubio_DSPComplex               DSPComplex
 #define aubio_DSPSplitComplex          DSPSplitComplex
-#define aubio_FFTSetup                 FFTSetup
+#define aubio_vDSP_DFT_Setup           vDSP_DFT_Setup
+#define aubio_vDSP_DFT_zrop_CreateSetup vDSP_DFT_zrop_CreateSetup
+#define aubio_vDSP_DFT_Execute         vDSP_DFT_Execute
+#define aubio_vDSP_DFT_DestroySetup    vDSP_DFT_DestroySetup
 #define aubio_vvsqrt                   vvsqrtf
 #else
@@ -104,9 +105,10 @@
 #define aubio_vDSP_vsadd               vDSP_vsaddD
 #define aubio_vDSP_vsmul               vDSP_vsmulD
-#define aubio_vDSP_create_fftsetup     vDSP_create_fftsetupD
-#define aubio_vDSP_destroy_fftsetup    vDSP_destroy_fftsetupD
 #define aubio_DSPComplex               DSPDoubleComplex
 #define aubio_DSPSplitComplex          DSPDoubleSplitComplex
-#define aubio_FFTSetup                 FFTSetupD
+#define aubio_vDSP_DFT_Setup           vDSP_DFT_SetupD
+#define aubio_vDSP_DFT_zrop_CreateSetup vDSP_DFT_zrop_CreateSetupD
+#define aubio_vDSP_DFT_Execute         vDSP_DFT_ExecuteD
+#define aubio_vDSP_DFT_DestroySetup    vDSP_DFT_DestroySetupD
 #define aubio_vvsqrt                   vvsqrt
 #endif /* HAVE_AUBIO_DOUBLE */
@@ -153,6 +155,6 @@
 
 #elif defined HAVE_ACCELERATE  // using ACCELERATE
-  int log2fftsize;
-  aubio_FFTSetup fftSetup;
+  aubio_vDSP_DFT_Setup fftSetupFwd;
+  aubio_vDSP_DFT_Setup fftSetupBwd;
   aubio_DSPSplitComplex spec;
   smpl_t *in, *out;
@@ -211,13 +213,30 @@
 
 #elif defined HAVE_ACCELERATE  // using ACCELERATE
+  {
+    uint_t radix = winsize;
+    uint_t order = 0;
+    while ((radix / 2) * 2 == radix) {
+      radix /= 2;
+      order++;
+    }
+    if (order < 4 || (radix != 1 && radix != 3 && radix != 5 && radix != 15)) {
+      AUBIO_ERR("fft: vDSP/Accelerate supports FFT with sizes = "
+          "f * 2 ** n, where n > 4 and f in [1, 3, 5, 15], but requested %d. "
+          "Use the closest power of two, or try recompiling aubio with "
+          "--enable-fftw3.\n", winsize);
+      goto beach;
+    }
+  }
   s->winsize = winsize;
   s->fft_size = winsize;
   s->compspec = new_fvec(winsize);
-  s->log2fftsize = aubio_power_of_two_order(s->fft_size);
   s->in = AUBIO_ARRAY(smpl_t, s->fft_size);
   s->out = AUBIO_ARRAY(smpl_t, s->fft_size);
   s->spec.realp = AUBIO_ARRAY(smpl_t, s->fft_size/2);
   s->spec.imagp = AUBIO_ARRAY(smpl_t, s->fft_size/2);
-  s->fftSetup = aubio_vDSP_create_fftsetup(s->log2fftsize, FFT_RADIX2);
+  s->fftSetupFwd = aubio_vDSP_DFT_zrop_CreateSetup(NULL,
+      s->fft_size, vDSP_DFT_FORWARD);
+  s->fftSetupBwd = aubio_vDSP_DFT_zrop_CreateSetup(s->fftSetupFwd,
+      s->fft_size, vDSP_DFT_INVERSE);
 
 #elif defined HAVE_INTEL_IPP  // using Intel IPP
@@ -293,5 +312,6 @@
   AUBIO_FREE(s->spec.realp);
   AUBIO_FREE(s->spec.imagp);
-  aubio_vDSP_destroy_fftsetup(s->fftSetup);
+  aubio_vDSP_DFT_DestroySetup(s->fftSetupBwd);
+  aubio_vDSP_DFT_DestroySetup(s->fftSetupFwd);
 
 #elif defined HAVE_INTEL_IPP  // using Intel IPP
@@ -351,5 +371,6 @@
   aubio_vDSP_ctoz((aubio_DSPComplex*)s->in, 2, &s->spec, 1, s->fft_size/2);
   // compute the FFT
-  aubio_vDSP_fft_zrip(s->fftSetup, &s->spec, 1, s->log2fftsize, FFT_FORWARD);
+  aubio_vDSP_DFT_Execute(s->fftSetupFwd, s->spec.realp, s->spec.imagp,
+      s->spec.realp, s->spec.imagp);
   // convert from vDSP complex split to [ r0, r1, ..., rN, iN-1, .., i2, i1]
   compspec->data[0] = s->spec.realp[0];
@@ -419,5 +440,6 @@
   aubio_vDSP_ctoz((aubio_DSPComplex*)s->out, 2, &s->spec, 1, s->fft_size/2);
   // compute the FFT
-  aubio_vDSP_fft_zrip(s->fftSetup, &s->spec, 1, s->log2fftsize, FFT_INVERSE);
+  aubio_vDSP_DFT_Execute(s->fftSetupBwd, s->spec.realp, s->spec.imagp,
+      s->spec.realp, s->spec.imagp);
   // convert result to real output
   aubio_vDSP_ztoc(&s->spec, 1, (aubio_DSPComplex*)output->data, 2, s->fft_size/2);
@@ -494,9 +516,20 @@
   }
 #endif
-  if (compspec->data[compspec->length/2] < 0) {
-    spectrum->phas[spectrum->length - 1] = PI;
+#ifdef HAVE_FFTW3
+  // for even length only, make sure last element is 0 or PI
+  if (2 * (compspec->length / 2) == compspec->length) {
+#endif
+    if (compspec->data[compspec->length/2] < 0) {
+      spectrum->phas[spectrum->length - 1] = PI;
+    } else {
+      spectrum->phas[spectrum->length - 1] = 0.;
+    }
+#ifdef HAVE_FFTW3
   } else {
-    spectrum->phas[spectrum->length - 1] = 0.;
-  }
+    i = spectrum->length - 1;
+    spectrum->phas[i] = ATAN2(compspec->data[compspec->length-i],
+        compspec->data[i]);
+  }
+#endif
 }
 
@@ -508,6 +541,17 @@
         + SQR(compspec->data[compspec->length - i]) );
   }
-  spectrum->norm[spectrum->length-1] =
-    ABS(compspec->data[compspec->length/2]);
+#ifdef HAVE_FFTW3
+  // for even length, make sure last element is > 0
+  if (2 * (compspec->length / 2) == compspec->length) {
+#endif
+    spectrum->norm[spectrum->length-1] =
+      ABS(compspec->data[compspec->length/2]);
+#ifdef HAVE_FFTW3
+  } else {
+    i = spectrum->length - 1;
+    spectrum->norm[i] = SQRT(SQR(compspec->data[i])
+        + SQR(compspec->data[compspec->length - i]) );
+  }
+#endif
 }
 
Index: src/spectral/filterbank.c
===================================================================
--- src/spectral/filterbank.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/filterbank.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -24,4 +24,5 @@
 #include "fmat.h"
 #include "cvec.h"
+#include "vecutils.h"
 #include "spectral/filterbank.h"
 #include "mathutils.h"
@@ -33,4 +34,6 @@
   uint_t n_filters;
   fmat_t *filters;
+  smpl_t norm;
+  smpl_t power;
 };
 
@@ -45,4 +48,8 @@
   /* allocate filter tables, a matrix of length win_s and of height n_filters */
   fb->filters = new_fmat (n_filters, win_s / 2 + 1);
+
+  fb->norm = 1;
+
+  fb->power = 1;
 
   return fb;
@@ -68,4 +75,6 @@
   tmp.data = in->norm;
 
+  if (f->power != 1.) fvec_pow(&tmp, f->power);
+
   fmat_vecmul(f->filters, &tmp, out);
 
@@ -85,2 +94,29 @@
   return 0;
 }
+
+uint_t
+aubio_filterbank_set_norm (aubio_filterbank_t *f, smpl_t norm)
+{
+  if (norm != 0 && norm != 1) return AUBIO_FAIL;
+  f->norm = norm;
+  return AUBIO_OK;
+}
+
+smpl_t
+aubio_filterbank_get_norm (aubio_filterbank_t *f)
+{
+  return f->norm;
+}
+
+uint_t
+aubio_filterbank_set_power (aubio_filterbank_t *f, smpl_t power)
+{
+  f->power = power;
+  return AUBIO_OK;
+}
+
+smpl_t
+aubio_filterbank_get_power (aubio_filterbank_t *f)
+{
+  return f->norm;
+}
Index: src/spectral/filterbank.h
===================================================================
--- src/spectral/filterbank.h	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/filterbank.h	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -84,4 +84,45 @@
 uint_t aubio_filterbank_set_coeffs (aubio_filterbank_t * f, const fmat_t * filters);
 
+/** set norm parameter
+
+  \param f filterbank object, as returned by new_aubio_filterbank()
+  \param norm `1` to norm the filters, `0` otherwise.
+
+  If set to `0`, the filters will not be normalized. If set to `1`,
+  each filter will be normalized to one. Defaults to `1`.
+
+  This function should be called *before* setting the filters with one of
+  aubio_filterbank_set_triangle_bands(), aubio_filterbank_set_mel_coeffs(),
+  aubio_filterbank_set_mel_coeffs_htk(), or
+  aubio_filterbank_set_mel_coeffs_slaney().
+
+ */
+uint_t aubio_filterbank_set_norm (aubio_filterbank_t *f, smpl_t norm);
+
+/** get norm parameter
+
+  \param f filterbank object, as returned by new_aubio_filterbank()
+  \returns `1` if norm is set, `0` otherwise. Defaults to `1`.
+
+ */
+smpl_t aubio_filterbank_get_norm (aubio_filterbank_t *f);
+
+/** set power parameter
+
+  \param f filterbank object, as returned by new_aubio_filterbank()
+  \param power Raise norm of the input spectrum norm to this power before
+  computing filterbank.  Defaults to `1`.
+
+ */
+uint_t aubio_filterbank_set_power (aubio_filterbank_t *f, smpl_t power);
+
+/** get power parameter
+
+  \param f filterbank object, as returned by new_aubio_filterbank()
+  \return current power parameter. Defaults to `1`.
+
+ */
+smpl_t aubio_filterbank_get_power (aubio_filterbank_t *f);
+
 #ifdef __cplusplus
 }
Index: src/spectral/filterbank_mel.c
===================================================================
--- src/spectral/filterbank_mel.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/filterbank_mel.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -55,7 +55,19 @@
   }
 
-  if (freqs->data[freqs->length - 1] > samplerate / 2) {
-    AUBIO_WRN ("Nyquist frequency is %fHz, but highest frequency band ends at \
-%fHz\n", samplerate / 2, freqs->data[freqs->length - 1]);
+  for (fn = 0; fn < freqs->length; fn++) {
+    if (freqs->data[fn] < 0) {
+      AUBIO_ERR("filterbank_mel: freqs must contain only positive values.\n");
+      return AUBIO_FAIL;
+    } else if (freqs->data[fn] > samplerate / 2) {
+      AUBIO_WRN("filterbank_mel: freqs should contain only "
+          "values < samplerate / 2.\n");
+    } else if (fn > 0 && freqs->data[fn] < freqs->data[fn-1]) {
+      AUBIO_ERR("filterbank_mel: freqs should be a list of frequencies "
+          "sorted from low to high, but freq[%d] < freq[%d-1]\n", fn, fn);
+      return AUBIO_FAIL;
+    } else if (fn > 0 && freqs->data[fn] == freqs->data[fn-1]) {
+      AUBIO_WRN("filterbank_mel: set_triangle_bands received a list "
+          "with twice the frequency %f\n", freqs->data[fn]);
+    }
   }
 
@@ -79,7 +91,11 @@
 
   /* compute triangle heights so that each triangle has unit area */
-  for (fn = 0; fn < n_filters; fn++) {
-    triangle_heights->data[fn] =
-        2. / (upper_freqs->data[fn] - lower_freqs->data[fn]);
+  if (aubio_filterbank_get_norm(fb)) {
+    for (fn = 0; fn < n_filters; fn++) {
+      triangle_heights->data[fn] =
+          2. / (upper_freqs->data[fn] - lower_freqs->data[fn]);
+    }
+  } else {
+    fvec_ones (triangle_heights);
   }
 
@@ -92,15 +108,4 @@
   /* zeroing of all filters */
   fmat_zeros (filters);
-
-  if (fft_freqs->data[1] >= lower_freqs->data[0]) {
-    /* - 1 to make sure we don't miss the smallest power of two */
-    uint_t min_win_s =
-        (uint_t) FLOOR (samplerate / lower_freqs->data[0]) - 1;
-    AUBIO_WRN ("Lowest frequency bin (%.2fHz) is higher than lowest frequency \
-band (%.2f-%.2fHz). Consider increasing the window size from %d to %d.\n",
-        fft_freqs->data[1], lower_freqs->data[0],
-        upper_freqs->data[0], (win_s - 1) * 2,
-        aubio_next_power_of_two (min_win_s));
-  }
 
   /* building each filter table */
@@ -117,7 +122,6 @@
 
     /* compute positive slope step size */
-    riseInc =
-        triangle_heights->data[fn] /
-        (center_freqs->data[fn] - lower_freqs->data[fn]);
+    riseInc = triangle_heights->data[fn]
+      / (center_freqs->data[fn] - lower_freqs->data[fn]);
 
     /* compute coefficients in positive slope */
@@ -133,7 +137,6 @@
 
     /* compute negative slope step size */
-    downInc =
-        triangle_heights->data[fn] /
-        (upper_freqs->data[fn] - center_freqs->data[fn]);
+    downInc = triangle_heights->data[fn]
+      / (upper_freqs->data[fn] - center_freqs->data[fn]);
 
     /* compute coefficents in negative slope */
@@ -161,5 +164,5 @@
   del_fvec (fft_freqs);
 
-  return 0;
+  return AUBIO_OK;
 }
 
@@ -168,21 +171,25 @@
     smpl_t samplerate)
 {
-  uint_t retval;
-
   /* Malcolm Slaney parameters */
-  smpl_t lowestFrequency = 133.3333;
-  smpl_t linearSpacing = 66.66666666;
-  smpl_t logSpacing = 1.0711703;
-
-  uint_t linearFilters = 13;
-  uint_t logFilters = 27;
-  uint_t n_filters = linearFilters + logFilters;
-
-  uint_t fn;                    /* filter counter */
-
+  const smpl_t lowestFrequency = 133.3333;
+  const smpl_t linearSpacing = 66.66666666;
+  const smpl_t logSpacing = 1.0711703;
+
+  const uint_t linearFilters = 13;
+  const uint_t logFilters = 27;
+  const uint_t n_filters = linearFilters + logFilters;
+
+  uint_t fn, retval;
   smpl_t lastlinearCF;
 
   /* buffers to compute filter frequencies */
-  fvec_t *freqs = new_fvec (n_filters + 2);
+  fvec_t *freqs;
+
+  if (samplerate <= 0) {
+    AUBIO_ERR("filterbank: set_mel_coeffs_slaney samplerate should be > 0\n");
+    return AUBIO_FAIL;
+  }
+
+  freqs = new_fvec (n_filters + 2);
 
   /* first step: fill all the linear filter frequencies */
@@ -206,2 +213,86 @@
   return retval;
 }
+
+static uint_t aubio_filterbank_check_freqs (aubio_filterbank_t *fb UNUSED,
+    smpl_t samplerate, smpl_t *freq_min, smpl_t *freq_max)
+{
+  if (samplerate <= 0) {
+    AUBIO_ERR("filterbank: set_mel_coeffs samplerate should be > 0\n");
+    return AUBIO_FAIL;
+  }
+  if (*freq_max < 0) {
+    AUBIO_ERR("filterbank: set_mel_coeffs freq_max should be > 0\n");
+    return AUBIO_FAIL;
+  } else if (*freq_max == 0) {
+    *freq_max = samplerate / 2.;
+  }
+  if (*freq_min < 0) {
+    AUBIO_ERR("filterbank: set_mel_coeffs freq_min should be > 0\n");
+    return AUBIO_FAIL;
+  }
+  return AUBIO_OK;
+}
+
+uint_t
+aubio_filterbank_set_mel_coeffs (aubio_filterbank_t * fb, smpl_t samplerate,
+    smpl_t freq_min, smpl_t freq_max)
+{
+  uint_t m, retval;
+  smpl_t start = freq_min, end = freq_max, step;
+  fvec_t *freqs;
+  fmat_t *coeffs = aubio_filterbank_get_coeffs(fb);
+  uint_t n_bands = coeffs->height;
+
+  if (aubio_filterbank_check_freqs(fb, samplerate, &start, &end)) {
+    return AUBIO_FAIL;
+  }
+
+  start = aubio_hztomel(start);
+  end = aubio_hztomel(end);
+
+  freqs = new_fvec(n_bands + 2);
+  step = (end - start) / (n_bands + 1);
+
+  for (m = 0; m < n_bands + 2; m++)
+  {
+    freqs->data[m] = MIN(aubio_meltohz(start + step * m), samplerate/2.);
+  }
+
+  retval = aubio_filterbank_set_triangle_bands (fb, freqs, samplerate);
+
+  /* destroy vector used to store frequency limits */
+  del_fvec (freqs);
+  return retval;
+}
+
+uint_t
+aubio_filterbank_set_mel_coeffs_htk (aubio_filterbank_t * fb, smpl_t samplerate,
+    smpl_t freq_min, smpl_t freq_max)
+{
+  uint_t m, retval;
+  smpl_t start = freq_min, end = freq_max, step;
+  fvec_t *freqs;
+  fmat_t *coeffs = aubio_filterbank_get_coeffs(fb);
+  uint_t n_bands = coeffs->height;
+
+  if (aubio_filterbank_check_freqs(fb, samplerate, &start, &end)) {
+    return AUBIO_FAIL;
+  }
+
+  start = aubio_hztomel_htk(start);
+  end = aubio_hztomel_htk(end);
+
+  freqs = new_fvec (n_bands + 2);
+  step = (end - start) / (n_bands + 1);
+
+  for (m = 0; m < n_bands + 2; m++)
+  {
+    freqs->data[m] = MIN(aubio_meltohz_htk(start + step * m), samplerate/2.);
+  }
+
+  retval = aubio_filterbank_set_triangle_bands (fb, freqs, samplerate);
+
+  /* destroy vector used to store frequency limits */
+  del_fvec (freqs);
+  return retval;
+}
Index: src/spectral/filterbank_mel.h
===================================================================
--- src/spectral/filterbank_mel.h	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/filterbank_mel.h	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -56,9 +56,13 @@
 
   \param fb filterbank object
-  \param samplerate audio sampling rate
+  \param samplerate audio sampling rate, in Hz
 
-  The filter coefficients are built according to Malcolm Slaney's Auditory
-  Toolbox, available online at the following address (see file mfcc.m):
+  The filter coefficients are built to match exactly Malcolm Slaney's Auditory
+  Toolbox implementation (see file mfcc.m). The number of filters should be 40.
 
+  References
+  ----------
+
+  Malcolm Slaney, *Auditory Toolbox Version 2, Technical Report #1998-010*
   https://engineering.purdue.edu/~malcolm/interval/1998-010/
 
@@ -67,4 +71,46 @@
     smpl_t samplerate);
 
+/** Mel filterbank initialization
+
+  \param fb filterbank object
+  \param samplerate audio sampling rate
+  \param fmin start frequency, in Hz
+  \param fmax end frequency, in Hz
+
+  The filterbank will be initialized with bands linearly spaced in the mel
+  scale, from `fmin` to `fmax`.
+
+  References
+  ----------
+
+  Malcolm Slaney, *Auditory Toolbox Version 2, Technical Report #1998-010*
+  https://engineering.purdue.edu/~malcolm/interval/1998-010/
+
+*/
+uint_t aubio_filterbank_set_mel_coeffs(aubio_filterbank_t * fb,
+    smpl_t samplerate, smpl_t fmin, smpl_t fmax);
+
+/** Mel filterbank initialization
+
+  \param fb filterbank object
+  \param samplerate audio sampling rate
+  \param fmin start frequency, in Hz
+  \param fmax end frequency, in Hz
+
+  The bank of filters will be initalized to to cover linearly spaced bands in
+  the Htk mel scale, from `fmin` to `fmax`.
+
+  References
+  ----------
+
+  Douglas O'Shaughnessy (1987). *Speech communication: human and machine*.
+  Addison-Wesley. p. 150. ISBN 978-0-201-16520-3.
+
+  HTK Speech Recognition Toolkit: http://htk.eng.cam.ac.uk/
+
+*/
+uint_t aubio_filterbank_set_mel_coeffs_htk(aubio_filterbank_t * fb,
+    smpl_t samplerate, smpl_t fmin, smpl_t fmax);
+
 #ifdef __cplusplus
 }
Index: src/spectral/mfcc.c
===================================================================
--- src/spectral/mfcc.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/mfcc.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -52,4 +52,5 @@
   fvec_t *output;
 #endif
+  smpl_t scale;
 };
 
@@ -75,5 +76,9 @@
   /* filterbank allocation */
   mfcc->fb = new_aubio_filterbank (n_filters, mfcc->win_s);
-  aubio_filterbank_set_mel_coeffs_slaney (mfcc->fb, samplerate);
+  if (n_filters == 40)
+    aubio_filterbank_set_mel_coeffs_slaney (mfcc->fb, samplerate);
+  else
+    aubio_filterbank_set_mel_coeffs(mfcc->fb, samplerate,
+        0, samplerate/2.);
 
   /* allocating buffers */
@@ -97,4 +102,6 @@
   mfcc->output = new_fvec (n_filters);
 #endif
+
+  mfcc->scale = 1.;
 
   return mfcc;
@@ -128,4 +135,5 @@
   fvec_t tmp;
 #endif
+
   /* compute filterbank */
   aubio_filterbank_do (mf->fb, in, mf->in_dct);
@@ -134,6 +142,5 @@
   fvec_log10 (mf->in_dct);
 
-  /* raise power */
-  //fvec_pow (mf->in_dct, 3.);
+  if (mf->scale != 1) fvec_mul (mf->in_dct, mf->scale);
 
   /* compute mfccs */
@@ -151,2 +158,42 @@
   return;
 }
+
+uint_t aubio_mfcc_set_power (aubio_mfcc_t *mf, smpl_t power)
+{
+  return aubio_filterbank_set_power(mf->fb, power);
+}
+
+uint_t aubio_mfcc_get_power (aubio_mfcc_t *mf)
+{
+  return aubio_filterbank_get_power(mf->fb);
+}
+
+uint_t aubio_mfcc_set_scale (aubio_mfcc_t *mf, smpl_t scale)
+{
+  mf->scale = scale;
+  return AUBIO_OK;
+}
+
+uint_t aubio_mfcc_get_scale (aubio_mfcc_t *mf)
+{
+  return mf->scale;
+}
+
+uint_t aubio_mfcc_set_mel_coeffs (aubio_mfcc_t *mf, smpl_t freq_min,
+    smpl_t freq_max)
+{
+  return aubio_filterbank_set_mel_coeffs(mf->fb, mf->samplerate,
+      freq_min, freq_max);
+}
+
+uint_t aubio_mfcc_set_mel_coeffs_htk (aubio_mfcc_t *mf, smpl_t freq_min,
+    smpl_t freq_max)
+{
+  return aubio_filterbank_set_mel_coeffs_htk(mf->fb, mf->samplerate,
+      freq_min, freq_max);
+}
+
+uint_t aubio_mfcc_set_mel_coeffs_slaney (aubio_mfcc_t *mf)
+{
+  return aubio_filterbank_set_mel_coeffs_slaney (mf->fb, mf->samplerate);
+}
Index: src/spectral/mfcc.h
===================================================================
--- src/spectral/mfcc.h	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ src/spectral/mfcc.h	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -74,4 +74,98 @@
 void aubio_mfcc_do (aubio_mfcc_t * mf, const cvec_t * in, fvec_t * out);
 
+/** set power parameter
+
+  \param mf mfcc object, as returned by new_aubio_mfcc()
+  \param power Raise norm of the input spectrum norm to this power before
+  computing filterbank.  Defaults to `1`.
+
+  See aubio_filterbank_set_power().
+
+ */
+uint_t aubio_mfcc_set_power (aubio_mfcc_t *mf, smpl_t power);
+
+/** get power parameter
+
+  \param mf mfcc object, as returned by new_aubio_mfcc()
+  \return current power parameter. Defaults to `1`.
+
+  See aubio_filterbank_get_power().
+
+ */
+uint_t aubio_mfcc_get_power (aubio_mfcc_t *mf);
+
+/** set scaling parameter
+
+  \param mf mfcc object, as returned by new_aubio_mfcc()
+  \param scale Scaling value to apply.
+
+  Scales the output of the filterbank after taking its logarithm and before
+  computing the DCT. Defaults to `1`.
+
+*/
+uint_t aubio_mfcc_set_scale (aubio_mfcc_t *mf, smpl_t scale);
+
+/** get scaling parameter
+
+  \param mf mfcc object, as returned by new_aubio_mfcc()
+  \return current scaling parameter. Defaults to `1`.
+
+ */
+uint_t aubio_mfcc_get_scale (aubio_mfcc_t *mf);
+
+/** Mel filterbank initialization
+
+  \param mf mfcc object
+  \param fmin start frequency, in Hz
+  \param fmax end frequency, in Hz
+
+  The filterbank will be initialized with bands linearly spaced in the mel
+  scale, from `fmin` to `fmax`.
+
+  See also
+  --------
+
+  aubio_filterbank_set_mel_coeffs()
+
+*/
+uint_t aubio_mfcc_set_mel_coeffs (aubio_mfcc_t *mf,
+        smpl_t fmin, smpl_t fmax);
+
+/** Mel filterbank initialization
+
+  \param mf mfcc object
+  \param fmin start frequency, in Hz
+  \param fmax end frequency, in Hz
+
+  The bank of filters will be initalized to to cover linearly spaced bands in
+  the Htk mel scale, from `fmin` to `fmax`.
+
+  See also
+  --------
+
+  aubio_filterbank_set_mel_coeffs_htk()
+
+*/
+uint_t aubio_mfcc_set_mel_coeffs_htk (aubio_mfcc_t *mf,
+        smpl_t fmin, smpl_t fmax);
+
+/** Mel filterbank initialization (Auditory Toolbox's parameters)
+
+  \param mf mfcc object
+  \param samplerate audio sampling rate, in Hz
+
+  The filter coefficients are built to match exactly Malcolm Slaney's Auditory
+  Toolbox implementation. The number of filters should be 40.
+
+  This is the default filterbank when `mf` was created with `n_filters = 40`.
+
+  See also
+  --------
+
+  aubio_filterbank_set_mel_coeffs_slaney()
+
+*/
+uint_t aubio_mfcc_set_mel_coeffs_slaney (aubio_mfcc_t *mf);
+
 #ifdef __cplusplus
 }
Index: tests/src/spectral/test-filterbank.c
===================================================================
--- tests/src/spectral/test-filterbank.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ tests/src/spectral/test-filterbank.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -11,4 +11,13 @@
   // create filterbank object
   aubio_filterbank_t *o = new_aubio_filterbank (n_filters, win_s);
+
+  smpl_t power = aubio_filterbank_get_power(o);
+  smpl_t norm = aubio_filterbank_get_norm(o);
+  if (aubio_filterbank_set_power(o, power)) {
+    return 1;
+  }
+  if (aubio_filterbank_set_norm(o, norm)) {
+    return 1;
+  }
 
   // apply filterbank ten times
Index: tests/src/temporal/test-filter.c
===================================================================
--- tests/src/temporal/test-filter.c	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ tests/src/temporal/test-filter.c	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -9,4 +9,17 @@
 
   aubio_filter_t *o = new_aubio_filter_c_weighting (44100);
+
+  if (new_aubio_filter(0))
+    return 1;
+
+  if (aubio_filter_get_samplerate(o) != 44100)
+    return 1;
+
+  if (aubio_filter_set_c_weighting (o, -1) == 0)
+    return 1;
+
+  if (aubio_filter_set_c_weighting (0, 32000) == 0)
+    return 1;
+
   in->data[impulse_at] = 0.5;
   fvec_print (in);
@@ -16,4 +29,10 @@
 
   o = new_aubio_filter_a_weighting (32000);
+
+  if (aubio_filter_set_a_weighting (o, -1) == 0)
+    return 1;
+  if (aubio_filter_set_a_weighting (0, 32000) == 0)
+    return 1;
+
   in->data[impulse_at] = 0.5;
   fvec_print (in);
Index: wscript
===================================================================
--- wscript	(revision 088760ebc3fcceb909d6819c5bf74b23bcb3844a)
+++ wscript	(revision c03d191d272e02cbd8d22d1adbbd48420ce44888)
@@ -43,6 +43,7 @@
             choices = ('debug', 'release'),
             dest = 'build_type',
-            help = 'whether to compile with (--build-type=release) or without (--build-type=debug) '\
-              ' compiler opimizations [default: release]')
+            help = 'whether to compile with (--build-type=release)' \
+                    ' or without (--build-type=debug)' \
+                    ' compiler opimizations [default: release]')
     add_option_enable_disable(ctx, 'fftw3f', default = False,
             help_str = 'compile with fftw3f instead of ooura (recommended)',
@@ -110,5 +111,6 @@
 
     ctx.add_option('--with-target-platform', type='string',
-            help='set target platform for cross-compilation', dest='target_platform')
+            help='set target platform for cross-compilation',
+            dest='target_platform')
 
     ctx.load('compiler_c')
@@ -206,5 +208,6 @@
             ctx.msg('Checking for AudioToolbox.framework', 'yes')
         else:
-            ctx.msg('Checking for AudioToolbox.framework', 'no (disabled)', color = 'YELLOW')
+            ctx.msg('Checking for AudioToolbox.framework', 'no (disabled)',
+                    color = 'YELLOW')
         if (ctx.options.enable_accelerate != False):
             ctx.define('HAVE_ACCELERATE', 1)
@@ -212,5 +215,6 @@
             ctx.msg('Checking for Accelerate framework', 'yes')
         else:
-            ctx.msg('Checking for Accelerate framework', 'no (disabled)', color = 'YELLOW')
+            ctx.msg('Checking for Accelerate framework', 'no (disabled)',
+                    color = 'YELLOW')
 
     if target_platform in [ 'ios', 'iosimulator' ]:
@@ -266,7 +270,9 @@
         # tell emscripten functions we want to expose
         from python.lib.gen_external import get_c_declarations, \
-                get_cpp_objects_from_c_declarations, get_all_func_names_from_lib, \
+                get_cpp_objects_from_c_declarations, \
+                get_all_func_names_from_lib, \
                 generate_lib_from_c_declarations
-        c_decls = get_c_declarations(usedouble=False)  # emscripten can't use double
+        # emscripten can't use double
+        c_decls = get_c_declarations(usedouble=False)
         objects = list(get_cpp_objects_from_c_declarations(c_decls))
         # ensure that aubio structs are exported
@@ -275,5 +281,6 @@
         exported_funcnames = get_all_func_names_from_lib(lib)
         c_mangled_names = ['_' + s for s in exported_funcnames]
-        ctx.env.LINKFLAGS_cshlib += ['-s', 'EXPORTED_FUNCTIONS=%s' % c_mangled_names]
+        ctx.env.LINKFLAGS_cshlib += ['-s',
+                'EXPORTED_FUNCTIONS=%s' % c_mangled_names]
 
     # check support for C99 __VA_ARGS__ macros
@@ -305,6 +312,6 @@
     # check for Intel IPP
     if (ctx.options.enable_intelipp != False):
-        has_ipp_headers = ctx.check(header_name=['ippcore.h', 'ippvm.h', 'ipps.h'],
-                mandatory = False)
+        has_ipp_headers = ctx.check(header_name=['ippcore.h', 'ippvm.h',
+            'ipps.h'], mandatory = False)
         has_ipp_libs = ctx.check(lib=['ippcore', 'ippvm', 'ipps'],
                 uselib_store='INTEL_IPP', mandatory = False)
@@ -313,5 +320,6 @@
             ctx.define('HAVE_INTEL_IPP', 1)
             if ctx.env.CC_NAME == 'msvc':
-                # force linking multi-threaded static IPP libraries on Windows with msvc
+                # force linking multi-threaded static IPP libraries on Windows
+                # with msvc
                 ctx.define('_IPP_SEQUENTIAL_STATIC', 1)
         else:
@@ -362,9 +370,10 @@
     if (ctx.options.enable_double):
         if (ctx.options.enable_samplerate):
-            ctx.fatal("Could not compile aubio in double precision mode with libsamplerate")
+            ctx.fatal("Could not compile aubio in double precision mode' \
+                    ' with libsamplerate")
         else:
             ctx.options.enable_samplerate = False
-            ctx.msg('Checking if using samplerate', 'no (disabled in double precision mode)',
-                    color = 'YELLOW')
+            ctx.msg('Checking if using samplerate',
+                    'no (disabled in double precision mode)', color = 'YELLOW')
     if (ctx.options.enable_samplerate != False):
         ctx.check_cfg(package = 'samplerate',
@@ -409,5 +418,6 @@
         elif 'HAVE_AVUTIL' not in ctx.env:
             ctx.msg(msg_check, 'not found (missing avutil)', color = 'YELLOW')
-        elif 'HAVE_SWRESAMPLE' not in ctx.env and 'HAVE_AVRESAMPLE' not in ctx.env:
+        elif 'HAVE_SWRESAMPLE' not in ctx.env \
+                and 'HAVE_AVRESAMPLE' not in ctx.env:
             resample_missing = 'not found (avresample or swresample required)'
             ctx.msg(msg_check, resample_missing, color = 'YELLOW')
@@ -422,8 +432,10 @@
     if (ctx.options.enable_wavread != False):
         ctx.define('HAVE_WAVREAD', 1)
-    ctx.msg('Checking if using source_wavread', ctx.options.enable_wavread and 'yes' or 'no')
+    ctx.msg('Checking if using source_wavread',
+            ctx.options.enable_wavread and 'yes' or 'no')
     if (ctx.options.enable_wavwrite!= False):
         ctx.define('HAVE_WAVWRITE', 1)
-    ctx.msg('Checking if using sink_wavwrite', ctx.options.enable_wavwrite and 'yes' or 'no')
+    ctx.msg('Checking if using sink_wavwrite',
+            ctx.options.enable_wavwrite and 'yes' or 'no')
 
     # use BLAS/ATLAS
@@ -543,10 +555,12 @@
 
 def sphinx(bld):
-    # build documentation from source files using sphinx-build
-    # note: build in ../doc/_build/html, otherwise waf wont install unsigned files
+    # build documentation from source files using sphinx-build note: build in
+    # ../doc/_build/html, otherwise waf wont install unsigned files
     if bld.env['SPHINX']:
         bld.env.VERSION = VERSION
         bld( name = 'sphinx',
-                rule = '${SPHINX} -b html -D release=${VERSION} -D version=${VERSION} -a -q `dirname ${SRC}` `dirname ${TGT}`',
+                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')
@@ -578,5 +592,6 @@
     from waflib import Logs
     if bld.options.target_platform in ['ios', 'iosimulator']:
-        msg ='building for %s, contact the author for a commercial license' % bld.options.target_platform
+        msg ='building for %s, contact the author for a commercial license' \
+                % bld.options.target_platform
         Logs.pprint('RED', msg)
         msg ='   Paul Brossier <piem@aubio.org>'
@@ -584,5 +599,7 @@
 
 def dist(ctx):
-    ctx.excl  = ' **/.waf* **/*~ **/*.pyc **/*.swp **/*.swo **/*.swn **/.lock-w* **/.git*'
+    ctx.excl  = ' **/.waf*'
+    ctx.excl += ' **/.git*'
+    ctx.excl += ' **/*~ **/*.pyc **/*.swp **/*.swo **/*.swn **/.lock-w*'
     ctx.excl += ' **/build/*'
     ctx.excl += ' doc/_build'
@@ -592,4 +609,7 @@
     ctx.excl += ' **/python/lib/aubio/_aubio.so'
     ctx.excl += ' **.egg-info'
+    ctx.excl += ' **/.eggs'
+    ctx.excl += ' **/.pytest_cache'
+    ctx.excl += ' **/.cache'
     ctx.excl += ' **/**.zip **/**.tar.bz2'
     ctx.excl += ' **.tar.bz2'
