diff --git a/src/lib/ecore_audio/Ecore_Audio.h b/src/lib/ecore_audio/Ecore_Audio.h index 355db7101f..db1d65131f 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 262b697b9d..4acd9fe2c6 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 6f9b7603f1..e7ec389622 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 0000000000..cdee2d597b --- /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 */