From 53c548a97e8e4274758e6a54b42aa5bea5c68e01 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Wed, 13 Dec 2017 10:04:49 -0800 Subject: [PATCH] Revert "Revert "ecore_audio: a minimal template for playing sound on WINDOWS is added"" This reverts commit 4457f8c9c8aea0dffa6fbbfd6caaa655da1c0094. --- configure.ac | 22 +- src/Makefile_Ecore_Audio.am | 11 +- src/lib/ecore_audio/Ecore_Audio.h | 50 +- src/lib/ecore_audio/ecore_audio_obj.h | 22 - src/lib/ecore_audio/ecore_audio_obj_in.h | 23 - .../ecore_audio/ecore_audio_obj_in_sndfile.h | 23 - src/lib/ecore_audio/ecore_audio_obj_in_tone.h | 23 - src/lib/ecore_audio/ecore_audio_obj_out.h | 23 - .../ecore_audio/ecore_audio_obj_out_pulse.h | 23 - .../ecore_audio/ecore_audio_obj_out_sndfile.h | 23 - .../ecore_audio/ecore_audio_obj_out_wasapi.c | 609 ++++++++++++++++++ .../ecore_audio/ecore_audio_obj_out_wasapi.h | 22 + src/lib/ecore_audio/ecore_audio_out_wasapi.eo | 18 + 13 files changed, 715 insertions(+), 177 deletions(-) create mode 100644 src/lib/ecore_audio/ecore_audio_obj_out_wasapi.c create mode 100644 src/lib/ecore_audio/ecore_audio_obj_out_wasapi.h create mode 100644 src/lib/ecore_audio/ecore_audio_out_wasapi.eo diff --git a/configure.ac b/configure.ac index b5ce7ccc27..c399877c59 100644 --- a/configure.ac +++ b/configure.ac @@ -4025,6 +4025,8 @@ want_alsa="no" # TODO: and the EFL_OPTIONAL_DEPEND_PKG(), use EFL_DEPEND_PKG() want_sndfile="yes" +want_wasapiaudio="no" + AC_ARG_ENABLE([pulseaudio], [AS_HELP_STRING([--disable-pulseaudio],[disable pulseaudio sound support. @<:@default=enabled@:>@])], [ @@ -4032,7 +4034,12 @@ AC_ARG_ENABLE([pulseaudio], want_pulseaudio="yes" else want_pulseaudio="no" - CFOPT_WARNING="xyes" + if test "x${have_win32}" = "xyes" ; then + want_wasapiaudio="yes" + else + want_wasapiaudio="no" + CFOPT_WARNING="xyes" + fi fi ], [want_pulseaudio="yes"]) @@ -4066,6 +4073,10 @@ if test "x${want_sndfile}" = "xyes" ; then PKG_CHECK_MODULES([ECORE_AUDIO_SNDFILE], [sndfile]) AC_DEFINE([HAVE_SNDFILE], [1], [Sndfile support]) fi +if test "x${want_wasapiaudio}" = "xyes" ; then + EFL_ADD_LIBS([ECORE_AUDIO], [-luuid -lwinmm -lksuser]) + AC_DEFINE([HAVE_WASAPI], [1], [Wasapiaudio support]) +fi dnl EFL_OPTIONAL_DEPEND_PKG([ECORE_AUDIO_ALSA], [${want_alsa}], [ALSA], [alsa]) dnl EFL_OPTIONAL_DEPEND_PKG([ECORE_AUDIO_PULSE], [${want_pulseaudio}], [PULSE], [libpulse]) @@ -4076,6 +4087,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], [wasapiaudio]) ### Checks for header files @@ -4092,6 +4104,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_WASASPI], [test "x${want_wasapiaudio}" = "xyes"]) #### End of Ecore_Audio @@ -4859,8 +4872,13 @@ AC_ARG_ENABLE([multisense], if test "x${want_pulseaudio}" = "xyes"; then want_multisense="yes" else - want_multisense="no" + if test "x${want_wasapiaudio}" = "xyes"; then + want_multisense="yes" + else + want_multisense="no" + fi fi + ]) # TODO: should we keep or remove these? diff --git a/src/Makefile_Ecore_Audio.am b/src/Makefile_Ecore_Audio.am index bd68dd6c4b..c8a1caecc2 100644 --- a/src/Makefile_Ecore_Audio.am +++ b/src/Makefile_Ecore_Audio.am @@ -9,7 +9,8 @@ ecore_audio_eolian_files = \ 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 + lib/ecore_audio/ecore_audio_in_tone.eo \ + lib/ecore_audio/ecore_audio_out_wasapi.eo ecore_audio_eolian_c = $(ecore_audio_eolian_files:%.eo=%.eo.c) @@ -60,6 +61,14 @@ lib/ecore_audio/ecore_audio_pulse_ml.c \ lib/ecore_audio/ecore_audio_obj_out_pulse.c endif +if HAVE_WIN32 +dist_installed_ecoreaudiomainheaders_DATA+= \ +lib/ecore_audio/ecore_audio_obj_out_wasapi.h + +lib_ecore_audio_libecore_audio_la_SOURCES += \ +lib/ecore_audio/ecore_audio_obj_out_wasapi.c +endif + if HAVE_ECORE_AUDIO_SNDFILE lib_ecore_audio_libecore_audio_la_SOURCES += \ lib/ecore_audio/ecore_audio_obj_in_sndfile.c \ diff --git a/src/lib/ecore_audio/Ecore_Audio.h b/src/lib/ecore_audio/Ecore_Audio.h index 0cc7bec29c..e508c9a6aa 100644 --- a/src/lib/ecore_audio/Ecore_Audio.h +++ b/src/lib/ecore_audio/Ecore_Audio.h @@ -8,15 +8,27 @@ #undef EAPI #endif -#ifdef __GNUC__ -#if __GNUC__ >= 4 -#define EAPI __attribute__ ((visibility("default"))) +#ifdef _WIN32 +# ifdef EFL_ECORE_AUDIO_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORE_AUDIO_BUILD */ #else -#define EAPI -#endif -#else -#define EAPI -#endif +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ /** * @file Ecore_Audio.h @@ -46,6 +58,7 @@ enum _Ecore_Audio_Type { ECORE_AUDIO_TYPE_TONE, /**< Use tone module */ ECORE_AUDIO_TYPE_CORE_AUDIO, /**< Use Core Audio module (Apple) - DEPRECATED */ ECORE_AUDIO_TYPE_CUSTOM, /**< Use custom module */ + ECORE_AUDIO_TYPE_WASAPI, /**< Use Wasapi module @since 1.21*/ ECORE_AUDIO_MODULE_LAST, /**< Sentinel */ }; @@ -179,11 +192,6 @@ EAPI int ecore_audio_init(void); */ EAPI int ecore_audio_shutdown(void); - -#ifdef __cplusplus -} -#endif - #include #include #include @@ -193,12 +201,26 @@ EAPI int ecore_audio_shutdown(void); #include -#include +#if HAVE_PULSE +# include +# define ECORE_AUDIO_OUT_RENDER_CLASS ECORE_AUDIO_OUT_PULSE_CLASS +# define ECORE_AUDIO_OUT_RENDER_EVENT_CONTEXT_FAIL ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL +#endif + +#if HAVE_WASAPI +# include +# define ECORE_AUDIO_OUT_RENDER_CLASS ECORE_AUDIO_OUT_WASAPI_CLASS +# define ECORE_AUDIO_OUT_RENDER_EVENT_CONTEXT_FAIL ECORE_AUDIO_OUT_WASAPI_EVENT_CONTEXT_FAIL +#endif /** * @} */ +#ifdef __cplusplus +} +#endif + #undef EAPI #define EAPI diff --git a/src/lib/ecore_audio/ecore_audio_obj.h b/src/lib/ecore_audio/ecore_audio_obj.h index 55acca21d0..cb74281f1e 100644 --- a/src/lib/ecore_audio/ecore_audio_obj.h +++ b/src/lib/ecore_audio/ecore_audio_obj.h @@ -4,30 +4,11 @@ #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.h * @brief Base Ecore_Audio object. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj - Base Ecore_Audio object * @ingroup Ecore_Audio_Group @@ -37,8 +18,5 @@ extern "C" /** * @} */ -#ifdef __cplusplus -} -#endif #endif diff --git a/src/lib/ecore_audio/ecore_audio_obj_in.h b/src/lib/ecore_audio/ecore_audio_obj_in.h index 003d5f7748..9dc0760863 100644 --- a/src/lib/ecore_audio/ecore_audio_obj_in.h +++ b/src/lib/ecore_audio/ecore_audio_obj_in.h @@ -4,30 +4,11 @@ #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.h * @brief Ecore_Audio Input Object. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj_in - Ecore_Audio input object * @ingroup Ecore_Audio_Group @@ -38,8 +19,4 @@ extern "C" * @} */ -#ifdef __cplusplus -} #endif - -#endif \ No newline at end of file diff --git a/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h b/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h index cbd7769ce3..ca8908e52b 100644 --- a/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h +++ b/src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h @@ -4,30 +4,11 @@ #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 Ecore_Audio sndfile input. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj_in_sndfile - Ecore_Audio sndfile input * @ingroup Ecore_Audio_Group @@ -38,8 +19,4 @@ extern "C" * @} */ -#ifdef __cplusplus -} #endif - -#endif \ No newline at end of file diff --git a/src/lib/ecore_audio/ecore_audio_obj_in_tone.h b/src/lib/ecore_audio/ecore_audio_obj_in_tone.h index e25a331de0..b393aade48 100644 --- a/src/lib/ecore_audio/ecore_audio_obj_in_tone.h +++ b/src/lib/ecore_audio/ecore_audio_obj_in_tone.h @@ -4,30 +4,11 @@ #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_tone.h * @brief Ecore_Audio tone input. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj_in_tone - Ecore_Audio tone input * @ingroup Ecore_Audio_Group @@ -47,8 +28,4 @@ extern "C" * @} */ -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/lib/ecore_audio/ecore_audio_obj_out.h b/src/lib/ecore_audio/ecore_audio_obj_out.h index 5433a7f9ea..1aa33e0f1a 100644 --- a/src/lib/ecore_audio/ecore_audio_obj_out.h +++ b/src/lib/ecore_audio/ecore_audio_obj_out.h @@ -4,30 +4,11 @@ #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.h * @brief Ecore_Audio output object. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj_out - Ecore_Audio output object * @ingroup Ecore_Audio_Group @@ -38,8 +19,4 @@ extern "C" * @} */ -#ifdef __cplusplus -} #endif - -#endif \ No newline at end of file diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h index ec0ac15beb..94fd352f8e 100644 --- a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h +++ b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.h @@ -4,30 +4,11 @@ #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_pulse.h * @brief Ecore_Audio pulseaudio output. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj_out_pulse - Ecore_Audio pulseaudio output * @ingroup Ecore_Audio_Group @@ -38,8 +19,4 @@ extern "C" * @} */ -#ifdef __cplusplus -} #endif - -#endif \ No newline at end of file diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h b/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h index f2b807dfee..9f5fac89f9 100644 --- a/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h +++ b/src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h @@ -4,30 +4,11 @@ #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 Ecore_Audio sndfile output. */ -#ifdef __cplusplus -extern "C" -{ -#endif - /** * @defgroup ecore_audio_obj_out_sndfile - Ecore_Audio sndfile output * @ingroup Ecore_Audio_Group @@ -38,8 +19,4 @@ extern "C" * @} */ -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_wasapi.c b/src/lib/ecore_audio/ecore_audio_obj_out_wasapi.c new file mode 100644 index 0000000000..899a0f991c --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_wasapi.c @@ -0,0 +1,609 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include "ecore_audio_private.h" + +#define INITGUID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MY_CLASS ECORE_AUDIO_OUT_WASAPI_CLASS +#define MY_CLASS_NAME "Ecore_Audio_Out_Wasapi" + +#define REFTIMES_PER_SEC 10000000 +#define REFTIMES_PER_MILLISEC 10000 + +static int client_connect_count = 0; + +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10); + +struct _Ecore_Audio_Wasapi_Class { + + Ecore_Job *state_job; + Eina_List *outputs; +}; + +typedef struct _Ecore_Audio_Out_Wasapi_Device Ecore_Audio_Out_Wasapi_Device; +typedef struct _Ecore_Audio_Out_Wasapi_Data Ecore_Audio_Out_Wasapi_Data; + +struct _Ecore_Audio_Out_Wasapi_Device +{ + IMMDevice *pDevice; + IMMDeviceEnumerator *pDeviceEnumerator; +}; + +struct _Ecore_Audio_Out_Wasapi_Data +{ + Eo *in; + Eo *out; + IAudioClient *client; + IAudioRenderClient *render; + IAudioStreamVolume *volume; + WAVEFORMATEXTENSIBLE *wave_format; + HANDLE event; + UINT32 NumBufferFrames; + Eina_Bool spec_format; + Eina_Bool play; +}; + +Ecore_Audio_Out_Wasapi_Device *device = NULL; + +EOLIAN static void +_ecore_audio_out_wasapi_ecore_audio_volume_set(Eo *eo_obj EINA_UNUSED, Ecore_Audio_Out_Wasapi_Data *_pd, double volume) +{ + HRESULT hr; + UINT32 count; + const float volumes = volume; + + hr = _pd->client->lpVtbl->GetService(_pd->client, + &IID_IAudioStreamVolume, + (void**)&(_pd->volume)); + + if (hr != S_OK) + { + ERR("GetService does not have access to the IID_IAudioStreamVolume interface."); + return; + } + + hr = _pd->volume->lpVtbl->GetChannelCount(_pd->volume, &count); + if (hr != S_OK) + { + ERR("The GetChannelCount method can not to gets a count of the channels."); + return; + } + + hr = _pd->volume->lpVtbl->SetAllVolumes(_pd->volume, count, &volumes); + if (hr != S_OK) + { + ERR("The SetAllVolumes method can not to sets the individual volume levels for all the channels in the audio stream."); + return; + } +} + + +static void +_close_cb(void *data, const Efl_Event *event EINA_UNUSED) +{ + Ecore_Audio_Out_Wasapi_Data *_pd = data; + + if (_pd->event) CloseHandle(_pd->event); + if (_pd->render) _pd->render->lpVtbl->Release(_pd->render); + if (_pd->client) _pd->client->lpVtbl->Release(_pd->client); + + _pd->event = NULL; + _pd->render = NULL; + _pd->client = NULL; + _pd->play = EINA_FALSE; +} + +static void +_samplerate_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) +{ + HRESULT hr; + + IAudioClient *client; + IAudioClockAdjustment *adjustment; + Ecore_Audio_Out_Wasapi_Data *_pd; + + double sr; + double speed; + unsigned int samplerate; + + _pd = data; + client = _pd->client; + + speed = ecore_audio_obj_in_speed_get(_pd->in); + samplerate = ecore_audio_obj_in_samplerate_get(_pd->in); + samplerate = samplerate * speed; + + hr = client->lpVtbl->GetService(client, + &IID_IAudioClockAdjustment, + (void**)&(adjustment)); + if (hr != S_OK) + { + ERR("GetService does not have access to the IID_IAudioClockAdjustments interface."); + return; + } + + sr = (REFERENCE_TIME)(_pd->NumBufferFrames / samplerate); + adjustment->lpVtbl->SetSampleRate(adjustment, sr); +} + + +static Eina_Bool +_write_cb(void *data, Ecore_Win32_Handler *wh EINA_UNUSED) +{ + Ecore_Audio_Out_Wasapi_Data *_pd; + + HRESULT hr; + BYTE *pData; + UINT32 numAvailableFrames; + UINT32 numPaddingFrames; + int nBlockAlign; + ssize_t bread; + + _pd = data; + pData = NULL; + numPaddingFrames = 0; + bread = 0; + + nBlockAlign = _pd->wave_format->Format.nBlockAlign; + + hr = (_pd->client->lpVtbl->GetBufferSize(_pd->client, &(_pd->NumBufferFrames))); + if (hr != S_OK) + { + ERR("The GetBufferSize does not can retrieves the size (maximum capacity) of the endpoint buffer."); + return ECORE_CALLBACK_CANCEL; + } + + if (!_pd->render) + { + hr = _pd->client->lpVtbl->GetService(_pd->client, + &IID_IAudioRenderClient, + (void**)&(_pd->render)); + if (hr != S_OK) + { + ERR("GetService does not have access to the IID_IAudioRenderClient interface."); + return ECORE_CALLBACK_CANCEL; + } + } + + numPaddingFrames = 0; + hr = _pd->client->lpVtbl->GetCurrentPadding(_pd->client, &(numPaddingFrames)); + if (hr == S_OK) + { + numAvailableFrames = _pd->NumBufferFrames - numPaddingFrames; + if (numAvailableFrames == 0) return ECORE_CALLBACK_RENEW; + + hr = _pd->render->lpVtbl->GetBuffer(_pd->render, numAvailableFrames, &pData); + if (hr == S_OK) + { + bread = ecore_audio_obj_in_read(_pd->in, pData, nBlockAlign*numAvailableFrames); + if (bread > 0) + { + if (bread < (nBlockAlign * numAvailableFrames)) + memset((char *)pData + bread, 0, (nBlockAlign * numAvailableFrames) - bread); + + hr = _pd->render->lpVtbl->ReleaseBuffer(_pd->render, (UINT32)(bread / nBlockAlign), 0); + if (hr == S_OK) + { + if ((bread % nBlockAlign) == 0) return ECORE_CALLBACK_RENEW; + } + else + { + ERR("ReleaseBuffer method cannot releases the buffer space acquired in the previous call to the IAudioRenderClient::GetBuffer method."); + } + } + } + else + { + ERR("GetBuffer method cannot retrieves a pointer to the next available space in the rendering endpoint buffer into which the caller can write a data packet."); + } + } + else + { + ERR("GetCurrentPadding method cannot retrieves the number of frames of padding in the endpoint buffer."); + } + + if (_pd->client) _pd->client->lpVtbl->Stop(_pd->client); + + ecore_main_win32_handler_del(_pd->event); + efl_event_callback_call(_pd->out, ECORE_AUDIO_OUT_WASAPI_EVENT_STOP, NULL); + + _pd->play = EINA_FALSE; + + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +wave_format_selection(Ecore_Audio_Out_Wasapi_Data *_pd, Eo *in) +{ + HRESULT hr; + + IAudioClient *client; + WAVEFORMATEXTENSIBLE *buff; + WAVEFORMATEXTENSIBLE *correct_wave_format; + + double speed; + unsigned int channels; + unsigned int samplerate; + + client = _pd->client; + buff = NULL; + correct_wave_format = NULL; + + speed = ecore_audio_obj_in_speed_get(in); + channels = ecore_audio_obj_in_channels_get(in); + samplerate = ecore_audio_obj_in_samplerate_get(in); + samplerate = samplerate * speed; + + if (_pd->spec_format) + { + CoTaskMemFree(_pd->wave_format); + _pd->wave_format = NULL; + _pd->spec_format = EINA_FALSE; + } + + hr = client->lpVtbl->GetMixFormat(client, (WAVEFORMATEX **)&(buff)); + if (hr != S_OK) + { + return EINA_FALSE; + } + + buff->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + buff->Format.nChannels = channels; + buff->Format.nSamplesPerSec = samplerate; + buff->Format.nBlockAlign = (buff->Format.nChannels * buff->Format.wBitsPerSample) / 8; + buff->Format.nAvgBytesPerSec = buff->Format.nSamplesPerSec * buff->Format.nBlockAlign; + buff->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + buff->dwChannelMask = 0; + + switch(channels) + { + case 1: + buff->dwChannelMask = KSAUDIO_SPEAKER_MONO; + break; + case 2: + buff->dwChannelMask = KSAUDIO_SPEAKER_STEREO; + break; + case 4: + buff->dwChannelMask = KSAUDIO_SPEAKER_QUAD; + break; + case 6: + buff->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; + break; + case 8: + buff->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; + break; + default: + buff->dwChannelMask = 0; + break; + } + + buff->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + + hr = client->lpVtbl->IsFormatSupported(client, + AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX *)buff, + (WAVEFORMATEX **)&correct_wave_format); + switch (hr) + { + case S_OK: + _pd->wave_format = buff; + return EINA_TRUE; + case S_FALSE: + ERR("Succeeded with a closest match to the specified format."); + _pd->spec_format = EINA_TRUE; + _pd->wave_format = correct_wave_format; + return EINA_TRUE; + case E_POINTER: + ERR("code: E_POINTER"); + return EINA_FALSE; + case E_INVALIDARG: + ERR("code: E_INVALIDARG"); + return EINA_FALSE; + case AUDCLNT_E_DEVICE_INVALIDATED: + ERR("code: AUDCLNT_E_DEVICE_INVALIDATED"); + return EINA_FALSE; + case AUDCLNT_E_SERVICE_NOT_RUNNING: + ERR("code: AUDCLNT_E_SERVICE_NOT_RUNNING"); + return EINA_FALSE; + default: + ERR("IsFormatSupported - return code is unknown"); + return EINA_FALSE; + } + return EINA_TRUE; +} + + +static Eina_Bool +_client_initialize( Ecore_Audio_Out_Wasapi_Data *_pd ) +{ + HRESULT hr; + IAudioClient *client; + REFERENCE_TIME hnsRequestedDuration; + WAVEFORMATEXTENSIBLE *correct_wave_format; + DWORD flags; + int nbf; + int sps; + + client = _pd->client; + hnsRequestedDuration = REFTIMES_PER_SEC; + correct_wave_format = _pd->wave_format; + flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_NOPERSIST; + + hr = client->lpVtbl->GetDevicePeriod(client, NULL, &hnsRequestedDuration); + + hr = client->lpVtbl->Initialize(client, + AUDCLNT_SHAREMODE_SHARED, + flags, + hnsRequestedDuration, + hnsRequestedDuration, + (WAVEFORMATEX *)correct_wave_format, + NULL); + switch (hr) + { + case S_OK: + break; + case AUDCLNT_E_ALREADY_INITIALIZED: + ERR("code: AUDCLNT_E_ALREADY_INITIALIZED"); + return EINA_FALSE; + case AUDCLNT_E_WRONG_ENDPOINT_TYPE: + ERR("code: AUDCLNT_E_WRONG_ENDPOINT_TYPE"); + return EINA_FALSE; + case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: + ERR("code: AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"); + + hr = client->lpVtbl->GetBufferSize(client, &(_pd->NumBufferFrames)); + if (hr != S_OK) + { + ERR("The GetBufferSize does not can retrieves the size (maximum capacity) of the endpoint buffer."); + return EINA_FALSE; + } + + nbf = _pd->NumBufferFrames; + sps = correct_wave_format->Format.nSamplesPerSec; + + //*Calculate the aligned buffer size in 100-nansecond units (hns).(https://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx)*// + hnsRequestedDuration = (REFERENCE_TIME)(((REFTIMES_PER_SEC * nbf) / sps) + 0.5); + if (client) + client->lpVtbl->Release(client); + + hr = device->pDevice->lpVtbl->Activate(device->pDevice, + &IID_IAudioClient, + CLSCTX_ALL, + NULL, + (void**)&(client)); + if (hr != S_OK) + { + ERR("The Activate method cannot create a COM object with the specified interface."); + return EINA_FALSE; + } + + hr = client->lpVtbl->GetMixFormat(client, (WAVEFORMATEX **)&(correct_wave_format)); + if (hr != S_OK) + { + ERR("The GetMixFormat cannot retrieves the stream format that the audio engine uses for its internal processing of shared-mode streams."); + return EINA_FALSE; + } + + hr = client->lpVtbl->Initialize(client, + AUDCLNT_SHAREMODE_SHARED, + flags, + hnsRequestedDuration, + hnsRequestedDuration, + (WAVEFORMATEX *)correct_wave_format, + NULL); + + if (hr != S_OK) return EINA_FALSE; + return EINA_TRUE; + case AUDCLNT_E_BUFFER_SIZE_ERROR: + ERR("code: AUDCLNT_E_BUFFER_SIZE_ERROR"); + return EINA_FALSE; + case AUDCLNT_E_CPUUSAGE_EXCEEDED: + ERR("code: AUDCLNT_E_CPUUSAGE_EXCEEDED"); + return EINA_FALSE; + case AUDCLNT_E_DEVICE_INVALIDATED: + ERR("code: AUDCLNT_E_DEVICE_INVALIDATED"); + return EINA_FALSE; + case AUDCLNT_E_DEVICE_IN_USE: + ERR("code: AUDCLNT_E_DEVICE_IN_USE"); + return EINA_FALSE; + case AUDCLNT_E_ENDPOINT_CREATE_FAILED: + ERR("code: AUDCLNT_E_ENDPOINT_CREATE_FAILED"); + return EINA_FALSE; + case AUDCLNT_E_INVALID_DEVICE_PERIOD: + ERR("code: AUDCLNT_E_INVALID_DEVICE_PERIOD"); + return EINA_FALSE; + case AUDCLNT_E_UNSUPPORTED_FORMAT: + ERR("code: AUDCLNT_E_UNSUPPORTED_FORMAT"); + return EINA_FALSE; + case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: + ERR("code: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"); + return EINA_FALSE; + case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: + ERR("code: AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"); + return EINA_FALSE; + case AUDCLNT_E_SERVICE_NOT_RUNNING: + ERR("code: AUDCLNT_E_SERVICE_NOT_RUNNING"); + return EINA_FALSE; + case E_POINTER: + ERR("code: E_POINTER"); + return EINA_FALSE; + case E_INVALIDARG: + ERR("code: E_INVALIDARG"); + return EINA_FALSE; + case E_OUTOFMEMORY: + ERR("code: E_OUTOFMEMORY"); + return EINA_FALSE; + default: + ERR("code: "); + return EINA_FALSE; + } + if (!_pd->event ) + { + _pd->event = CreateEvent(NULL, 0, 0, NULL); + hr = client->lpVtbl->SetEventHandle(client, _pd->event); + if (hr != S_OK) return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_input_attach_internal(Eo *eo_obj EINA_UNUSED, Eo *in, Ecore_Audio_Out_Wasapi_Data *_pd) +{ + HRESULT hr; + + if (!device || !device->pDevice) return EINA_FALSE; + + if (!_pd->client) + { + hr = device->pDevice->lpVtbl->Activate(device->pDevice, + &IID_IAudioClient, + CLSCTX_ALL, + NULL, + (void**)&(_pd->client)); + if (hr != S_OK) + { + ERR("The Activate method cannot create a COM object with the specified interface."); + return EINA_FALSE; + } + } + + if (!wave_format_selection( _pd, in)) + return EINA_FALSE; + + if (!_client_initialize(_pd)) + return EINA_FALSE; + + _pd->in = in; + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_ecore_audio_out_wasapi_ecore_audio_out_input_attach(Eo *eo_obj, Ecore_Audio_Out_Wasapi_Data *_pd, Eo *in) +{ + Eina_Bool ret = ecore_audio_obj_out_input_attach(efl_super(eo_obj, MY_CLASS), in); + + if (!ret) return EINA_FALSE; + if (!device) return EINA_FALSE; + if (_pd->play) return EINA_TRUE; + if (!_input_attach_internal(eo_obj, in, _pd)) return EINA_FALSE; + + ecore_main_win32_handler_add(_pd->event, _write_cb, _pd); + efl_event_callback_add(in, ECORE_AUDIO_IN_EVENT_IN_SAMPLERATE_CHANGED, _samplerate_changed_cb, eo_obj); + efl_event_callback_add(eo_obj, ECORE_AUDIO_OUT_WASAPI_EVENT_STOP, _close_cb, _pd); + + _pd->play = EINA_TRUE; + _pd->out = eo_obj; + _pd->client->lpVtbl->Start(_pd->client); + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_ecore_audio_out_wasapi_ecore_audio_out_input_detach(Eo *eo_obj, Ecore_Audio_Out_Wasapi_Data *_pd EINA_UNUSED, Eo *in) +{ + Eina_Bool ret = ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in); + if (!ret) return EINA_FALSE; + + efl_event_callback_call(_pd->out, ECORE_AUDIO_OUT_WASAPI_EVENT_STOP, NULL); + return EINA_TRUE; +} + +EOLIAN static Eo * +_ecore_audio_out_wasapi_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Wasapi_Data *_pd EINA_UNUSED) +{ + HRESULT hr; + + if (!device) + { + device = calloc(1, sizeof(Ecore_Audio_Out_Wasapi_Device)); + if (!device) return NULL; + } + + if (device->pDeviceEnumerator && device->pDevice) + { + client_connect_count++; + eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS)); + Ecore_Audio_Output *out_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_OUT_CLASS); + out_obj->need_writer = EINA_FALSE; + return eo_obj; + } + + hr = CoInitialize(NULL); + + if (hr == S_OK || hr == S_FALSE) + { + if (device->pDeviceEnumerator) + device->pDeviceEnumerator->lpVtbl->Release(device->pDeviceEnumerator); + + if (device->pDevice) + device->pDevice->lpVtbl->Release(device->pDevice); + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, + NULL, + CLSCTX_ALL, + &IID_IMMDeviceEnumerator, + (void**)&(device->pDeviceEnumerator)); + if (hr == S_OK) + { + hr = device->pDeviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(device->pDeviceEnumerator, + eRender, + eMultimedia, + &(device->pDevice)); + if (hr == S_OK) + { + client_connect_count++; + eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS)); + Ecore_Audio_Output *out_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_OUT_CLASS); + out_obj->need_writer = EINA_FALSE; + return eo_obj; + } + device->pDeviceEnumerator->lpVtbl->Release(device->pDeviceEnumerator); + } + } + + CoUninitialize(); + free(device); + device = NULL; + return NULL; +} + +EOLIAN static void +_ecore_audio_out_wasapi_efl_object_destructor(Eo *eo_obj, Ecore_Audio_Out_Wasapi_Data *_pd EINA_UNUSED) +{ + client_connect_count--; + efl_destructor(efl_super(eo_obj, MY_CLASS)); + + if (!client_connect_count) + { + if (device->pDevice) + device->pDevice->lpVtbl->Release(device->pDevice); + + if (device->pDeviceEnumerator) + device->pDeviceEnumerator->lpVtbl->Release(device->pDeviceEnumerator); + + free(device); + device = NULL; + CoUninitialize(); + } +} + +#include "ecore_audio_out_wasapi.eo.c" diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_wasapi.h b/src/lib/ecore_audio/ecore_audio_obj_out_wasapi.h new file mode 100644 index 0000000000..87457e0cbc --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_obj_out_wasapi.h @@ -0,0 +1,22 @@ +#ifndef __ECORE_AUDIO_OUT_WASAPI_H__ +#define __ECORE_AUDIO_OUT_WASAPI_H__ + +#include +#include + +/** + * @file ecore_audio_obj_out_wasapi.h + * @brief Ecore_Audio wasapi output. + */ + +/** + * @defgroup ecore_audio_obj_out_wasapi - Ecore_Audio wasapi output + * @ingroup Ecore_Audio_Group + * @{ + */ +#include "ecore_audio_out_wasapi.eo.h" +/** + * @} + */ + +#endif /* __ECORE_AUDIO_OUT_WASAPI_H__ */ diff --git a/src/lib/ecore_audio/ecore_audio_out_wasapi.eo b/src/lib/ecore_audio/ecore_audio_out_wasapi.eo new file mode 100644 index 0000000000..fd362f18c0 --- /dev/null +++ b/src/lib/ecore_audio/ecore_audio_out_wasapi.eo @@ -0,0 +1,18 @@ +class Ecore.Audio.Out.Wasapi (Ecore.Audio.Out) +{ + [[Ecore audio ouput for WasapiAudio.]] + eo_prefix: ecore_audio_obj_out_wasapi; + event_prefix: ecore_audio_out_wasapi; + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Ecore.Audio.volume { set;} + Ecore.Audio.Out.input_attach; + Ecore.Audio.Out.input_detach; + } + events { + context,ready; [[Called when the output is ready for playback.]] + context,fail; [[Called when context fails.]] + stop; [[Called when need to stop.]] + } +}