+++ /dev/null
-#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"