From 8c96a841e49ce22eaddd912f78449e506a51b1d8 Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Fri, 14 Dec 2012 23:38:11 +0000 Subject: [PATCH] ecore_audio: Add support for libsndfile 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 SVN revision: 80996 --- src/lib/ecore_audio/Ecore_Audio.h | 10 + src/lib/ecore_audio/ecore_audio.c | 8 + src/lib/ecore_audio/ecore_audio_private.h | 25 ++ src/lib/ecore_audio/ecore_audio_sndfile.c | 488 ++++++++++++++++++++++++++++++ 4 files changed, 531 insertions(+) create mode 100644 src/lib/ecore_audio/ecore_audio_sndfile.c diff --git a/src/lib/ecore_audio/Ecore_Audio.h b/src/lib/ecore_audio/Ecore_Audio.h index 355db71..db1d651 100644 --- a/src/lib/ecore_audio/Ecore_Audio.h +++ b/src/lib/ecore_audio/Ecore_Audio.h @@ -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); + /** * @} */ diff --git a/src/lib/ecore_audio/ecore_audio.c b/src/lib/ecore_audio/ecore_audio.c index 262b697..4acd9fe 100644 --- a/src/lib/ecore_audio/ecore_audio.c +++ b/src/lib/ecore_audio/ecore_audio.c @@ -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); diff --git a/src/lib/ecore_audio/ecore_audio_private.h b/src/lib/ecore_audio/ecore_audio_private.h index 6f9b760..e7ec389 100644 --- a/src/lib/ecore_audio/ecore_audio_private.h +++ b/src/lib/ecore_audio/ecore_audio_private.h @@ -13,6 +13,10 @@ #include #endif +#ifdef HAVE_SNDFILE +#include +#endif + #include #include @@ -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 index 0000000..cdee2d5 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_sndfile.c @@ -0,0 +1,488 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SNDFILE +#include "Ecore.h" +#include "ecore_private.h" +#include "Ecore_Audio.h" +#include "ecore_audio_private.h" + +#include + +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 */ -- 2.7.4