ecore_audio: Add support for libsndfile
authorDaniel Willmann <d.willmann@samsung.com>
Fri, 14 Dec 2012 23:38:11 +0000 (23:38 +0000)
committerDaniel Willmann <daniel@totalueberwachung.de>
Fri, 14 Dec 2012 23:38:11 +0000 (23:38 +0000)
Allows reading from and writing to wav, ogg, etc. files. Support for
virtual IO as well to allow playing sounds from eet, which will be
needed in edje multisense.

Signed-off-by: Daniel Willmann <d.willmann@samsung.com>
SVN revision: 80996

src/lib/ecore_audio/Ecore_Audio.h
src/lib/ecore_audio/ecore_audio.c
src/lib/ecore_audio/ecore_audio_private.h
src/lib/ecore_audio/ecore_audio_sndfile.c [new file with mode: 0644]

index 355db71..db1d651 100644 (file)
@@ -431,6 +431,16 @@ EAPI Ecore_Audio_Object *ecore_audio_input_output_get(Ecore_Audio_Object *input)
  */
 EAPI double              ecore_audio_input_remaining_get(Ecore_Audio_Object *input);
 
+/* libsndfile */
+
+EAPI void ecore_audio_input_sndfile_filename_set(Ecore_Audio_Object *input, const char *filename);
+EAPI void ecore_audio_input_sndfile_format_set(Ecore_Audio_Object *input, int format);
+EAPI void ecore_audio_input_sndfile_vio_set(Ecore_Audio_Object *input, Ecore_Audio_Vio *vio);
+
+EAPI void ecore_audio_output_sndfile_filename_set(Ecore_Audio_Object *output, const char *filename);
+EAPI void ecore_audio_output_sndfile_format_set(Ecore_Audio_Object *output, int format);
+EAPI void ecore_audio_output_sndfile_vio_set(Ecore_Audio_Object *output, Ecore_Audio_Vio *vio);
+
 /**
  * @}
  */
index 262b697..4acd9fe 100644 (file)
@@ -81,6 +81,11 @@ ecore_audio_init(void)
    if (mod)
      ecore_audio_modules = eina_list_append(ecore_audio_modules, mod);
 #endif
+#ifdef HAVE_SNDFILE
+   mod = ecore_audio_sndfile_init();
+   if (mod)
+     ecore_audio_modules = eina_list_append(ecore_audio_modules, mod);
+#endif
 
    return _ecore_audio_init_count;
 }
@@ -97,6 +102,9 @@ ecore_audio_shutdown(void)
 #ifdef HAVE_PULSE
    ecore_audio_pulse_shutdown();
 #endif
+#ifdef HAVE_SNDFILE
+   ecore_audio_sndfile_shutdown();
+#endif
 
    eina_list_free(ecore_audio_modules);
 
index 6f9b760..e7ec389 100644 (file)
 #include <pulse/pulseaudio.h>
 #endif
 
+#ifdef HAVE_SNDFILE
+#include <sndfile.h>
+#endif
+
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -312,6 +316,27 @@ Ecore_Audio_Module *ecore_audio_pulse_init(void);
 void                ecore_audio_pulse_shutdown(void);
 #endif /* HAVE_PULSE */
 
+#ifdef HAVE_SNDFILE
+/* ecore_audio_sndfile */
+struct _Ecore_Audio_Sndfile_Private
+{
+   SF_VIRTUAL_IO vio_wrapper;
+};
+
+struct _Ecore_Audio_Sndfile
+{
+   const char *filename;
+   SNDFILE *handle;
+   SF_INFO  sfinfo;
+   Ecore_Audio_Vio *vio;
+   double volume;
+   Ecore_Timer *timer;
+};
+
+Ecore_Audio_Module *ecore_audio_sndfile_init(void);
+void                ecore_audio_sndfile_shutdown(void);
+#endif /* HAVE_SNDFILE */
+
 /**
  * @}
  */
diff --git a/src/lib/ecore_audio/ecore_audio_sndfile.c b/src/lib/ecore_audio/ecore_audio_sndfile.c
new file mode 100644 (file)
index 0000000..cdee2d5
--- /dev/null
@@ -0,0 +1,488 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SNDFILE
+#include "Ecore.h"
+#include "ecore_private.h"
+#include "Ecore_Audio.h"
+#include "ecore_audio_private.h"
+
+#include <sndfile.h>
+
+static Ecore_Audio_Module *module = NULL;
+
+/* Virtual IO wrapper functions */
+
+static sf_count_t _sndfile_wrap_get_filelen(void *data)
+{
+  Ecore_Audio_Object *obj = data;
+  struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
+
+  if (!sndfile->vio)
+    goto error;
+
+  if (sndfile->vio->get_length)
+    return sndfile->vio->get_length(obj);
+
+error:
+  return -1;
+}
+
+static sf_count_t _sndfile_wrap_seek(sf_count_t offset, int whence, void *data)
+{
+  Ecore_Audio_Object *obj = data;
+  struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
+
+  if (!sndfile->vio)
+    goto error;
+
+  if (sndfile->vio->seek)
+    return sndfile->vio->seek(obj, offset, whence);
+
+error:
+  return -1;
+}
+
+static sf_count_t _sndfile_wrap_read(void *buffer, sf_count_t count, void *data)
+{
+  Ecore_Audio_Object *obj = data;
+  struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
+
+  if (!sndfile->vio)
+    goto error;
+
+  if (sndfile->vio->read)
+    return sndfile->vio->read(obj, buffer, count);
+
+error:
+  return 0;
+}
+
+static sf_count_t _sndfile_wrap_write(const void *buffer, sf_count_t count, void *data)
+{
+  Ecore_Audio_Object *obj = data;
+  struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
+
+  if (!sndfile->vio)
+    goto error;
+
+  if (sndfile->vio->write)
+    return sndfile->vio->write(obj, buffer, count);
+
+error:
+  return 0;
+}
+
+static sf_count_t _sndfile_wrap_tell(void *data)
+{
+  Ecore_Audio_Object *obj = data;
+  struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
+
+  if (!sndfile->vio)
+    goto error;
+
+  if (sndfile->vio->tell)
+    return sndfile->vio->tell(obj);
+
+error:
+  return -1;
+}
+
+/* End virtual IO wrapper functions */
+
+static Ecore_Audio_Object *
+_sndfile_input_new(Ecore_Audio_Object *input)
+{
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+   struct _Ecore_Audio_Sndfile *sndfile;
+
+   sndfile = calloc(1, sizeof(struct _Ecore_Audio_Sndfile));
+   if (!sndfile)
+     {
+        ERR("Could not allocate memory for private structure.");
+        free(in);
+        return NULL;
+     }
+
+   in->module_data = sndfile;
+
+   sndfile->sfinfo.format = 0;
+   sndfile->volume = 1.0;
+
+   return (Ecore_Audio_Object *)in;
+}
+
+static void
+_sndfile_input_del(Ecore_Audio_Object *input)
+{
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+   struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
+
+   eina_stringshare_del(sndfile->filename);
+
+   sf_close(sndfile->handle);
+   free(sndfile);
+}
+
+static int
+_sndfile_input_read(Ecore_Audio_Object *input, void *data, int len)
+{
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+   struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
+   int read;
+
+   /* FIXME: Nicer way to return bytes read instead of items */
+   read = sf_read_float(sndfile->handle, data, len / 4) * 4;
+
+   return read;
+}
+
+static double
+_sndfile_input_seek(Ecore_Audio_Object *input, double offs, int mode)
+{
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+   struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
+   sf_count_t count, pos;
+
+   count = offs * sndfile->sfinfo.samplerate;
+   pos = sf_seek(sndfile->handle, count, mode);
+   return (double)pos / sndfile->sfinfo.samplerate;
+}
+
+static struct input_api inops = {
+   .input_new = _sndfile_input_new,
+   .input_del = _sndfile_input_del,
+   .input_read = _sndfile_input_read,
+   .input_seek = _sndfile_input_seek,
+};
+
+EAPI void ecore_audio_input_sndfile_format_set(Ecore_Audio_Object *input, int format)
+{
+   Ecore_Audio_Output *in = (Ecore_Audio_Output *)input;
+   struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
+
+   sndfile->sfinfo.format = format;
+}
+
+void ecore_audio_input_sndfile_filename_set(Ecore_Audio_Object *input, const char *filename)
+{
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+   struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
+
+   if (sndfile->handle)
+     sf_close(sndfile->handle);
+
+   if (sndfile->filename)
+     eina_stringshare_del(sndfile->filename);
+
+   if (sndfile->vio)
+     {
+        free(sndfile->vio);
+        sndfile->vio = NULL;
+     }
+
+   sndfile->filename = eina_stringshare_add(filename);
+
+   sndfile->handle = sf_open(sndfile->filename, SFM_READ, &sndfile->sfinfo);
+
+   in->length = (double)sndfile->sfinfo.frames / sndfile->sfinfo.samplerate;
+
+   // XXX: Propagate samplerate/channel update to output
+   in->samplerate = sndfile->sfinfo.samplerate;
+   in->channels = sndfile->sfinfo.channels;
+}
+
+void ecore_audio_input_sndfile_vio_set(Ecore_Audio_Object *input, Ecore_Audio_Vio *vio)
+{
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+   struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
+   struct _Ecore_Audio_Sndfile_Private *priv = in->module->priv;
+
+   if (sndfile->handle)
+     sf_close(sndfile->handle);
+
+   if (sndfile->filename)
+     {
+        eina_stringshare_del(sndfile->filename);
+        sndfile->filename = NULL;
+     }
+
+   if (sndfile->vio)
+     free(sndfile->vio);
+
+   sndfile->vio = calloc(1, sizeof(Ecore_Audio_Vio));
+   sndfile->vio->get_length = vio->get_length;
+   sndfile->vio->seek = vio->seek;
+   sndfile->vio->read = vio->read;
+   sndfile->vio->tell = vio->tell;
+
+   if (sndfile->sfinfo.format != 0) {
+       sndfile->sfinfo.samplerate = in->samplerate;
+       sndfile->sfinfo.channels = in->channels;
+   }
+
+   sndfile->handle = sf_open_virtual(&priv->vio_wrapper, SFM_READ, &sndfile->sfinfo, in);
+
+   in->length = (double)sndfile->sfinfo.frames / sndfile->sfinfo.samplerate;
+
+   // XXX: Propagate samplerate/channel update to output
+   in->samplerate = sndfile->sfinfo.samplerate;
+   in->channels = sndfile->sfinfo.channels;
+}
+
+static Ecore_Audio_Object *
+_sndfile_output_new(Ecore_Audio_Object *output)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   struct _Ecore_Audio_Sndfile *sndfile;
+
+   sndfile = calloc(1, sizeof(struct _Ecore_Audio_Sndfile));
+   if (!sndfile)
+     {
+        ERR("Could not allocate memory for private structure.");
+        free(out);
+        return NULL;
+     }
+
+   out->module_data = sndfile;
+
+   sndfile->sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
+
+   return (Ecore_Audio_Object *)out;
+}
+
+static void
+_sndfile_output_del(Ecore_Audio_Object *output)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
+
+   eina_stringshare_del(sndfile->filename);
+   free(sndfile->vio);
+
+   sf_close(sndfile->handle);
+   free(sndfile);
+}
+
+static void
+_sndfile_output_volume_set(Ecore_Audio_Object *output, double volume)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
+
+   if ((volume < 0) || (volume > 1.0))
+     return;
+
+   sndfile->volume = volume;
+}
+
+static double
+_sndfile_output_volume_get(Ecore_Audio_Object *output)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
+
+   return sndfile->volume;
+}
+
+static Eina_Bool
+_sndfile_output_write_cb(void *data)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)data;
+   struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
+   Ecore_Audio_Input *in;
+   int written, bread;
+   void *buf;
+
+   buf = malloc(1024);
+
+   in = eina_list_data_get(out->inputs);
+
+   bread = ecore_audio_input_read((Ecore_Audio_Object *)in, buf, 1024);
+   DBG("read: %i", bread);
+   if (bread == 0)
+     {
+        DBG("read done");
+        out->paused = EINA_TRUE;
+        return EINA_FALSE;
+     }
+
+   // XXX: Check for errors
+   written = sf_write_float(sndfile->handle, buf, bread/4)*4;
+
+   return EINA_TRUE;
+}
+
+static void
+_sndfile_output_paused_set(Ecore_Audio_Object *output, Eina_Bool paused)
+{
+   struct _Ecore_Audio_Sndfile *sndfile = output->module_data;
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+
+   if (paused && sndfile->timer)
+     ecore_timer_del(sndfile->timer);
+   else if (out->inputs)
+     sndfile->timer = ecore_timer_add(0.01, _sndfile_output_write_cb, output);
+}
+
+static Eina_Bool
+_sndfile_output_add_input(Ecore_Audio_Object *output, Ecore_Audio_Object *input)
+{
+   struct _Ecore_Audio_Sndfile *sndfile = output->module_data;
+   struct _Ecore_Audio_Sndfile_Private *priv = output->module->priv;
+
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
+
+   sndfile->sfinfo.samplerate = in->samplerate;
+   sndfile->sfinfo.channels = in->channels;
+
+   if (sndfile->vio)
+     sndfile->handle = sf_open_virtual(&priv->vio_wrapper, SFM_WRITE, &sndfile->sfinfo, out);
+   else if (sndfile->filename)
+     sndfile->handle = sf_open(sndfile->filename, SFM_WRITE, &sndfile->sfinfo);
+
+   if (output->paused)
+     return EINA_TRUE;
+
+   if (!out->inputs)
+     sndfile->timer = ecore_timer_add(0.01, _sndfile_output_write_cb, output);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_sndfile_output_del_input(Ecore_Audio_Object *out EINA_UNUSED, Ecore_Audio_Object *in EINA_UNUSED)
+{
+   //XXX: Check if it's the last one and pause the timer
+   return EINA_TRUE;
+}
+
+static void
+_sndfile_output_update_input_format(Ecore_Audio_Object *out, Ecore_Audio_Object *in)
+{
+}
+
+static struct output_api outops = {
+   .output_new = _sndfile_output_new,
+   .output_del = _sndfile_output_del,
+   .output_volume_set = _sndfile_output_volume_set,
+   .output_volume_get = _sndfile_output_volume_get,
+   .output_paused_set = _sndfile_output_paused_set,
+   .output_add_input = _sndfile_output_add_input,
+   .output_del_input = _sndfile_output_del_input,
+   .output_update_input_format = _sndfile_output_update_input_format,
+};
+
+EAPI void ecore_audio_output_sndfile_filename_set(Ecore_Audio_Object *output, const char *filename)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
+
+   if (sndfile->handle)
+     sf_close(sndfile->handle);
+
+   if (sndfile->filename)
+     eina_stringshare_del(sndfile->filename);
+
+   if (sndfile->vio)
+     {
+        free(sndfile->vio);
+        sndfile->vio = NULL;
+     }
+
+   sndfile->filename = eina_stringshare_add(filename);
+}
+
+EAPI void ecore_audio_output_sndfile_format_set(Ecore_Audio_Object *output, int format)
+{
+   Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
+   struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
+
+   sndfile->sfinfo.format = format;
+}
+
+EAPI void ecore_audio_output_sndfile_vio_set(Ecore_Audio_Object *output, Ecore_Audio_Vio *vio)
+{
+   struct _Ecore_Audio_Sndfile *sndfile = output->module_data;
+
+   if (sndfile->handle)
+     sf_close(sndfile->handle);
+
+   if (sndfile->filename)
+     {
+        eina_stringshare_del(sndfile->filename);
+        sndfile->filename = NULL;
+     }
+
+   if (sndfile->vio)
+     free(sndfile->vio);
+
+   sndfile->vio = calloc(1, sizeof(Ecore_Audio_Vio));
+   sndfile->vio->get_length = vio->get_length;
+   sndfile->vio->seek = vio->seek;
+   sndfile->vio->write = vio->write;
+   sndfile->vio->tell = vio->tell;
+}
+
+/**
+ * @brief Initialize the Ecore_Audio sndfile module
+ *
+ * @return the initialized module on success, NULL on error
+ */
+Ecore_Audio_Module *
+ecore_audio_sndfile_init(void)
+{
+   struct _Ecore_Audio_Sndfile_Private *priv;
+
+   module = calloc(1, sizeof(Ecore_Audio_Module));
+   if (!module)
+     {
+        ERR("Could not allocate memory for module.");
+        return NULL;
+     }
+
+   priv = calloc(1, sizeof(struct _Ecore_Audio_Sndfile_Private));
+   if (!priv)
+     {
+        ERR("Could not allocate memory for private module region.");
+        free(module);
+        return NULL;
+     }
+
+   priv->vio_wrapper.get_filelen = _sndfile_wrap_get_filelen;
+   priv->vio_wrapper.seek = _sndfile_wrap_seek;
+   priv->vio_wrapper.read = _sndfile_wrap_read;
+   priv->vio_wrapper.write = _sndfile_wrap_write;
+   priv->vio_wrapper.tell = _sndfile_wrap_tell;
+
+   ECORE_MAGIC_SET(module, ECORE_MAGIC_AUDIO_MODULE);
+   module->type = ECORE_AUDIO_TYPE_SNDFILE;
+   module->name = "sndfile";
+   module->priv = priv;
+   module->inputs = NULL;
+   module->outputs = NULL;
+   module->in_ops = &inops;
+   module->out_ops = &outops;
+
+   DBG("Initialized");
+   return module;
+}
+
+/**
+ * @brief Shut down the Ecore_Audio sndfile module
+ */
+void
+ecore_audio_sndfile_shutdown(void)
+{
+   struct _Ecore_Audio_Sndfile_Private *priv = (struct _Ecore_Audio_Sndfile_Private *)module->priv;
+
+   free(priv);
+   free(module);
+   module = NULL;
+
+   DBG("Shutting down");
+}
+
+#endif /* HAVE_SNDFILE */