shithub: aubio

Download patch

ref: ad653468d96f7c2f05dafd9e1cb67b507cba088f
parent: 3ffedf225ec8309798271fee2ed2a18730fdda76
parent: bd8a92d0fbc90828df0c08f30bf5c63ebd4690d4
author: Paul Brossier <piem@piem.org>
date: Fri Sep 23 02:57:20 EDT 2016

Merge branch 'master' into pitchshift

--- a/python/demos/demo_mfcc.py
+++ b/python/demos/demo_mfcc.py
@@ -2,20 +2,27 @@
 
 import sys
 from aubio import source, pvoc, mfcc
-from numpy import vstack, zeros
+from numpy import vstack, zeros, diff
 
-win_s = 512                 # fft size
-hop_s = win_s // 4          # hop size
 n_filters = 40              # must be 40 for mfcc
 n_coeffs = 13
-samplerate = 44100
 
 if len(sys.argv) < 2:
-    print("Usage: %s <source_filename>" % sys.argv[0])
+    print("Usage: %s <source_filename> [samplerate] [win_s] [hop_s] [mode]" % sys.argv[0])
+    print("  where [mode] can be 'delta' or 'ddelta' for first and second derivatives")
     sys.exit(1)
 
 source_filename = sys.argv[1]
 
+if len(sys.argv) > 2: samplerate = int(sys.argv[2])
+else: samplerate = 0
+if len(sys.argv) > 3: win_s = int(sys.argv[3])
+else: win_s = 512
+if len(sys.argv) > 4: hop_s = int(sys.argv[4])
+else: hop_s = win_s // 4
+if len(sys.argv) > 5: mode = sys.argv[5]
+else: mode = "default"
+
 samplerate = 0
 if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
 
@@ -48,18 +55,28 @@
 wave.xaxis.set_visible(False)
 wave.yaxis.set_visible(False)
 
+# compute first and second derivatives
+if mode in ["delta", "ddelta"]:
+    mfccs = diff(mfccs, axis = 0)
+if mode == "ddelta":
+    mfccs = diff(mfccs, axis = 0)
+
 all_times = arange(mfccs.shape[0]) * hop_s
 n_coeffs = mfccs.shape[1]
 for i in range(n_coeffs):
     ax = plt.axes ( [0.1, 0.75 - ((i+1) * 0.65 / n_coeffs),  0.8, 0.65 / n_coeffs], sharex = wave )
     ax.xaxis.set_visible(False)
-    ax.yaxis.set_visible(False)
+    ax.set_yticks([])
+    ax.set_ylabel('%d' % i)
     ax.plot(all_times, mfccs.T[i])
 
 # add time to the last axis
-set_xlabels_sample2time( ax, frames_read, samplerate) 
+set_xlabels_sample2time( ax, frames_read, samplerate)
 
 #plt.ylabel('spectral descriptor value')
 ax.xaxis.set_visible(True)
-wave.set_title('MFCC for %s' % source_filename)
+title = 'MFCC for %s' % source_filename
+if mode == "delta": title = mode + " " + title
+elif mode == "ddelta": title = "double-delta" + " " + title
+wave.set_title(title)
 plt.show()
--- a/python/ext/aubiomodule.c
+++ b/python/ext/aubiomodule.c
@@ -256,6 +256,22 @@
 };
 #endif
 
+void
+aubio_log_function(int level, const char *message, void *data)
+{
+  // remove trailing \n
+  char *pos;
+  if ((pos=strchr(message, '\n')) != NULL) {
+        *pos = '\0';
+  }
+  // warning or error
+  if (level == AUBIO_LOG_ERR) {
+    PyErr_Format(PyExc_RuntimeError, "%s", message);
+  } else {
+    PyErr_WarnEx(PyExc_UserWarning, message, 1);
+  }
+}
+
 static PyObject *
 initaubio (void)
 {
@@ -315,6 +331,8 @@
   // add ufunc
   add_ufuncs(m);
 
+  aubio_log_set_level_function(AUBIO_LOG_ERR, aubio_log_function, NULL);
+  aubio_log_set_level_function(AUBIO_LOG_WRN, aubio_log_function, NULL);
   return m;
 }
 
--- a/python/ext/py-fft.c
+++ b/python/ext/py-fft.c
@@ -51,11 +51,8 @@
 {
   self->o = new_aubio_fft (self->win_s);
   if (self->o == NULL) {
-    PyErr_Format(PyExc_RuntimeError,
-        "error creating fft with win_s=%d "
-        "(should be a power of 2 greater than 1; "
-        "try recompiling aubio with --enable-fftw3)",
-        self->win_s);
+    // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called
+    // AUBIO_ERR when failing
     return -1;
   }
 
--- a/python/ext/py-phasevoc.c
+++ b/python/ext/py-phasevoc.c
@@ -66,9 +66,8 @@
 {
   self->o = new_aubio_pvoc ( self->win_s, self->hop_s);
   if (self->o == NULL) {
-    PyErr_Format(PyExc_RuntimeError,
-        "failed creating pvoc with win_s=%d, hop_s=%d",
-        self->win_s, self->hop_s);
+    // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called
+    // AUBIO_ERR when failing
     return -1;
   }
 
--- a/python/ext/py-source.c
+++ b/python/ext/py-source.c
@@ -140,8 +140,8 @@
 {
   self->o = new_aubio_source ( self->uri, self->samplerate, self->hop_size );
   if (self->o == NULL) {
-    PyErr_Format (PyExc_RuntimeError, "error creating source with \"%s\"",
-        self->uri);
+    // PyErr_Format(PyExc_RuntimeError, ...) was set above by new_ which called
+    // AUBIO_ERR when failing
     return -1;
   }
   self->samplerate = aubio_source_get_samplerate ( self->o );
--- a/python/lib/moresetuptools.py
+++ b/python/lib/moresetuptools.py
@@ -65,7 +65,8 @@
     for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H',
                          'HAVE_MATH_H', 'HAVE_STRING_H',
                          'HAVE_C99_VARARGS_MACROS',
-                         'HAVE_LIMITS_H', 'HAVE_MEMCPY_HACKS']:
+                         'HAVE_LIMITS_H', 'HAVE_STDARG_H',
+                         'HAVE_MEMCPY_HACKS']:
         ext.define_macros += [(define_macro, 1)]
 
     # loof for additional packages
--- a/python/tests/test_fvec.py
+++ b/python/tests/test_fvec.py
@@ -98,7 +98,7 @@
         x = np.random.rand(1024).astype(float_type)
         alpha = np.random.rand() * 5.
         x_alpha_norm = (np.sum(np.abs(x)**alpha)/len(x))**(1/alpha)
-        assert_almost_equal(alpha_norm(x, alpha), x_alpha_norm, decimal = 5)
+        assert_almost_equal(alpha_norm(x, alpha), x_alpha_norm, decimal = 4)
 
 class aubio_zero_crossing_rate_test(TestCase):
 
--- a/python/tests/test_source.py
+++ b/python/tests/test_source.py
@@ -6,6 +6,9 @@
 from aubio import source
 from utils import list_all_sounds
 
+import warnings
+warnings.filterwarnings('ignore', category=UserWarning, append=True)
+
 list_of_sounds = list_all_sounds('sounds')
 samplerates = [0, 44100, 8000, 32000]
 hop_sizes = [512, 1024, 64]
@@ -22,7 +25,8 @@
 class aubio_source_test_case_base(TestCase):
 
     def setUp(self):
-        if not len(list_of_sounds): self.skipTest('add some sound files in \'python/tests/sounds\'')
+        if not len(list_of_sounds):
+            self.skipTest('add some sound files in \'python/tests/sounds\'')
         self.default_test_sound = list_of_sounds[0]
 
 class aubio_source_test_case(aubio_source_test_case_base):
--- a/python/tests/test_specdesc.py
+++ b/python/tests/test_specdesc.py
@@ -225,10 +225,8 @@
             specdesc("default", -10)
 
     def test_unknown(self):
-        # FIXME should fail?
-        with self.assertRaises(ValueError):
+        with self.assertRaises(RuntimeError):
             specdesc("unknown", 512)
-            self.skipTest('todo: new_specdesc should fail on wrong method')
 
 if __name__ == '__main__':
     main()
--- a/src/aubio.h
+++ b/src/aubio.h
@@ -109,7 +109,14 @@
   \endcode
 
   Several examples of C programs are available in the \p examples/ and \p tests/src
-  directories of the source tree.
+  directories of the source tree. See more examples:
+  @ref spectral/test-fft.c
+  @ref spectral/test-phasevoc.c
+  @ref onset/test-onset.c
+  @ref pitch/test-pitch.c
+  @ref tempo/test-tempo.c
+  @ref test-fvec.c
+  @ref test-cvec.c
 
   \subsection unstable_api Unstable API
 
@@ -188,6 +195,7 @@
 #include "synth/sampler.h"
 #include "synth/wavetable.h"
 #include "utils/parameter.h"
+#include "utils/log.h"
 
 #if AUBIO_UNSTABLE
 #include "mathutils.h"
--- a/src/aubio_priv.h
+++ b/src/aubio_priv.h
@@ -64,6 +64,10 @@
 #include <limits.h> // for CHAR_BIT, in C99 standard
 #endif
 
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
 #ifdef HAVE_ACCELERATE
 #define HAVE_ATLAS 1
 #include <Accelerate/Accelerate.h>
@@ -168,16 +172,23 @@
   AUBIO_FAIL = 1
 } aubio_status;
 
+/* Logging */
+
+#include "utils/log.h"
+
+/** internal logging function, defined in utils/log.c */
+uint_t aubio_log(sint_t level, const char_t *fmt, ...);
+
 #ifdef HAVE_C99_VARARGS_MACROS
-#define AUBIO_ERR(...)               fprintf(stderr, "AUBIO ERROR: " __VA_ARGS__)
-#define AUBIO_MSG(...)               fprintf(stdout, __VA_ARGS__)
-#define AUBIO_DBG(...)               fprintf(stderr, __VA_ARGS__)
-#define AUBIO_WRN(...)               fprintf(stderr, "AUBIO WARNING: " __VA_ARGS__)
+#define AUBIO_ERR(...)               aubio_log(AUBIO_LOG_ERR, "AUBIO ERROR: " __VA_ARGS__)
+#define AUBIO_MSG(...)               aubio_log(AUBIO_LOG_MSG, __VA_ARGS__)
+#define AUBIO_DBG(...)               aubio_log(AUBIO_LOG_DBG, __VA_ARGS__)
+#define AUBIO_WRN(...)               aubio_log(AUBIO_LOG_WRN, "AUBIO WARNING: " __VA_ARGS__)
 #else
-#define AUBIO_ERR(format, args...)   fprintf(stderr, "AUBIO ERROR: " format , ##args)
-#define AUBIO_MSG(format, args...)   fprintf(stdout, format , ##args)
-#define AUBIO_DBG(format, args...)   fprintf(stderr, format , ##args)
-#define AUBIO_WRN(format, args...)   fprintf(stderr, "AUBIO WARNING: " format, ##args)
+#define AUBIO_ERR(format, args...)   aubio_log(stderr, "AUBIO ERROR: " format , ##args)
+#define AUBIO_MSG(format, args...)   aubio_log(stdout, format , ##args)
+#define AUBIO_DBG(format, args...)   aubio_log(stderr, format , ##args)
+#define AUBIO_WRN(format, args...)   aubio_log(stderr, "AUBIO WARNING: " format, ##args)
 #endif
 
 #define AUBIO_ERROR   AUBIO_ERR
--- a/src/io/source_sndfile.c
+++ b/src/io/source_sndfile.c
@@ -59,8 +59,9 @@
   smpl_t ratio;
   uint_t input_hop_size;
 #ifdef HAVE_SAMPLERATE
-  aubio_resampler_t *resampler;
+  aubio_resampler_t **resamplers;
   fvec_t *input_data;
+  fmat_t *input_mat;
 #endif /* HAVE_SAMPLERATE */
 
   // some temporary memory for sndfile to write at
@@ -126,11 +127,17 @@
   }
 
 #ifdef HAVE_SAMPLERATE
-  s->resampler = NULL;
   s->input_data = NULL;
+  s->input_mat = NULL;
+  s->resamplers = NULL;
   if (s->ratio != 1) {
+    uint_t i;
+    s->resamplers = AUBIO_ARRAY(aubio_resampler_t*, s->input_channels);
     s->input_data = new_fvec(s->input_hop_size);
-    s->resampler = new_aubio_resampler(s->ratio, 4);
+    s->input_mat = new_fmat(s->input_channels, s->input_hop_size);
+    for (i = 0; i < (uint_t)s->input_channels; i++) {
+      s->resamplers[i] = new_aubio_resampler(s->ratio, 4);
+    }
     if (s->ratio > 1) {
       // we would need to add a ring buffer for these
       if ( (uint_t)(s->input_hop_size * s->ratio + .5)  != s->hop_size ) {
@@ -189,8 +196,8 @@
   }
 
 #ifdef HAVE_SAMPLERATE
-  if (s->resampler) {
-    aubio_resampler_do(s->resampler, s->input_data, read_data);
+  if (s->resamplers) {
+    aubio_resampler_do(s->resamplers[0], s->input_data, read_data);
   }
 #endif /* HAVE_SAMPLERATE */
 
@@ -213,9 +220,7 @@
   smpl_t **ptr_data;
 #ifdef HAVE_SAMPLERATE
   if (s->ratio != 1) {
-    AUBIO_ERR("source_sndfile: no multi channel resampling yet\n");
-    return;
-    //ptr_data = s->input_data->data;
+    ptr_data = s->input_mat->data;
   } else
 #endif /* HAVE_SAMPLERATE */
   {
@@ -251,8 +256,15 @@
   }
 
 #ifdef HAVE_SAMPLERATE
-  if (s->resampler) {
-    //aubio_resampler_do(s->resampler, s->input_data, read_data);
+  if (s->resamplers) {
+    for (i = 0; i < input_channels; i++) {
+      fvec_t input_chan, read_chan;
+      input_chan.data = s->input_mat->data[i];
+      input_chan.length = s->input_mat->length;
+      read_chan.data = read_data->data[i];
+      read_chan.length = read_data->length;
+      aubio_resampler_do(s->resamplers[i], &input_chan, &read_chan);
+    }
   }
 #endif /* HAVE_SAMPLERATE */
 
@@ -313,11 +325,20 @@
   if (!s) return;
   aubio_source_sndfile_close(s);
 #ifdef HAVE_SAMPLERATE
-  if (s->resampler != NULL) {
-    del_aubio_resampler(s->resampler);
+  if (s->resamplers != NULL) {
+    uint_t i = 0, input_channels = s->input_channels;
+    for (i = 0; i < input_channels; i ++) {
+      if (s->resamplers[i] != NULL) {
+        del_aubio_resampler(s->resamplers[i]);
+      }
+    }
+    AUBIO_FREE(s->resamplers);
   }
   if (s->input_data) {
     del_fvec(s->input_data);
+  }
+  if (s->input_mat) {
+    del_fmat(s->input_mat);
   }
 #endif /* HAVE_SAMPLERATE */
   if (s->path) AUBIO_FREE(s->path);
--- a/src/notes/notes.c
+++ b/src/notes/notes.c
@@ -81,8 +81,7 @@
   o->pitch_output = new_fvec (1);
 
   if (strcmp(method, "default") != 0) {
-    AUBIO_ERR("unknown notes detection method %s, using default.\n",
-       method);
+    AUBIO_ERR("notes: unknown notes detection method \"%s\"\n", method);
     goto fail;
   }
   o->note_buffer = new_fvec(o->median);
--- a/src/spectral/fft.h
+++ b/src/spectral/fft.h
@@ -27,7 +27,7 @@
     - [FFTW3](http://www.fftw.org)
     - [vDSP](https://developer.apple.com/library/mac/#documentation/Accelerate/Reference/vDSPRef/Reference/reference.html)
 
-  \example src/spectral/test-fft.c
+  \example spectral/test-fft.c
 
 */
 
--- a/src/spectral/specdesc.c
+++ b/src/spectral/specdesc.c
@@ -273,12 +273,13 @@
   else if (strcmp (onset_mode, "default") == 0)
       onset_type = aubio_onset_default;
   else {
-      AUBIO_ERR("unknown spectral descriptor type %s, using default.\n", onset_mode);
-      onset_type = aubio_onset_default;
+      AUBIO_ERR("unknown spectral descriptor type %s\n", onset_mode);
+      AUBIO_FREE(o);
+      return NULL;
   }
   switch(onset_type) {
     /* for both energy and hfc, only fftgrain->norm is required */
-    case aubio_onset_energy: 
+    case aubio_onset_energy:
       break;
     case aubio_onset_hfc:
       break;
@@ -366,7 +367,7 @@
 
 void del_aubio_specdesc (aubio_specdesc_t *o){
   switch(o->onset_type) {
-    case aubio_onset_energy: 
+    case aubio_onset_energy:
       break;
     case aubio_onset_hfc:
       break;
--- /dev/null
+++ b/src/utils/log.c
@@ -1,0 +1,93 @@
+/*
+  Copyright (C) 2016 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 "config.h"
+#include "aubio_priv.h"
+#include "log.h"
+
+/** array of pointers to logging functions, one per level */
+static aubio_log_function_t aubio_log_function[AUBIO_LOG_LAST_LEVEL];
+/** array of pointers to closure passed to logging functions, one per level */
+static void* aubio_log_user_data[AUBIO_LOG_LAST_LEVEL];
+/** buffer for logging messages */
+static char aubio_log_buffer[512];
+
+/** private function used by default by logging functions */
+void
+aubio_default_log(sint_t level, const char_t *message, void * data UNUSED)
+{
+  FILE *out;
+  out = stdout;
+  if (level == AUBIO_LOG_DBG || level == AUBIO_LOG_ERR) {
+    out = stderr;
+  }
+  fprintf(out, "%s", message);
+  //fflush(out);
+}
+
+uint_t
+aubio_log(sint_t level, const char_t *fmt, ...)
+{
+  aubio_log_function_t fun = NULL;
+
+  va_list args;
+  va_start(args, fmt);
+  vsnprintf(aubio_log_buffer, sizeof(aubio_log_buffer), fmt, args);
+  va_end(args);
+
+  if ((level >= 0) && (level < AUBIO_LOG_LAST_LEVEL)) {
+    fun = aubio_log_function[level];
+    if (fun != NULL) {
+      (*fun)(level, aubio_log_buffer, aubio_log_user_data[level]);
+    } else {
+      aubio_default_log(level, aubio_log_buffer, NULL);
+    }
+  }
+  return AUBIO_FAIL;
+}
+
+void
+aubio_log_reset(void)
+{
+  uint_t i = 0;
+  for (i = 0; i < AUBIO_LOG_LAST_LEVEL; i++) {
+    aubio_log_set_level_function(i, aubio_default_log, NULL);
+  }
+}
+
+aubio_log_function_t
+aubio_log_set_level_function(sint_t level, aubio_log_function_t fun, void * data)
+{
+  aubio_log_function_t old = NULL;
+  if ((level >= 0) && (level < AUBIO_LOG_LAST_LEVEL)) {
+    old = aubio_log_function[level];
+    aubio_log_function[level] = fun;
+    aubio_log_user_data[level] = data;
+  }
+  return old;
+}
+
+void
+aubio_log_set_function(aubio_log_function_t fun, void * data) {
+  uint_t i = 0;
+  for (i = 0; i < AUBIO_LOG_LAST_LEVEL; i++) {
+    aubio_log_set_level_function(i, fun, data);
+  }
+}
--- /dev/null
+++ b/src/utils/log.h
@@ -1,0 +1,98 @@
+/*
+  Copyright (C) 2016 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/>.
+
+*/
+
+#ifndef AUBIO_LOG_H
+#define AUBIO_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+
+  Logging features
+
+  This file specifies ::aubio_log_set_function and
+  ::aubio_log_set_level_function, which let you define one or several custom
+  logging functions to redirect warnings and errors from aubio to your
+  application. The custom function should have the prototype defined in
+  ::aubio_log_function_t.
+
+  After a call to ::aubio_log_set_level_function, ::aubio_log_reset can be used
+  to reset each logging functions to the default ones.
+
+  \example utils/test-log.c
+
+*/
+
+/** list of logging levels */
+enum aubio_log_level {
+  AUBIO_LOG_ERR, /**< critical errors */
+  AUBIO_LOG_WRN, /**< warnings */
+  AUBIO_LOG_MSG, /**< general messages */
+  AUBIO_LOG_DBG, /**< debug messages */
+  AUBIO_LOG_LAST_LEVEL, /**< number of valid levels */
+};
+
+/** Logging function prototype, to be passed to ::aubio_log_set_function
+
+  \param level log level
+  \param message text to log
+  \param data optional closure used by the callback
+
+  See @ref utils/test-log.c for an example of logging function.
+
+ */
+typedef void (*aubio_log_function_t)(sint_t level, const char_t *message, void
+    *data);
+
+/** Set logging function for all levels
+
+  \param fun the function to be used to log, of type ::aubio_log_function_t
+  \param data optional closure to be passed to the function (can be NULL if
+  nothing to pass)
+
+ */
+void aubio_log_set_function(aubio_log_function_t fun, void* data);
+
+/** Set logging function for a given level
+
+  \param level the level for which to set the logging function
+  \param fun the function to be used to log, of type ::aubio_log_function_t
+  \param data optional closure to be passed to the function (can be NULL if
+  nothing to pass)
+
+*/
+aubio_log_function_t aubio_log_set_level_function(sint_t level,
+    aubio_log_function_t fun, void* data);
+
+/** Reset all logging functions to the default one
+
+ After calling this function, the default logging function will be used to
+ print error, warning, normal, and debug messages to `stdout` or `stderr`.
+
+ */
+void aubio_log_reset(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUBIO_LOG_H */
--- a/tests/src/spectral/test-fft.c
+++ b/tests/src/spectral/test-fft.c
@@ -4,7 +4,7 @@
 {
   int return_code = 0;
   uint_t i, n_iters = 100; // number of iterations
-  uint_t win_s = 500; // window size
+  uint_t win_s = 512; // window size
   fvec_t * in = new_fvec (win_s); // input buffer
   cvec_t * fftgrain = new_cvec (win_s); // fft norm and phase
   fvec_t * out = new_fvec (win_s); // output buffer
--- /dev/null
+++ b/tests/src/utils/test-log.c
@@ -1,0 +1,56 @@
+#include <aubio.h>
+#include <stdio.h>
+#include "aubio_priv.h"
+
+const char_t *hdr = "CUSTOM HEADER: ";
+const char_t *hdr2 = "OTHER HEADER: ";
+
+/* an example of logging function that adds a custom header and prints
+ * aubio debug messages on stdout instead of stderr */
+void logging(int level, const char_t *message, void *data) {
+  FILE *out;
+  fprintf(stdout, "using custom logging function\n");
+  if (level == AUBIO_LOG_ERR) {
+    out = stderr;
+  } else {
+    out = stdout;
+  }
+  if ((level >= 0) && (data != NULL)) {
+    fprintf(out, "%s", (const char_t *)data);
+  }
+  fprintf(out, "%s", message);
+}
+
+int main (void)
+{
+  fprintf(stdout, "### testing normal logging\n");
+  AUBIO_ERR("testing normal AUBIO_LOG_ERR\n");
+  AUBIO_WRN("testing normal AUBIO_LOG_WRN\n");
+  AUBIO_MSG("testing normal AUBIO_LOG_MSG\n");
+  AUBIO_DBG("testing normal AUBIO_LOG_DBG\n");
+
+  fprintf(stdout, "### testing with one custom function\n");
+  aubio_log_set_function(logging, (void *)hdr);
+  AUBIO_ERR("testing recustom AUBIO_LOG_ERR\n");
+  AUBIO_WRN("testing recustom AUBIO_LOG_WRN\n");
+  AUBIO_MSG("testing recustom AUBIO_LOG_MSG\n");
+  AUBIO_DBG("testing recustom AUBIO_LOG_DBG\n");
+
+  fprintf(stdout, "### testing resetted logging\n");
+  aubio_log_reset();
+  AUBIO_ERR("testing uncustom AUBIO_LOG_ERR\n");
+  AUBIO_WRN("testing uncustom AUBIO_LOG_WRN\n");
+  AUBIO_MSG("testing uncustom AUBIO_LOG_MSG\n");
+  AUBIO_DBG("testing uncustom AUBIO_LOG_DBG\n");
+
+  fprintf(stdout, "### testing per level customization\n");
+  aubio_log_set_level_function(AUBIO_LOG_ERR, logging, (void *)hdr2);
+  aubio_log_set_level_function(AUBIO_LOG_WRN, logging, NULL);
+  aubio_log_set_level_function(AUBIO_LOG_MSG, logging, (void *)hdr);
+  AUBIO_ERR("testing custom AUBIO_LOG_ERR\n");
+  AUBIO_WRN("testing custom AUBIO_LOG_WRN with data=NULL\n");
+  AUBIO_MSG("testing custom AUBIO_LOG_MSG\n");
+  AUBIO_DBG("testing uncustomized AUBIO_LOG_DBG\n");
+
+  return 0;
+}
--- a/wscript
+++ b/wscript
@@ -113,6 +113,7 @@
     ctx.check(header_name='math.h')
     ctx.check(header_name='string.h')
     ctx.check(header_name='limits.h')
+    ctx.check(header_name='stdarg.h')
     ctx.check(header_name='getopt.h', mandatory = False)
     ctx.check(header_name='unistd.h', mandatory = False)
 
--