ecore_audio: a minimal template for playing sound on WINDOWS is added
authorIvan Furs <i.furs@samsung.com>
Tue, 3 Oct 2017 18:21:07 +0000 (11:21 -0700)
committerCedric BAIL <cedric@osg.samsung.com>
Tue, 3 Oct 2017 18:36:49 +0000 (11:36 -0700)
Summary:
Realization of audio data playback through WASAPI (Windows Audio Session API).(minimal)
WASAPI model:
1. Find  a playback device (in the default system)
2. Register the client on the playback device.
3. Create a playback format for audio data.
4. Initialize the client with the created format, and access mode,  ....
5. Take the object-rendering from the client for play the data stream.
6. Play data stream

ecore_audio model:
1. create a object for play the data stream(out object)
2. create a object to receive the data stream(in object)
3. register in the out-object the in-object
4. play data stream

Necessary:
Realize the ecore_audio object to play the data stream using the WASAPI model.

How implemented:
1. The object ecore_audio_out_wasapi is implemented
2. object ecore_audio_out_wasapi - the object constructor is find a playback device (in the default system)(WASAPI)
3. _ecore_audio_out_wasapi_ecore_audio_out_input_attach -  register in the out-object the in-object
   3.1 Register the client on the playback device.(WASAPI)
   3.2 Create a playback format for audio data.(WASAPI)
   3.3 Initialize the client with the created format, and access mode,  ....(WASAPI)
4. _write_cb - play data
   4.1  Take the object-rendering from the client for play the data stream.(WASAPI)
   4.2  Play data stream(WASAPI)

Reviewers: cedric, vtorri, raster, an.kroitor, NikaWhite, FurryMyad, rimmed, t.naumenko, Jaehyun, bowonryu

Reviewed By: vtorri, NikaWhite

Subscribers: artem.popov, cedric, jpeg

Tags: #windows

Differential Revision: https://phab.enlightenment.org/D5029

Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
13 files changed:
configure.ac
src/Makefile_Ecore_Audio.am
src/lib/ecore_audio/Ecore_Audio.h
src/lib/ecore_audio/ecore_audio_obj.h
src/lib/ecore_audio/ecore_audio_obj_in.h
src/lib/ecore_audio/ecore_audio_obj_in_sndfile.h
src/lib/ecore_audio/ecore_audio_obj_in_tone.h
src/lib/ecore_audio/ecore_audio_obj_out.h
src/lib/ecore_audio/ecore_audio_obj_out_pulse.h
src/lib/ecore_audio/ecore_audio_obj_out_sndfile.h
src/lib/ecore_audio/ecore_audio_obj_out_wasapi.c [new file with mode: 0644]
src/lib/ecore_audio/ecore_audio_obj_out_wasapi.h [new file with mode: 0644]
src/lib/ecore_audio/ecore_audio_out_wasapi.eo [new file with mode: 0644]

index ff49983..93ecf88 100644 (file)
@@ -3930,6 +3930,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@:>@])],
    [
@@ -3937,7 +3939,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"])
@@ -3971,6 +3978,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])
@@ -3981,6 +3992,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
 
@@ -3997,6 +4009,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
 
@@ -4764,8 +4777,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?
index bd68dd6..c8a1cae 100644 (file)
@@ -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 \
index 0cc7bec..e508c9a 100644 (file)
@@ -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 <ecore_audio_obj.h>
 #include <ecore_audio_obj_in.h>
 #include <ecore_audio_obj_out.h>
@@ -193,12 +201,26 @@ EAPI int                 ecore_audio_shutdown(void);
 
 #include <ecore_audio_obj_in_tone.h>
 
-#include <ecore_audio_obj_out_pulse.h>
+#if HAVE_PULSE
+# include <ecore_audio_obj_out_pulse.h>
+# 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 <ecore_audio_obj_out_wasapi.h>
+# 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
 
index 55acca2..cb74281 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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
index 003d5f7..9dc0760 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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
index cbd7769..ca8908e 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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
index e25a331..b393aad 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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
index 5433a7f..1aa33e0 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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
index ec0ac15..94fd352 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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
index f2b807d..9f5fac8 100644 (file)
@@ -4,30 +4,11 @@
 #include <Eina.h>
 #include <Eo.h>
 
-#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 (file)
index 0000000..899a0f9
--- /dev/null
@@ -0,0 +1,609 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <Eo.h>
+#include "ecore_audio_private.h"
+
+#define INITGUID
+#include <initguid.h>
+#include <functiondiscoverykeys.h>
+#include <audioclient.h>
+#include <audiopolicy.h>
+#include <endpointvolume.h>
+#include <mmdeviceapi.h>
+#include <mmreg.h>
+#include <wtypes.h>
+#include <rpc.h>
+#include <rpcdce.h>
+#include <propkey.h>
+
+#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 (file)
index 0000000..87457e0
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __ECORE_AUDIO_OUT_WASAPI_H__
+#define __ECORE_AUDIO_OUT_WASAPI_H__
+
+#include <Eina.h>
+#include <Eo.h>
+
+/**
+ * @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 (file)
index 0000000..fd362f1
--- /dev/null
@@ -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.]]
+   }
+}