diff --git a/configure.ac b/configure.ac index 45fa102ffe..9eed98ce9b 100644 --- a/configure.ac +++ b/configure.ac @@ -3208,6 +3208,54 @@ AC_ARG_ENABLE([pulseaudio], ], [want_pulseaudio="yes"]) +if test "x${have_darwin}" = "xyes"; then + want_pulseaudio="no" + want_alsa="no" + want_coreaudio="yes" +else + want_coreaudio="no" +fi + + +# CoreAudio flags +if test "x${want_coreaudio}" = "xyes"; then + coreaudio_ldflags="" + have_coreaudio="no" + LIBS_save="$LIBS" + LIBS="$LIBS -framework CoreAudio" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include + ]], + [[ +UInt32 size; +AudioDeviceID dev_id; +AudioObjectPropertyAddress prop = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster +}; +size = sizeof(AudioDeviceID); +AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, + &size, &dev_id); + ]])], + [ + have_coreaudio="yes" + coreaudio_ldflags="-framework CoreAudio" + ], + [have_coreaudio="no"]) + LIBS="$LIBS_save" + AC_MSG_CHECKING([whether Apple CoreAudio framework is supported]) + AC_MSG_RESULT([${have_coreaudio}]) +fi +AC_SUBST(coreaudio_ldflags) +if test "x${have_coreaudio}" = "xyes"; then + AC_DEFINE([HAVE_COREAUDIO], [1], [CoreAudio support enabled]) +else + AC_DEFINE([HAVE_COREAUDIO], [0], [CoreAudio support disabled]) +fi + ### Default values @@ -3234,6 +3282,7 @@ EFL_EVAL_PKGS([ECORE_AUDIO]) EFL_ADD_FEATURE([ECORE_AUDIO], [alsa]) EFL_ADD_FEATURE([ECORE_AUDIO], [pulseaudio]) EFL_ADD_FEATURE([ECORE_AUDIO], [sndfile]) +EFL_ADD_FEATURE([ECORE_AUDIO], [coreaudio]) ### Checks for header files @@ -3250,6 +3299,7 @@ EFL_ADD_FEATURE([ECORE_AUDIO], [sndfile]) EFL_LIB_END_OPTIONAL([Ecore_Audio]) AM_CONDITIONAL([HAVE_ECORE_AUDIO_PULSE], [test "x${want_pulseaudio}" = "xyes"]) AM_CONDITIONAL([HAVE_ECORE_AUDIO_SNDFILE], [test "x${want_sndfile}" = "xyes"]) +AM_CONDITIONAL([HAVE_ECORE_AUDIO_CORE_AUDIO], [test "x${want_coreaudio}" = "xyes"]) #### End of Ecore_Audio @@ -4117,7 +4167,7 @@ EFL_LIB_START([Edje]) ### Additional options to configure ### Default values -want_multisense="${want_pulseaudio}" + AC_ARG_ENABLE([multisense], [AS_HELP_STRING([--enable-multisense],[Enable multisense support. @<:@default=enabled@:>@])], [ @@ -4128,7 +4178,13 @@ AC_ARG_ENABLE([multisense], CFOPT_WARNING="xyes" fi ], - [want_multisense="${want_pulseaudio}"]) + [ + if test "x${want_pulseaudio}" = "xyes" -o "x${want_coreaudio}" = "xyes"; then + want_multisense="yes" + else + want_multisense="no" + fi + ]) # TODO: should we keep or remove these? want_edje_program_cache="no" @@ -4960,11 +5016,12 @@ if test -n "$CFOPT_WARNING"; then echo "Reconsider disabling audio." echo "_____________________________________________________________________" fi - if test "x${want_pulseaudio}" = "xno"; then + if test "x${have_darwin}" = "xno" -a "x${want_pulseaudio}" = "xno"; then echo "_____________________________________________________________________" - echo "The only audio output method supported by Ecore right now is via" - echo "Pulseaudio. You have disabled that and likely have broken a whole" - echo "bunch of things in the process. Reconsider your configure options." + echo "The only audio output method supported by Ecore right now on your" + echo "system is via Pulseaudio. You have disabled that and likely have" + echo "broken a whole bunch of things in the process. Reconsider your" + echo "configure options." echo "_____________________________________________________________________" fi if test "x${want_xinput2}" = "xno"; then @@ -5143,3 +5200,4 @@ if test "x${efl_deprecated_option}" = "xyes"; then echo "" echo "#-------------------------------------------------------------------#" fi + diff --git a/src/Makefile_Ecore_Audio.am b/src/Makefile_Ecore_Audio.am index e87ffb408a..8881f19401 100644 --- a/src/Makefile_Ecore_Audio.am +++ b/src/Makefile_Ecore_Audio.am @@ -8,9 +8,17 @@ ecore_audio_eolian_files = \ lib/ecore_audio/ecore_audio_out.eo \ lib/ecore_audio/ecore_audio_in_sndfile.eo \ lib/ecore_audio/ecore_audio_out_sndfile.eo \ - lib/ecore_audio/ecore_audio_out_pulse.eo \ lib/ecore_audio/ecore_audio_in_tone.eo + +if HAVE_ECORE_AUDIO_PULSE +ecore_audio_eolian_files += lib/ecore_audio/ecore_audio_out_pulse.eo +endif + +if HAVE_ECORE_AUDIO_CORE_AUDIO +ecore_audio_eolian_files += lib/ecore_audio/ecore_audio_out_core_audio.eo +endif + ecore_audio_eolian_c = $(ecore_audio_eolian_files:%.eo=%.eo.c) ecore_audio_eolian_h = $(ecore_audio_eolian_files:%.eo=%.eo.h) @@ -40,7 +48,7 @@ lib/ecore_audio/ecore_audio_obj_in_tone.h \ lib/ecore_audio/ecore_audio_protected.h nodist_installed_ecoreaudiomainheaders_DATA = $(ecore_audio_eolian_h) - + lib_ecore_audio_libecore_audio_la_SOURCES = \ lib/ecore_audio/ecore_audio.c \ lib/ecore_audio/ecore_audio_obj.c \ @@ -74,6 +82,15 @@ lib/ecore_audio/ecore_audio_obj_out_sndfile.c \ lib/ecore_audio/ecore_audio_sndfile_vio.c endif +if HAVE_ECORE_AUDIO_CORE_AUDIO +dist_installed_ecoreaudiomainheaders_DATA += \ +lib/ecore_audio/ecore_audio_obj_out_core_audio.h +lib_ecore_audio_libecore_audio_la_SOURCES += \ +lib/ecore_audio/ecore_audio_obj_out_core_audio.c +lib_ecore_audio_libecore_audio_la_LDFLAGS += @coreaudio_ldflags@ + +endif + endif if HAVE_ELUA diff --git a/src/lib/ecore_audio/Ecore_Audio.h b/src/lib/ecore_audio/Ecore_Audio.h index 0835d6d99d..edb9b49ea7 100644 --- a/src/lib/ecore_audio/Ecore_Audio.h +++ b/src/lib/ecore_audio/Ecore_Audio.h @@ -44,6 +44,7 @@ enum _Ecore_Audio_Type { ECORE_AUDIO_TYPE_ALSA, /**< Use ALSA module*/ ECORE_AUDIO_TYPE_SNDFILE, /**< Use libsndfile module */ ECORE_AUDIO_TYPE_TONE, /**< Use tone module */ + ECORE_AUDIO_TYPE_CORE_AUDIO, /**< Use Core Audio module (Apple) */ ECORE_AUDIO_TYPE_CUSTOM, /**< Use custom module */ ECORE_AUDIO_MODULE_LAST, /**< Sentinel */ }; @@ -211,7 +212,13 @@ EAPI int ecore_audio_shutdown(void); #include -#include +#if HAVE_COREAUDIO +# include +#endif + +#if HAVE_PULSE +# include +#endif /** * @} diff --git a/src/lib/ecore_audio/ecore_audio_core_audio.c b/src/lib/ecore_audio/ecore_audio_core_audio.c new file mode 100644 index 0000000000..2141d9d330 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_core_audio.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_COREAUDIO +#include "Ecore.h" +#include "ecore_private.h" +#include "Ecore_Audio.h" +#include "ecore_audio_private.h" + +static Ecore_Audio_Module *_module = NULL; + +EAPI Ecore_Audio_Module * +ecore_audio_core_audio_init(void) +{ + /* Don't call this twice */ + if (_module != NULL) return _module; + + _module = calloc(1, sizeof(Ecore_Audio_Module)); + if (EINA_UNLIKELY(_module == NULL)) + { + CRI("Failed to allocate Ecore_Audio_Module"); + goto ret_null; + } + + ECORE_MAGIC_SET(_module, ECORE_MAGIC_AUDIO_MODULE); + _module->type = ECORE_AUDIO_TYPE_CORE_AUDIO; + _module->name = "CoreAudio"; + + return _module; + +ret_null: + return NULL; +} + +EAPI void +ecore_audio_nssound_shutdown(void) +{ + free(_module); + _module = NULL; +} + +#endif /* HAVE_COREAUDIO */ + diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_core_audio.c b/src/lib/ecore_audio/ecore_audio_obj_out_core_audio.c new file mode 100644 index 0000000000..d358ec036d --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_core_audio.c @@ -0,0 +1,358 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "ecore_audio_private.h" + +#include + +/* Notes: + * + * A lot of source code on the internet dealing with CoreAudio is deprecated. + * sndfile-play (bundled with libsndfile) is no exception and uses an almost + * 10 years old API. Nethertheless, sndfile-play has been heavily used to + * create the CoreAudio module. + * + * Documentation is almost non-existant, but here is the technical note from + * Apple explaining how CoreAudio objects should be manipulated: + * https://developer.apple.com/library/mac/technotes/tn2223/_index.html + */ + +#include "ecore_audio_obj_out_core_audio.h" + +typedef struct +{ + Eo *input; + Eo *output; + AudioDeviceIOProcID proc_id; + AudioStreamBasicDescription format; + AudioObjectID obj_id; + UInt32 buf_size; + + Eina_Bool is_playing; + Eina_Bool fake_stereo; +} Core_Audio_Helper; + + +/* Apple's error codes are tricky: they are stored as 32 bits integers. + * However, they are supposed to be represented as 4-bytes strings. + * There is no equivalent of strerror() (of what I know). + * + * Ref: http://vgable.com/blog/2008/04/23/printing-a-fourcharcode/ + * + * In case of error, take a look at CoreAudio/AudioHardwareBase.h where + * the error codes are explained. + */ +#define APPLE_ERROR(err_) \ + (char[5]){((err_) >> 24) & 0xff, ((err_) >> 16) & 0xff, ((err_) >> 8) & 0xff, (err_) & 0xff, 0} + +#define MY_CLASS ECORE_AUDIO_OUT_CORE_AUDIO_CLASS +#define MY_CLASS_NAME "Ecore_Audio_Out_Core_Audio" + +/* + * Unused structure. Only here because of Eolian. + * XXX Maybe it is possible to get rid of it. + */ +typedef struct +{ + void *this_data_is_here_to_silent_warnings; +} Ecore_Audio_Out_Core_Audio_Data; + + +/*============================================================================* + * Helper API * + *============================================================================*/ + +static Core_Audio_Helper * +_core_audio_helper_new(void) +{ + return calloc(1, sizeof(Core_Audio_Helper)); +} + +static void +_core_audio_helper_stop(Core_Audio_Helper *helper) +{ + EINA_SAFETY_ON_NULL_RETURN(helper); + + OSStatus err; + + if (!helper->is_playing) return; + + /* Stop audio device */ + err = AudioDeviceStop(helper->obj_id, helper->proc_id); + if (EINA_UNLIKELY(err != noErr)) + ERR("Failed to stop audio device %i for proc id %p: '%s'", + helper->obj_id, helper->proc_id, APPLE_ERROR(err)); + + /* Remove proc ID */ + err = AudioDeviceDestroyIOProcID(helper->obj_id, helper->proc_id); + if (EINA_UNLIKELY(err != noErr)) + ERR("Failed to stop audio device %i for proc id %p: '%s'", + helper->obj_id, helper->proc_id, APPLE_ERROR(err)); + + helper->is_playing = EINA_FALSE; +} + +static void +_core_audio_helper_free(Core_Audio_Helper *helper) +{ + EINA_SAFETY_ON_NULL_RETURN(helper); + + if (helper->is_playing) + _core_audio_helper_stop(helper); + free(helper); +} + + +/*============================================================================* + * Audio Object Properties * + *============================================================================*/ + +static OSStatus +_audio_object_id_get(AudioObjectID *obj_id) +{ + OSStatus err; + UInt32 size; + AudioObjectPropertyAddress prop = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopePlayThrough, + kAudioObjectPropertyElementMaster + }; + + /* Default output device */ + size = sizeof(AudioObjectID); + err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, + &size, obj_id); + return err; +} + +static OSStatus +_audio_device_stream_format_get(AudioObjectID obj_id, + AudioStreamBasicDescription *format) +{ + OSStatus err; + UInt32 size; + AudioObjectPropertyAddress prop = { + kAudioDevicePropertyStreamFormat, + kAudioObjectPropertyScopePlayThrough, + kAudioObjectPropertyElementMaster /* Channel number */ + }; + + size = sizeof(AudioStreamBasicDescription); + err = AudioObjectGetPropertyData(obj_id, &prop, 0, NULL, &size, format); + return err; +} + +static OSStatus +_audio_device_stream_format_set(AudioObjectID obj_id, + AudioStreamBasicDescription *format) +{ + OSStatus err; + UInt32 size; + AudioObjectPropertyAddress prop = { + kAudioDevicePropertyStreamFormat, + kAudioObjectPropertyScopePlayThrough, + kAudioObjectPropertyElementMaster /* Channel number */ + }; + + size = sizeof(AudioStreamBasicDescription); + err = AudioObjectSetPropertyData(obj_id, &prop, 0, NULL, size, format); + return err; +} + + +/*============================================================================* + * Audio Callback * + *============================================================================*/ + +static OSStatus +_audio_io_proc_cb(AudioObjectID obj_id EINA_UNUSED, + const AudioTimeStamp *in_now EINA_UNUSED, + const AudioBufferList *input_data EINA_UNUSED, + const AudioTimeStamp *input_time EINA_UNUSED, + AudioBufferList *output_data, + const AudioTimeStamp *in_output_time EINA_UNUSED, + void *data) +{ + Core_Audio_Helper *helper = data; + float *buf; + int size, bread, sample_count, k; + + size = output_data->mBuffers[0].mDataByteSize; + buf = output_data->mBuffers[0].mData; + sample_count = size / sizeof(float); + + if (helper->fake_stereo) + { + eo_do(helper->input, bread = ecore_audio_obj_in_read(buf, size * 2)); + + for (k = bread - 1; k >= 0; --k) + { + buf[2 * k + 0] = buf[k]; + buf[2 * k + 1] = buf[k]; + } + bread /= 2; + } + else + { + eo_do(helper->input, bread = ecore_audio_obj_in_read(buf, size * 4)); + bread /= 4; + } + + /* Done playing */ + if (bread < sample_count) + { + INF("Done playing: %i < %i", bread, sample_count); + /* Auto-detached. Don't need to do more. */ + } + + return noErr; +} + + +/*============================================================================* + * Eo API * + *============================================================================*/ + +EOLIAN static void +_ecore_audio_out_core_audio_eo_base_constructor(Eo *obj, Ecore_Audio_Out_Core_Audio_Data *sd EINA_UNUSED) +{ + eo_do_super(obj, MY_CLASS, eo_constructor()); +} + +EOLIAN static void +_ecore_audio_out_core_audio_eo_base_destructor(Eo *obj, Ecore_Audio_Out_Core_Audio_Data *sd EINA_UNUSED) +{ + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +EOLIAN static void +_ecore_audio_out_core_audio_ecore_audio_volume_set(Eo *obj, Ecore_Audio_Out_Core_Audio_Data *sd EINA_UNUSED, double volume) +{ + // TODO Change volume of playing inputs + eo_do_super(obj, MY_CLASS, ecore_audio_obj_volume_set(volume)); +} + +EOLIAN static Eina_Bool +_ecore_audio_out_core_audio_ecore_audio_out_input_attach(Eo *obj, Ecore_Audio_Out_Core_Audio_Data *sd EINA_UNUSED, Eo *input) +{ + Core_Audio_Helper *helper; + UInt32 channels; + OSStatus err; + Eina_Bool chk; + + eo_do_super(obj, MY_CLASS, chk = ecore_audio_obj_out_input_attach(input)); + if (EINA_UNLIKELY(!chk)) + { + ERR("Failed to attach input (eo_do_super)"); + goto return_failure; + } + + helper = _core_audio_helper_new(); + if (EINA_UNLIKELY(helper == NULL)) + { + CRI("Failed to allocate memory"); + goto detach; + } + + /* Keep track of input source and output object */ + helper->input = input; + helper->output = obj; + + /* Default output device */ + err = _audio_object_id_get(&(helper->obj_id)); + if (EINA_UNLIKELY(err != noErr)) + { + ERR("Failed to get object property: default output device: '%s'", + APPLE_ERROR(err)); + goto free_helper; + } + + /* Get data format description */ + err = _audio_device_stream_format_get(helper->obj_id, &(helper->format)); + if (EINA_UNLIKELY(err != noErr)) + { + ERR("Failed to get property: stream format: '%s'", APPLE_ERROR(err)); + goto free_helper; + } + + /* Forward samplerate to CoreAudio */ + eo_do(input, helper->format.mSampleRate = ecore_audio_obj_in_samplerate_get()); + + /* Set channels. If only 1 channel, emulate stereo */ + eo_do(input, channels = ecore_audio_obj_in_channels_get()); + if (channels == 1) + { + DBG("Fake stereo enabled for input %p", input); + helper->fake_stereo = EINA_TRUE; + channels = 2; + } + helper->format.mChannelsPerFrame = channels; + + /* Set new format description */ + err = _audio_device_stream_format_set(helper->obj_id, &(helper->format)); + if (EINA_UNLIKELY(err != noErr)) + { + ERR("Failed to set property: stream format: '%s'", APPLE_ERROR(err)); + goto free_helper; + } + + /* We want linear PCM */ + if (helper->format.mFormatID != kAudioFormatLinearPCM) + { + ERR("Invalid format ID. Expected linear PCM: '%s'", APPLE_ERROR(err)); + goto free_helper; + } + + /* Create IO proc ID */ + err = AudioDeviceCreateIOProcID(helper->obj_id, _audio_io_proc_cb, + helper, &(helper->proc_id)); + if (err != noErr) + { + ERR("Failed to create IO proc ID. Error: '%s'", APPLE_ERROR(err)); + goto free_helper; + } + + /* Keep track of data for deallocation */ + eo_do(input, eo_key_data_set("coreaudio_data", helper, NULL)); + + /* Start playing */ + helper->is_playing = EINA_TRUE; + err = AudioDeviceStart(helper->obj_id, helper->proc_id); + if (err != noErr) + { + ERR("Failed to start proc ID %p for device id %i: '%s'", + helper->proc_id, helper->obj_id, APPLE_ERROR(err)); + goto free_proc_id; + } + + return EINA_TRUE; + +free_proc_id: + AudioDeviceDestroyIOProcID(helper->obj_id, helper->proc_id); +free_helper: + free(helper); +detach: + eo_do_super(obj, MY_CLASS, ecore_audio_obj_out_input_detach(input)); +return_failure: + return EINA_FALSE; +} + +EOLIAN static Eina_Bool +_ecore_audio_out_core_audio_ecore_audio_out_input_detach(Eo *obj, Ecore_Audio_Out_Core_Audio_Data *sd EINA_UNUSED, Eo *input) +{ + Core_Audio_Helper *data; + Eina_Bool ret; + + DBG("Detach"); + /* Free helper */ + eo_do(input, data = eo_key_data_get("coreaudio_data")); + _core_audio_helper_free(data); + + eo_do_super(obj, MY_CLASS, ret = ecore_audio_obj_out_input_detach(input)); + + return ret; +} + +#include "ecore_audio_out_core_audio.eo.c" diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_core_audio.h b/src/lib/ecore_audio/ecore_audio_obj_out_core_audio.h new file mode 100644 index 0000000000..9b8eb7d8f8 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_core_audio.h @@ -0,0 +1,15 @@ +#ifndef _ECORE_AUDIO_OBJ_OUT_CORE_AUDIO_H_ +#define _ECORE_AUDIO_OBJ_OUT_CORE_AUDIO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ecore_audio_out_core_audio.eo.h" + +#ifdef __cplusplus +} +#endif + +#endif /* ! _ECORE_AUDIO_OBJ_OUT_CORE_AUDIO_H_ */ + diff --git a/src/lib/ecore_audio/ecore_audio_out_core_audio.eo b/src/lib/ecore_audio/ecore_audio_out_core_audio.eo new file mode 100644 index 0000000000..6176ce73a9 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_out_core_audio.eo @@ -0,0 +1,12 @@ +class Ecore_Audio_Out_Core_Audio (Ecore_Audio_Out) +{ + eo_prefix: ecore_audio_obj_out_core_audio; + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Ecore_Audio.volume.set; + Ecore_Audio_Out.input_attach; + Ecore_Audio_Out.input_detach; + } +} + diff --git a/src/lib/ecore_audio/ecore_audio_private.h b/src/lib/ecore_audio/ecore_audio_private.h index 36a06f5f4c..13a7b6ae4c 100644 --- a/src/lib/ecore_audio/ecore_audio_private.h +++ b/src/lib/ecore_audio/ecore_audio_private.h @@ -169,6 +169,12 @@ Ecore_Audio_Module *ecore_audio_sndfile_init(void); void ecore_audio_sndfile_shutdown(void); #endif /* HAVE_SNDFILE */ +#ifdef HAVE_COREAUDIO +/* ecore_audio_core_audio */ +Ecore_Audio_Module *ecore_audio_core_audio_init(void); +void ecore_audio_core_audio_shutdown(void); +#endif /* HAVE_COREAUDIO */ + Ecore_Audio_Module *ecore_audio_tone_init(void); void ecore_audio_tone_shutdown(void); diff --git a/src/lib/edje/edje_multisense.c b/src/lib/edje/edje_multisense.c index da24e0cf4a..913c1f9a89 100644 --- a/src/lib/edje/edje_multisense.c +++ b/src/lib/edje/edje_multisense.c @@ -204,13 +204,21 @@ _edje_multisense_internal_sound_sample_play(Edje *ed, const char *sample_name, c eo_event_callback_add(ECORE_AUDIO_IN_EVENT_IN_STOPPED, _play_finished, NULL)); if (!out) { +#if HAVE_COREAUDIO + out = eo_add(ECORE_AUDIO_OUT_CORE_AUDIO_CLASS, NULL); +#elif HAVE_PULSE out = eo_add(ECORE_AUDIO_OUT_PULSE_CLASS, NULL, eo_event_callback_add(ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, _out_fail, NULL)); +#endif if (out) outs++; } if (!out) { +#if HAVE_COREAUDIO + ERR("Could not create multisense audio out (CoreAudio)"); +#elif HAVE_PULSE ERR("Could not create multisense audio out (pulse)"); +#endif eo_del(in); return EINA_FALSE; } @@ -269,8 +277,12 @@ _edje_multisense_internal_sound_tone_play(Edje *ed, const char *tone_name, const if (!out) { +#if HAVE_COREAUDIO + out = eo_add(ECORE_AUDIO_OUT_CORE_AUDIO_CLASS, NULL); +#elif HAVE_PULSE out = eo_add(ECORE_AUDIO_OUT_PULSE_CLASS, NULL, eo_event_callback_add(ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, _out_fail, NULL)); +#endif if (out) outs++; }