shithub: aubio

Download patch

ref: f56f795eefadc3d2f430670f133fa5608e88271a
parent: 65628c43478b66ade07332fa659636e804377e33
parent: e2f1e6d0d5e0a949bd1e4210448b6c597f27a68b
author: Paul Brossier <piem@piem.org>
date: Thu Dec 20 15:31:00 EST 2018

Merge branch 'feature/sink_flac' into feature/autosink

--- a/.travis.yml
+++ b/.travis.yml
@@ -18,7 +18,7 @@
     - python: 3.4
       os: linux
       compiler: gcc
-      env: HAVE_AUBIO_DOUBLE=1 CFLAGS="-O3" WAFOPTS="--enable-fftw3"
+      env: HAVE_AUBIO_DOUBLE=1 CFLAGS="-O3" WAFOPTS="--enable-fftw3 --disable-avcodec"
     - python: 2.7
       os: linux
       compiler: gcc
--- a/src/aubio_priv.h
+++ b/src/aubio_priv.h
@@ -324,6 +324,12 @@
 #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
+
 /* handy shortcuts */
 #define DB2LIN(g) (POW(10.0,(g)*0.05f))
 #define LIN2DB(v) (20.0*LOG10(v))
--- 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_flac.c
+++ b/src/io/sink_flac.c
@@ -264,6 +264,18 @@
   return s->channels;
 }
 
+static void aubio_sink_write_frames(aubio_sink_flac_t *s, uint_t length)
+{
+  // send to encoder
+  if (!FLAC__stream_encoder_process_interleaved(s->encoder,
+        (const FLAC__int32*)s->buffer, length)) {
+    FLAC__StreamEncoderState state =
+      FLAC__stream_encoder_get_state(s->encoder);
+    AUBIO_WRN("sink_flac: error writing to %s (%s)\n",
+        s->path, FLAC__StreamEncoderStateString[state]);
+  }
+}
+
 void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t *write_data,
     uint_t write)
 {
@@ -281,8 +293,7 @@
     }
   }
   // send to encoder
-  FLAC__stream_encoder_process_interleaved(s->encoder,
-      (const FLAC__int32*)s->buffer, length);
+  aubio_sink_write_frames(s, length);
 }
 
 void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t *write_data,
@@ -302,10 +313,9 @@
         s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[c][v]);
       }
     }
-    // send to encoder
-    FLAC__stream_encoder_process_interleaved(s->encoder,
-        (const FLAC__int32*)s->buffer, length);
   }
+  // send to encoder
+  aubio_sink_write_frames(s, length);
 }
 
 uint_t aubio_sink_flac_close (aubio_sink_flac_t *s)
--- a/src/io/sink_vorbis.c
+++ b/src/io/sink_vorbis.c
@@ -36,7 +36,7 @@
 #include <errno.h> // errno
 #include <time.h> // time
 
-#define MAX_SIZE 2048
+#define MAX_SIZE 4096
 
 struct _aubio_sink_vorbis_t {
   FILE *fid;            // file id
@@ -63,6 +63,8 @@
 uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s);
 void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
 
+static uint_t aubio_sink_vorbis_write_page(aubio_sink_vorbis_t *s);
+
 aubio_sink_vorbis_t * new_aubio_sink_vorbis (const char_t *uri,
     uint_t samplerate)
 {
@@ -117,8 +119,10 @@
 
   s->fid = fopen((const char *)s->path, "wb");
   if (!s->fid) {
-    AUBIO_ERR("sink_vorbis: Error opening file %s (%s)\n",
-        s->path, strerror(errno));
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_ERR("sink_vorbis: Error opening file \'%s\' (%s)\n",
+        s->path, errorstr);
     return AUBIO_FAIL;
   }
 
@@ -143,8 +147,6 @@
 
   // write header
   {
-    int ret = 0;
-    size_t wrote;
     ogg_packet header;
     ogg_packet header_comm;
     ogg_packet header_code;
@@ -159,17 +161,8 @@
     // make sure audio data will start on a new page
     while (1)
     {
-      ret = ogg_stream_flush(&s->os, &s->og);
-      if (ret==0) break;
-      wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
-      ret = (wrote == (unsigned)s->og.header_len);
-      wrote = fwrite(s->og.body,   1, s->og.body_len,   s->fid);
-      ret &= (wrote == (unsigned)s->og.body_len);
-      if (ret == 0) {
-        AUBIO_ERR("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
-            s->path, strerror(errno));
-        return AUBIO_FAIL;
-      }
+      if (!ogg_stream_flush(&s->os, &s->og)) break;
+      if (aubio_sink_vorbis_write_page(s)) return AUBIO_FAIL;
     }
   }
 
@@ -211,10 +204,27 @@
   return s->channels;
 }
 
-void aubio_sink_vorbis_write(aubio_sink_vorbis_t *s)
-{
+static
+uint_t aubio_sink_vorbis_write_page(aubio_sink_vorbis_t *s) {
   int result;
   size_t wrote;
+  wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
+  result = (wrote == (unsigned)s->og.header_len);
+  wrote = fwrite(s->og.body, 1, s->og.body_len,     s->fid);
+  result &= (wrote == (unsigned)s->og.body_len);
+  if (result == 0) {
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_ERR("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
+        s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+  return AUBIO_OK;
+}
+
+static
+void aubio_sink_vorbis_write(aubio_sink_vorbis_t *s)
+{
   // pre-analysis
   while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
 
@@ -226,16 +236,8 @@
       ogg_stream_packetin(&s->os, &s->op);
 
       while (1) {
-        result = ogg_stream_pageout (&s->os, &s->og);
-        if (result == 0) break;
-        wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
-        result = (wrote == (unsigned)s->og.header_len);
-        wrote = fwrite(s->og.body, 1, s->og.body_len,     s->fid);
-        result &= (wrote == (unsigned)s->og.body_len);
-        if (result == 0) {
-          AUBIO_WRN("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
-              s->path, strerror(errno));
-        }
+        if (!ogg_stream_pageout (&s->os, &s->og)) break;
+        aubio_sink_vorbis_write_page(s);
         if (ogg_page_eos(&s->og)) break;
       }
     }
@@ -290,7 +292,7 @@
       }
     }
     // tell vorbis how many frames were written
-    vorbis_analysis_wrote(&s->vd, (long)write);
+    vorbis_analysis_wrote(&s->vd, (long)length);
   }
 
   aubio_sink_vorbis_write(s);
@@ -305,8 +307,10 @@
   aubio_sink_vorbis_write(s);
 
   if (s->fid && fclose(s->fid)) {
-    AUBIO_ERR("sink_vorbis: Error closing file %s (%s)\n",
-        s->path, strerror(errno));
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_ERR("sink_vorbis: Error closing file \'%s\' (%s)\n",
+        s->path, errorstr);
     return AUBIO_FAIL;
   }
   s->fid = NULL;
--- a/src/io/sink_wavwrite.c
+++ b/src/io/sink_wavwrite.c
@@ -162,55 +162,67 @@
 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));
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_ERR("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) {
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_WRN("sink_wavwrite: writing header to %s failed, expected %d"
+        " write but got only %d (%s)\n", s->path, 13, written, errorstr);
+    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 +238,24 @@
   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) {
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_WRN("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 +264,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 +281,32 @@
       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) {
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_WRN("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));
+    char errorstr[256];
+    AUBIO_STRERROR(errno, errorstr, sizeof(errorstr));
+    AUBIO_ERR("sink_wavwrite: Error closing file %s (%s)\n", s->path, errorstr);
   }
   s->fid = NULL;
   return AUBIO_OK;
--- 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)
--- a/src/io/source_avcodec.c
+++ b/src/io/source_avcodec.c
@@ -60,6 +60,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)
@@ -488,8 +489,10 @@
   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);
+  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 +502,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 +514,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 +525,13 @@
   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_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);
+  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 +538,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 +550,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;
 }
 
--- 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,8 +170,13 @@
 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->input_channels * length);
 
+  length = MIN(read_samples / s->input_channels, length);
+
   /* where to store de-interleaved data */
   smpl_t *ptr_data;
 #ifdef HAVE_SAMPLERATE
@@ -183,7 +189,7 @@
   }
 
   /* de-interleaving and down-mixing data  */
-  for (j = 0; j < read_samples / input_channels; j++) {
+  for (j = 0; j < 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 +203,9 @@
   }
 #endif /* HAVE_SAMPLERATE */
 
-  *read = (int)FLOOR(s->ratio * read_samples / input_channels + .5);
+  *read = (int)FLOOR(s->ratio * 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,8 +212,15 @@
 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,
+      length * s->input_channels);
 
+  length = MIN(read_samples / s->input_channels, length);
+
   /* where to store de-interleaved data */
   smpl_t **ptr_data;
 #ifdef HAVE_SAMPLERATE
@@ -223,34 +232,12 @@
     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 < 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 +251,9 @@
   }
 #endif /* HAVE_SAMPLERATE */
 
-  *read = (int)FLOOR(s->ratio * read_samples / input_channels + .5);
+  *read = (int)FLOOR(s->ratio * 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,6 +24,7 @@
 
 #include "fvec.h"
 #include "fmat.h"
+#include "ioutils.h"
 #include "source_wavread.h"
 
 #include <errno.h>
@@ -347,13 +348,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 +365,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 +377,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 +387,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 +416,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;
 }
 
--- a/tests/src/io/base-source_custom.h
+++ b/tests/src/io/base-source_custom.h
@@ -93,10 +93,46 @@
   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;
 
   // read again as multiple channels
+  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;
 
--- a/tests/src/io/test-source.c
+++ b/tests/src/io/test-source.c
@@ -89,10 +89,46 @@
   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;
 
   // read again as multiple channels
+  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;