aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile_Ecore_Audio.am21
-rw-r--r--src/lib/ecore_audio/Ecore_Audio.h9
-rw-r--r--src/lib/ecore_audio/ecore_audio_core_audio.c44
-rw-r--r--src/lib/ecore_audio/ecore_audio_obj_out_core_audio.c358
-rw-r--r--src/lib/ecore_audio/ecore_audio_obj_out_core_audio.h15
-rw-r--r--src/lib/ecore_audio/ecore_audio_out_core_audio.eo12
-rw-r--r--src/lib/ecore_audio/ecore_audio_private.h6
-rw-r--r--src/lib/edje/edje_multisense.c12
8 files changed, 474 insertions, 3 deletions
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 <ecore_audio_obj_in_tone.h>
-#include <ecore_audio_obj_out_pulse.h>
+#if HAVE_COREAUDIO
+# include <ecore_audio_obj_out_core_audio.h>
+#endif
+
+#if HAVE_PULSE
+# include <ecore_audio_obj_out_pulse.h>
+#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 <config.h>
+#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 <config.h>
+#endif
+
+#include <Eo.h>
+#include "ecore_audio_private.h"
+
+#include <CoreAudio/CoreAudio.h>
+
+/* 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++;
}