shithub: aubio

Download patch

ref: 439ba7b8d961fdecf963fba9a26736b481c4619b
parent: 5f57ea953c01b88705d8e9d1a3b81d9ec68e6267
parent: f55630c5e9397c94d3f0e683c6eeb2c89fd6d490
author: Paul Brossier <piem@piem.org>
date: Sun Mar 31 21:30:06 EDT 2019

Merge branch 'master' into feature/pitchshift

--- a/.landscape.yml
+++ /dev/null
@@ -1,5 +1,0 @@
-strictness: medium
-test-warnings: true
-python-targets:
-  - 2
-  - 3
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,20 +2,25 @@
 
 matrix:
   include:
-    - python: 3.5
+    - python: 3.6
       os: linux
       compiler: gcc
-    - python: 3.4
+    - python: 3.5
       os: linux
       compiler: gcc
+      env: WAFOPTS="--build-type=debug"
     - python: 2.7
       os: linux
       compiler: gcc
-    - python: 3.5
+    - python: "pypy3.5"
       os: linux
       compiler: gcc
-      env: CFLAGS="-Os" WAFOPTS="--disable-samplerate --disable-sndfile"
-    - python: 3.4
+      env: CFLAGS="-Os" WAFOPTS="--disable-avcodec"
+    - python: 3.6
+      os: linux
+      compiler: gcc
+      env: CFLAGS="-Os" WAFOPTS="--disable-samplerate"
+    - python: 3.5
       os: linux
       compiler: gcc
       env: HAVE_AUBIO_DOUBLE=1 CFLAGS="-O3" WAFOPTS="--enable-fftw3"
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,83 @@
+2018-12-19 Paul Brossier <piem@aubio.org>
+
+	[ Overview ]
+
+	* VERSION: bump to 0.4.9
+	* library: improve stability, fixing potential crashes and memory leaks on
+	invalid arguments; improve library messages and reporting of system errors
+	* tests/: major clean-up, check return codes, increase code coverage
+	* python/tests/: switch to pytest (closes gh-163), check emitted warnings
+	* python/: add pages to manual with brief descriptions of classes
+
+	[ Fixes ]
+
+	* security: improve arguments validation in new_aubio_filterbank (prevent
+	possible null-pointer dereference on invalid n_filters, CVE-2018-19801),
+	new_aubio-tempo (prevent possible buffer overflow, CVE-2018-19800), and
+	new_aubio_onset (prevent null-pointer dereference, CVE-2018-19802). Thanks
+	to Guoxiang Niu (@niugx), from the EaglEye Team for reporting these issues.
+	* tempo: fix delay_ms methods
+	* filterbank: fix aubio_filterbank_get_power (thanks to @romanbsd who
+	also noticed this issue)
+	* dct: creation fail on negative sizes or invalid accelerate radix,
+	fix typo in error and warning messages, prevent possible memory leak
+	* pitch: prevent null pointer dereference in yinfast, comment out unused
+	functions in mcomb and yin, prevent possible leak in specacf
+	* mfcc: always use dct module, strengthen input validation, change
+	get_{scale,power} to return smpl_t
+	* specdesc: improve error message
+	* notes: prevent null pointer dereference
+	* hist: add validation for size argument, prevent possible leak
+	* awhitening: use shortest length available (closes gh-216)
+	* io: add macros to display system errors, add helpers to validate input
+	arguments of source and sink methods, always clean-up after failure
+	* source: validate input sizes to prevent invalid reads
+	* apple_audio: use native format conversions in source and sink, prevent
+	possible apple_audio crash on empty string, get_duration returns 0 on failure
+	* ffmpeg/avcodec: prevent deprecation warnings, read after close, and skipped
+	samples warnings, improve warning messages, only show a warning when
+	swr_convert failed, prevent possible memory leak when closing swr context
+	* wavwrite: copy to all channels if needed, check fseek and fwrite return
+	values, call fflush in open to return failure on full disk-system
+	* source_sndfile: fix reading sizes when resampling, set error message when
+	reading after close
+	* aubio_priv.h: include blas first (see gh-225), add STRERROR macros
+
+	[ Python ]
+
+	* documentation: add pages to manual, add minimal docstrings for fft,
+	digital_filter, and generated objects, improve specdesc documentation
+	* filterbank: add get_norm/power documentation
+	* source: take a copy of the last frame before resizing it, raise an
+	exception when read failed, fix compilation warning
+	* fixes: remove unneeded check convert with PyFloat_FromDouble or
+	PyFloat_FromDouble, check if sink, digital_filter, were created before
+	deleting
+
+	[ Tests ]
+
+	* python/tests/: switch to pytest (slightly slower than nose2 but better at
+	capturing warnings and parametrization), improve coding style and coverage.
+	Tests should now be run with `pytest`.
+	* tests/: Each test program in C must now return 0, otherwise the test will
+	fail. Examples have been modified to run themselves on a test audio file,
+	but can still be run with arguments. Tests for `source` and `sink` have been
+	factorised, and some code cleaning. A python script is used to create a
+	test sound file. Tested on linux, macos, and windows, improvements to
+	test-mfcc (closes gh-219).
+
+	[ Build system ]
+
+	* waf: upgrade to 2.0.14, check the return code of each test program,
+	update rules to build manual and api documentation into build/, check
+	for errno.h
+	* osx: use -Os in scripts/build_apple_frameworks
+	* Makefile: improve coverage reports
+	* appveyor, travis, circleci: switch to pytest, set one travis config to use
+	sndfile only
+	* travis: add py3.6, drop py3.4, use py3.5 to test debug mode
+	* azure: add basic configuration
+
 2018-11-21 Paul Brossier <piem@aubio.org>
 
 	[ Overview ]
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
 
 [![Travis build status](https://travis-ci.org/aubio/aubio.svg?branch=master)](https://travis-ci.org/aubio/aubio "Travis build status")
 [![Appveyor build status](https://img.shields.io/appveyor/ci/piem/aubio/master.svg)](https://ci.appveyor.com/project/piem/aubio "Appveyor build status")
-[![Landscape code health](https://landscape.io/github/aubio/aubio/master/landscape.svg?style=flat)](https://landscape.io/github/aubio/aubio/master "Landscape code health")
 [![Commits since last release](https://img.shields.io/github/commits-since/aubio/aubio/latest.svg)](https://github.com/aubio/aubio "Commits since last release")
 
 [![Documentation](https://readthedocs.org/projects/aubio/badge/?version=latest)](http://aubio.readthedocs.io/en/latest/?badge=latest "Latest documentation")
--- a/VERSION
+++ b/VERSION
@@ -1,6 +1,6 @@
 AUBIO_MAJOR_VERSION=0
-AUBIO_MINOR_VERSION=4
-AUBIO_PATCH_VERSION=9
+AUBIO_MINOR_VERSION=5
+AUBIO_PATCH_VERSION=0
 AUBIO_VERSION_STATUS='~alpha'
 LIBAUBIO_LT_CUR=5
 LIBAUBIO_LT_REV=4
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -3,7 +3,7 @@
 
 - job: linux
   pool:
-    vmImage: 'Ubuntu 16.04'
+    vmImage: 'ubuntu-16.04'
   steps:
   - script: |
       make
@@ -13,7 +13,7 @@
 
 - job: windows
   pool:
-    vmIMage: 'VS2017-Win2016'
+    vmImage: 'vs2017-win2016'
   steps:
   - script: |
       make
@@ -24,7 +24,7 @@
 
 - job: macos
   pool:
-    vmIMage: macOS-10.13
+    vmImage: 'macos-10.13'
   steps:
   - script: |
       brew update
--- a/doc/statuslinks.rst
+++ b/doc/statuslinks.rst
@@ -9,10 +9,6 @@
    :target: https://ci.appveyor.com/project/piem/aubio/
    :alt: Appveyor build status
 
-.. image:: https://landscape.io/github/aubio/aubio/master/landscape.svg?style=flat
-   :target: https://landscape.io/github/aubio/aubio/master
-   :alt: Landscape code health
-
 .. image:: https://readthedocs.org/projects/aubio/badge/?version=latest
    :target: https://aubio.readthedocs.io/en/latest/?badge=latest
    :alt: Documentation status
--- a/examples/utils.c
+++ b/examples/utils.c
@@ -184,7 +184,8 @@
         total_read, blocks, hop_size, source_uri, samplerate);
 
     del_aubio_source (this_source);
-    del_aubio_sink   (this_sink);
+    if (this_sink)
+      del_aubio_sink   (this_sink);
 
   }
 }
--- a/python/demos/demo_wav2midi.py
+++ b/python/demos/demo_wav2midi.py
@@ -63,7 +63,7 @@
         delta = frames2tick(total_frames) - last_time
         if new_note[2] > 0:
             track.append(Message('note_off', note=int(new_note[2]),
-                velocity=127, time=0)
+                velocity=127, time=delta)
                 )
         track.append(Message('note_on',
             note=int(new_note[0]),
--- a/python/ext/py-filter.c
+++ b/python/ext/py-filter.c
@@ -109,7 +109,8 @@
 Py_filter_del (Py_filter * self)
 {
   Py_XDECREF(self->out);
-  del_aubio_filter (self->o);
+  if (self->o)
+    del_aubio_filter (self->o);
   Py_TYPE(self)->tp_free ((PyObject *) self);
 }
 
--- a/python/ext/py-sink.c
+++ b/python/ext/py-sink.c
@@ -150,8 +150,10 @@
 static void
 Py_sink_del (Py_sink *self, PyObject *unused)
 {
-  del_aubio_sink(self->o);
-  free(self->mwrite_data.data);
+  if (self->o) {
+    del_aubio_sink(self->o);
+    free(self->mwrite_data.data);
+  }
   if (self->uri) {
     free(self->uri);
   }
--- a/python/ext/py-source.c
+++ b/python/ext/py-source.c
@@ -436,6 +436,10 @@
   /* compute _do function */
   aubio_source_do (self->o, &(self->c_read_to), &read);
 
+  if (PyErr_Occurred() != NULL) {
+    return NULL;
+  }
+
   outputs = PyTuple_New(2);
   PyTuple_SetItem( outputs, 0, self->read_to );
   PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read));
@@ -457,6 +461,10 @@
   /* compute _do function */
   aubio_source_do_multi (self->o, &(self->c_mread_to), &read);
 
+  if (PyErr_Occurred() != NULL) {
+    return NULL;
+  }
+
   outputs = PyTuple_New(2);
   PyTuple_SetItem( outputs, 0, self->mread_to);
   PyTuple_SetItem( outputs, 1, (PyObject *)PyLong_FromLong(read));
@@ -573,7 +581,10 @@
       return vec;
     } else if (PyLong_AsLong(size) > 0) {
       // short read, return a shorter array
-      PyArrayObject *shortread = (PyArrayObject*)PyTuple_GetItem(done, 0);
+      PyObject *vec = PyTuple_GetItem(done, 0);
+      // take a copy to prevent resizing internal arrays
+      PyArrayObject *shortread = (PyArrayObject*)PyArray_FROM_OTF(vec,
+          NPY_NOTYPE, NPY_ARRAY_ENSURECOPY);
       PyArray_Dims newdims;
       PyObject *reshaped;
       newdims.len = PyArray_NDIM(shortread);
@@ -586,6 +597,7 @@
       }
       reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER);
       Py_DECREF(shortread);
+      Py_DECREF(vec);
       return reshaped;
     } else {
       PyErr_SetNone(PyExc_StopIteration);
--- a/python/lib/moresetuptools.py
+++ b/python/lib/moresetuptools.py
@@ -68,7 +68,7 @@
     # define macros (waf puts them in build/src/config.h)
     for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H',
                          'HAVE_MATH_H', 'HAVE_STRING_H',
-                         'HAVE_C99_VARARGS_MACROS',
+                         'HAVE_ERRNO_H', 'HAVE_C99_VARARGS_MACROS',
                          'HAVE_LIMITS_H', 'HAVE_STDARG_H',
                          'HAVE_MEMCPY_HACKS']:
         ext.define_macros += [(define_macro, 1)]
--- a/python/tests/test_sink.py
+++ b/python/tests/test_sink.py
@@ -3,7 +3,8 @@
 from numpy.testing import TestCase
 from aubio import fvec, source, sink
 from utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path
-from _tools import parametrize, skipTest, assert_raises
+from utils import parse_file_samplerate
+from _tools import parametrize, skipTest, assert_raises, assert_warns
 
 list_of_sounds = list_all_sounds('sounds')
 samplerates = [0, 44100, 8000, 32000]
@@ -60,8 +61,14 @@
 
     @parametrize('hop_size, samplerate, path', all_params)
     def test_read_and_write(self, hop_size, samplerate, path):
+        orig_samplerate = parse_file_samplerate(soundfile)
         try:
-            f = source(path, samplerate, hop_size)
+            if orig_samplerate is not None and orig_samplerate < samplerate:
+                # upsampling should emit a warning
+                with assert_warns(UserWarning):
+                    f = source(soundfile, samplerate, hop_size)
+            else:
+                f = source(soundfile, samplerate, hop_size)
         except RuntimeError as e:
             err_msg = '{:s} (hop_s = {:d}, samplerate = {:d})'
             skipTest(err_msg.format(str(e), hop_size, samplerate))
@@ -78,8 +85,14 @@
 
     @parametrize('hop_size, samplerate, path', all_params)
     def test_read_and_write_multi(self, hop_size, samplerate, path):
+        orig_samplerate = parse_file_samplerate(soundfile)
         try:
-            f = source(path, samplerate, hop_size)
+            if orig_samplerate is not None and orig_samplerate < samplerate:
+                # upsampling should emit a warning
+                with assert_warns(UserWarning):
+                    f = source(soundfile, samplerate, hop_size)
+            else:
+                f = source(soundfile, samplerate, hop_size)
         except RuntimeError as e:
             err_msg = '{:s} (hop_s = {:d}, samplerate = {:d})'
             skipTest(err_msg.format(str(e), hop_size, samplerate))
--- a/python/tests/test_source.py
+++ b/python/tests/test_source.py
@@ -3,9 +3,10 @@
 
 from numpy.testing import TestCase, assert_equal
 from aubio import source
-from utils import list_all_sounds
+from utils import list_all_sounds, parse_file_samplerate
 import unittest
-from _tools import parametrize, assert_raises, assert_equal, skipTest
+from _tools import assert_raises, assert_equal, assert_warns
+from _tools import parametrize, skipTest
 
 list_of_sounds = list_all_sounds('sounds')
 samplerates = [0, 44100, 8000, 32000]
@@ -23,23 +24,38 @@
 
 _debug = False
 
-class Test_aubio_source_test_case(object):
 
-    @parametrize('filename', list_of_sounds)
-    def test_close_file(self, filename):
+class Test_aubio_source_test_case(TestCase):
+
+    def setUp(self):
+        if not default_test_sound:
+            skipTest(no_sounds_msg)
+
+    def test_close_file(self):
         samplerate = 0 # use native samplerate
         hop_size = 256
-        f = source(filename, samplerate, hop_size)
+        f = source(default_test_sound, samplerate, hop_size)
         f.close()
 
-    @parametrize('filename', list_of_sounds)
-    def test_close_file_twice(self, filename):
+    def test_close_file_twice(self):
         samplerate = 0 # use native samplerate
         hop_size = 256
-        f = source(filename, samplerate, hop_size)
+        f = source(default_test_sound, samplerate, hop_size)
         f.close()
         f.close()
 
+    def test_read_after_close(self):
+        samplerate = 0 # use native samplerate
+        hop_size = 256
+        f = source(default_test_sound, samplerate, hop_size)
+        read, frames = f()
+        f.close()
+        with assert_raises(RuntimeError):
+            read, frames = f()
+        with assert_raises(RuntimeError):
+            read, frames = f.do_multi()
+
+
 class Test_aubio_source_read(object):
 
     def read_from_source(self, f):
@@ -60,8 +76,14 @@
 
     @parametrize('hop_size, samplerate, soundfile', all_params)
     def test_samplerate_hopsize(self, hop_size, samplerate, soundfile):
+        orig_samplerate = parse_file_samplerate(soundfile)
         try:
-            f = source(soundfile, samplerate, hop_size)
+            if orig_samplerate is not None and orig_samplerate < samplerate:
+                # upsampling should emit a warning
+                with assert_warns(UserWarning):
+                    f = source(soundfile, samplerate, hop_size)
+            else:
+                f = source(soundfile, samplerate, hop_size)
         except RuntimeError as e:
             err_msg = 'failed opening with hop_s={:d}, samplerate={:d} ({:s})'
             skipTest(err_msg.format(hop_size, samplerate, str(e)))
--- a/python/tests/utils.py
+++ b/python/tests/utils.py
@@ -1,6 +1,7 @@
 #! /usr/bin/env python
 
 import os
+import re
 import glob
 import numpy as np
 from tempfile import mkstemp
@@ -77,3 +78,16 @@
                 if file_path:
                     total_files += 1
     return total_files
+
+def parse_file_samplerate(soundfile):
+    samplerate = None
+    # parse samplerate
+    re_sr = re.compile(r'/([0-9]{4,})Hz_.*')
+    match_samplerate = re_sr.findall(soundfile)
+    if match_samplerate:
+        samplerate = int(match_samplerate[0])
+    else:
+        import warnings
+        warnings.warn(UserWarning("could not parse samplerate for {:s}"
+            .format(soundfile)))
+    return samplerate
--- a/scripts/build_apple_frameworks
+++ b/scripts/build_apple_frameworks
@@ -13,7 +13,7 @@
 # add git abbreviated commit hash
 #VERSION+=+$(git log --pretty=format:"%h" -1)
 
-CFLAGS="-Werror -Ofast"
+CFLAGS="-Werror -Os"
 WAFCONF="--disable-sndfile --disable-avcodec --disable-samplerate --enable-fat" # --disable-memcpy --disable-accelerate"
 
 export VERSION
--- a/scripts/get_waf.sh
+++ b/scripts/get_waf.sh
@@ -3,7 +3,7 @@
 set -e
 #set -x
 
-WAFVERSION=2.0.13
+WAFVERSION=2.0.14
 WAFTARBALL=waf-$WAFVERSION.tar.bz2
 WAFURL=https://waf.io/$WAFTARBALL
 WAFUPSTREAMKEY=https://gitlab.com/ita1024/waf/raw/master/utils/pubkey.asc
--- a/src/aubio_priv.h
+++ b/src/aubio_priv.h
@@ -62,6 +62,10 @@
 #include <string.h>
 #endif
 
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
 #ifdef HAVE_LIMITS_H
 #include <limits.h> // for CHAR_BIT, in C99 standard
 #endif
@@ -70,12 +74,8 @@
 #include <stdarg.h>
 #endif
 
-#if defined(HAVE_ACCELERATE)
-#define HAVE_ATLAS 1
-#define HAVE_BLAS 1
-#include <Accelerate/Accelerate.h>
-#elif defined(HAVE_ATLAS_CBLAS_H)
-#elif defined(HAVE_BLAS)
+#if defined(HAVE_BLAS) // --enable-blas=true
+// check which cblas header we found
 #if defined(HAVE_ATLAS_CBLAS_H)
 #define HAVE_ATLAS 1
 #include <atlas/cblas.h>
@@ -83,11 +83,17 @@
 #include <openblas/cblas.h>
 #elif defined(HAVE_CBLAS_H)
 #include <cblas.h>
+#elif !defined(HAVE_ACCELERATE)
+#error "HAVE_BLAS was defined, but no blas header was found"
+#endif /* end of cblas includes */
 #endif
-#endif
 
-#ifdef HAVE_ACCELERATE
+#if defined(HAVE_ACCELERATE)
+// include accelerate framework after blas
+#define HAVE_ATLAS 1
+#define HAVE_BLAS 1
 #include <Accelerate/Accelerate.h>
+
 #ifndef HAVE_AUBIO_DOUBLE
 #define aubio_vDSP_mmov       vDSP_mmov
 #define aubio_vDSP_vmul       vDSP_vmul
@@ -322,6 +328,24 @@
 /* avoid unresolved symbol with msvc 9 */
 #if defined(_MSC_VER) && (_MSC_VER < 1900)
 #define isnan _isnan
+#endif
+
+#if !defined(_MSC_VER)
+#define AUBIO_STRERROR(errno,buf,len) strerror_r(errno, buf, len)
+#else
+#define AUBIO_STRERROR(errno,buf,len) strerror_s(buf, len, errno)
+#endif
+
+#ifdef HAVE_C99_VARARGS_MACROS
+#define AUBIO_STRERR(...)            \
+    char errorstr[256]; \
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr)); \
+    AUBIO_ERR(__VA_ARGS__)
+#else
+#define AUBIO_STRERR(format, args...)   \
+    char errorstr[256]; \
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr)); \
+    AUBIO_ERR(format, ##args)
 #endif
 
 /* handy shortcuts */
--- a/src/io/ioutils.c
+++ b/src/io/ioutils.c
@@ -19,6 +19,7 @@
 */
 
 #include "aubio_priv.h"
+#include "fmat.h"
 
 uint_t
 aubio_io_validate_samplerate(const char_t *kind, const char_t *path, uint_t samplerate)
@@ -50,6 +51,75 @@
     return AUBIO_FAIL;
   }
   return AUBIO_OK;
+}
+
+uint_t
+aubio_source_validate_input_length(const char_t *kind, const char_t *path,
+    uint_t hop_size, uint_t read_data_length)
+{
+  uint_t length = hop_size;
+  if (hop_size < read_data_length) {
+    AUBIO_WRN("%s: partial read from %s, trying to read %d frames, but"
+        " hop_size is %d\n", kind, path, read_data_length, hop_size);
+  } else if (hop_size > read_data_length) {
+    AUBIO_WRN("%s: partial read from %s, trying to read %d frames into"
+        " a buffer of length %d\n", kind, path, hop_size, read_data_length);
+    length = read_data_length;
+  }
+  return length;
+}
+
+uint_t
+aubio_source_validate_input_channels(const char_t *kind, const char_t *path,
+    uint_t source_channels, uint_t read_data_height)
+{
+  uint_t channels = source_channels;
+  if (read_data_height < source_channels) {
+    AUBIO_WRN("%s: partial read from %s, trying to read %d channels,"
+        " but found output of height %d\n", kind, path, source_channels,
+        read_data_height);
+    channels = read_data_height;
+  } else if (read_data_height > source_channels) {
+    // do not show a warning when trying to read into more channels than
+    // the input source.
+#if 0
+    AUBIO_WRN("%s: partial read from %s, trying to read %d channels,"
+        " but found output of height %d\n", kind, path, source_channels,
+        read_data_height);
+#endif
+    channels = source_channels;
+  }
+  return channels;
+}
+
+void
+aubio_source_pad_output (fvec_t *read_data, uint_t source_read)
+{
+  if (source_read < read_data->length) {
+    AUBIO_MEMSET(read_data->data + source_read, 0,
+        (read_data->length - source_read) * sizeof(smpl_t));
+  }
+}
+
+void
+aubio_source_pad_multi_output (fmat_t *read_data,
+    uint_t source_channels, uint_t source_read) {
+  uint_t i;
+  if (source_read < read_data->length) {
+    for (i = 0; i < read_data->height; i++) {
+      AUBIO_MEMSET(read_data->data[i] + source_read, 0,
+          (read_data->length - source_read) * sizeof(smpl_t));
+    }
+  }
+
+  // destination matrix has more channels than the file
+  // copy channels from the source to extra output channels
+  if (read_data->height > source_channels) {
+    for (i = source_channels; i < read_data->height; i++) {
+      AUBIO_MEMCPY(read_data->data[i], read_data->data[i % source_channels],
+          sizeof(smpl_t) * read_data->length);
+    }
+  }
 }
 
 uint_t
--- a/src/io/ioutils.h
+++ b/src/io/ioutils.h
@@ -53,12 +53,58 @@
 uint_t aubio_io_validate_channels(const char_t *kind, const char_t *path,
     uint_t channels);
 
-/** validate length of input
+/** validate length of source output
 
   \param kind       the object kind to report on
   \param path       the path to report on
+  \param hop_size   number of frames to be read
+  \param read_data_length actual length of input
+
+  \return hop_size or the maximum number of frames that can be written
+*/
+uint_t
+aubio_source_validate_input_length(const char_t *kind, const char_t *path,
+    uint_t hop_size, uint_t read_data_length);
+
+/** validate height of source output
+
+  \param kind       the object kind to report on
+  \param path       the path to report on
+  \param source_channels maximum number of channels that can be written
+  \param read_data_height actual height of input
+
+  \return write_data_height or the maximum number of channels
+*/
+uint_t
+aubio_source_validate_input_channels(const char_t *kind, const char_t *path,
+    uint_t source_channels, uint_t read_data_height);
+
+/** pad end of source output vector with zeroes
+
+  \param read_data   output vector to pad
+  \param source_read number of frames read
+
+*/
+void
+aubio_source_pad_output (fvec_t *read_data, uint_t source_read);
+
+/** pad end of source output matrix with zeroes
+
+  \param read_data   output matrix to pad
+  \param source_channels number of channels in the source
+  \param source_read number of frames read
+
+*/
+void
+aubio_source_pad_multi_output (fmat_t *read_data, uint_t source_channels,
+        uint_t source_read);
+
+/** validate length of sink input
+
+  \param kind       the object kind to report on
+  \param path       the path to report on
   \param max_size   maximum number of frames that can be written
-  \param write_data_length actual length of input vector/matrix
+  \param write_data_length actual length of input
   \param write number of samples asked
 
   \return write or the maximum number of frames that can be written
@@ -67,11 +113,11 @@
 aubio_sink_validate_input_length(const char_t *kind, const char_t *path,
     uint_t max_size, uint_t write_data_length, uint_t write);
 
-/** validate height of input
+/** validate height of sink input
 
   \param kind       the object kind to report on
   \param path       the path to report on
-  \param max_size   maximum number of channels that can be written
+  \param sink_channels maximum number of channels that can be written
   \param write_data_height actual height of input matrix
 
   \return write_data_height or the maximum number of channels
--- a/src/io/sink.c
+++ b/src/io/sink.c
@@ -135,8 +135,8 @@
 }
 
 void del_aubio_sink(aubio_sink_t * s) {
-  AUBIO_ASSERT(s);
-  if (s->s_del && s->sink)
+  //AUBIO_ASSERT(s);
+  if (s && s->s_del && s->sink)
     s->s_del((void *)s->sink);
   AUBIO_FREE(s);
 }
--- a/src/io/sink_wavwrite.c
+++ b/src/io/sink_wavwrite.c
@@ -28,8 +28,6 @@
 #include "io/sink_wavwrite.h"
 #include "io/ioutils.h"
 
-#include <errno.h>
-
 #define MAX_SIZE 4096
 
 #define FLOAT_TO_SHORT(x) (short)(x * 32768)
@@ -162,55 +160,65 @@
 uint_t aubio_sink_wavwrite_open(aubio_sink_wavwrite_t *s) {
   unsigned char buf[5];
   uint_t byterate, blockalign;
+  size_t written = 0;
 
   /* open output file */
   s->fid = fopen((const char *)s->path, "wb");
   if (!s->fid) {
-    AUBIO_ERR("sink_wavwrite: could not open %s (%s)\n", s->path, strerror(errno));
+    AUBIO_STRERR("sink_wavwrite: could not open %s (%s)\n", s->path, errorstr);
     goto beach;
   }
 
   // ChunkID
-  fwrite("RIFF", 4, 1, s->fid);
+  written += fwrite("RIFF", 4, 1, s->fid);
 
   // ChunkSize (0 for now, actual size will be written in _close)
-  fwrite(write_little_endian(0, buf, 4), 4, 1, s->fid);
+  written += fwrite(write_little_endian(0, buf, 4), 4, 1, s->fid);
 
   // Format
-  fwrite("WAVE", 4, 1, s->fid);
+  written += fwrite("WAVE", 4, 1, s->fid);
 
   // Subchunk1ID
-  fwrite("fmt ", 4, 1, s->fid);
+  written += fwrite("fmt ", 4, 1, s->fid);
 
   // Subchunk1Size
-  fwrite(write_little_endian(16, buf, 4), 4, 1, s->fid);
+  written += fwrite(write_little_endian(16, buf, 4), 4, 1, s->fid);
 
   // AudioFormat
-  fwrite(write_little_endian(1, buf, 2), 2, 1, s->fid);
+  written += fwrite(write_little_endian(1, buf, 2), 2, 1, s->fid);
 
   // NumChannels
-  fwrite(write_little_endian(s->channels, buf, 2), 2, 1, s->fid);
+  written += fwrite(write_little_endian(s->channels, buf, 2), 2, 1, s->fid);
 
   // SampleRate
-  fwrite(write_little_endian(s->samplerate, buf, 4), 4, 1, s->fid);
+  written += fwrite(write_little_endian(s->samplerate, buf, 4), 4, 1, s->fid);
 
   // ByteRate
   byterate = s->samplerate * s->channels * s->bitspersample / 8;
-  fwrite(write_little_endian(byterate, buf, 4), 4, 1, s->fid);
+  written += fwrite(write_little_endian(byterate, buf, 4), 4, 1, s->fid);
 
   // BlockAlign
   blockalign = s->channels * s->bitspersample / 8;
-  fwrite(write_little_endian(blockalign, buf, 2), 2, 1, s->fid);
+  written += fwrite(write_little_endian(blockalign, buf, 2), 2, 1, s->fid);
 
   // BitsPerSample
-  fwrite(write_little_endian(s->bitspersample, buf, 2), 2, 1, s->fid);
+  written += fwrite(write_little_endian(s->bitspersample, buf, 2), 2, 1, s->fid);
 
   // Subchunk2ID
-  fwrite("data", 4, 1, s->fid);
+  written += fwrite("data", 4, 1, s->fid);
 
   // Subchunk1Size (0 for now, actual size will be written in _close)
-  fwrite(write_little_endian(0, buf, 4), 4, 1, s->fid);
+  written += fwrite(write_little_endian(0, buf, 4), 4, 1, s->fid);
 
+  // fwrite(*, *, 1, s->fid) was called 13 times, check success
+  if (written != 13 || fflush(s->fid)) {
+    AUBIO_STRERR("sink_wavwrite: writing header to %s failed"
+        " (wrote %d/%d, %s)\n", s->path, written, 13, errorstr);
+    fclose(s->fid);
+    s->fid = NULL;
+    return AUBIO_FAIL;
+  }
+
   s->scratch_size = s->max_size * s->channels;
   /* allocate data for de/interleaving reallocated when needed. */
   if (s->scratch_size >= MAX_SIZE * AUBIO_MAX_CHANNELS) {
@@ -226,9 +234,22 @@
   return AUBIO_FAIL;
 }
 
+static
+void aubio_sink_wavwrite_write_frames(aubio_sink_wavwrite_t *s, uint_t write)
+{
+  uint_t written_frames = 0;
 
+  written_frames = fwrite(s->scratch_data, 2 * s->channels, write, s->fid);
+
+  if (written_frames != write) {
+    AUBIO_STRERR("sink_wavwrite: trying to write %d frames to %s, but only %d"
+        " could be written (%s)\n", write, s->path, written_frames, errorstr);
+  }
+  s->total_frames_written += written_frames;
+}
+
 void aubio_sink_wavwrite_do(aubio_sink_wavwrite_t *s, fvec_t * write_data, uint_t write){
-  uint_t c = 0, i = 0, written_frames = 0;
+  uint_t c = 0, i = 0;
   uint_t length = aubio_sink_validate_input_length("sink_wavwrite", s->path,
       s->max_size, write_data->length, write);
 
@@ -237,18 +258,12 @@
       s->scratch_data[i * s->channels + c] = HTOLES(FLOAT_TO_SHORT(write_data->data[i]));
     }
   }
-  written_frames = fwrite(s->scratch_data, 2, length * s->channels, s->fid);
 
-  if (written_frames != write) {
-    AUBIO_WRN("sink_wavwrite: trying to write %d frames to %s, "
-        "but only %d could be written\n", write, s->path, written_frames);
-  }
-  s->total_frames_written += written_frames;
-  return;
+  aubio_sink_wavwrite_write_frames(s, length);
 }
 
 void aubio_sink_wavwrite_do_multi(aubio_sink_wavwrite_t *s, fmat_t * write_data, uint_t write){
-  uint_t c = 0, i = 0, written_frames = 0;
+  uint_t c = 0, i = 0;
 
   uint_t channels = aubio_sink_validate_input_channels("sink_wavwrite", s->path,
       s->channels, write_data->height);
@@ -260,29 +275,28 @@
       s->scratch_data[i * s->channels + c] = HTOLES(FLOAT_TO_SHORT(write_data->data[c][i]));
     }
   }
-  written_frames = fwrite(s->scratch_data, 2, length * s->channels, s->fid);
 
-  if (written_frames != write * s->channels) {
-    AUBIO_WRN("sink_wavwrite: trying to write %d frames to %s, "
-        "but only %d could be written\n", write, s->path, written_frames / s->channels);
-  }
-  s->total_frames_written += written_frames;
-  return;
+  aubio_sink_wavwrite_write_frames(s, length);
 }
 
 uint_t aubio_sink_wavwrite_close(aubio_sink_wavwrite_t * s) {
   uint_t data_size = s->total_frames_written * s->bitspersample * s->channels / 8;
   unsigned char buf[5];
+  size_t written = 0, err = 0;
   if (!s->fid) return AUBIO_FAIL;
   // ChunkSize
-  fseek(s->fid, 4, SEEK_SET);
-  fwrite(write_little_endian(data_size + 36, buf, 4), 4, 1, s->fid);
+  err += fseek(s->fid, 4, SEEK_SET);
+  written += fwrite(write_little_endian(data_size + 36, buf, 4), 4, 1, s->fid);
   // Subchunk2Size
-  fseek(s->fid, 40, SEEK_SET);
-  fwrite(write_little_endian(data_size, buf, 4), 4, 1, s->fid);
+  err += fseek(s->fid, 40, SEEK_SET);
+  written += fwrite(write_little_endian(data_size, buf, 4), 4, 1, s->fid);
+  if (written != 2 || err != 0) {
+    AUBIO_STRERR("sink_wavwrite: updating header of %s failed, expected %d"
+        " write but got only %d (%s)\n", s->path, 2, written, errorstr);
+  }
   // close file
   if (fclose(s->fid)) {
-    AUBIO_ERR("sink_wavwrite: Error closing file %s (%s)\n", s->path, strerror(errno));
+    AUBIO_STRERR("sink_wavwrite: Error closing file %s (%s)\n", s->path, errorstr);
   }
   s->fid = NULL;
   return AUBIO_OK;
--- a/src/io/source.c
+++ b/src/io/source.c
@@ -138,8 +138,8 @@
 }
 
 void del_aubio_source(aubio_source_t * s) {
-  AUBIO_ASSERT(s);
-  if (s->s_del && s->source)
+  //AUBIO_ASSERT(s);
+  if (s && s->s_del && s->source)
     s->s_del((void *)s->source);
   AUBIO_FREE(s);
 }
--- a/src/io/source_apple_audio.c
+++ b/src/io/source_apple_audio.c
@@ -24,6 +24,7 @@
 
 #include "fvec.h"
 #include "fmat.h"
+#include "ioutils.h"
 #include "io/source_apple_audio.h"
 
 // ExtAudioFileRef, AudioStreamBasicDescription, AudioBufferList, ...
@@ -209,9 +210,13 @@
     uint_t * read) {
   uint_t c, v;
   UInt32 loadedPackets = aubio_source_apple_audio_read_frame(s);
+  uint_t length = aubio_source_validate_input_length("source_apple_audio",
+      s->path, s->block_size, read_to->length);
   smpl_t *data = (smpl_t*)s->bufferList.mBuffers[0].mData;
 
-  for (v = 0; v < loadedPackets; v++) {
+  length = MIN(loadedPackets, length);
+
+  for (v = 0; v < length; v++) {
     read_to->data[v] = 0.;
     for (c = 0; c < s->channels; c++) {
       read_to->data[v] += data[ v * s->channels + c];
@@ -219,46 +224,31 @@
     read_to->data[v] /= (smpl_t)s->channels;
   }
   // short read, fill with zeros
-  if (loadedPackets < s->block_size) {
-    for (v = loadedPackets; v < s->block_size; v++) {
-      read_to->data[v] = 0.;
-    }
-  }
+  aubio_source_pad_output(read_to, length);
 
-  *read = (uint_t)loadedPackets;
-  return;
+  *read = (uint_t)length;
 }
 
 void aubio_source_apple_audio_do_multi(aubio_source_apple_audio_t *s, fmat_t * read_to, uint_t * read) {
   uint_t c, v;
+  uint_t length = aubio_source_validate_input_length("source_apple_audio",
+      s->path, s->block_size, read_to->length);
+  uint_t channels = aubio_source_validate_input_channels("source_apple_audio",
+      s->path, s->channels, read_to->height);
   UInt32 loadedPackets = aubio_source_apple_audio_read_frame(s);
   smpl_t *data = (smpl_t*)s->bufferList.mBuffers[0].mData;
 
-  for (v = 0; v < loadedPackets; v++) {
-    for (c = 0; c < read_to->height; c++) {
+  length = MIN(loadedPackets, length);
+
+  for (v = 0; v < length; v++) {
+    for (c = 0; c < channels; c++) {
       read_to->data[c][v] = data[ v * s->channels + c];
     }
   }
-  // if read_data has more channels than the file
-  if (read_to->height > s->channels) {
-    // copy last channel to all additional channels
-    for (v = 0; v < loadedPackets; v++) {
-      for (c = s->channels; c < read_to->height; c++) {
-        read_to->data[c][v] = data[ v * s->channels + (s->channels - 1)];
-      }
-    }
-  }
-  // short read, fill with zeros
-  if (loadedPackets < s->block_size) {
-    for (v = loadedPackets; v < s->block_size; v++) {
-      for (c = 0; c < read_to->height; c++) {
-        read_to->data[c][v] = 0.;
-      }
-    }
-  }
 
-  *read = (uint_t)loadedPackets;
-  return;
+  aubio_source_pad_multi_output(read_to, s->channels, (uint_t)length);
+
+  *read = (uint_t)length;
 }
 
 uint_t aubio_source_apple_audio_close (aubio_source_apple_audio_t *s)
@@ -354,7 +344,7 @@
     AUBIO_ERROR("source_apple_audio: Failed getting %s duration, "
         "error in ExtAudioFileGetProperty (%s)\n", s->path,
         getPrintableOSStatusError(errorstr, err));
-    return err;
+    return 0;
   }
   return (uint_t)fileLengthFrames;
 }
--- a/src/io/source_avcodec.c
+++ b/src/io/source_avcodec.c
@@ -30,7 +30,6 @@
 #include <libavresample/avresample.h>
 #endif
 #include <libavutil/opt.h>
-#include <stdlib.h>
 
 // determine whether we use libavformat from ffmpeg or from libav
 #define FFMPEG_LIBAVFORMAT (LIBAVFORMAT_VERSION_MICRO > 99 )
@@ -60,6 +59,7 @@
 #include "aubio_priv.h"
 #include "fvec.h"
 #include "fmat.h"
+#include "ioutils.h"
 #include "source_avcodec.h"
 
 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56, 56, 0)
@@ -119,9 +119,9 @@
 aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path,
     uint_t samplerate, uint_t hop_size) {
   aubio_source_avcodec_t * s = AUBIO_NEW(aubio_source_avcodec_t);
-  AVFormatContext *avFormatCtx = s->avFormatCtx;
-  AVCodecContext *avCodecCtx = s->avCodecCtx;
-  AVFrame *avFrame = s->avFrame;
+  AVFormatContext *avFormatCtx = NULL;
+  AVCodecContext *avCodecCtx = NULL;
+  AVFrame *avFrame = NULL;
   sint_t selected_stream = -1;
 #if FF_API_LAVF_AVCTX
   AVCodecParameters *codecpar;
@@ -463,9 +463,9 @@
       (uint8_t **)&output, max_out_samples,
       (const uint8_t **)avFrame->data, in_samples);
 #endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
-  if (out_samples <= 0) {
-    AUBIO_WRN("source_avcodec: no sample found while converting frame (%s)\n",
-        s->path);
+  if (out_samples < 0) {
+    AUBIO_WRN("source_avcodec: error while resampling %s (%d)\n",
+        s->path, out_samples);
     goto beach;
   }
 
@@ -472,14 +472,6 @@
   *read_samples = out_samples;
 
 beach:
-  s->avFormatCtx = avFormatCtx;
-  s->avCodecCtx = avCodecCtx;
-  s->avFrame = avFrame;
-#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
-  s->avr = avr;
-#endif /* HAVE_AVRESAMPLE || HAVE_SWRESAMPLE */
-  s->output = output;
-
   av_packet_unref(&avPacket);
 }
 
@@ -488,8 +480,16 @@
   uint_t i, j;
   uint_t end = 0;
   uint_t total_wrote = 0;
-  while (total_wrote < s->hop_size) {
-    end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
+  uint_t length = aubio_source_validate_input_length("source_avcodec", s->path,
+      s->hop_size, read_data->length);
+  if (!s->avr || !s->avFormatCtx || !s->avCodecCtx) {
+    AUBIO_ERR("source_avcodec: could not read from %s (file was closed)\n",
+        s->path);
+    *read= 0;
+    return;
+  }
+  while (total_wrote < length) {
+    end = MIN(s->read_samples - s->read_index, length - total_wrote);
     for (i = 0; i < end; i++) {
       read_data->data[i + total_wrote] = 0.;
       for (j = 0; j < s->input_channels; j++) {
@@ -499,7 +499,7 @@
       read_data->data[i + total_wrote] *= 1./s->input_channels;
     }
     total_wrote += end;
-    if (total_wrote < s->hop_size) {
+    if (total_wrote < length) {
       uint_t avcodec_read = 0;
       aubio_source_avcodec_readframe(s, &avcodec_read);
       s->read_samples = avcodec_read;
@@ -511,11 +511,9 @@
       s->read_index += end;
     }
   }
-  if (total_wrote < s->hop_size) {
-    for (i = total_wrote; i < s->hop_size; i++) {
-      read_data->data[i] = 0.;
-    }
-  }
+
+  aubio_source_pad_output(read_data, total_wrote);
+
   *read = total_wrote;
 }
 
@@ -524,9 +522,19 @@
   uint_t i,j;
   uint_t end = 0;
   uint_t total_wrote = 0;
-  while (total_wrote < s->hop_size) {
-    end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
-    for (j = 0; j < read_data->height; j++) {
+  uint_t length = aubio_source_validate_input_length("source_avcodec", s->path,
+      s->hop_size, read_data->length);
+  uint_t channels = aubio_source_validate_input_channels("source_avcodec",
+      s->path, s->input_channels, read_data->height);
+  if (!s->avr || !s->avFormatCtx || !s->avCodecCtx) {
+    AUBIO_ERR("source_avcodec: could not read from %s (file was closed)\n",
+        s->path);
+    *read= 0;
+    return;
+  }
+  while (total_wrote < length) {
+    end = MIN(s->read_samples - s->read_index, length - total_wrote);
+    for (j = 0; j < channels; j++) {
       for (i = 0; i < end; i++) {
         read_data->data[j][i + total_wrote] =
           s->output[(i + s->read_index) * s->input_channels + j];
@@ -533,7 +541,7 @@
       }
     }
     total_wrote += end;
-    if (total_wrote < s->hop_size) {
+    if (total_wrote < length) {
       uint_t avcodec_read = 0;
       aubio_source_avcodec_readframe(s, &avcodec_read);
       s->read_samples = avcodec_read;
@@ -545,13 +553,9 @@
       s->read_index += end;
     }
   }
-  if (total_wrote < s->hop_size) {
-    for (j = 0; j < read_data->height; j++) {
-      for (i = total_wrote; i < s->hop_size; i++) {
-        read_data->data[j][i] = 0.;
-      }
-    }
-  }
+
+  aubio_source_pad_multi_output(read_data, s->input_channels, total_wrote);
+
   *read = total_wrote;
 }
 
@@ -615,10 +619,11 @@
   if (s->avr != NULL) {
 #ifdef HAVE_AVRESAMPLE
     avresample_close( s->avr );
+    av_free ( s->avr );
 #elif defined(HAVE_SWRESAMPLE)
     swr_close ( s->avr );
+    swr_free ( &s->avr );
 #endif
-    av_free ( s->avr );
   }
   s->avr = NULL;
   if (s->avCodecCtx != NULL) {
--- a/src/io/source_sndfile.c
+++ b/src/io/source_sndfile.c
@@ -26,6 +26,7 @@
 
 #include "fvec.h"
 #include "fmat.h"
+#include "ioutils.h"
 #include "source_sndfile.h"
 
 #include "temporal/resampler.h"
@@ -169,10 +170,22 @@
 void aubio_source_sndfile_do(aubio_source_sndfile_t * s, fvec_t * read_data, uint_t * read){
   uint_t i,j, input_channels = s->input_channels;
   /* read from file into scratch_data */
-  sf_count_t read_samples = aubio_sf_read_smpl (s->handle, s->scratch_data, s->scratch_size);
+  uint_t length = aubio_source_validate_input_length("source_sndfile", s->path,
+      s->hop_size, read_data->length);
+  sf_count_t read_samples = aubio_sf_read_smpl (s->handle, s->scratch_data,
+      s->scratch_size);
+  uint_t read_length = read_samples / s->input_channels;
 
   /* where to store de-interleaved data */
   smpl_t *ptr_data;
+
+  if (!s->handle) {
+    AUBIO_ERR("source_sndfile: could not read from %s (file was closed)\n",
+        s->path);
+    *read = 0;
+    return;
+  }
+
 #ifdef HAVE_SAMPLERATE
   if (s->ratio != 1) {
     ptr_data = s->input_data->data;
@@ -179,11 +192,12 @@
   } else
 #endif /* HAVE_SAMPLERATE */
   {
+    read_length = MIN(length, read_length);
     ptr_data = read_data->data;
   }
 
   /* de-interleaving and down-mixing data  */
-  for (j = 0; j < read_samples / input_channels; j++) {
+  for (j = 0; j < read_length; j++) {
     ptr_data[j] = 0;
     for (i = 0; i < input_channels; i++) {
       ptr_data[j] += s->scratch_data[input_channels*j+i];
@@ -197,13 +211,9 @@
   }
 #endif /* HAVE_SAMPLERATE */
 
-  *read = (int)FLOOR(s->ratio * read_samples / input_channels + .5);
+  *read = MIN(length, (uint_t)FLOOR(s->ratio * read_length + .5));
 
-  if (*read < s->hop_size) {
-    for (j = *read; j < s->hop_size; j++) {
-      read_data->data[j] = 0;
-    }
-  }
+  aubio_source_pad_output (read_data, *read);
 
 }
 
@@ -210,10 +220,24 @@
 void aubio_source_sndfile_do_multi(aubio_source_sndfile_t * s, fmat_t * read_data, uint_t * read){
   uint_t i,j, input_channels = s->input_channels;
   /* do actual reading */
-  sf_count_t read_samples = aubio_sf_read_smpl (s->handle, s->scratch_data, s->scratch_size);
+  uint_t length = aubio_source_validate_input_length("source_sndfile", s->path,
+      s->hop_size, read_data->length);
+  uint_t channels = aubio_source_validate_input_channels("source_sndfile",
+      s->path, s->input_channels, read_data->height);
+  sf_count_t read_samples = aubio_sf_read_smpl (s->handle, s->scratch_data,
+      s->scratch_size);
+  uint_t read_length = read_samples / s->input_channels;
 
   /* where to store de-interleaved data */
   smpl_t **ptr_data;
+
+  if (!s->handle) {
+    AUBIO_ERR("source_sndfile: could not read from %s (file was closed)\n",
+        s->path);
+    *read = 0;
+    return;
+  }
+
 #ifdef HAVE_SAMPLERATE
   if (s->ratio != 1) {
     ptr_data = s->input_mat->data;
@@ -220,37 +244,16 @@
   } else
 #endif /* HAVE_SAMPLERATE */
   {
+    read_length = MIN(read_length, length);
     ptr_data = read_data->data;
   }
 
-  if (read_data->height < input_channels) {
-    // destination matrix has less channels than the file; copy only first
-    // channels of the file, de-interleaving data
-    for (j = 0; j < read_samples / input_channels; j++) {
-      for (i = 0; i < read_data->height; i++) {
-        ptr_data[i][j] = s->scratch_data[j * input_channels + i];
-      }
+  for (j = 0; j < read_length; j++) {
+    for (i = 0; i < channels; i++) {
+      ptr_data[i][j] = s->scratch_data[j * input_channels + i];
     }
-  } else {
-    // destination matrix has as many or more channels than the file; copy each
-    // channel from the file to the destination matrix, de-interleaving data
-    for (j = 0; j < read_samples / input_channels; j++) {
-      for (i = 0; i < input_channels; i++) {
-        ptr_data[i][j] = s->scratch_data[j * input_channels + i];
-      }
-    }
   }
 
-  if (read_data->height > input_channels) {
-    // destination matrix has more channels than the file; copy last channel
-    // of the file to each additional channels, de-interleaving data
-    for (j = 0; j < read_samples / input_channels; j++) {
-      for (i = input_channels; i < read_data->height; i++) {
-        ptr_data[i][j] = s->scratch_data[j * input_channels + (input_channels - 1)];
-      }
-    }
-  }
-
 #ifdef HAVE_SAMPLERATE
   if (s->resamplers) {
     for (i = 0; i < input_channels; i++) {
@@ -264,16 +267,9 @@
   }
 #endif /* HAVE_SAMPLERATE */
 
-  *read = (int)FLOOR(s->ratio * read_samples / input_channels + .5);
+  *read = MIN(length, (uint_t)FLOOR(s->ratio * read_length + .5));
 
-  if (*read < s->hop_size) {
-    for (i = 0; i < read_data->height; i++) {
-      for (j = *read; j < s->hop_size; j++) {
-        read_data->data[i][j] = 0.;
-      }
-    }
-  }
-
+  aubio_source_pad_multi_output(read_data, input_channels, *read);
 }
 
 uint_t aubio_source_sndfile_get_samplerate(aubio_source_sndfile_t * s) {
--- a/src/io/source_wavread.c
+++ b/src/io/source_wavread.c
@@ -24,13 +24,12 @@
 
 #include "fvec.h"
 #include "fmat.h"
+#include "ioutils.h"
 #include "source_wavread.h"
 
-#include <errno.h>
-
 #define AUBIO_WAVREAD_BUFSIZE 1024
 
-#define SHORT_TO_FLOAT(x) (smpl_t)(x * 3.0517578125e-05)
+//#define SHORT_TO_FLOAT(x) (smpl_t)(x * 3.0517578125e-05)
 
 struct _aubio_source_wavread_t {
   uint_t hop_size;
@@ -99,7 +98,7 @@
 
   s->fid = fopen((const char *)path, "rb");
   if (!s->fid) {
-    AUBIO_ERR("source_wavread: Failed opening %s (System error: %s)\n", s->path, strerror(errno));
+    AUBIO_STRERR("source_wavread: Failed opening %s (%s)\n", s->path, errorstr);
     goto beach;
   }
 
@@ -132,8 +131,8 @@
     buf[4] = '\0';
     bytes_junk += read_little_endian(buf, 4);
     if (fseek(s->fid, bytes_read + bytes_junk, SEEK_SET) != 0) {
-      AUBIO_ERR("source_wavread: Failed opening %s (could not seek past JUNK Chunk: %s)\n",
-          s->path, strerror(errno));
+      AUBIO_STRERR("source_wavread: Failed opening %s (could not seek past JUNK Chunk: %s)\n",
+          s->path, errorstr);
       goto beach;
     }
     bytes_read += bytes_junk;
@@ -260,8 +259,8 @@
     buf[4] = '\0';
     bytes_junk += read_little_endian(buf, 4);
     if (fseek(s->fid, bytes_read + bytes_junk, SEEK_SET) != 0) {
-      AUBIO_ERR("source_wavread: could not seek past unknown chunk in %s (%s)\n",
-          s->path, strerror(errno));
+      AUBIO_STRERR("source_wavread: could not seek past unknown chunk in %s (%s)\n",
+          s->path, errorstr);
       goto beach;
     }
     bytes_read += bytes_junk;
@@ -347,13 +346,15 @@
   uint_t i, j;
   uint_t end = 0;
   uint_t total_wrote = 0;
+  uint_t length = aubio_source_validate_input_length("source_wavread", s->path,
+      s->hop_size, read_data->length);
   if (s->fid == NULL) {
     AUBIO_ERR("source_wavread: could not read from %s (file not opened)\n",
         s->path);
     return;
   }
-  while (total_wrote < s->hop_size) {
-    end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
+  while (total_wrote < length) {
+    end = MIN(s->read_samples - s->read_index, length - total_wrote);
     for (i = 0; i < end; i++) {
       read_data->data[i + total_wrote] = 0;
       for (j = 0; j < s->input_channels; j++ ) {
@@ -362,7 +363,7 @@
       read_data->data[i + total_wrote] /= (smpl_t)(s->input_channels);
     }
     total_wrote += end;
-    if (total_wrote < s->hop_size) {
+    if (total_wrote < length) {
       uint_t wavread_read = 0;
       aubio_source_wavread_readframe(s, &wavread_read);
       s->read_samples = wavread_read;
@@ -374,11 +375,9 @@
       s->read_index += end;
     }
   }
-  if (total_wrote < s->hop_size) {
-    for (i = end; i < s->hop_size; i++) {
-      read_data->data[i] = 0.;
-    }
-  }
+
+  aubio_source_pad_output (read_data, total_wrote);
+
   *read = total_wrote;
 }
 
@@ -386,20 +385,24 @@
   uint_t i,j;
   uint_t end = 0;
   uint_t total_wrote = 0;
+  uint_t length = aubio_source_validate_input_length("source_wavread", s->path,
+      s->hop_size, read_data->length);
+  uint_t channels = aubio_source_validate_input_channels("source_wavread",
+      s->path, s->input_channels, read_data->height);
   if (s->fid == NULL) {
     AUBIO_ERR("source_wavread: could not read from %s (file not opened)\n",
         s->path);
     return;
   }
-  while (total_wrote < s->hop_size) {
-    end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
-    for (j = 0; j < read_data->height; j++) {
+  while (total_wrote < length) {
+    end = MIN(s->read_samples - s->read_index, length - total_wrote);
+    for (j = 0; j < channels; j++) {
       for (i = 0; i < end; i++) {
         read_data->data[j][i + total_wrote] = s->output->data[j][i];
       }
     }
     total_wrote += end;
-    if (total_wrote < s->hop_size) {
+    if (total_wrote < length) {
       uint_t wavread_read = 0;
       aubio_source_wavread_readframe(s, &wavread_read);
       s->read_samples = wavread_read;
@@ -411,13 +414,9 @@
       s->read_index += end;
     }
   }
-  if (total_wrote < s->hop_size) {
-    for (j = 0; j < read_data->height; j++) {
-      for (i = end; i < s->hop_size; i++) {
-        read_data->data[j][i] = 0.;
-      }
-    }
-  }
+
+  aubio_source_pad_multi_output(read_data, s->input_channels, total_wrote);
+
   *read = total_wrote;
 }
 
@@ -441,7 +440,7 @@
   }
   ret = fseek(s->fid, s->seek_start + pos * s->blockalign, SEEK_SET);
   if (ret != 0) {
-    AUBIO_ERR("source_wavread: could not seek %s at %d (%s)\n", s->path, pos, strerror(errno));
+    AUBIO_STRERR("source_wavread: could not seek %s at %d (%s)\n", s->path, pos, errorstr);
     return AUBIO_FAIL;
   }
   // reset some values
@@ -462,7 +461,7 @@
     return AUBIO_OK;
   }
   if (fclose(s->fid)) {
-    AUBIO_ERR("source_wavread: could not close %s (%s)\n", s->path, strerror(errno));
+    AUBIO_STRERR("source_wavread: could not close %s (%s)\n", s->path, errorstr);
     return AUBIO_FAIL;
   }
   s->fid = NULL;
--- a/tests/src/io/base-sink_custom.h
+++ b/tests/src/io/base-sink_custom.h
@@ -151,6 +151,9 @@
   // delete temp file
   close_temp_sink(sink_path, fd);
 
+  // shouldn't crash on null (bypassed, only check del_aubio_sink)
+  // del_aubio_sink_custom(NULL);
+
   return run_on_default_source_and_sink(base_main);
 }
 
--- a/tests/src/io/base-source_custom.h
+++ b/tests/src/io/base-source_custom.h
@@ -93,6 +93,18 @@
   aubio_source_custom_do(s, vec, &read);
   if (read != hop_size) return 1;
 
+  // read again in undersized vector
+  del_fvec(vec);
+  vec = new_fvec(hop_size - 1);
+  aubio_source_custom_do(s, vec, &read);
+  if (read != hop_size - 1) return 1;
+
+  // read again in oversized vector
+  del_fvec(vec);
+  vec = new_fvec(hop_size + 1);
+  aubio_source_custom_do(s, vec, &read);
+  if (read != hop_size) return 1;
+
   // seek to 0
   if(aubio_source_custom_seek(s, 0)) return 1;
 
@@ -100,14 +112,49 @@
   aubio_source_custom_do_multi(s, mat, &read);
   if (read != hop_size) return 1;
 
+  // read again as multiple channels in an undersized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels - 1, hop_size);
+  aubio_source_custom_do_multi(s, mat, &read);
+  if (read != hop_size) return 1;
+
+  // read again as multiple channels in an undersized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels, hop_size - 1);
+  aubio_source_custom_do_multi(s, mat, &read);
+  if (read != hop_size - 1) return 1;
+
+  // read again as multiple channels in an oversized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels + 1, hop_size);
+  aubio_source_custom_do_multi(s, mat, &read);
+  if (read != hop_size) return 1;
+
+  // read again as multiple channels in an oversized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels, hop_size + 1);
+  aubio_source_custom_do_multi(s, mat, &read);
+  if (read != hop_size) return 1;
+
   // close the file (optional)
   aubio_source_custom_close(s);
   // test closing the file a second time
   aubio_source_custom_close(s);
 
+  // reading after close fails
+  del_fvec(vec);
+  vec = new_fvec(hop_size);
+  aubio_source_custom_do(s, vec, &read);
+  del_fmat(mat);
+  mat = new_fmat(channels, hop_size);
+  aubio_source_custom_do_multi(s, mat, &read);
+
   del_aubio_source_custom(s);
   del_fmat(mat);
   del_fvec(vec);
+
+  // shouldn't crash on null (bypassed, only check del_aubio_source)
+  // del_aubio_source_custom(NULL);
 
   return run_on_default_source(base_main);
 }
--- a/tests/src/io/test-sink.c
+++ b/tests/src/io/test-sink.c
@@ -147,5 +147,8 @@
   // delete temp file
   close_temp_sink(sink_path, fd);
 
+  // shouldn't crash on null
+  del_aubio_sink(NULL);
+
   return run_on_default_source_and_sink(main);
 }
--- a/tests/src/io/test-source.c
+++ b/tests/src/io/test-source.c
@@ -89,6 +89,18 @@
   aubio_source_do(s, vec, &read);
   if (read != hop_size) return 1;
 
+  // read again in undersized vector
+  del_fvec(vec);
+  vec = new_fvec(hop_size - 1);
+  aubio_source_do(s, vec, &read);
+  if (read != hop_size - 1) return 1;
+
+  // read again in oversized vector
+  del_fvec(vec);
+  vec = new_fvec(hop_size + 1);
+  aubio_source_do(s, vec, &read);
+  if (read != hop_size) return 1;
+
   // seek to 0
   if(aubio_source_seek(s, 0)) return 1;
 
@@ -96,14 +108,49 @@
   aubio_source_do_multi(s, mat, &read);
   if (read != hop_size) return 1;
 
+  // read again as multiple channels in an undersized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels - 1, hop_size);
+  aubio_source_do_multi(s, mat, &read);
+  if (read != hop_size) return 1;
+
+  // read again as multiple channels in an undersized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels, hop_size - 1);
+  aubio_source_do_multi(s, mat, &read);
+  if (read != hop_size - 1) return 1;
+
+  // read again as multiple channels in an oversized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels + 1, hop_size);
+  aubio_source_do_multi(s, mat, &read);
+  if (read != hop_size) return 1;
+
+  // read again as multiple channels in an oversized matrix
+  del_fmat(mat);
+  mat = new_fmat(channels, hop_size + 1);
+  aubio_source_do_multi(s, mat, &read);
+  if (read != hop_size) return 1;
+
   // close the file (optional)
   aubio_source_close(s);
   // test closing the file a second time
   aubio_source_close(s);
 
+  // reading after close fails
+  del_fvec(vec);
+  vec = new_fvec(hop_size);
+  aubio_source_do(s, vec, &read);
+  del_fmat(mat);
+  mat = new_fmat(channels, hop_size);
+  aubio_source_do_multi(s, mat, &read);
+
   del_aubio_source(s);
   del_fmat(mat);
   del_fvec(vec);
+
+  // shouldn't crash on null
+  del_aubio_source(NULL);
 
   return run_on_default_source(main);
 }
--- a/tests/src/spectral/test-mfcc.c
+++ b/tests/src/spectral/test-mfcc.c
@@ -33,9 +33,9 @@
   aubio_pvoc_t *pv = 0;
   aubio_mfcc_t *mfcc = 0;
 
-  fvec_t *in = new_fvec (win_s); // input buffer
-  cvec_t *fftgrain = new_cvec (win_s); // input buffer
-  fvec_t *out = new_fvec (n_coeffs); // output coefficients
+  fvec_t *in = new_fvec (hop_s);       // phase vocoder input
+  cvec_t *fftgrain = new_cvec (win_s); // pvoc output / mfcc input
+  fvec_t *out = new_fvec (n_coeffs);   // mfcc output
 
   if (!in || !fftgrain || !out) { err = 1; goto failure; }
 
--- a/tests/src/test-cvec.c
+++ b/tests/src/test-cvec.c
@@ -3,46 +3,75 @@
 
 int main (void)
 {
-  uint_t i, window_size = 16; // window size
-  cvec_t * complex_vector = new_cvec (window_size); // input buffer
-  uint_t rand_times = 4;
+  uint_t i, window_size = 16;
+  cvec_t * complex_vector = new_cvec(window_size);
+  cvec_t * other_cvector = new_cvec(window_size);
 
-  utils_init_random();
+  assert(cvec_norm_get_data(complex_vector) == complex_vector->norm);
+  assert(cvec_phas_get_data(complex_vector) == complex_vector->phas);
+  assert(complex_vector->length == window_size / 2 + 1);
 
-  while (rand_times -- ) {
-    // fill with random phas and norm
-    for ( i = 0; i < complex_vector->length; i++ ) {
-      complex_vector->norm[i] = ( 2. / RAND_MAX * random() - 1. );
-      complex_vector->phas[i] = ( 2. / RAND_MAX * random() - 1. ) * M_PI;
-    }
-    // print the vector
-    cvec_print(complex_vector);
+  // all elements are initialized to 0
+  for ( i = 0; i < complex_vector->length; i++ ) {
+    assert( complex_vector->norm[i] == 0. );
+    assert( complex_vector->phas[i] == 0. );
   }
 
-  // set all vector elements to `0`
-  cvec_norm_zeros(complex_vector);
+  cvec_norm_set_sample(complex_vector, 2., 1);
+  assert(cvec_norm_get_sample(complex_vector, 1));
+
+  cvec_phas_set_sample(complex_vector, 2., 1);
+  assert(cvec_phas_get_sample(complex_vector, 1));
+
+  cvec_print(complex_vector);
+
+  // set all norm and phas elements to 0
+  cvec_zeros(complex_vector);
   for ( i = 0; i < complex_vector->length; i++ ) {
     assert( complex_vector->norm[i] == 0. );
-    // assert( complex_vector->phas[i] == 0 );
+    assert( complex_vector->phas[i] == 0. );
   }
-  cvec_print(complex_vector);
 
-  // set all vector elements to `1`
+  // set all norm elements to 1
   cvec_norm_ones(complex_vector);
   for ( i = 0; i < complex_vector->length; i++ ) {
     assert( complex_vector->norm[i] == 1. );
-    // assert( complex_vector->phas[i] == 0 );
   }
-  cvec_print(complex_vector);
 
-  cvec_zeros(complex_vector);
-  cvec_phas_zeros(complex_vector);
+  // set all norm elements to 0
   cvec_norm_zeros(complex_vector);
-  cvec_norm_ones(complex_vector);
+  for ( i = 0; i < complex_vector->length; i++ ) {
+    assert( complex_vector->norm[i] == 0. );
+  }
+
+  // set all phas elements to 1
   cvec_phas_ones(complex_vector);
+  for ( i = 0; i < complex_vector->length; i++ ) {
+    assert( complex_vector->phas[i] == 1. );
+  }
+
+  // set all phas elements to 0
+  cvec_phas_zeros(complex_vector);
+  for ( i = 0; i < complex_vector->length; i++ ) {
+    assert( complex_vector->phas[i] == 0. );
+  }
+
+  cvec_copy(complex_vector, other_cvector);
+  // copy to self
   cvec_copy(complex_vector, complex_vector);
+  // copy to a different size fails
+  del_cvec(other_cvector);
+  other_cvector = new_cvec(window_size + 2);
+  cvec_copy(complex_vector, other_cvector);
 
-  // destroy it
-  del_cvec(complex_vector);
+  if (complex_vector)
+    del_cvec(complex_vector);
+  if (other_cvector)
+    del_cvec(other_cvector);
+
+  // wrong parameters
+  assert(new_cvec(-1) == NULL);
+  assert(new_cvec(0) == NULL);
+
   return 0;
 }
--- a/tests/src/test-fmat.c
+++ b/tests/src/test-fmat.c
@@ -4,27 +4,93 @@
 // create a new matrix and fill it with i * 1. + j * .1, where i is the row,
 // and j the column.
 
-int main (void)
+void assert_fmat_all_equal(fmat_t *mat, smpl_t scalar)
 {
-  uint_t height = 3, length = 9, i, j;
-  // create fmat_t object
-  fmat_t * mat = new_fmat (height, length);
+  uint_t i, j;
   for ( i = 0; i < mat->height; i++ ) {
     for ( j = 0; j < mat->length; j++ ) {
+      assert(mat->data[i][j] == scalar);
+    }
+  }
+}
+
+int main (void)
+{
+  uint_t i, j;
+  uint_t height = 3, length = 9;
+
+  // create fmat_t object
+  fmat_t * mat = new_fmat(height, length);
+  fmat_t * other_mat = new_fmat(height, length);
+
+  assert(mat);
+  assert(other_mat);
+
+  assert(mat->length == length);
+  assert(mat->height == height);
+
+  for (i = 0; i < mat->height; i++) {
+    for (j = 0; j < mat->length; j++) {
       // all elements are already initialized to 0.
       assert(mat->data[i][j] == 0);
       // setting element of row i, column j
-      mat->data[i][j] = i * 1. + j *.1;
+      mat->data[i][j] = i * 10. + j;
     }
   }
+
+  // print out matrix
+  fmat_print(mat);
+
+  // helpers
+  fmat_rev(mat);
+  fmat_print(mat);
+  for (i = 0; i < mat->height; i++) {
+    for (j = 0; j < mat->length; j++) {
+      assert(mat->data[i][j] == i * 10. + mat->length - 1. - j);
+    }
+  }
+
+  fmat_set_sample(mat, 3, 1, 1);
+  assert(fmat_get_sample(mat, 1, 1) == 3.);
+
+  fmat_ones(mat);
+  assert_fmat_all_equal(mat, 1.);
+
+  fmat_set(other_mat, .5);
+  assert_fmat_all_equal(other_mat, .5);
+
+  fmat_weight(mat, other_mat);
+  assert_fmat_all_equal(mat, .5);
+
   fvec_t channel_onstack;
   fvec_t *channel = &channel_onstack;
   fmat_get_channel(mat, 1, channel);
-  fvec_print (channel);
-  // print out matrix
-  fmat_print(mat);
-  // destroy it
-  del_fmat(mat);
+  assert(channel->data == mat->data[1]);
+
+  // copy of the same size
+  fmat_copy(mat, other_mat);
+  del_fmat(other_mat);
+
+  // copy to undersized
+  other_mat = new_fmat(height - 1, length);
+  fmat_copy(mat, other_mat);
+  del_fmat(other_mat);
+
+  // copy from undersized
+  other_mat = new_fmat(height, length + 1);
+  fmat_copy(mat, other_mat);
+
+  // wrong parameters
+  assert(new_fmat(-1, length) == NULL);
+  assert(new_fmat(height, -1) == NULL);
+
+  // methods for wrappers with opaque structure
+  assert (fmat_get_channel_data(mat, 0) == mat->data[0]);
+  assert (fmat_get_data(mat) == mat->data);
+
+  if (mat)
+    del_fmat(mat);
+  if (other_mat)
+    del_fmat(other_mat);
   return 0;
 }
-
--- a/tests/src/test-fvec.c
+++ b/tests/src/test-fvec.c
@@ -1,13 +1,27 @@
 #include "aubio.h"
 #include "utils_tests.h"
 
+void assert_fvec_all_equal(fvec_t *vec, smpl_t scalar)
+{
+  uint_t i;
+  for (i = 0; i < vec->length; i++) {
+    assert(vec->data[i] == scalar);
+  }
+}
+
 int main (void)
 {
-  uint_t vec_size = 10, i;
-  fvec_t * vec = new_fvec (vec_size);
+  uint_t length = 10;
+  uint_t i;
 
+  fvec_t * vec = new_fvec (length);
+  fvec_t * other_vec = new_fvec (length);
+
+  assert (vec);
+  assert (other_vec);
+
   // vec->length matches requested size
-  assert(vec->length == vec_size);
+  assert(vec->length == length);
 
   // all elements are initialized to `0.`
   for ( i = 0; i < vec->length; i++ ) {
@@ -14,20 +28,14 @@
     assert(vec->data[i] == 0.);
   }
 
-  // all elements can be set to `0.`
-  fvec_zeros(vec);
-  for ( i = 0; i < vec->length; i++ ) {
-    assert(vec->data[i] == 0.);
-  }
-  fvec_print(vec);
-
   // all elements can be set to `1.`
   fvec_ones(vec);
-  for ( i = 0; i < vec->length; i++ ) {
-    assert(vec->data[i] == 1.);
-  }
-  fvec_print(vec);
+  assert_fvec_all_equal(vec, 1.);
 
+  // all elements can be set to `0.`
+  fvec_zeros(vec);
+  assert_fvec_all_equal(vec, 0.);
+
   // each element can be accessed directly
   for ( i = 0; i < vec->length; i++ ) {
     vec->data[i] = i;
@@ -35,9 +43,31 @@
   }
   fvec_print(vec);
 
-  // now destroys the vector
-  del_fvec(vec);
+  fvec_set_sample(vec, 3, 2);
+  assert(fvec_get_sample(vec, 2) == 3);
 
+  assert(fvec_get_data(vec) == vec->data);
+
+  // wrong parameters
+  assert(new_fvec(-1) == NULL);
+
+  // copy to an identical size works
+  fvec_copy(vec, other_vec);
+  del_fvec(other_vec);
+
+  // copy to a different size fail
+  other_vec = new_fvec(length + 1);
+  fvec_copy(vec, other_vec);
+  del_fvec(other_vec);
+
+  // copy to a different size fail
+  other_vec = new_fvec(length - 1);
+  fvec_copy(vec, other_vec);
+
+  // now destroys the vector
+  if (vec)
+    del_fvec(vec);
+  if (other_vec)
+    del_fvec(other_vec);
   return 0;
 }
-
--- a/tests/src/test-lvec.c
+++ b/tests/src/test-lvec.c
@@ -1,18 +1,47 @@
 #include "aubio.h"
 #include "utils_tests.h"
 
+void assert_lvec_all_equal(lvec_t *vec, lsmp_t scalar)
+{
+  uint_t i;
+  for (i = 0; i < vec->length; i++) {
+    assert(vec->data[i] == scalar);
+  }
+}
+
 int main (void)
 {
-  uint_t win_s = 32; // window size
-  lvec_t * sp = new_lvec (win_s); // input buffer
-  lvec_set_sample (sp, 2./3., 0);
-  PRINT_MSG(AUBIO_LSMP_FMT "\n", lvec_get_sample (sp, 0));
-  lvec_print (sp);
-  lvec_ones (sp);
-  lvec_print (sp);
-  lvec_set_all (sp, 3./5.);
-  lvec_print (sp);
-  del_lvec(sp);
+  uint_t length = 32; // window size
+
+  lvec_t * vec = new_lvec (length); // input buffer
+
+  assert(vec);
+
+  assert(vec->length == length);
+
+  lvec_set_sample (vec, 3., 0);
+  assert(lvec_get_sample(vec, 0) == 3.);
+
+  assert(lvec_get_data(vec) == vec->data);
+
+  lvec_print (vec);
+  // note AUBIO_LSMP_FMT can be used to print lsmp_t
+  PRINT_MSG(AUBIO_LSMP_FMT "\n", lvec_get_sample (vec, 0));
+
+  lvec_set_all (vec, 2.);
+  assert_lvec_all_equal(vec, 2.);
+
+  lvec_ones (vec);
+  assert_lvec_all_equal(vec, 1.);
+
+  lvec_zeros (vec);
+  assert_lvec_all_equal(vec, 0.);
+
+  del_lvec(vec);
+
+  // wrong parameters
+  assert(new_lvec(0) == NULL);
+  assert(new_lvec(-1) == NULL);
+
   return 0;
 }
-
--- a/tests/src/test-mathutils-window.c
+++ b/tests/src/test-mathutils-window.c
@@ -7,8 +7,8 @@
   uint_t n_length = 4, n_types = 10, i, t;
   uint_t lengths[4] = { 8, 10, 15, 16 };
   char *method = "default";
-  char *window_types[10] = { "default",
-    "rectangle", "hamming", "hanning", "hanningz",
+  char *window_types[11] = { "default",
+    "ones", "rectangle", "hamming", "hanning", "hanningz",
     "blackman", "blackman_harris", "gaussian", "welch", "parzen"};
 
   for ( t = 0; t < n_types; t ++ ) {
@@ -26,6 +26,10 @@
       del_fvec(window);
     }
   }
+
+  assert (new_aubio_window("parzen", -1) == NULL);
+  assert (new_aubio_window(NULL, length) == NULL);
+  assert (new_aubio_window("\0", length) == NULL);
   return 0;
 }
 
--- /dev/null
+++ b/tests/src/test-vecutils.c
@@ -1,0 +1,65 @@
+#include "aubio.h"
+#include "utils_tests.h"
+
+void assert_fvec_all_almost_equal(fvec_t *vec, smpl_t scalar, smpl_t err)
+{
+  uint_t i;
+  for (i = 0; i < vec->length; i++) {
+    assert( fabs(vec->data[i] - scalar) < (smpl_t)err );
+  }
+}
+
+int main (void)
+{
+  uint_t length = 10;
+
+  fvec_t * vec = new_fvec(length);
+
+  fvec_set_all(vec, 2);
+  fvec_exp(vec);
+  assert_fvec_all_almost_equal(vec, exp(2), 1e-10);
+
+  fvec_set_all(vec, 0);
+  fvec_cos(vec);
+  assert_fvec_all_almost_equal(vec, 1., 1e-10);
+
+  fvec_set_all(vec, 0);
+  fvec_sin(vec);
+  assert_fvec_all_almost_equal(vec, 0., 1e-10);
+
+  fvec_set_all(vec, -1);
+  fvec_abs(vec);
+  assert_fvec_all_almost_equal(vec, 1., 1e-10);
+
+  fvec_set_all(vec, 4);
+  fvec_sqrt(vec);
+  assert_fvec_all_almost_equal(vec, 2., 1e-10);
+
+  fvec_set_all(vec, 10.);
+  fvec_log10(vec);
+  assert_fvec_all_almost_equal(vec, 1., 1e-10);
+
+  fvec_set_all(vec, 1.);
+  fvec_log(vec);
+  assert_fvec_all_almost_equal(vec, 0., 1e-10);
+
+  fvec_set_all(vec, 1.6);
+  fvec_floor(vec);
+  assert_fvec_all_almost_equal(vec, 1., 1e-10);
+
+  fvec_set_all(vec, 1.6);
+  fvec_ceil(vec);
+  assert_fvec_all_almost_equal(vec, 2., 1e-10);
+
+  fvec_set_all(vec, 1.6);
+  fvec_round(vec);
+  assert_fvec_all_almost_equal(vec, 2., 1e-10);
+
+  fvec_set_all(vec, 2);
+  fvec_pow(vec, 3);
+  assert_fvec_all_almost_equal(vec, 8., 1e-10);
+
+  if (vec)
+    del_fvec(vec);
+  return 0;
+}
--- a/wscript
+++ b/wscript
@@ -142,6 +142,7 @@
     ctx.check(header_name='stdio.h')
     ctx.check(header_name='math.h')
     ctx.check(header_name='string.h')
+    ctx.check(header_name='errno.h')
     ctx.check(header_name='limits.h')
     ctx.check(header_name='stdarg.h')
     ctx.check(header_name='getopt.h', mandatory = False)
@@ -638,7 +639,7 @@
     ctx.excl += ' **/.pytest_cache'
     ctx.excl += ' **/.cache'
     ctx.excl += ' **/**.zip **/**.tar.bz2'
-    ctx.excl += ' **.tar.bz2'
+    ctx.excl += ' **.tar.bz2**'
     ctx.excl += ' **/doc/full/* **/doc/web/*'
     ctx.excl += ' **/doc/full.cfg'
     ctx.excl += ' **/python/*.db'
@@ -650,7 +651,6 @@
     ctx.excl += ' **/dist*'
     ctx.excl += ' **/.DS_Store'
     ctx.excl += ' **/.travis.yml'
-    ctx.excl += ' **/.landscape.yml'
     ctx.excl += ' **/.appveyor.yml'
     ctx.excl += ' **/.circleci/*'
     ctx.excl += ' **/azure-pipelines.yml'