From bb083ad3db58a57d8e14accdcb9bbec4c4b985ad Mon Sep 17 00:00:00 2001 From: Daniel Willmann Date: Tue, 14 May 2013 13:39:06 +0100 Subject: [PATCH] ecore_audio: Add alsa support Add alsa .c and .h file for Ecore_Audio alsa support This is not well tested and thus disabled by default Signed-off-by: Daniel Willmann --- configure.ac | 16 +- src/Makefile_Ecore_Audio.am | 8 + .../ecore_audio/ecore_audio_obj_out_alsa.c | 252 ++++++++++++++++++ .../ecore_audio/ecore_audio_obj_out_alsa.h | 63 +++++ src/lib/ecore_audio/ecore_audio_private.h | 8 - src/lib/edje/edje_multisense.c | 3 + 6 files changed, 340 insertions(+), 10 deletions(-) create mode 100644 src/lib/ecore_audio/ecore_audio_obj_out_alsa.c create mode 100644 src/lib/ecore_audio/ecore_audio_obj_out_alsa.h diff --git a/configure.ac b/configure.ac index 35f400c2c1..0431e1ef8d 100644 --- a/configure.ac +++ b/configure.ac @@ -2398,8 +2398,19 @@ EFL_LIB_START_OPTIONAL([Ecore_Audio], [test "${want_audio}" = "yes"]) ### Additional options to configure -# ALSA support is still not there, thus no option for it yet. -want_alsa="no" +# ALSA support is not well tested, disable by default +AC_ARG_ENABLE([alsa], + [AC_HELP_STRING([--enable-alsa], + [enable alsa sound support. @<:@default=enabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + want_alsa="yes" + else + want_alsa="no" + fi + ], + [want_alsa="no"]) + # sndfile is mandatory otherwise it won't read from/write to files. # TODO: if confirmed sndfile is mandatory, remove this variable @@ -2457,6 +2468,7 @@ EFL_ADD_FEATURE([ECORE_AUDIO], [sndfile]) ### Checks for library functions EFL_LIB_END_OPTIONAL([Ecore_Audio]) +AM_CONDITIONAL([HAVE_ECORE_AUDIO_ALSA], [test "x${want_alsa}" = "xyes"]) AM_CONDITIONAL([HAVE_ECORE_AUDIO_PULSE], [test "x${want_pulseaudio}" = "xyes"]) AM_CONDITIONAL([HAVE_ECORE_AUDIO_SNDFILE], [test "x${want_sndfile}" = "xyes"]) diff --git a/src/Makefile_Ecore_Audio.am b/src/Makefile_Ecore_Audio.am index 35a414cc52..62f2485410 100644 --- a/src/Makefile_Ecore_Audio.am +++ b/src/Makefile_Ecore_Audio.am @@ -27,6 +27,14 @@ lib_ecore_audio_libecore_audio_la_LIBADD = @ECORE_AUDIO_LIBS@ lib_ecore_audio_libecore_audio_la_DEPENDENCIES = @ECORE_AUDIO_INTERNAL_LIBS@ lib_ecore_audio_libecore_audio_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ +if HAVE_ECORE_AUDIO_ALSA +dist_installed_ecoreaudiomainheaders_DATA += \ +lib/ecore_audio/ecore_audio_obj_out_alsa.h + +lib_ecore_audio_libecore_audio_la_SOURCES += \ +lib/ecore_audio/ecore_audio_obj_out_alsa.c +endif + if HAVE_ECORE_AUDIO_PULSE dist_installed_ecoreaudiomainheaders_DATA += \ lib/ecore_audio/ecore_audio_obj_out_pulse.h diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_alsa.c b/src/lib/ecore_audio/ecore_audio_obj_out_alsa.c new file mode 100644 index 0000000000..38a5fd9ef9 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_alsa.c @@ -0,0 +1,252 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_FEATURES_H +#include +#endif + +#include +#include "ecore_audio_private.h" +#include "ecore_audio_obj_out_alsa.h" +#include + +#include +#include + +EAPI Eo_Op ECORE_AUDIO_OBJ_OUT_ALSA_BASE_ID = EO_NOOP; + +#define MY_CLASS ECORE_AUDIO_OBJ_OUT_ALSA_CLASS +#define MY_CLASS_NAME "ecore_audio_obj_out_alsa" + + +struct _Ecore_Audio_Alsa +{ + char *foo; +}; + +typedef struct _Ecore_Audio_Alsa Ecore_Audio_Alsa; + +struct _Ecore_Audio_Alsa_Data +{ + snd_pcm_t *handle; + Ecore_Timer *timer; +}; + +typedef struct _Ecore_Audio_Alsa_Data Ecore_Audio_Alsa_Data; + +static void _volume_set(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Eo *in; + Ecore_Audio_Alsa_Data *alsa; + Eina_List *input; + Ecore_Audio_Output *out_obj = eo_data_scope_get(eo_obj, ECORE_AUDIO_OBJ_OUT_CLASS); + + double volume = va_arg(*list, double); + + if (volume < 0) + volume = 0; + + eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_volume_set(volume)); + + EINA_LIST_FOREACH(out_obj->inputs, input, in) { + eo_do(in, eo_base_data_get("alsa_data", (void **)&alsa)); + /* FIXME: Volume control */ + } + +} + +static Eina_Bool _write_cb(void *data) +{ + Eo *in = data; + + Ecore_Audio_Alsa_Data *alsa; + void *buf; + + size_t avail; + ssize_t bread; + int ret; + + eo_do(in, eo_base_data_get("alsa_data", (void **)&alsa)); + + avail = snd_pcm_avail_update(alsa->handle); + + buf = calloc(1, sizeof(float) * avail * 8); + + eo_do(in, ecore_audio_obj_in_read(buf, avail*8, &bread)); + + ret = snd_pcm_writei(alsa->handle, buf, bread/8); + if (ret < 0) + { + ERR("Error when writing: %s", snd_strerror(ret)); + snd_pcm_recover(alsa->handle, ret, 0); + } + + return EINA_TRUE; +} + +static Eina_Bool _update_samplerate_cb(void *data EINA_UNUSED, Eo *eo_obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Ecore_Audio_Alsa_Data *alsa; + int samplerate, channels; + int ret; + double speed; + + eo_do(eo_obj, ecore_audio_obj_in_samplerate_get(&samplerate)); + eo_do(eo_obj, ecore_audio_obj_in_channels_get(&channels)); + eo_do(eo_obj, ecore_audio_obj_in_speed_get(&speed)); + + samplerate *= speed; + + eo_do(eo_obj, eo_base_data_get("alsa_data", (void **)&alsa)); + + ret = snd_pcm_set_params(alsa->handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, channels, samplerate, 1, 500000); + + return EINA_TRUE; +} + +static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in) +{ + Ecore_Audio_Alsa_Data *alsa; + const char *name; + double speed; + unsigned int samplerate, channels; + int ret; + Eina_Bool success; + + Ecore_Audio_Object *ea_obj = eo_data_scope_get(eo_obj, ECORE_AUDIO_OBJ_CLASS); + + eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_out_input_attach(in, &success)); + if (!success) + return EINA_FALSE; + + eo_do(in, ecore_audio_obj_in_samplerate_get((int *)&samplerate)); + eo_do(in, ecore_audio_obj_in_speed_get(&speed)); + eo_do(in, ecore_audio_obj_in_channels_get((int *)&channels)); + eo_do(in, ecore_audio_obj_name_get(&name)); + + samplerate *= speed; + + alsa = calloc(1, sizeof(Ecore_Audio_Alsa_Data)); + + ret = snd_pcm_open(&alsa->handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if (ret < 0) + { + ERR("Could not open for playback: %s", snd_strerror(ret)); + ERR("Could not create stream"); + free(alsa); + eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_out_input_detach(in, NULL)); + return EINA_FALSE; + } + + ret = snd_pcm_set_params(alsa->handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, channels, samplerate, 1, 500000); + if (ret < 0) + { + free(alsa); + ERR("Could not set parameters: %s", snd_strerror(ret)); + snd_pcm_close(alsa->handle); + return EINA_FALSE; + } + + eo_do(in, eo_event_callback_add(ECORE_AUDIO_EV_IN_SAMPLERATE_CHANGED, _update_samplerate_cb, eo_obj)); + + eo_do(in, eo_base_data_set("alsa_data", alsa, free)); + + if (ea_obj->paused) + return EINA_TRUE; + + alsa->timer = ecore_timer_add(0.3, _write_cb, in); + + return EINA_TRUE; +} + +static void _input_attach(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Eina_Bool retval = EINA_TRUE; + + Eo *in = va_arg(*list, Eo *); + Eina_Bool *ret = va_arg(*list, Eina_Bool *); + + retval = _input_attach_internal(eo_obj, in); + if (ret) + *ret = retval; +} + +static void _input_detach(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list) +{ + Ecore_Audio_Alsa_Data *alsa; + Eina_Bool ret2; + + Eo *in = va_arg(*list, Eo *); + Eina_Bool *ret = va_arg(*list, Eina_Bool *); + + if (ret) + *ret = EINA_FALSE; + + eo_do_super(eo_obj, MY_CLASS, ecore_audio_obj_out_input_detach(in, &ret2)); + if (!ret2) + return; + + eo_do(in, eo_base_data_get("alsa_data", (void **)&alsa)); + + ecore_timer_del(alsa->timer); + + snd_pcm_close(alsa->handle); + + eo_do(in, eo_base_data_del("alsa_data")); + + if (ret) + *ret = EINA_TRUE; +} + +static void _constructor(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED) +{ + Ecore_Audio_Output *out_obj = eo_data_scope_get(eo_obj, ECORE_AUDIO_OBJ_OUT_CLASS); + + eo_do_super(eo_obj, MY_CLASS, eo_constructor()); + + out_obj->need_writer = EINA_FALSE; +} + +static void _destructor(Eo *eo_obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED) +{ + 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_VOLUME_SET), _volume_set), + + EO_OP_FUNC(ECORE_AUDIO_OBJ_OUT_ID(ECORE_AUDIO_OBJ_OUT_SUB_ID_INPUT_ATTACH), _input_attach), + EO_OP_FUNC(ECORE_AUDIO_OBJ_OUT_ID(ECORE_AUDIO_OBJ_OUT_SUB_ID_INPUT_DETACH), _input_detach), + 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_ALSA_BASE_ID, op_desc, ECORE_AUDIO_OBJ_OUT_ALSA_SUB_ID_LAST), + NULL, + sizeof(Ecore_Audio_Alsa), + _class_constructor, + NULL +}; + +EO_DEFINE_CLASS(ecore_audio_obj_out_alsa_class_get, &class_desc, ECORE_AUDIO_OBJ_OUT_CLASS, NULL); diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_alsa.h b/src/lib/ecore_audio/ecore_audio_obj_out_alsa.h new file mode 100644 index 0000000000..0dd6524e31 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_alsa.h @@ -0,0 +1,63 @@ +#ifndef ECORE_AUDIO_OUT_ALSA_H +#define ECORE_AUDIO_OUT_ALSA_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_alsa.h + * @brief Ecore_Audio alsa output + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @defgroup ecore_audio_obj_out_alsa - Ecore_Audio alsa output + * @intogroup Ecore_Audio_Group + * @{ + */ + +#define ECORE_AUDIO_OBJ_OUT_ALSA_CLASS ecore_audio_obj_out_alsa_class_get() /**< Ecore_Audio alsa output */ + +/** + * @brief Get the Eo class ID + * + * @return The Eo class ID + */ +const Eo_Class *ecore_audio_obj_out_alsa_class_get() EINA_CONST; + +extern EAPI Eo_Op ECORE_AUDIO_OBJ_OUT_ALSA_BASE_ID; + +enum _Ecore_Audio_Obj_Out_Alsa_Sub_Ids +{ + ECORE_AUDIO_OBJ_OUT_ALSA_SUB_ID_LAST +}; + +#define ECORE_AUDIO_OBJ_OUT_ALSA_ID(sub_id) (ECORE_AUDIO_OBJ_OUT_ALSA_BASE_ID + EO_TYPECHECK(enum _Ecore_Audio_Obj_Out_Alsa_Sub_Ids, sub_id)) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/ecore_audio/ecore_audio_private.h b/src/lib/ecore_audio/ecore_audio_private.h index 35afa85f4c..a28ca7c374 100644 --- a/src/lib/ecore_audio/ecore_audio_private.h +++ b/src/lib/ecore_audio/ecore_audio_private.h @@ -146,14 +146,6 @@ extern Eina_List *ecore_audio_modules; #ifdef HAVE_ALSA /* ecore_audio_alsa */ -struct _Ecore_Audio_Alsa -{ - ECORE_MAGIC; - snd_pcm_t *handle; - unsigned int channels; - unsigned int samplerate; -}; - Ecore_Audio_Module *ecore_audio_alsa_init(void); void ecore_audio_alsa_shutdown(void); #endif /* HAVE_ALSA */ diff --git a/src/lib/edje/edje_multisense.c b/src/lib/edje/edje_multisense.c index a6860c933c..8bcbbf83c6 100644 --- a/src/lib/edje/edje_multisense.c +++ b/src/lib/edje/edje_multisense.c @@ -9,6 +9,9 @@ #ifdef HAVE_PULSE #include "ecore_audio_obj_out_pulse.h" #define MY_CLASS ECORE_AUDIO_OBJ_OUT_PULSE_CLASS +#elif HAVE_ALSA +#include "ecore_audio_obj_out_alsa.h" +#define MY_CLASS ECORE_AUDIO_OBJ_OUT_ALSA_CLASS #else #error "Multisense needs Pulseaudio suport!" #endif