forked from enlightenment/efl
Revert "Revert "ecore_audio: a minimal template for playing sound on WINDOWS is added""
This reverts commit 4457f8c9c8
.
This commit is contained in:
parent
71e5c74eb6
commit
53c548a97e
18
configure.ac
18
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,8 +4034,13 @@ AC_ARG_ENABLE([pulseaudio],
|
|||
want_pulseaudio="yes"
|
||||
else
|
||||
want_pulseaudio="no"
|
||||
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
|
||||
|
||||
|
@ -4858,9 +4871,14 @@ AC_ARG_ENABLE([multisense],
|
|||
[
|
||||
if test "x${want_pulseaudio}" = "xyes"; then
|
||||
want_multisense="yes"
|
||||
else
|
||||
if test "x${want_wasapiaudio}" = "xyes"; then
|
||||
want_multisense="yes"
|
||||
else
|
||||
want_multisense="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
])
|
||||
|
||||
# TODO: should we keep or remove these?
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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__ */
|
|
@ -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.]]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue