Index: src/io/sink.c
===================================================================
--- src/io/sink.c	(revision d1decd52d29b621d07bf45ed2b79dcb20ce414d1)
+++ src/io/sink.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -54,6 +54,84 @@
 };
 
+extern uint_t aubio_str_path_has_extension(const char_t *filename,
+    const char_t *pattern);
+
+#ifdef HAVE_VORBISENC
+typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
+extern aubio_sink_vorbis_t * new_aubio_sink_vorbis(const char_t *uri,
+    uint_t samplerate);
+extern void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_close(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels);
+extern uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate);
+extern uint_t aubio_sink_vorbis_get_channels(aubio_sink_vorbis_t *s);
+extern uint_t aubio_sink_vorbis_get_samplerate(aubio_sink_vorbis_t *s);
+extern void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t*
+    write_data, uint_t write);
+extern void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s, fmat_t*
+    write_data, uint_t write);
+#endif /* HAVE_VORBISENC */
+
+#ifdef HAVE_FLAC
+typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
+extern aubio_sink_flac_t * new_aubio_sink_flac(const char_t *uri,
+    uint_t samplerate);
+extern void del_aubio_sink_flac (aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_close(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels);
+extern uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate);
+extern uint_t aubio_sink_flac_get_channels(aubio_sink_flac_t *s);
+extern uint_t aubio_sink_flac_get_samplerate(aubio_sink_flac_t *s);
+extern void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t*
+    write_data, uint_t write);
+extern void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t*
+    write_data, uint_t write);
+#endif /* HAVE_FLAC */
+
 aubio_sink_t * new_aubio_sink(const char_t * uri, uint_t samplerate) {
   aubio_sink_t * s = AUBIO_NEW(aubio_sink_t);
+
+#ifdef HAVE_VORBISENC
+  // check if this uri could be for us
+  if (aubio_str_path_has_extension(uri, "ogg")) {
+    s->sink = (void *)new_aubio_sink_vorbis(uri, samplerate);
+    if (s->sink) {
+      s->s_do = (aubio_sink_do_t)(aubio_sink_vorbis_do);
+      s->s_do_multi = (aubio_sink_do_multi_t)(aubio_sink_vorbis_do_multi);
+      s->s_preset_samplerate = (aubio_sink_preset_samplerate_t)(aubio_sink_vorbis_preset_samplerate);
+      s->s_preset_channels = (aubio_sink_preset_channels_t)(aubio_sink_vorbis_preset_channels);
+      s->s_get_samplerate = (aubio_sink_get_samplerate_t)(aubio_sink_vorbis_get_samplerate);
+      s->s_get_channels = (aubio_sink_get_channels_t)(aubio_sink_vorbis_get_channels);
+      s->s_close = (aubio_sink_close_t)(aubio_sink_vorbis_close);
+      s->s_del = (del_aubio_sink_t)(del_aubio_sink_vorbis);
+      return s;
+    }
+  }
+#endif /* HAVE_VORBISENC */
+
+#ifdef HAVE_FLAC
+  // check if this uri could be for us
+  if (aubio_str_path_has_extension(uri, "flac")) {
+    s->sink = (void *)new_aubio_sink_flac(uri, samplerate);
+    if (s->sink) {
+      s->s_do = (aubio_sink_do_t)(aubio_sink_flac_do);
+      s->s_do_multi = (aubio_sink_do_multi_t)(aubio_sink_flac_do_multi);
+      s->s_preset_samplerate = (aubio_sink_preset_samplerate_t)(aubio_sink_flac_preset_samplerate);
+      s->s_preset_channels = (aubio_sink_preset_channels_t)(aubio_sink_flac_preset_channels);
+      s->s_get_samplerate = (aubio_sink_get_samplerate_t)(aubio_sink_flac_get_samplerate);
+      s->s_get_channels = (aubio_sink_get_channels_t)(aubio_sink_flac_get_channels);
+      s->s_close = (aubio_sink_close_t)(aubio_sink_flac_close);
+      s->s_del = (del_aubio_sink_t)(del_aubio_sink_flac);
+      return s;
+    }
+  }
+#endif /* HAVE_FLAC */
+
 #ifdef HAVE_SINK_APPLE_AUDIO
   s->sink = (void *)new_aubio_sink_apple_audio(uri, samplerate);
@@ -100,5 +178,7 @@
 #if !defined(HAVE_WAVWRITE) && \
   !defined(HAVE_SNDFILE) && \
-  !defined(HAVE_SINK_APPLE_AUDIO)
+  !defined(HAVE_SINK_APPLE_AUDIO) && \
+  !defined(HAVE_VORBISENC) && \
+  !defined(HAVE_FLAC)
   AUBIO_ERROR("sink: failed creating '%s' at %dHz (no sink built-in)\n", uri, samplerate);
 #endif
Index: src/io/sink_apple_audio.c
===================================================================
--- src/io/sink_apple_audio.c	(revision d1decd52d29b621d07bf45ed2b79dcb20ce414d1)
+++ src/io/sink_apple_audio.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -39,4 +39,8 @@
 uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s);
 
+uint_t aubio_str_extension_matches(const char_t *ext,
+    const char_t *pattern);
+const char_t *aubio_str_get_extension(const char_t *filename);
+
 #define MAX_SIZE 4096 // the maximum number of frames that can be written at a time
 
@@ -53,4 +57,5 @@
   ExtAudioFileRef audioFile;
   bool async;
+  AudioFileTypeID fileType;
 };
 
@@ -71,4 +76,6 @@
   s->channels = 0;
 
+  aubio_sink_apple_audio_preset_format(s, aubio_str_get_extension(uri));
+
   // zero samplerate given. do not open yet
   if ((sint_t)samplerate == 0) {
@@ -121,4 +128,72 @@
 }
 
+uint_t aubio_sink_apple_audio_preset_format(aubio_sink_apple_audio_t *s,
+    const char_t *fmt)
+{
+  if (aubio_str_extension_matches(fmt, "wav")) {
+    s->fileType = kAudioFileWAVEType;
+  } else if (aubio_str_extension_matches(fmt, "m4a")
+      || aubio_str_extension_matches(fmt, "mp4") ) {
+    // use alac for "mp4" and "m4a"
+    s->fileType = kAudioFileM4AType;
+  } else if (aubio_str_extension_matches(fmt, "aac") ) {
+    // only use lossy codec for "aac"
+    s->fileType = kAudioFileMPEG4Type;
+  } else if (aubio_str_extension_matches(fmt, "aiff") ) {
+    // only use lossy codec for "aac"
+    s->fileType = kAudioFileAIFFType;
+  } else {
+    s->fileType = kAudioFileWAVEType;
+    if (fmt && strnlen(fmt, PATH_MAX)) {
+      AUBIO_WRN("sink_apple_audio: could not guess format for %s,"
+         " using default (wav)\n", s->path);
+      return AUBIO_FAIL;
+    }
+  }
+  return AUBIO_OK;
+}
+
+static void aubio_sink_apple_audio_set_client_format(aubio_sink_apple_audio_t* s,
+    AudioStreamBasicDescription *clientFormat)
+{
+  memset(clientFormat, 0, sizeof(AudioStreamBasicDescription));
+  // always set samplerate and channels first
+  clientFormat->mSampleRate       = (Float64)(s->samplerate);
+  clientFormat->mChannelsPerFrame = s->channels;
+
+  switch (s->fileType) {
+    case kAudioFileM4AType:
+      clientFormat->mFormatID         = kAudioFormatAppleLossless;
+      break;
+    case kAudioFileMPEG4Type:
+      clientFormat->mFormatID         = kAudioFormatMPEG4AAC;
+      clientFormat->mFormatFlags      = kMPEG4Object_AAC_Main;
+      clientFormat->mFormatFlags     |= kAppleLosslessFormatFlag_16BitSourceData;
+      clientFormat->mFramesPerPacket  = 1024;
+      break;
+    case kAudioFileWAVEType:
+      clientFormat->mFormatID         = kAudioFormatLinearPCM;
+      clientFormat->mFormatFlags      = kAudioFormatFlagIsSignedInteger;
+      clientFormat->mFormatFlags     |= kAudioFormatFlagIsPacked;
+      clientFormat->mBitsPerChannel   = sizeof(short) * 8;
+      clientFormat->mFramesPerPacket  = 1;
+      clientFormat->mBytesPerFrame    = clientFormat->mBitsPerChannel * clientFormat->mChannelsPerFrame / 8;
+      clientFormat->mBytesPerPacket   = clientFormat->mFramesPerPacket * clientFormat->mBytesPerFrame;
+      break;
+    case kAudioFileAIFFType:
+      clientFormat->mFormatID         = kAudioFormatLinearPCM;
+      clientFormat->mFormatFlags      = kAudioFormatFlagIsSignedInteger;
+      clientFormat->mFormatFlags     |= kAudioFormatFlagIsPacked;
+      clientFormat->mFormatFlags     |= kAudioFormatFlagIsBigEndian;
+      clientFormat->mBitsPerChannel   = sizeof(short) * 8;
+      clientFormat->mFramesPerPacket  = 1;
+      clientFormat->mBytesPerFrame    = clientFormat->mBitsPerChannel * clientFormat->mChannelsPerFrame / 8;
+      clientFormat->mBytesPerPacket   = clientFormat->mFramesPerPacket * clientFormat->mBytesPerFrame;
+      break;
+    default:
+      break;
+  }
+}
+
 uint_t aubio_sink_apple_audio_get_samplerate(const aubio_sink_apple_audio_t *s)
 {
@@ -135,17 +210,4 @@
   if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
 
-  AudioStreamBasicDescription clientFormat;
-  memset(&clientFormat, 0, sizeof(AudioStreamBasicDescription));
-  clientFormat.mFormatID         = kAudioFormatLinearPCM;
-  clientFormat.mSampleRate       = (Float64)(s->samplerate);
-  clientFormat.mFormatFlags      = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
-  clientFormat.mChannelsPerFrame = s->channels;
-  clientFormat.mBitsPerChannel   = sizeof(short) * 8;
-  clientFormat.mFramesPerPacket  = 1;
-  clientFormat.mBytesPerFrame    = clientFormat.mBitsPerChannel * clientFormat.mChannelsPerFrame / 8;
-  clientFormat.mBytesPerPacket   = clientFormat.mFramesPerPacket * clientFormat.mBytesPerFrame;
-  clientFormat.mReserved         = 0;
-
-  AudioFileTypeID fileType = kAudioFileWAVEType;
   CFURLRef fileURL = createURLFromPath(s->path);
   bool overwrite = true;
@@ -162,6 +224,11 @@
   inputFormat.mBytesPerFrame    = inputFormat.mBitsPerChannel * inputFormat.mChannelsPerFrame / 8;
   inputFormat.mBytesPerPacket   = inputFormat.mFramesPerPacket * inputFormat.mBytesPerFrame;
+
+  // get the in-file format
+  AudioStreamBasicDescription clientFormat;
+  aubio_sink_apple_audio_set_client_format(s, &clientFormat);
+
   OSStatus err = noErr;
-  err = ExtAudioFileCreateWithURL(fileURL, fileType, &clientFormat, NULL,
+  err = ExtAudioFileCreateWithURL(fileURL, s->fileType, &clientFormat, NULL,
      overwrite ? kAudioFileFlags_EraseFile : 0, &s->audioFile);
   CFRelease(fileURL);
@@ -173,4 +240,18 @@
     goto beach;
   }
+
+#if defined(kAppleSoftwareAudioCodecManufacturer)
+  // on iOS, set software based encoding before setting clientDataFormat
+  UInt32 codecManf = kAppleSoftwareAudioCodecManufacturer;
+  err = ExtAudioFileSetProperty(s->audioFile,
+      kExtAudioFileProperty_CodecManufacturer,
+      sizeof(UInt32), &codecManf);
+  if (err) {
+    char_t errorstr[20];
+    AUBIO_ERR("sink_apple_audio: error when trying to set sofware codec on %s "
+        "(%s)\n", s->path, getPrintableOSStatusError(errorstr, err));
+    goto beach;
+  }
+#endif
 
   err = ExtAudioFileSetProperty(s->audioFile,
Index: src/io/sink_apple_audio.h
===================================================================
--- src/io/sink_apple_audio.h	(revision d1decd52d29b621d07bf45ed2b79dcb20ce414d1)
+++ src/io/sink_apple_audio.h	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -97,4 +97,27 @@
 /**
 
+  preset sink format
+
+  \param s sink, created with ::new_aubio_sink_apple_audio
+  \param fmt format of the file to create
+
+  \return 0 on success, 1 on error
+
+  Preset the format of the sink. Supported format strings:
+   - "wav": WAVE, 16 bit (default)
+   - "aiff": AIFF, 16 bit
+   - "m4a" or "mp4": Apple Audio Lossless Codec (ALAC)
+   - "aac": Audio Advanced Codec, lossy
+
+  Full list of supported encoding format is available in Table 1-2 of
+  `Multimedia Programming Guide
+  <https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html>`_.
+
+ */
+uint_t aubio_sink_apple_audio_preset_format(aubio_sink_apple_audio_t *s,
+    const char_t *fmt);
+
+/**
+
   get samplerate of sink object
 
Index: src/io/sink_flac.c
===================================================================
--- src/io/sink_flac.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
+++ src/io/sink_flac.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -0,0 +1,367 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+  This file is largely inspired by `examples/c/encode/file/main.c` in the
+  flac source package (versions 1.3.2 and later) available online at
+  https://xiph.org/flac/
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_FLAC
+
+#include "fmat.h"
+#include "io/ioutils.h"
+
+#include <FLAC/metadata.h>
+#include <FLAC/stream_encoder.h>
+
+#define MAX_WRITE_SIZE 4096
+
+// swap host to little endian
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define HTOLES(x) SWAPS(x)
+#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define HTOLES(x) x
+#else
+#ifdef HAVE_WIN_HACKS
+#define HTOLES(x) x
+#else
+#define HTOLES(x) SWAPS(htons(x))
+#endif
+#endif
+
+// convert to short, taking care of endianness
+#define FLOAT_TO_SHORT(x) (HTOLES((FLAC__int32)(x * 32768)))
+
+struct _aubio_sink_flac_t {
+  uint_t samplerate;
+  uint_t channels;
+  char_t *path;
+
+  FILE *fid;            // file id
+  FLAC__StreamEncoder* encoder;
+  FLAC__int32 *buffer;
+  FLAC__StreamMetadata **metadata;
+};
+
+typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
+
+uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels);
+uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate);
+uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
+uint_t aubio_sink_flac_close (aubio_sink_flac_t *s);
+void del_aubio_sink_flac (aubio_sink_flac_t *s);
+
+#if 0
+static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder,
+    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
+    unsigned frames_writtten, unsigned total_frames_estimate,
+    void *client_data);
+#endif
+
+aubio_sink_flac_t * new_aubio_sink_flac (const char_t *uri,
+    uint_t samplerate)
+{
+  aubio_sink_flac_t * s = AUBIO_NEW(aubio_sink_flac_t);
+
+  if (!uri) {
+    AUBIO_ERROR("sink_flac: Aborted opening null path\n");
+    goto failure;
+  }
+
+  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
+  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
+  s->path[strnlen(uri, PATH_MAX)] = '\0';
+
+  s->channels = 0;
+  s->samplerate = 0;
+
+  if ((sint_t)samplerate == 0)
+    return s;
+
+  aubio_sink_flac_preset_samplerate(s, samplerate);
+  s->channels = 1;
+
+  if (aubio_sink_flac_open(s) != AUBIO_OK)
+    goto failure;
+
+  return s;
+
+failure:
+  del_aubio_sink_flac(s);
+  return NULL;
+}
+
+void del_aubio_sink_flac (aubio_sink_flac_t *s)
+{
+  if (s->fid)
+    aubio_sink_flac_close(s);
+  if (s->buffer)
+    AUBIO_FREE(s->buffer);
+  if (s->path)
+    AUBIO_FREE(s->path);
+  AUBIO_FREE(s);
+}
+
+uint_t aubio_sink_flac_open(aubio_sink_flac_t *s)
+{
+  uint_t ret = AUBIO_FAIL;
+  FLAC__bool ok = true;
+  FLAC__StreamEncoderInitStatus init_status;
+  FLAC__StreamMetadata_VorbisComment_Entry entry;
+  const unsigned comp_level = 5;
+  const unsigned bps = 16;
+
+  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
+
+  s->buffer = AUBIO_ARRAY(FLAC__int32, s->channels * MAX_WRITE_SIZE);
+  if (!s->buffer) {
+    AUBIO_ERR("sink_flac: failed allocating buffer for %s\n", s->path);
+    return AUBIO_FAIL;
+  }
+
+  s->fid = fopen((const char *)s->path, "wb");
+  if (!s->fid) {
+    AUBIO_STRERR("sink_flac: Failed opening %s (%s)\n", s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+
+  if((s->encoder = FLAC__stream_encoder_new()) == NULL) {
+    AUBIO_ERR("sink_flac: failed allocating encoder for %s\n", s->path);
+    goto failure;
+  }
+  ok &= FLAC__stream_encoder_set_verify(s->encoder, true);
+  ok &= FLAC__stream_encoder_set_compression_level(s->encoder, comp_level);
+  ok &= FLAC__stream_encoder_set_channels(s->encoder, s->channels);
+  ok &= FLAC__stream_encoder_set_bits_per_sample(s->encoder, bps);
+  ok &= FLAC__stream_encoder_set_sample_rate(s->encoder, s->samplerate);
+  // the total number of samples can not be estimated (streaming)
+  // it will be set by the encoder in FLAC__stream_encoder_finish
+  //ok &= FLAC__stream_encoder_set_total_samples_estimate(s->encoder, 0);
+
+  if (!ok) {
+    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
+    goto failure;
+  }
+
+  s->metadata = AUBIO_ARRAY(FLAC__StreamMetadata*, 2);
+  if (!s->metadata) {
+    AUBIO_ERR("sink_flac: failed allocating memory for %s\n", s->path);
+    goto failure;
+  }
+
+  s->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+  if (!s->metadata[0]) {
+    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
+    goto failure;
+  }
+
+  s->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+  if (!s->metadata[1]) {
+    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
+    goto failure;
+  }
+
+  ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry,
+      "encoder", "aubio");
+  ok &= FLAC__metadata_object_vorbiscomment_append_comment(s->metadata[0],
+      entry, false);
+  if (!ok) {
+    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
+    goto failure;
+  }
+
+  // padding length
+  s->metadata[1]->length = 1234;
+  if (!FLAC__stream_encoder_set_metadata(s->encoder, s->metadata, 2)) {
+    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
+    goto failure;
+  }
+
+  // initialize encoder
+  init_status = FLAC__stream_encoder_init_file(s->encoder, s->path,
+      NULL, NULL);
+      //aubio_sink_flac_callback, s);
+  if (init_status == FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE) {
+    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
+       " (invalid samplerate %d)\n", s->path, s->samplerate);
+    goto failure;
+  } else if (init_status ==
+      FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS) {
+    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
+       " (invalid number of channel %d)\n", s->path, s->channels);
+    goto failure;
+  } else if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
+    AUBIO_ERR("sink_flac: failed initilizing encoder for %s (%d)\n",
+        s->path, (int)init_status);
+    goto failure;
+  }
+
+  // mark success
+  ret = AUBIO_OK;
+
+failure:
+
+  return ret;
+}
+
+uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
+    uint_t samplerate)
+{
+  if (aubio_io_validate_samplerate("sink_flac", s->path, samplerate))
+    return AUBIO_FAIL;
+  s->samplerate = samplerate;
+  if (s->samplerate != 0 && s->channels != 0)
+    return aubio_sink_flac_open(s);
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
+    uint_t channels)
+{
+  if (aubio_io_validate_channels("sink_flac", s->path, channels)) {
+    return AUBIO_FAIL;
+  }
+  s->channels = channels;
+  // automatically open when both samplerate and channels have been set
+  if (s->samplerate != 0 && s->channels != 0) {
+    return aubio_sink_flac_open(s);
+  }
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_flac_get_samplerate(const aubio_sink_flac_t *s)
+{
+  return s->samplerate;
+}
+
+uint_t aubio_sink_flac_get_channels(const aubio_sink_flac_t *s)
+{
+  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)
+{
+  uint_t c, v;
+  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
+      MAX_WRITE_SIZE, write_data->length, write);
+  // fill buffer
+  if (!write) {
+    return;
+  } else {
+    for (c = 0; c < s->channels; c++) {
+      for (v = 0; v < length; v++) {
+        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[v]);
+      }
+    }
+  }
+  // send to encoder
+  aubio_sink_write_frames(s, length);
+}
+
+void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t channels = aubio_sink_validate_input_channels("sink_flac", s->path,
+      s->channels, write_data->height);
+  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
+      MAX_WRITE_SIZE, write_data->length, write);
+  // fill buffer
+  if (!write) {
+    return;
+  } else {
+    for (c = 0; c < channels; c++) {
+      for (v = 0; v < length; v++) {
+        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[c][v]);
+      }
+    }
+  }
+  // send to encoder
+  aubio_sink_write_frames(s, length);
+}
+
+uint_t aubio_sink_flac_close (aubio_sink_flac_t *s)
+{
+  uint_t ret = AUBIO_OK;
+
+  if (!s->fid) return AUBIO_FAIL;
+
+  if (s->encoder) {
+    // mark the end of stream
+    if (!FLAC__stream_encoder_finish(s->encoder)) {
+      FLAC__StreamEncoderState state =
+        FLAC__stream_encoder_get_state(s->encoder);
+      AUBIO_ERR("sink_flac: Error closing encoder for %s (%s)\n",
+          s->path, FLAC__StreamEncoderStateString[state]);
+      ret &= AUBIO_FAIL;
+    }
+
+    FLAC__stream_encoder_delete(s->encoder);
+  }
+
+  if (s->metadata) {
+    // clean up metadata after stream finished
+    if (s->metadata[0])
+      FLAC__metadata_object_delete(s->metadata[0]);
+    if (s->metadata[1])
+      FLAC__metadata_object_delete(s->metadata[1]);
+    AUBIO_FREE(s->metadata);
+  }
+
+  if (s->fid && fclose(s->fid)) {
+    AUBIO_STRERR("sink_flac: Error closing file %s (%s)\n", s->path, errorstr);
+    ret &= AUBIO_FAIL;
+  }
+  s->fid = NULL;
+
+  return ret;
+}
+
+#if 0
+static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder UNUSED,
+    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
+    unsigned frames_written, unsigned total_frames_estimate,
+    void *client_data UNUSED)
+{
+  AUBIO_WRN("sink_flac: %d bytes_written, %d samples_written,"
+      " %d/%d frames writen\n",
+      bytes_written, samples_written, frames_written, total_frames_estimate);
+}
+#endif
+
+#endif /* HAVE_FLAC */
Index: src/io/sink_sndfile.c
===================================================================
--- src/io/sink_sndfile.c	(revision d1decd52d29b621d07bf45ed2b79dcb20ce414d1)
+++ src/io/sink_sndfile.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -49,7 +49,12 @@
   uint_t scratch_size;
   smpl_t *scratch_data;
+  int format;
 };
 
 uint_t aubio_sink_sndfile_open(aubio_sink_sndfile_t *s);
+
+uint_t aubio_str_extension_matches(const char_t *ext,
+    const char_t *pattern);
+const char_t *aubio_str_get_extension(const char_t *filename);
 
 aubio_sink_sndfile_t * new_aubio_sink_sndfile(const char_t * path, uint_t samplerate) {
@@ -67,4 +72,6 @@
   s->samplerate = 0;
   s->channels = 0;
+
+  aubio_sink_sndfile_preset_format(s, aubio_str_get_extension(path));
 
   // zero samplerate given. do not open yet
@@ -112,4 +119,28 @@
   if (s->samplerate != 0 /* && s->channels != 0 */) {
     return aubio_sink_sndfile_open(s);
+  }
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_sndfile_preset_format(aubio_sink_sndfile_t *s,
+    const char_t *fmt)
+{
+  if (aubio_str_extension_matches(fmt, "wav")) {
+    s->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+  } else if (aubio_str_extension_matches(fmt, "aiff")) {
+    s->format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16;
+  } else if (aubio_str_extension_matches(fmt, "flac")) {
+    s->format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
+  } else if (aubio_str_extension_matches(fmt, "ogg")) {
+    s->format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
+  } else if (atoi(fmt) > 0x010000) {
+    s->format = atoi(fmt);
+  } else {
+    s->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+    if (fmt && strnlen(fmt, PATH_MAX))  {
+      AUBIO_WRN("sink_sndfile: could not guess format %s for %s,"
+          " using default (wav)\n", fmt, s->path);
+      return AUBIO_FAIL;
+    }
   }
   return AUBIO_OK;
@@ -132,5 +163,5 @@
   sfinfo.samplerate = s->samplerate;
   sfinfo.channels   = s->channels;
-  sfinfo.format     = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+  sfinfo.format     = s->format;
 
   /* try creating the file */
Index: src/io/sink_sndfile.h
===================================================================
--- src/io/sink_sndfile.h	(revision d1decd52d29b621d07bf45ed2b79dcb20ce414d1)
+++ src/io/sink_sndfile.h	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -96,4 +96,37 @@
 /**
 
+  preset sink format
+
+  \param s sink, created with ::new_aubio_sink_sndfile
+  \param fmt format of the file to create
+
+  \return 0 on success, 1 on error
+
+  Preset the format of the sink. Supported format strings:
+   - "wav": 16 bit (default)
+   - "aiff": aiff, 16 bit
+   - "flac": flac, 16 bit
+   - "ogg": ogg vorbis stream
+
+  Alternatively, any sndfile format can be set by passing the corresponding
+  integer as a string:
+
+  \code{.c}
+  char_t fmt[10];
+  snprintf(fmt, sizeof(fmt), "%d", SF_FORMAT_FLAC | SF_FORMAT_PCM_24);
+  aubio_sink_sndfile_preset_format(s, fmt);
+  \endcode
+
+  The file should have been created using a samplerate of 0.
+
+  This function should be called before aubio_sink_sndfile_preset_samplerate()
+  and aubio_sink_sndfile_preset_channels().
+
+*/
+uint_t aubio_sink_sndfile_preset_format(aubio_sink_sndfile_t *s,
+        const char_t* fmt);
+
+/**
+
   get samplerate of sink object
 
Index: src/io/sink_vorbis.c
===================================================================
--- src/io/sink_vorbis.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
+++ src/io/sink_vorbis.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -0,0 +1,314 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+  This file is largely inspired by `examples/encoder_example.c` in the
+  libvorbis source package (versions 1.3.5 and later) available online at
+  https://xiph.org/vorbis/
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_VORBISENC
+
+#include "fmat.h"
+#include "io/ioutils.h"
+
+#include <vorbis/vorbisenc.h>
+#include <string.h> // strerror
+#include <errno.h> // errno
+#include <time.h> // time
+
+#define MAX_SIZE 4096
+
+struct _aubio_sink_vorbis_t {
+  FILE *fid;            // file id
+  ogg_stream_state os;  // stream
+  ogg_page og;          // page
+  ogg_packet op;        // data packet
+  vorbis_info vi;       // vorbis bitstream settings
+  vorbis_comment vc;    // user comment
+  vorbis_dsp_state vd;  // working state
+  vorbis_block vb;      // working space
+
+  uint_t samplerate;
+  uint_t channels;
+  char_t *path;
+};
+
+typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
+
+uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels);
+uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate);
+uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
+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)
+{
+  aubio_sink_vorbis_t * s = AUBIO_NEW(aubio_sink_vorbis_t);
+
+  if (!uri) {
+    AUBIO_ERROR("sink_vorbis: Aborted opening null path\n");
+    goto failure;
+  }
+
+  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
+  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
+  s->path[strnlen(uri, PATH_MAX)] = '\0';
+
+  s->channels = 0;
+
+  if ((sint_t)samplerate == 0)
+    return s;
+
+  aubio_sink_vorbis_preset_samplerate(s, samplerate);
+  s->channels = 1;
+
+  if (aubio_sink_vorbis_open(s) != AUBIO_OK)
+    goto failure;
+
+  return s;
+
+failure:
+  del_aubio_sink_vorbis(s);
+  return NULL;
+}
+
+void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s)
+{
+  if (s->fid) aubio_sink_vorbis_close(s);
+  // clean up
+  ogg_stream_clear(&s->os);
+  vorbis_block_clear(&s->vb);
+  vorbis_dsp_clear(&s->vd);
+  vorbis_comment_clear(&s->vc);
+  vorbis_info_clear(&s->vi);
+
+  if (s->path) AUBIO_FREE(s->path);
+  AUBIO_FREE(s);
+}
+
+uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s)
+{
+  float quality_mode = .9;
+
+  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
+
+  s->fid = fopen((const char *)s->path, "wb");
+  if (!s->fid) {
+    AUBIO_STRERR("sink_vorbis: Error opening file \'%s\' (%s)\n",
+        s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+
+  vorbis_info_init(&s->vi);
+  if (vorbis_encode_init_vbr(&s->vi, s->channels, s->samplerate, quality_mode))
+  {
+    AUBIO_ERR("sink_vorbis: vorbis_encode_init_vbr failed\n");
+    return AUBIO_FAIL;
+  }
+
+  // add comment
+  vorbis_comment_init(&s->vc);
+  vorbis_comment_add_tag(&s->vc, "ENCODER", "aubio");
+
+  // initalise analysis and block
+  vorbis_analysis_init(&s->vd, &s->vi);
+  vorbis_block_init(&s->vd, &s->vb);
+
+  // pick randome serial number
+  srand(time(NULL));
+  ogg_stream_init(&s->os, rand());
+
+  // write header
+  {
+    ogg_packet header;
+    ogg_packet header_comm;
+    ogg_packet header_code;
+
+    vorbis_analysis_headerout(&s->vd, &s->vc, &header, &header_comm,
+        &header_code);
+
+    ogg_stream_packetin(&s->os, &header);
+    ogg_stream_packetin(&s->os, &header_comm);
+    ogg_stream_packetin(&s->os, &header_code);
+
+    // make sure audio data will start on a new page
+    while (1)
+    {
+      if (!ogg_stream_flush(&s->os, &s->og)) break;
+      if (aubio_sink_vorbis_write_page(s)) return AUBIO_FAIL;
+    }
+  }
+
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
+    uint_t samplerate)
+{
+  if (aubio_io_validate_samplerate("sink_vorbis", s->path, samplerate))
+    return AUBIO_FAIL;
+  s->samplerate = samplerate;
+  if (/* s->samplerate != 0 && */ s->channels != 0)
+    return aubio_sink_vorbis_open(s);
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
+    uint_t channels)
+{
+  if (aubio_io_validate_channels("sink_vorbis", s->path, channels)) {
+    return AUBIO_FAIL;
+  }
+  s->channels = channels;
+  // automatically open when both samplerate and channels have been set
+  if (s->samplerate != 0 /* && s->channels != 0 */) {
+    return aubio_sink_vorbis_open(s);
+  }
+  return AUBIO_OK;
+}
+
+uint_t aubio_sink_vorbis_get_samplerate(const aubio_sink_vorbis_t *s)
+{
+  return s->samplerate;
+}
+
+uint_t aubio_sink_vorbis_get_channels(const aubio_sink_vorbis_t *s)
+{
+  return s->channels;
+}
+
+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) {
+    AUBIO_STRERR("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) {
+
+    vorbis_analysis(&s->vb, NULL);
+    vorbis_bitrate_addblock(&s->vb);
+
+    while (vorbis_bitrate_flushpacket(&s->vd, &s->op))
+    {
+      ogg_stream_packetin(&s->os, &s->op);
+
+      while (1) {
+        if (!ogg_stream_pageout (&s->os, &s->og)) break;
+        aubio_sink_vorbis_write_page(s);
+        if (ogg_page_eos(&s->og)) break;
+      }
+    }
+  }
+}
+
+void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t length = aubio_sink_validate_input_length("sink_vorbis", s->path,
+      MAX_SIZE, write_data->length, write);
+  float **buffer = vorbis_analysis_buffer(&s->vd, (long)length);
+  // fill buffer
+  if (!write) {
+    return;
+  } else if (!buffer) {
+    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
+    return;
+  } else {
+    for (c = 0; c < s->channels; c++) {
+      for (v = 0; v < length; v++) {
+        buffer[c][v] = write_data->data[v];
+      }
+    }
+    // tell vorbis how many frames were written
+    vorbis_analysis_wrote(&s->vd, (long)length);
+  }
+  // write to file
+  aubio_sink_vorbis_write(s);
+}
+
+void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s, fmat_t *write_data,
+    uint_t write)
+{
+  uint_t c, v;
+  uint_t channels = aubio_sink_validate_input_channels("sink_vorbis", s->path,
+      s->channels, write_data->height);
+  uint_t length = aubio_sink_validate_input_length("sink_vorbis", s->path,
+      MAX_SIZE, write_data->length, write);
+  float **buffer = vorbis_analysis_buffer(&s->vd, (long)length);
+  // fill buffer
+  if (!write) {
+    return;
+  } else if (!buffer) {
+    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
+    return;
+  } else {
+    for (c = 0; c < channels; c++) {
+      for (v = 0; v < length; v++) {
+        buffer[c][v] = write_data->data[c][v];
+      }
+    }
+    // tell vorbis how many frames were written
+    vorbis_analysis_wrote(&s->vd, (long)length);
+  }
+
+  aubio_sink_vorbis_write(s);
+}
+
+uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s)
+{
+  if (!s->fid) return AUBIO_FAIL;
+  //mark the end of stream
+  vorbis_analysis_wrote(&s->vd, 0);
+
+  aubio_sink_vorbis_write(s);
+
+  if (s->fid && fclose(s->fid)) {
+    AUBIO_STRERR("sink_vorbis: Error closing file \'%s\' (%s)\n",
+        s->path, errorstr);
+    return AUBIO_FAIL;
+  }
+  s->fid = NULL;
+  return AUBIO_OK;
+}
+
+#endif /* HAVE_VORBISENC */
Index: src/utils/strutils.c
===================================================================
--- src/utils/strutils.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
+++ src/utils/strutils.c	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -0,0 +1,47 @@
+/*
+  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
+
+  This file is part of aubio.
+
+  aubio is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  aubio is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "aubio_priv.h"
+
+#ifdef HAVE_WIN_HACKS
+#define strncasecmp _strnicmp
+#endif
+
+const char_t *aubio_str_get_extension(const char_t *filename)
+{
+  // find last occurence of dot character
+  const char_t *ext;
+  if (!filename) return NULL;
+  ext = strrchr(filename, '.');
+  if (!ext || ext == filename) return "";
+  else return ext + 1;
+}
+
+uint_t aubio_str_extension_matches(const char_t *ext, const char_t *pattern)
+{
+  return ext && pattern && (strncasecmp(ext, pattern, PATH_MAX) == 0);
+}
+
+uint_t aubio_str_path_has_extension(const char_t *filename,
+    const char_t *pattern)
+{
+  const char_t *ext = aubio_str_get_extension(filename);
+  return aubio_str_extension_matches(ext, pattern);
+}
Index: src/wscript_build
===================================================================
--- src/wscript_build	(revision d1decd52d29b621d07bf45ed2b79dcb20ce414d1)
+++ src/wscript_build	(revision 05ed7f562616cee876d2e4cd397cdb88f600942f)
@@ -13,4 +13,6 @@
 uselib += ['AVRESAMPLE']
 uselib += ['AVUTIL']
+uselib += ['VORBISENC']
+uselib += ['FLAC']
 uselib += ['BLAS']
 
