mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
WASAPI: Add initial support for playback.
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
// - null
|
||||
// - ... and more in the future.
|
||||
// - OpenSL|ES / Android (Unstable)
|
||||
// - WASAPI (Unstable)
|
||||
// - Core Audio (OSX, iOS)
|
||||
// - Maybe OSS
|
||||
// - Maybe OpenAL
|
||||
@@ -260,6 +261,10 @@ typedef int mal_result;
|
||||
#define MAL_ALSA_FAILED_TO_OPEN_DEVICE -2048
|
||||
#define MAL_ALSA_FAILED_TO_SET_HW_PARAMS -2049
|
||||
#define MAL_ALSA_FAILED_TO_SET_SW_PARAMS -2050
|
||||
#define MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR -3072
|
||||
#define MAL_WASAPI_FAILED_TO_CREATE_DEVICE -3073
|
||||
#define MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE -3074
|
||||
#define MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE -3075
|
||||
|
||||
typedef struct mal_device mal_device;
|
||||
|
||||
@@ -355,7 +360,13 @@ struct mal_device
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
struct
|
||||
{
|
||||
/*IMMDevice**/ mal_ptr pDevice;
|
||||
/*IAudioClient*/ mal_ptr pAudioClient;
|
||||
/*IAudioRenderClient */ mal_ptr pRenderClient;
|
||||
/*IAudioCaptureClient */ mal_ptr pCaptureClient;
|
||||
/*HANDLE*/ mal_handle hStopEvent;
|
||||
mal_bool32 needCoUninit; // Whether or not COM needs to be uninitialized.
|
||||
mal_bool32 breakFromMainLoop;
|
||||
} wasapi;
|
||||
#endif
|
||||
|
||||
@@ -1324,6 +1335,15 @@ static inline mal_uint32 mal_device__get_state(mal_device* pDevice)
|
||||
}
|
||||
|
||||
|
||||
#ifdef MAL_WIN32
|
||||
static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
//static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
//static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Null Backend
|
||||
@@ -1361,6 +1381,8 @@ static mal_result mal_device_init__null(mal_device* pDevice, mal_device_type typ
|
||||
|
||||
mal_assert(pDevice != NULL);
|
||||
pDevice->api = mal_api_null;
|
||||
mal_zero_object(&pDevice->null_device);
|
||||
|
||||
pDevice->bufferSizeInFrames = pConfig->bufferSizeInFrames;
|
||||
pDevice->periods = pConfig->periods;
|
||||
|
||||
@@ -1412,7 +1434,7 @@ static mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_u
|
||||
return MAL_TRUE;
|
||||
}
|
||||
|
||||
static mal_bool32 mal_device__get_available_frames__null(mal_device* pDevice)
|
||||
static mal_uint32 mal_device__get_available_frames__null(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
@@ -1544,13 +1566,22 @@ const PROPERTYKEY g_malPKEY_Device_FriendlyName = {{0xa45c254e, 0xdf1c, 0x4efd,
|
||||
|
||||
const IID g_malCLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; // BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator)
|
||||
const IID g_malIID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; // A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator)
|
||||
const IID g_malIID_IAudioClient_Instance = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; // 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient)
|
||||
const IID g_malIID_IAudioRenderClient_Instance = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient)
|
||||
const IID g_malIID_IAudioCaptureClient_Instance = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define g_malCLSID_MMDeviceEnumerator g_malCLSID_MMDeviceEnumerator_Instance
|
||||
#define g_malIID_IMMDeviceEnumerator g_malIID_IMMDeviceEnumerator_Instance
|
||||
#define g_malIID_IAudioClient g_malIID_IAudioClient_Instance
|
||||
#define g_malIID_IAudioRenderClient g_malIID_IAudioRenderClient_Instance
|
||||
#define g_malIID_IAudioCaptureClient g_malIID_IAudioCaptureClient_Instance
|
||||
#else
|
||||
#define g_malCLSID_MMDeviceEnumerator &g_malCLSID_MMDeviceEnumerator_Instance
|
||||
#define g_malIID_IMMDeviceEnumerator &g_malIID_IMMDeviceEnumerator_Instance
|
||||
#define g_malIID_IAudioClient &g_malIID_IAudioClient_Instance
|
||||
#define g_malIID_IAudioRenderClient &g_malIID_IAudioRenderClient_Instance
|
||||
#define g_malIID_IAudioCaptureClient &g_malIID_IAudioCaptureClient_Instance
|
||||
#endif
|
||||
|
||||
static mal_result mal_enumerate_devices__wasapi(mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo)
|
||||
@@ -1636,6 +1667,269 @@ static mal_result mal_enumerate_devices__wasapi(mal_device_type type, mal_uint32
|
||||
if (needCoUninit) CoUninitialize();
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static void mal_device_uninit__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
if (pDevice->wasapi.pRenderClient) {
|
||||
((IAudioRenderClient*)pDevice->wasapi.pRenderClient)->lpVtbl->Release((IAudioRenderClient*)pDevice->wasapi.pRenderClient);
|
||||
}
|
||||
if (pDevice->wasapi.pCaptureClient) {
|
||||
((IAudioCaptureClient*)pDevice->wasapi.pCaptureClient)->lpVtbl->Release((IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
|
||||
}
|
||||
|
||||
if (pDevice->wasapi.pAudioClient) {
|
||||
((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->Release((IAudioClient*)pDevice->wasapi.pAudioClient);
|
||||
}
|
||||
|
||||
if (pDevice->wasapi.pDevice) {
|
||||
((IMMDevice*)pDevice->wasapi.pDevice)->lpVtbl->Release((IMMDevice*)pDevice->wasapi.pDevice);
|
||||
}
|
||||
|
||||
if (pDevice->wasapi.hStopEvent) {
|
||||
CloseHandle(pDevice->wasapi.hStopEvent);
|
||||
}
|
||||
|
||||
if (pDevice->wasapi.needCoUninit) {
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
static mal_result mal_device_init__wasapi(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
pDevice->api = mal_api_wasapi;
|
||||
mal_zero_object(&pDevice->wasapi);
|
||||
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
if (hr == S_OK || hr == S_FALSE) {
|
||||
pDevice->wasapi.needCoUninit = MAL_TRUE;
|
||||
}
|
||||
|
||||
IMMDeviceEnumerator* pDeviceEnumerator;
|
||||
hr = CoCreateInstance(g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
||||
if (FAILED(hr)) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR);
|
||||
}
|
||||
|
||||
if (pDeviceID == NULL) {
|
||||
hr = pDeviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(pDeviceEnumerator, (type == mal_device_type_playback) ? eRender : eCapture, eConsole, (IMMDevice**)&pDevice->wasapi.pDevice);
|
||||
if (FAILED(hr)) {
|
||||
pDeviceEnumerator->lpVtbl->Release(pDeviceEnumerator);
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to create default backend device.", MAL_WASAPI_FAILED_TO_CREATE_DEVICE);
|
||||
}
|
||||
} else {
|
||||
hr = pDeviceEnumerator->lpVtbl->GetDevice(pDeviceEnumerator, pDeviceID->wstr, (IMMDevice**)&pDevice->wasapi.pDevice);
|
||||
if (FAILED(hr)) {
|
||||
pDeviceEnumerator->lpVtbl->Release(pDeviceEnumerator);
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to create backend device.", MAL_WASAPI_FAILED_TO_CREATE_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
pDeviceEnumerator->lpVtbl->Release(pDeviceEnumerator);
|
||||
|
||||
hr = ((IMMDevice*)pDevice->wasapi.pDevice)->lpVtbl->Activate((IMMDevice*)pDevice->wasapi.pDevice, g_malIID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient);
|
||||
if (FAILED(hr)) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to activate device.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
|
||||
}
|
||||
|
||||
REFERENCE_TIME bufferDurationInMicroseconds = ((mal_uint64)pConfig->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate;
|
||||
|
||||
WAVEFORMATEXTENSIBLE wf;
|
||||
mal_zero_object(&wf);
|
||||
wf.Format.cbSize = sizeof(wf);
|
||||
wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wf.Format.nChannels = (WORD)pConfig->channels;
|
||||
wf.Format.nSamplesPerSec = (DWORD)pConfig->sampleRate;
|
||||
wf.Format.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pConfig->format)*8;
|
||||
wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
|
||||
wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
|
||||
wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
|
||||
wf.dwChannelMask = (pConfig->channels <= 2) ? 0 : ~(((DWORD)-1) << pConfig->channels);
|
||||
wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
||||
WAVEFORMATEXTENSIBLE* pClosestWF;
|
||||
#if 0
|
||||
hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->IsFormatSupported((IAudioClient*)pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pClosestWF);
|
||||
if (hr != S_OK && hr != S_FALSE) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to get device mix format.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
|
||||
}
|
||||
#else
|
||||
hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->GetMixFormat((IAudioClient*)pDevice->wasapi.pAudioClient, (WAVEFORMATEX**)&pClosestWF);
|
||||
if (hr != S_OK && hr != S_FALSE) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to get device mix format.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
|
||||
}
|
||||
#endif
|
||||
|
||||
//if (hr != S_OK) {
|
||||
mal_copy_memory(&wf, pClosestWF, sizeof(wf));
|
||||
//}
|
||||
|
||||
hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->Initialize((IAudioClient*)pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_SHARED, 0, bufferDurationInMicroseconds*10, 0, (WAVEFORMATEX*)&wf, NULL);
|
||||
if (FAILED(hr)) {
|
||||
CoTaskMemFree(pClosestWF);
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to activate device.", MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE);
|
||||
}
|
||||
|
||||
CoTaskMemFree(pClosestWF);
|
||||
|
||||
hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->GetBufferSize((IAudioClient*)pDevice->wasapi.pAudioClient, &pDevice->bufferSizeInFrames);
|
||||
if (FAILED(hr)) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to get audio client's actual buffer size.", MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE);
|
||||
}
|
||||
|
||||
if (type == mal_device_type_playback) {
|
||||
hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->GetService((IAudioClient*)pDevice->wasapi.pAudioClient, g_malIID_IAudioRenderClient, &pDevice->wasapi.pRenderClient);
|
||||
} else {
|
||||
hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->GetService((IAudioClient*)pDevice->wasapi.pAudioClient, g_malIID_IAudioCaptureClient, &pDevice->wasapi.pCaptureClient);
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to get audio client service.", MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE);
|
||||
}
|
||||
|
||||
|
||||
// When the device is playing the worker thread will be waiting on a bunch of notification events. To return from
|
||||
// this wait state we need to signal a special event.
|
||||
pDevice->wasapi.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
if (pDevice->wasapi.hStopEvent == NULL) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
return mal_post_error(pDevice, "[WASAPI] Failed to create event for main loop break notification.", MAL_FAILED_TO_CREATE_EVENT);
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_result mal_device__start_backend__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
// Playback devices need to have an initial chunk of data loaded.
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
BYTE* pData;
|
||||
HRESULT hr = ((IAudioRenderClient*)pDevice->wasapi.pRenderClient)->lpVtbl->GetBuffer((IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, &pData);
|
||||
if (FAILED(hr)) {
|
||||
return MAL_FAILED_TO_READ_DATA_FROM_CLIENT;
|
||||
}
|
||||
|
||||
mal_device__read_frames_from_client(pDevice, pDevice->bufferSizeInFrames, pData);
|
||||
|
||||
hr = ((IAudioRenderClient*)pDevice->wasapi.pRenderClient)->lpVtbl->ReleaseBuffer((IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, 0);
|
||||
if (FAILED(hr)) {
|
||||
return MAL_FAILED_TO_READ_DATA_FROM_CLIENT;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->Start((IAudioClient*)pDevice->wasapi.pAudioClient);
|
||||
if (FAILED(hr)) {
|
||||
return MAL_FAILED_TO_START_BACKEND_DEVICE;
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_result mal_device__stop_backend__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
HRESULT hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->Stop((IAudioClient*)pDevice->wasapi.pAudioClient);
|
||||
if (FAILED(hr)) {
|
||||
return MAL_FAILED_TO_STOP_BACKEND_DEVICE;
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
// The main loop will be waiting on a bunch of events via the WaitForMultipleObjects() API. One of those events
|
||||
// is a special event we use for forcing that function to return.
|
||||
pDevice->wasapi.breakFromMainLoop = MAL_TRUE;
|
||||
SetEvent(pDevice->wasapi.hStopEvent);
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
UINT32 paddingFramesCount;
|
||||
HRESULT hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->GetCurrentPadding((IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount);
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pDevice->bufferSizeInFrames - paddingFramesCount;
|
||||
}
|
||||
|
||||
static mal_uint32 mal_device__wait_for_frames__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
while (!pDevice->wasapi.breakFromMainLoop) {
|
||||
mal_uint32 framesAvailable = mal_device__get_available_frames__wasapi(pDevice);
|
||||
if (framesAvailable > 0) {
|
||||
return framesAvailable;
|
||||
}
|
||||
|
||||
DWORD timeoutInMilliseconds = 1;
|
||||
WaitForSingleObject(pDevice->wasapi.hStopEvent, timeoutInMilliseconds);
|
||||
}
|
||||
|
||||
// We'll get here if the loop was terminated. Just return whatever's available.
|
||||
return mal_device__get_available_frames__wasapi(pDevice);
|
||||
}
|
||||
|
||||
static mal_result mal_device__main_loop__wasapi(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
// Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects().
|
||||
ResetEvent(pDevice->wasapi.hStopEvent);
|
||||
|
||||
pDevice->wasapi.breakFromMainLoop = MAL_FALSE;
|
||||
while (!pDevice->wasapi.breakFromMainLoop) {
|
||||
mal_uint32 framesAvailable = mal_device__wait_for_frames__wasapi(pDevice);
|
||||
if (framesAvailable == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's a playback device, don't bother grabbing more data if the device is being stopped.
|
||||
if (pDevice->wasapi.breakFromMainLoop && pDevice->type == mal_device_type_playback) {
|
||||
return MAL_FALSE;
|
||||
}
|
||||
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
BYTE* pData;
|
||||
HRESULT hr = ((IAudioRenderClient*)pDevice->wasapi.pRenderClient)->lpVtbl->GetBuffer((IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailable, &pData);
|
||||
if (FAILED(hr)) {
|
||||
return MAL_FAILED_TO_READ_DATA_FROM_CLIENT;
|
||||
}
|
||||
|
||||
mal_device__read_frames_from_client(pDevice, framesAvailable, pData);
|
||||
|
||||
hr = ((IAudioRenderClient*)pDevice->wasapi.pRenderClient)->lpVtbl->ReleaseBuffer((IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailable, 0);
|
||||
if (FAILED(hr)) {
|
||||
return MAL_FAILED_TO_READ_DATA_FROM_CLIENT;
|
||||
}
|
||||
} else {
|
||||
// TODO: Implement me.
|
||||
}
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1794,6 +2088,8 @@ static mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type t
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
pDevice->api = mal_api_dsound;
|
||||
mal_zero_object(&pDevice->dsound);
|
||||
|
||||
pDevice->dsound.rewindTarget = ~0UL;
|
||||
|
||||
pDevice->dsound.hDSoundDLL = (mal_handle)mal_open_dsound_dll();
|
||||
@@ -2096,7 +2392,7 @@ static mal_bool32 mal_device__get_current_frame__dsound(mal_device* pDevice, mal
|
||||
return MAL_TRUE;
|
||||
}
|
||||
|
||||
static mal_bool32 mal_device__get_available_frames__dsound(mal_device* pDevice)
|
||||
static mal_uint32 mal_device__get_available_frames__dsound(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
@@ -2203,10 +2499,6 @@ static mal_result mal_device__main_loop__dsound(mal_device* pDevice)
|
||||
DWORD lockSize = framesAvailable * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format);
|
||||
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
if (pDevice->dsound.breakFromMainLoop) {
|
||||
return MAL_FALSE;
|
||||
}
|
||||
|
||||
void* pLockPtr;
|
||||
DWORD actualLockSize;
|
||||
void* pLockPtr2;
|
||||
@@ -2677,6 +2969,7 @@ static mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type typ
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
pDevice->api = mal_api_alsa;
|
||||
mal_zero_object(&pDevice->alsa);
|
||||
|
||||
snd_pcm_format_t formatALSA;
|
||||
switch (pConfig->format)
|
||||
@@ -3180,6 +3473,8 @@ static mal_result mal_device_init__sles(mal_device* pDevice, mal_device_type typ
|
||||
// Now we can start initializing the device properly.
|
||||
mal_assert(pDevice != NULL);
|
||||
pDevice->api = mal_api_sles;
|
||||
mal_zero_object(&pDevice->sles);
|
||||
|
||||
pDevice->sles.currentBufferIndex = 0;
|
||||
pDevice->sles.periodSizeInFrames = pConfig->bufferSizeInFrames / pConfig->periods;
|
||||
pDevice->bufferSizeInFrames = pDevice->sles.periodSizeInFrames * pConfig->periods;
|
||||
@@ -3406,6 +3701,11 @@ static mal_result mal_device__start_backend(mal_device* pDevice)
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
mal_result result = MAL_NO_BACKEND;
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (pDevice->api == mal_api_wasapi) {
|
||||
result = mal_device__start_backend__wasapi(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_DSOUND
|
||||
if (pDevice->api == mal_api_dsound) {
|
||||
result = mal_device__start_backend__dsound(pDevice);
|
||||
@@ -3430,6 +3730,11 @@ static mal_result mal_device__stop_backend(mal_device* pDevice)
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
mal_result result = MAL_NO_BACKEND;
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (pDevice->api == mal_api_wasapi) {
|
||||
result = mal_device__stop_backend__wasapi(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_DSOUND
|
||||
if (pDevice->api == mal_api_dsound) {
|
||||
result = mal_device__stop_backend__dsound(pDevice);
|
||||
@@ -3454,6 +3759,11 @@ static mal_result mal_device__break_main_loop(mal_device* pDevice)
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
mal_result result = MAL_NO_BACKEND;
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (pDevice->api == mal_api_wasapi) {
|
||||
result = mal_device__break_main_loop__wasapi(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_DSOUND
|
||||
if (pDevice->api == mal_api_dsound) {
|
||||
result = mal_device__break_main_loop__dsound(pDevice);
|
||||
@@ -3478,6 +3788,11 @@ static mal_result mal_device__main_loop(mal_device* pDevice)
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
mal_result result = MAL_NO_BACKEND;
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (pDevice->api == mal_api_wasapi) {
|
||||
result = mal_device__main_loop__wasapi(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_DSOUND
|
||||
if (pDevice->api == mal_api_dsound) {
|
||||
result = mal_device__main_loop__dsound(pDevice);
|
||||
@@ -3502,6 +3817,10 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
|
||||
mal_device* pDevice = (mal_device*)pData;
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
#ifdef MAL_WIN32
|
||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
#endif
|
||||
|
||||
// This is only used to prevent posting onStop() when the device is first initialized.
|
||||
mal_bool32 skipNextStopEvent = MAL_TRUE;
|
||||
|
||||
@@ -3556,6 +3875,11 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
|
||||
|
||||
// Make sure we aren't continuously waiting on a stop event.
|
||||
mal_event_signal(&pDevice->stopEvent); // <-- Is this still needed?
|
||||
|
||||
#ifdef MAL_WIN32
|
||||
CoUninitialize();
|
||||
#endif
|
||||
|
||||
return (mal_thread_result)0;
|
||||
}
|
||||
|
||||
@@ -3573,16 +3897,16 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d
|
||||
if (pCount == NULL) return mal_post_error(NULL, "mal_enumerate_devices() called with invalid arguments.", MAL_INVALID_ARGS);
|
||||
|
||||
mal_result result = MAL_NO_BACKEND;
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_enumerate_devices__wasapi(type, pCount, pInfo);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_DSOUND
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_enumerate_devices__dsound(type, pCount, pInfo);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_enumerate_devices__wasapi(type, pCount, pInfo);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_ALSA
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_enumerate_devices__alsa(type, pCount, pInfo);
|
||||
@@ -3671,6 +3995,11 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device
|
||||
result = mal_device_init__dsound(pDevice, type, pDeviceID, pConfig);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_device_init__wasapi(pDevice, type, pDeviceID, pConfig);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_ALSA
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_device_init__alsa(pDevice, type, pDeviceID, pConfig);
|
||||
@@ -3736,6 +4065,11 @@ void mal_device_uninit(mal_device* pDevice)
|
||||
mal_event_delete(&pDevice->wakeupEvent);
|
||||
mal_mutex_delete(&pDevice->lock);
|
||||
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
if (pDevice->api == mal_api_wasapi) {
|
||||
mal_device_uninit__wasapi(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_DSOUND
|
||||
if (pDevice->api == mal_api_dsound) {
|
||||
mal_device_uninit__dsound(pDevice);
|
||||
@@ -4003,8 +4337,19 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format)
|
||||
|
||||
// TODO
|
||||
// ====
|
||||
// - Implement the notion of a context.
|
||||
// - Needed to ensure device enumeration and initialization use the same backend.
|
||||
// - Add support for channel mapping.
|
||||
//
|
||||
//
|
||||
// WASAPI
|
||||
// ------
|
||||
// - Add support for non-f32 formats.
|
||||
// - Add support for exclusive mode?
|
||||
// - GetMixFormat() instead of IsFormatSupported().
|
||||
// - Requires a large suite of conversion routines including channel shuffling, SRC and format conversion.
|
||||
//
|
||||
//
|
||||
// ALSA
|
||||
// ----
|
||||
// - Use runtime linking for asound.
|
||||
|
||||
Reference in New Issue
Block a user