#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 */