From 422d3313e8363017bf96f28f4de33160fc08e5f5 Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Fri, 12 Apr 2013 17:45:12 +0100 Subject: [PATCH] ecore_audio: Add sndfile in- and output Signed-off-by: Daniel Willmann --- src/Makefile_Ecore_Audio.am | 4 + .../ecore_audio/ecore_audio_obj_in_sndfile.c | 196 ++++++++++++++++ .../ecore_audio/ecore_audio_obj_in_sndfile.h | 57 +++++ .../ecore_audio/ecore_audio_obj_out_sndfile.c | 218 ++++++++++++++++++ .../ecore_audio/ecore_audio_obj_out_sndfile.h | 57 +++++ 5 files changed, 532 insertions(+) create mode 100644 src/lib/ecore_audio/ecore_audio_obj_in_sndfile.c create mode 100644 src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h create mode 100644 src/lib/ecore_audio/ecore_audio_obj_out_sndfile.c create mode 100644 src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h diff --git a/src/Makefile_Ecore_Audio.am b/src/Makefile_Ecore_Audio.am index f19cae2abe..ad6a4d623a 100644 --- a/src/Makefile_Ecore_Audio.am +++ b/src/Makefile_Ecore_Audio.am @@ -10,6 +10,8 @@ lib/ecore_audio/Ecore_Audio.h \ lib/ecore_audio/ecore_audio_obj.h \ lib/ecore_audio/ecore_audio_obj_in.h \ lib/ecore_audio/ecore_audio_obj_out.h \ +lib/ecore_audio/ecore_audio_obj_in_sndfile.h \ +lib/ecore_audio/ecore_audio_obj_out_sndfile.h \ lib/ecore_audio/ecore_audio_protected.h @@ -18,6 +20,8 @@ lib/ecore_audio/ecore_audio.c \ lib/ecore_audio/ecore_audio_obj.c \ lib/ecore_audio/ecore_audio_obj_in.c \ lib/ecore_audio/ecore_audio_obj_out.c \ +lib/ecore_audio/ecore_audio_obj_in_sndfile.c \ +lib/ecore_audio/ecore_audio_obj_out_sndfile.c \ lib/ecore_audio/ecore_audio_private.h lib_ecore_audio_libecore_audio_la_CPPFLAGS = @ECORE_AUDIO_CFLAGS@ diff --git a/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.c b/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.c new file mode 100644 index 0000000000..75eafbdf20 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.c @@ -0,0 +1,196 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_FEATURES_H +#include +#endif + +#include +#include "ecore_audio_private.h" +#include + +EAPI Eo_Op ECORE_AUDIO_OBJ_IN_SNDFILE_BASE_ID = EO_NOOP; + +#define MY_CLASS ECORE_AUDIO_OBJ_IN_SNDFILE_CLASS +#define MY_CLASS_NAME "ecore_audio_obj_in_sndfile" + +struct _Ecore_Audio_Sndfile +{ + SNDFILE *handle; + SF_INFO sfinfo; + Ecore_Audio_Vio *vio; +}; + +typedef struct _Ecore_Audio_Sndfile Ecore_Audio_Sndfile; + +static void _read(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + int read; + void *data = va_arg(*list, void *); + int len = va_arg(*list, int); + int *ret = va_arg(*list, int *); + + read = sf_read_float(obj->handle, data, len/4)*4; + + if (ret) + *ret = read; +} + +static void _seek(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + sf_count_t count, pos; + + double offs = va_arg(*list, double); + int mode = va_arg(*list, int); + double *ret = va_arg(*list, double *); + + count = offs * obj->sfinfo.samplerate; + pos = sf_seek(obj->handle, count, mode); + + if (ret) + *ret = (double)pos / obj->sfinfo.samplerate; +} + +static void _source_set(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + + Ecore_Audio_Object *ea_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + Ecore_Audio_Input *in_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_IN_CLASS); + + const char *source = va_arg(*list, const char *); + + if (obj->handle) { + sf_close(obj->handle); + obj->handle = NULL; + } + + eina_stringshare_replace(&ea_obj->source, source); + + if (!ea_obj->source) + return; + + obj->handle = sf_open(ea_obj->source, SFM_READ, &obj->sfinfo); + + if (!obj->handle) { + eina_stringshare_del(ea_obj->source); + ea_obj->source = NULL; + return; + } + + in_obj->length = (double)obj->sfinfo.frames / obj->sfinfo.samplerate; + + in_obj->samplerate = obj->sfinfo.samplerate; + in_obj->channels = obj->sfinfo.channels; + + if (obj->sfinfo.format& SF_FORMAT_WAV) + ea_obj->format = ECORE_AUDIO_FORMAT_WAV; + else if (obj->sfinfo.format& SF_FORMAT_OGG) + ea_obj->format = ECORE_AUDIO_FORMAT_OGG; + else if (obj->sfinfo.format& SF_FORMAT_FLAC) + ea_obj->format = ECORE_AUDIO_FORMAT_FLAC; + else + ea_obj->format = ECORE_AUDIO_FORMAT_AUTO; +} + +static void _source_get(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Ecore_Audio_Object *obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + const char **ret = va_arg(*list, const char **); + + if (ret) + *ret = obj->source; +} + +static void _format_set(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + Ecore_Audio_Object *ea_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + Ecore_Audio_Format format= va_arg(*list, Ecore_Audio_Format); + + if (ea_obj->source) { + ERR("Input is already open - cannot change format"); + return; + } + + switch (format) { + case ECORE_AUDIO_FORMAT_AUTO: + obj->sfinfo.format = 0; + case ECORE_AUDIO_FORMAT_WAV: + obj->sfinfo.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16; + break; + case ECORE_AUDIO_FORMAT_OGG: + obj->sfinfo.format = SF_FORMAT_OGG|SF_FORMAT_VORBIS; + break; + case ECORE_AUDIO_FORMAT_FLAC: + obj->sfinfo.format = SF_FORMAT_FLAC; + break; + default: + ERR("Format not supported!"); + return; + } + ea_obj->format = format; +} + +static void _format_get(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Ecore_Audio_Object *obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + Ecore_Audio_Format *ret = va_arg(*list, Ecore_Audio_Format *); + + if (ret) + *ret = obj->format; +} + +static void _constructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED) +{ + eo_do_super(eo_obj, MY_CLASS, eo_constructor()); + +} + +static void _class_constructor(Eo_Class *klass) +{ + const Eo_Op_Func_Description func_desc[] = { + /* Virtual functions of parent class implemented in this class */ + EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor), + //EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor), + + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_SOURCE_SET), _source_set), + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_SOURCE_GET), _source_get), + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_FORMAT_SET), _format_set), + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_FORMAT_GET), _format_get), + + EO_OP_FUNC(ECORE_AUDIO_OBJ_IN_ID(ECORE_AUDIO_OBJ_IN_SUB_ID_SEEK), _seek), + EO_OP_FUNC(ECORE_AUDIO_OBJ_IN_ID(ECORE_AUDIO_OBJ_IN_SUB_ID_READ_INTERNAL), _read), + + EO_OP_FUNC_SENTINEL + }; + + eo_class_funcs_set(klass, func_desc); +} + +static const Eo_Op_Description op_desc[] = { + EO_OP_DESCRIPTION_SENTINEL +}; + +static const Eo_Class_Description class_desc = { + EO_VERSION, + MY_CLASS_NAME, + EO_CLASS_TYPE_REGULAR, + EO_CLASS_DESCRIPTION_OPS(&ECORE_AUDIO_OBJ_IN_SNDFILE_BASE_ID, op_desc, ECORE_AUDIO_OBJ_IN_SNDFILE_SUB_ID_LAST), + NULL, + sizeof(Ecore_Audio_Sndfile), + _class_constructor, + NULL +}; + +EO_DEFINE_CLASS(ecore_audio_obj_in_sndfile_class_get, &class_desc, ECORE_AUDIO_OBJ_IN_CLASS, NULL); diff --git a/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h b/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h new file mode 100644 index 0000000000..98a56ca66c --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h @@ -0,0 +1,57 @@ +#ifndef ECORE_AUDIO_IN_SNDFILE_H +#define ECORE_AUDIO_IN_SNDFILE_H + +#include +#include + +#ifdef EAPI +#undef EAPI +#endif + +#ifdef __GNUC__ +#if __GNUC__ >= 4 +#define EAPI __attribute__ ((visibility("default"))) +#else +#define EAPI +#endif +#else +#define EAPI +#endif + +/** + * @file ecore_audio_obj_in_sndfile.h + * @brief Audio Module + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup Ecore_Audio_Group + * @{ + */ + +#define ECORE_AUDIO_OBJ_IN_SNDFILE_CLASS ecore_audio_obj_in_sndfile_class_get() + +const Eo_Class *ecore_audio_obj_in_sndfile_class_get() EINA_CONST; + +extern EAPI Eo_Op ECORE_AUDIO_OBJ_IN_SNDFILE_BASE_ID; + +enum Ecore_Audio_Obj_In_Sndfile_Sub_Ids +{ + ECORE_AUDIO_OBJ_IN_SNDFILE_SUB_ID_LAST +}; + +#define ECORE_AUDIO_OBJ_IN_SNDFILE_ID(sub_id) (ECORE_AUDIO_OBJ_IN_SNDFILE_BASE_ID + EO_TYPECHECK(enum Ecore_Audio_Obj_In_Sndfile_Sub_Ids, sub_id) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.c b/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.c new file mode 100644 index 0000000000..b5fd2d59ff --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.c @@ -0,0 +1,218 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_FEATURES_H +#include +#endif + +#include +#include "ecore_audio_private.h" +#include + +EAPI Eo_Op ECORE_AUDIO_OBJ_OUT_SNDFILE_BASE_ID = EO_NOOP; + +#define MY_CLASS ECORE_AUDIO_OBJ_OUT_SNDFILE_CLASS +#define MY_CLASS_NAME "ecore_audio_obj_out_sndfile" + +struct _Ecore_Audio_Sndfile +{ + SNDFILE *handle; + SF_INFO sfinfo; + Ecore_Audio_Vio *vio; + Ecore_Idler *idler; +}; + +typedef struct _Ecore_Audio_Sndfile Ecore_Audio_Sndfile; + +static Eina_Bool _write_cb(void *data) +{ + Eo *eo_obj = data; + Eo *in; + + Ecore_Audio_Sndfile *obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_OUT_SNDFILE_CLASS); + Ecore_Audio_Output *out_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_OUT_CLASS); + + int written, bread; + float buf[1024]; + + /* TODO: Support mixing of multiple inputs */ + in = eina_list_data_get(out_obj->inputs); + + eo_do(in, ecore_audio_obj_in_read(buf, 4*1024, &bread)); + + if (bread == 0) { + sf_write_sync(obj->handle); + out_obj->paused = EINA_TRUE; + obj->idler = NULL; + return EINA_FALSE; + } + written = sf_write_float(obj->handle, buf, bread/4)*4; + + if (written != bread) + ERR("Short write! (%s)\n", sf_strerror(obj->handle)); + + return EINA_TRUE; +} + +static void _input_attach(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + Ecore_Audio_Object *ea_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + Ecore_Audio_Output *out_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_OUT_CLASS); + + Eo *in = va_arg(*list, Eo *); + + eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_out_input_attach(in)); + + eo_do(in, ecore_audio_obj_in_samplerate_get(&obj->sfinfo.samplerate)); + eo_do(in, ecore_audio_obj_in_channels_get(&obj->sfinfo.channels)); + + obj->handle = sf_open(ea_obj->source, SFM_WRITE, &obj->sfinfo); + + if (!obj->handle) { + eina_stringshare_del(ea_obj->source); + ea_obj->source = NULL; + return; + } + + if (ea_obj->paused) + return; + + if (out_obj->inputs) { + obj->idler = ecore_idler_add(_write_cb, eo_obj); + } +} + +static void _source_set(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + + Ecore_Audio_Object *ea_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + const char *source = va_arg(*list, const char *); + + if (obj->handle) { + sf_close(obj->handle); + obj->handle = NULL; + } + + eina_stringshare_replace(&ea_obj->source, source); + + if (!ea_obj->source) + return; + +} + +static void _source_get(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Ecore_Audio_Object *obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + const char **ret = va_arg(*list, const char **); + + if (ret) + *ret = obj->source; +} + +static void _format_set(Eo *eo_obj, void *_pd, va_list *list) +{ + Ecore_Audio_Sndfile *obj = _pd; + Ecore_Audio_Object *ea_obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + Ecore_Audio_Format format= va_arg(*list, Ecore_Audio_Format); + + if (ea_obj->source) { + ERR("Input is already open - cannot change format"); + return; + } + + switch (format) { + case ECORE_AUDIO_FORMAT_AUTO: + obj->sfinfo.format = 0; + case ECORE_AUDIO_FORMAT_WAV: + obj->sfinfo.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16; + break; + case ECORE_AUDIO_FORMAT_OGG: + obj->sfinfo.format = SF_FORMAT_OGG|SF_FORMAT_VORBIS; + break; + case ECORE_AUDIO_FORMAT_FLAC: + obj->sfinfo.format = SF_FORMAT_FLAC; + break; + default: + ERR("Format not supported!"); + return; + } + ea_obj->format = format; +} + +static void _format_get(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Ecore_Audio_Object *obj = eo_data_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + Ecore_Audio_Format *ret = va_arg(*list, Ecore_Audio_Format *); + + if (ret) + *ret = obj->format; +} + +static void _constructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED) +{ + Ecore_Audio_Sndfile *obj = _pd; + + eo_do_super(eo_obj, MY_CLASS, eo_constructor()); + + eo_do(eo_obj, ecore_audio_obj_format_set(ECORE_AUDIO_FORMAT_OGG)); +} + +static void _destructor(Eo *eo_obj, void *_pd, va_list *list EINA_UNUSED) +{ + Ecore_Audio_Sndfile *obj = _pd; + + if (obj->handle) + sf_close(obj->handle); + if (obj->idler) + ecore_idler_del(obj->idler); + + eo_do_super(eo_obj, MY_CLASS, eo_destructor()); +} + +static void _class_constructor(Eo_Class *klass) +{ + const Eo_Op_Func_Description func_desc[] = { + /* Virtual functions of parent class implemented in this class */ + EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor), + EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor), + + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_SOURCE_SET), _source_set), + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_SOURCE_GET), _source_get), + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_FORMAT_SET), _format_set), + EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_SUB_ID_FORMAT_GET), _format_get), + + EO_OP_FUNC(ECORE_AUDIO_OBJ_OUT_ID(ECORE_AUDIO_OBJ_OUT_SUB_ID_INPUT_ATTACH), _input_attach), + //EO_OP_FUNC(ECORE_AUDIO_OBJ_ID(ECORE_AUDIO_OBJ_OUT_SUB_ID_FORMAT_GET), _format_get), + EO_OP_FUNC_SENTINEL + }; + + eo_class_funcs_set(klass, func_desc); +} + +static const Eo_Op_Description op_desc[] = { + EO_OP_DESCRIPTION_SENTINEL +}; + +static const Eo_Class_Description class_desc = { + EO_VERSION, + MY_CLASS_NAME, + EO_CLASS_TYPE_REGULAR, + EO_CLASS_DESCRIPTION_OPS(&ECORE_AUDIO_OBJ_OUT_SNDFILE_BASE_ID, op_desc, ECORE_AUDIO_OBJ_OUT_SNDFILE_SUB_ID_LAST), + NULL, + sizeof(Ecore_Audio_Sndfile), + _class_constructor, + NULL +}; + +EO_DEFINE_CLASS(ecore_audio_obj_out_sndfile_class_get, &class_desc, ECORE_AUDIO_OBJ_OUT_CLASS, NULL); diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h b/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h new file mode 100644 index 0000000000..faea33cb0c --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h @@ -0,0 +1,57 @@ +#ifndef ECORE_AUDIO_OUT_SNDFILE_H +#define ECORE_AUDIO_OUT_SNDFILE_H + +#include +#include + +#ifdef EAPI +#undef EAPI +#endif + +#ifdef __GNUC__ +#if __GNUC__ >= 4 +#define EAPI __attribute__ ((visibility("default"))) +#else +#define EAPI +#endif +#else +#define EAPI +#endif + +/** + * @file ecore_audio_obj_out_sndfile.h + * @brief Audio Module + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup Ecore_Audio_Group + * @{ + */ + +#define ECORE_AUDIO_OBJ_OUT_SNDFILE_CLASS ecore_audio_obj_out_sndfile_class_get() + +const Eo_Class *ecore_audio_obj_out_sndfile_class_get() EINA_CONST; + +extern EAPI Eo_Op ECORE_AUDIO_OBJ_OUT_SNDFILE_BASE_ID; + +enum _Ecore_Audio_Obj_Out_Sndfile_Sub_Ids +{ + ECORE_AUDIO_OBJ_OUT_SNDFILE_SUB_ID_LAST +}; + +#define ECORE_AUDIO_OBJ_OUT_SNDFILE_ID(sub_id) (ECORE_AUDIO_OBJ_OUT_SNDFILE_BASE_ID + EO_TYPECHECK(enum _Ecore_Audio_Obj_Out_Sndfile_Sub_Ids, sub_id)) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif