Commit 5e9657aa authored by David Reid's avatar David Reid

Add basic logging and improve error reporting.

parent 1301af97
...@@ -126,22 +126,34 @@ typedef int mal_result; ...@@ -126,22 +126,34 @@ typedef int mal_result;
#define MAL_OUT_OF_MEMORY -3 #define MAL_OUT_OF_MEMORY -3
#define MAL_FORMAT_NOT_SUPPORTED -4 #define MAL_FORMAT_NOT_SUPPORTED -4
#define MAL_NO_BACKEND -5 #define MAL_NO_BACKEND -5
#define MAL_DEVICE_BUSY -6 #define MAL_API_NOT_FOUND -6
#define MAL_DEVICE_NOT_INITIALIZED -7 #define MAL_DEVICE_BUSY -7
#define MAL_DEVICE_ALREADY_STARTED -8 #define MAL_DEVICE_NOT_INITIALIZED -8
#define MAL_DEVICE_ALREADY_STARTING -9 #define MAL_DEVICE_ALREADY_STARTED -9
#define MAL_DEVICE_ALREADY_STOPPED -10 #define MAL_DEVICE_ALREADY_STARTING -10
#define MAL_DEVICE_ALREADY_STOPPING -11 #define MAL_DEVICE_ALREADY_STOPPED -11
#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -12 #define MAL_DEVICE_ALREADY_STOPPING -12
#define MAL_FAILED_TO_INIT_BACKEND -13 #define MAL_FAILED_TO_MAP_DEVICE_BUFFER -13
#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -14 #define MAL_FAILED_TO_INIT_BACKEND -14
#define MAL_FAILED_TO_START_BACKEND_DEVICE -15 #define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -15
#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -16 #define MAL_FAILED_TO_START_BACKEND_DEVICE -16
#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -17
#define MAL_FAILED_TO_CREATE_EVENT -18
#define MAL_FAILED_TO_CREATE_THREAD -19
#define MAL_DSOUND_FAILED_TO_CREATE_DEVICE -1024
#define MAL_DSOUND_FAILED_TO_SET_COOP_LEVEL -1025
#define MAL_DSOUND_FAILED_TO_CREATE_BUFFER -1026
#define MAL_DSOUND_FAILED_TO_QUERY_INTERFACE -1027
#define MAL_DSOUND_FAILED_TO_SET_NOTIFICATIONS -1028
#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
typedef struct mal_device mal_device; typedef struct mal_device mal_device;
typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 sampleCount, const void* pSamples); typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 sampleCount, const void* pSamples);
typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 sampleCount, void* pSamples); typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 sampleCount, void* pSamples);
typedef void (* mal_log_proc) (mal_device* pDevice, const char* message);
typedef enum typedef enum
{ {
...@@ -158,7 +170,8 @@ typedef enum ...@@ -158,7 +170,8 @@ typedef enum
typedef enum typedef enum
{ {
// I like to keep these explicitly defined because they're used as a key into a lookup table. // I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
// added to this, make sure there are no gaps and that they're added to the lookup table in mal_get_sample_size_in_bytes().
mal_format_u8 = 0, mal_format_u8 = 0,
mal_format_s16 = 1, mal_format_s16 = 1,
mal_format_s24 = 2, mal_format_s24 = 2,
...@@ -193,6 +206,7 @@ struct mal_device ...@@ -193,6 +206,7 @@ struct mal_device
mal_uint32 state; mal_uint32 state;
mal_recv_proc onRecv; mal_recv_proc onRecv;
mal_send_proc onSend; mal_send_proc onSend;
mal_log_proc onLog;
void* pUserData; // Application defined data. void* pUserData; // Application defined data.
mal_event wakeupEvent; mal_event wakeupEvent;
mal_event startEvent; mal_event startEvent;
...@@ -457,8 +471,6 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format); ...@@ -457,8 +471,6 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format);
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> // For printf() debugging. TODO: Delete this and replace with a proper logging system.
#ifdef _WIN32 #ifdef _WIN32
#ifdef _WIN64 #ifdef _WIN64
#define MAL_64BIT #define MAL_64BIT
...@@ -830,6 +842,22 @@ mal_bool32 mal_event_signal(mal_event* pEvent) ...@@ -830,6 +842,22 @@ mal_bool32 mal_event_signal(mal_event* pEvent)
} }
// Posts a log message.
static void mal_log(mal_device* pDevice, const char* message)
{
if (pDevice && pDevice->onLog) {
pDevice->onLog(pDevice, message);
}
}
// Posts an error. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode".
static mal_result mal_post_error(mal_device* pDevice, const char* message, mal_result resultCode)
{
mal_log(pDevice, message);
return resultCode;
}
// A helper function for reading sample data from the client. Returns the number of samples read from the client. Remaining samples // A helper function for reading sample data from the client. Returns the number of samples read from the client. Remaining samples
// are filled with silence. // are filled with silence.
...@@ -912,35 +940,35 @@ mal_result mal_device_init__null(mal_device* pDevice, mal_device_type type, mal_ ...@@ -912,35 +940,35 @@ mal_result mal_device_init__null(mal_device* pDevice, mal_device_type type, mal_
pDevice->api = mal_api_null; pDevice->api = mal_api_null;
// TODO: Implement me. // TODO: Implement me.
return MAL_NO_BACKEND; return mal_post_error(pDevice, "Not yet implemented.", MAL_ERROR);
} }
static mal_result mal_device__start_backend__null(mal_device* pDevice) static mal_result mal_device__start_backend__null(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
return MAL_ERROR; return mal_post_error(pDevice, "Not yet implemented.", MAL_ERROR);
} }
static mal_result mal_device__stop_backend__null(mal_device* pDevice) static mal_result mal_device__stop_backend__null(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
return MAL_ERROR; return mal_post_error(pDevice, "Not yet implemented.", MAL_ERROR);
} }
static mal_result mal_device__break_main_loop__null(mal_device* pDevice) static mal_result mal_device__break_main_loop__null(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
return MAL_ERROR; return mal_post_error(pDevice, "Not yet implemented.", MAL_ERROR);
} }
static mal_result mal_device__main_loop__null(mal_device* pDevice) static mal_result mal_device__main_loop__null(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
return MAL_ERROR; return mal_post_error(pDevice, "Not yet implemented.", MAL_ERROR);
} }
#endif #endif
...@@ -1157,18 +1185,18 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1157,18 +1185,18 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
mal_DirectSoundCreate8Proc pDirectSoundCreate8 = (mal_DirectSoundCreate8Proc)GetProcAddress(pDevice->dsound.hDSoundDLL, "DirectSoundCreate8"); mal_DirectSoundCreate8Proc pDirectSoundCreate8 = (mal_DirectSoundCreate8Proc)GetProcAddress(pDevice->dsound.hDSoundDLL, "DirectSoundCreate8");
if (pDirectSoundCreate8 == NULL) { if (pDirectSoundCreate8 == NULL) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] Could not find DirectSoundCreate8().", MAL_API_NOT_FOUND);
} }
if (FAILED(pDirectSoundCreate8((pDeviceID == NULL) ? NULL : (LPCGUID)pDeviceID->guid, (LPDIRECTSOUND8*)&pDevice->dsound.pPlayback, NULL))) { if (FAILED(pDirectSoundCreate8((pDeviceID == NULL) ? NULL : (LPCGUID)pDeviceID->guid, (LPDIRECTSOUND8*)&pDevice->dsound.pPlayback, NULL))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] DirectSoundCreate8() failed for playback device.", MAL_DSOUND_FAILED_TO_CREATE_DEVICE);
} }
// The cooperative level must be set before doing anything else. // The cooperative level must be set before doing anything else.
if (FAILED(IDirectSound_SetCooperativeLevel((LPDIRECTSOUND8)pDevice->dsound.pPlayback, GetForegroundWindow(), DSSCL_PRIORITY))) { if (FAILED(IDirectSound_SetCooperativeLevel((LPDIRECTSOUND8)pDevice->dsound.pPlayback, GetForegroundWindow(), DSSCL_PRIORITY))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MAL_DSOUND_FAILED_TO_SET_COOP_LEVEL);
} }
DSBUFFERDESC descDSPrimary; DSBUFFERDESC descDSPrimary;
...@@ -1177,7 +1205,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1177,7 +1205,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND8)pDevice->dsound.pPlayback, &descDSPrimary, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) { if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND8)pDevice->dsound.pPlayback, &descDSPrimary, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER);
} }
// From MSDN: // From MSDN:
...@@ -1187,21 +1215,21 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1187,21 +1215,21 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
// and compare the result with the format that was requested with the SetFormat method. // and compare the result with the format that was requested with the SetFormat method.
if (FAILED(IDirectSoundBuffer_SetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) { if (FAILED(IDirectSoundBuffer_SetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] Failed to set format of playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED);
} }
// Get the _actual_ properties of the buffer. This is silly API design... // Get the _actual_ properties of the buffer. This is silly API design...
DWORD requiredSize; DWORD requiredSize;
if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, NULL, 0, &requiredSize))) { if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, NULL, 0, &requiredSize))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED);
} }
char rawdata[1024]; char rawdata[1024];
WAVEFORMATEXTENSIBLE* pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; WAVEFORMATEXTENSIBLE* pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, requiredSize, NULL))) { if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, requiredSize, NULL))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED);
} }
pDevice->channels = pActualFormat->Format.nChannels; pDevice->channels = pActualFormat->Format.nChannels;
...@@ -1231,25 +1259,25 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1231,25 +1259,25 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND8)pDevice->dsound.pPlayback, &descDS, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackBuffer, NULL))) { if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND8)pDevice->dsound.pPlayback, &descDS, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackBuffer, NULL))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER);
} }
// Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer. // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer.
if (FAILED(IDirectSoundBuffer8_QueryInterface((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, g_mal_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { if (FAILED(IDirectSoundBuffer8_QueryInterface((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, g_mal_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer8_QueryInterface() failed for playback device's IDirectSoundNotify object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE);
} }
} else { } else {
mal_DirectSoundCaptureCreate8Proc pDirectSoundCaptureCreate8 = (mal_DirectSoundCaptureCreate8Proc)GetProcAddress(pDevice->dsound.hDSoundDLL, "DirectSoundCaptureCreate8"); mal_DirectSoundCaptureCreate8Proc pDirectSoundCaptureCreate8 = (mal_DirectSoundCaptureCreate8Proc)GetProcAddress(pDevice->dsound.hDSoundDLL, "DirectSoundCaptureCreate8");
if (pDirectSoundCaptureCreate8 == NULL) { if (pDirectSoundCaptureCreate8 == NULL) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] Could not find DirectSoundCreate8().", MAL_API_NOT_FOUND);
} }
if (FAILED(pDirectSoundCaptureCreate8((pDeviceID == NULL) ? NULL : (LPCGUID)pDeviceID->guid, (LPDIRECTSOUNDCAPTURE8*)&pDevice->dsound.pCapture, NULL))) { if (FAILED(pDirectSoundCaptureCreate8((pDeviceID == NULL) ? NULL : (LPCGUID)pDeviceID->guid, (LPDIRECTSOUNDCAPTURE8*)&pDevice->dsound.pCapture, NULL))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] DirectSoundCaptureCreate8() failed for capture device.", MAL_DSOUND_FAILED_TO_CREATE_DEVICE);
} }
DSCBUFFERDESC descDS; DSCBUFFERDESC descDS;
...@@ -1261,20 +1289,20 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1261,20 +1289,20 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
LPDIRECTSOUNDCAPTUREBUFFER pDSCB_Temp; LPDIRECTSOUNDCAPTUREBUFFER pDSCB_Temp;
if (FAILED(IDirectSoundCapture_CreateCaptureBuffer((LPDIRECTSOUNDCAPTURE8)pDevice->dsound.pCapture, &descDS, &pDSCB_Temp, NULL))) { if (FAILED(IDirectSoundCapture_CreateCaptureBuffer((LPDIRECTSOUNDCAPTURE8)pDevice->dsound.pCapture, &descDS, &pDSCB_Temp, NULL))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER);
} }
HRESULT hr = IDirectSoundCapture_QueryInterface(pDSCB_Temp, g_mal_GUID_IID_IDirectSoundCaptureBuffer8, (LPVOID*)&pDevice->dsound.pCaptureBuffer); HRESULT hr = IDirectSoundCapture_QueryInterface(pDSCB_Temp, g_mal_GUID_IID_IDirectSoundCaptureBuffer8, (LPVOID*)&pDevice->dsound.pCaptureBuffer);
IDirectSoundCaptureBuffer_Release(pDSCB_Temp); IDirectSoundCaptureBuffer_Release(pDSCB_Temp);
if (FAILED(hr)) { if (FAILED(hr)) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_QueryInterface() failed for capture device's IDirectSoundCaptureBuffer8 object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE);
} }
// Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer. // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer.
if (FAILED(IDirectSoundCaptureBuffer8_QueryInterface((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, g_mal_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { if (FAILED(IDirectSoundCaptureBuffer8_QueryInterface((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, g_mal_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer8_QueryInterface() failed for capture device's IDirectSoundNotify object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE);
} }
} }
...@@ -1287,7 +1315,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1287,7 +1315,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
pDevice->dsound.pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL); pDevice->dsound.pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL);
if (pDevice->dsound.pNotifyEvents[i] == NULL) { if (pDevice->dsound.pNotifyEvents[i] == NULL) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] Failed to create event for buffer notifications.", MAL_FAILED_TO_CREATE_EVENT);
} }
// The notification offset is in bytes. // The notification offset is in bytes.
...@@ -1304,7 +1332,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1304,7 +1332,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
if (FAILED(IDirectSoundNotify_SetNotificationPositions((LPDIRECTSOUNDNOTIFY)pDevice->dsound.pNotify, pDevice->fragmentCount, notifyPoints))) { if (FAILED(IDirectSoundNotify_SetNotificationPositions((LPDIRECTSOUNDNOTIFY)pDevice->dsound.pNotify, pDevice->fragmentCount, notifyPoints))) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_NO_BACKEND; return mal_post_error(pDevice, "[DirectSound] IDirectSoundNotify_SetNotificationPositions() failed.", MAL_DSOUND_FAILED_TO_SET_NOTIFICATIONS);
} }
...@@ -1314,7 +1342,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma ...@@ -1314,7 +1342,7 @@ mal_result mal_device_init__dsound(mal_device* pDevice, mal_device_type type, ma
pDevice->dsound.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL); pDevice->dsound.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
if (pDevice->dsound.hStopEvent == NULL) { if (pDevice->dsound.hStopEvent == NULL) {
mal_device_uninit__dsound(pDevice); mal_device_uninit__dsound(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[DirectSound] Failed to create event for main loop break notification.", MAL_FAILED_TO_CREATE_EVENT);
} }
...@@ -1332,7 +1360,7 @@ static mal_result mal_device__read_fragment_from_client__dsound(mal_device* pDev ...@@ -1332,7 +1360,7 @@ static mal_result mal_device__read_fragment_from_client__dsound(mal_device* pDev
void* pLockPtr; void* pLockPtr;
DWORD lockSize; DWORD lockSize;
if (FAILED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, offset, fragmentSizeInBytes, &pLockPtr, &lockSize, NULL, NULL, 0))) { if (FAILED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, offset, fragmentSizeInBytes, &pLockPtr, &lockSize, NULL, NULL, 0))) {
return MAL_FAILED_TO_MAP_DEVICE_BUFFER; return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER);
} }
mal_device__read_samples_from_client(pDevice, pDevice->fragmentSizeInFrames * pDevice->channels, pLockPtr); mal_device__read_samples_from_client(pDevice, pDevice->fragmentSizeInFrames * pDevice->channels, pLockPtr);
...@@ -1351,7 +1379,7 @@ static mal_result mal_device__send_fragment_to_client__dsound(mal_device* pDevic ...@@ -1351,7 +1379,7 @@ static mal_result mal_device__send_fragment_to_client__dsound(mal_device* pDevic
void* pLockPtr; void* pLockPtr;
DWORD lockSize; DWORD lockSize;
if (FAILED(IDirectSoundCaptureBuffer_Lock((LPDIRECTSOUNDCAPTUREBUFFER8)pDevice->dsound.pCaptureBuffer, offset, fragmentSizeInBytes, &pLockPtr, &lockSize, NULL, NULL, 0))) { if (FAILED(IDirectSoundCaptureBuffer_Lock((LPDIRECTSOUNDCAPTUREBUFFER8)pDevice->dsound.pCaptureBuffer, offset, fragmentSizeInBytes, &pLockPtr, &lockSize, NULL, NULL, 0))) {
return MAL_FAILED_TO_MAP_DEVICE_BUFFER; return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER);
} }
mal_device__send_samples_to_client(pDevice, pDevice->fragmentSizeInFrames * pDevice->channels, pLockPtr); mal_device__send_samples_to_client(pDevice, pDevice->fragmentSizeInFrames * pDevice->channels, pLockPtr);
...@@ -1367,16 +1395,17 @@ static mal_result mal_device__start_backend__dsound(mal_device* pDevice) ...@@ -1367,16 +1395,17 @@ static mal_result mal_device__start_backend__dsound(mal_device* pDevice)
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
// Before playing anything we need to grab an initial fragment of sample data from the client. // Before playing anything we need to grab an initial fragment of sample data from the client.
if (mal_device__read_fragment_from_client__dsound(pDevice, 0) != MAL_SUCCESS) { mal_result result = mal_device__read_fragment_from_client__dsound(pDevice, 0);
return MAL_FAILED_TO_READ_DATA_FROM_CLIENT; if (result != MAL_SUCCESS) {
return result; // The error will have already been posted.
} }
if (IDirectSoundBuffer_Play((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0, 0, DSBPLAY_LOOPING) != DS_OK) { if (IDirectSoundBuffer_Play((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0, 0, DSBPLAY_LOOPING) != DS_OK) {
return MAL_FAILED_TO_START_BACKEND_DEVICE; return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Play() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE);
} }
} else { } else {
if (IDirectSoundCaptureBuffer8_Start((LPDIRECTSOUNDCAPTUREBUFFER8)pDevice->dsound.pCaptureBuffer, DSCBSTART_LOOPING) != DS_OK) { if (IDirectSoundCaptureBuffer8_Start((LPDIRECTSOUNDCAPTUREBUFFER8)pDevice->dsound.pCaptureBuffer, DSCBSTART_LOOPING) != DS_OK) {
return MAL_FAILED_TO_START_BACKEND_DEVICE; return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer8_Start() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE);
} }
} }
...@@ -1389,13 +1418,13 @@ static mal_result mal_device__stop_backend__dsound(mal_device* pDevice) ...@@ -1389,13 +1418,13 @@ static mal_result mal_device__stop_backend__dsound(mal_device* pDevice)
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
if (IDirectSoundBuffer_Stop((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer) != DS_OK) { if (IDirectSoundBuffer_Stop((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer) != DS_OK) {
return MAL_FAILED_TO_STOP_BACKEND_DEVICE; return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
} }
IDirectSoundBuffer_SetCurrentPosition((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0); IDirectSoundBuffer_SetCurrentPosition((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0);
} else { } else {
if (IDirectSoundCaptureBuffer_Stop((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer) != DS_OK) { if (IDirectSoundCaptureBuffer_Stop((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer) != DS_OK) {
return MAL_FAILED_TO_STOP_BACKEND_DEVICE; return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
} }
} }
...@@ -1679,7 +1708,7 @@ mal_result mal_enumerate_devices__alsa(mal_device_type type, mal_uint32* pCount, ...@@ -1679,7 +1708,7 @@ mal_result mal_enumerate_devices__alsa(mal_device_type type, mal_uint32* pCount,
char** ppDeviceHints; char** ppDeviceHints;
if (snd_device_name_hint(-1, "pcm", (void***)&ppDeviceHints) < 0) { if (snd_device_name_hint(-1, "pcm", (void***)&ppDeviceHints) < 0) {
return MAL_SUCCESS; // <-- This is unintuitive, but it just emulates the case where there are no devices. return MAL_NO_BACKEND;
} }
char** ppNextDeviceHint = ppDeviceHints; char** ppNextDeviceHint = ppDeviceHints;
...@@ -1750,55 +1779,55 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_ ...@@ -1750,55 +1779,55 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_
case mal_format_f32: formatALSA = SND_PCM_FORMAT_FLOAT_LE; break; case mal_format_f32: formatALSA = SND_PCM_FORMAT_FLOAT_LE; break;
case mal_format_alaw: formatALSA = SND_PCM_FORMAT_A_LAW; break; case mal_format_alaw: formatALSA = SND_PCM_FORMAT_A_LAW; break;
case mal_format_mulaw: formatALSA = SND_PCM_FORMAT_MU_LAW; break; case mal_format_mulaw: formatALSA = SND_PCM_FORMAT_MU_LAW; break;
default: return MAL_FORMAT_NOT_SUPPORTED; return mal_post_error(pDevice, "[ALSA] Format not supported.", MAL_FORMAT_NOT_SUPPORTED);
} }
if (snd_pcm_open((snd_pcm_t**)&pDevice->alsa.pPCM, deviceName, (type == mal_device_type_playback) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0) < 0) { if (snd_pcm_open((snd_pcm_t**)&pDevice->alsa.pPCM, deviceName, (type == mal_device_type_playback) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0) < 0) {
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed.", MAL_ALSA_FAILED_TO_OPEN_DEVICE);
} }
snd_pcm_hw_params_t* pHWParams = NULL; snd_pcm_hw_params_t* pHWParams = NULL;
if (snd_pcm_hw_params_malloc(&pHWParams) < 0) { if (snd_pcm_hw_params_malloc(&pHWParams) < 0) {
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_OUT_OF_MEMORY; return mal_post_error(pDevice, "[ALSA] Failed to allocate snd_pcm_hw_params_t.", MAL_OUT_OF_MEMORY);
} }
if (snd_pcm_hw_params_any((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { if (snd_pcm_hw_params_any((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MAL_ALSA_FAILED_TO_SET_HW_PARAMS);
} }
if (snd_pcm_hw_params_set_access((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { if (snd_pcm_hw_params_set_access((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set access mode to SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
if (snd_pcm_hw_params_set_format((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, formatALSA) < 0) { if (snd_pcm_hw_params_set_format((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, formatALSA) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FORMAT_NOT_SUPPORTED; return mal_post_error(pDevice, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
if (snd_pcm_hw_params_set_rate_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) { if (snd_pcm_hw_params_set_rate_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
if (snd_pcm_hw_params_set_channels_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &channels) < 0) { if (snd_pcm_hw_params_set_channels_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &channels) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
int dir = 0; int dir = 0;
if (snd_pcm_hw_params_set_periods_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &fragmentCount, &dir) < 0) { if (snd_pcm_hw_params_set_periods_near((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &fragmentCount, &dir) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set fragment count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
// A few properties may have been adjusted so we need to make sure the device object is aware. // A few properties may have been adjusted so we need to make sure the device object is aware.
...@@ -1815,13 +1844,13 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_ ...@@ -1815,13 +1844,13 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_
if (snd_pcm_hw_params_set_buffer_size((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, pDevice->fragmentSizeInFrames * pDevice->fragmentCount) < 0) { if (snd_pcm_hw_params_set_buffer_size((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, pDevice->fragmentSizeInFrames * pDevice->fragmentCount) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
if (snd_pcm_hw_params((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { if (snd_pcm_hw_params((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) {
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS);
} }
snd_pcm_hw_params_free(pHWParams); snd_pcm_hw_params_free(pHWParams);
...@@ -1831,33 +1860,33 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_ ...@@ -1831,33 +1860,33 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_
snd_pcm_sw_params_t* pSWParams = NULL; snd_pcm_sw_params_t* pSWParams = NULL;
if (snd_pcm_sw_params_malloc(&pSWParams) < 0) { if (snd_pcm_sw_params_malloc(&pSWParams) < 0) {
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to allocate software parameters. snd_pcm_dw_params_malloc() failed.", MAL_OUT_OF_MEMORY);
} }
if (snd_pcm_sw_params_current((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { if (snd_pcm_sw_params_current((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) {
snd_pcm_sw_params_free(pSWParams); snd_pcm_sw_params_free(pSWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS);
} }
if (snd_pcm_sw_params_set_avail_min((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, pDevice->fragmentSizeInFrames) != 0) { if (snd_pcm_sw_params_set_avail_min((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, pDevice->fragmentSizeInFrames) != 0) {
snd_pcm_sw_params_free(pSWParams); snd_pcm_sw_params_free(pSWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set fragment size. snd_pcm_sw_params_set_avail_min() failed.", MAL_FORMAT_NOT_SUPPORTED);
} }
if (type == mal_device_type_playback) { if (type == mal_device_type_playback) {
if (snd_pcm_sw_params_set_start_threshold((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, pDevice->fragmentSizeInFrames) != 0) { if (snd_pcm_sw_params_set_start_threshold((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, pDevice->fragmentSizeInFrames) != 0) {
snd_pcm_sw_params_free(pSWParams); snd_pcm_sw_params_free(pSWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS);
} }
} }
if (snd_pcm_sw_params((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { if (snd_pcm_sw_params((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) {
snd_pcm_sw_params_free(pSWParams); snd_pcm_sw_params_free(pSWParams);
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS);
} }
snd_pcm_sw_params_free(pSWParams); snd_pcm_sw_params_free(pSWParams);
...@@ -1869,7 +1898,7 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_ ...@@ -1869,7 +1898,7 @@ mal_result mal_device_init__alsa(mal_device* pDevice, mal_device_type type, mal_
pDevice->alsa.pIntermediaryBuffer = mal_malloc(pDevice->fragmentSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format)); pDevice->alsa.pIntermediaryBuffer = mal_malloc(pDevice->fragmentSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format));
if (pDevice->alsa.pIntermediaryBuffer == NULL) { if (pDevice->alsa.pIntermediaryBuffer == NULL) {
mal_device_uninit__alsa(pDevice); mal_device_uninit__alsa(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MAL_OUT_OF_MEMORY);
} }
} }
...@@ -2097,7 +2126,7 @@ mal_bool32 mal_device__is_initialized(mal_device* pDevice) ...@@ -2097,7 +2126,7 @@ mal_bool32 mal_device__is_initialized(mal_device* pDevice)
mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo)
{ {
if (pCount == NULL) return MAL_INVALID_ARGS; if (pCount == NULL) return mal_post_error(NULL, "mal_enumerate_devices() called with invalid arguments.", MAL_INVALID_ARGS);
mal_result result = MAL_NO_BACKEND; mal_result result = MAL_NO_BACKEND;
#ifdef MAL_ENABLE_DSOUND #ifdef MAL_ENABLE_DSOUND
...@@ -2123,14 +2152,14 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d ...@@ -2123,14 +2152,14 @@ mal_result mal_enumerate_devices(mal_device_type type, mal_uint32* pCount, mal_d
mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 fragmentSizeInFrames, mal_uint32 fragmentCount) mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device_id* pDeviceID, mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 fragmentSizeInFrames, mal_uint32 fragmentCount)
{ {
if (pDevice == NULL) return MAL_INVALID_ARGS; if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS);
mal_zero_object(pDevice); mal_zero_object(pDevice);
if (((mal_uint64)pDevice % sizeof(pDevice)) != 0) { if (((mal_uint64)pDevice % sizeof(pDevice)) != 0) {
// TODO: Emit a warning that the device is not thread safe. // TODO: Emit a warning that the device is not thread safe.
} }
if (channels == 0 || sampleRate == 0 || fragmentSizeInFrames == 0 || fragmentCount == 0) return MAL_INVALID_ARGS; if (channels == 0 || sampleRate == 0 || fragmentSizeInFrames == 0 || fragmentCount == 0) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS);
pDevice->type = type; pDevice->type = type;
pDevice->format = format; pDevice->format = format;
...@@ -2146,16 +2175,16 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device ...@@ -2146,16 +2175,16 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device
// Each of these semaphores is released internally by the worker thread when the work is completed. The start // Each of these semaphores is released internally by the worker thread when the work is completed. The start
// semaphore is also used to wake up the worker thread. // semaphore is also used to wake up the worker thread.
if (!mal_event_create(&pDevice->wakeupEvent)) { if (!mal_event_create(&pDevice->wakeupEvent)) {
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT);
} }
if (!mal_event_create(&pDevice->startEvent)) { if (!mal_event_create(&pDevice->startEvent)) {
mal_event_delete(&pDevice->wakeupEvent); mal_event_delete(&pDevice->wakeupEvent);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT);
} }
if (!mal_event_create(&pDevice->stopEvent)) { if (!mal_event_create(&pDevice->stopEvent)) {
mal_event_delete(&pDevice->startEvent); mal_event_delete(&pDevice->startEvent);
mal_event_delete(&pDevice->wakeupEvent); mal_event_delete(&pDevice->wakeupEvent);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT);
} }
...@@ -2179,14 +2208,14 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device ...@@ -2179,14 +2208,14 @@ mal_result mal_device_init(mal_device* pDevice, mal_device_type type, mal_device
#endif #endif
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
return MAL_NO_BACKEND; return MAL_NO_BACKEND; // The error message will have been posted by the source of the error.
} }
// The worker thread. // The worker thread.
if (!mal_thread_create(&pDevice->thread, mal_worker_thread, pDevice)) { if (!mal_thread_create(&pDevice->thread, mal_worker_thread, pDevice)) {
mal_device_uninit(pDevice); mal_device_uninit(pDevice);
return MAL_FAILED_TO_INIT_BACKEND; return mal_post_error(pDevice, "Failed to create worker thread.", MAL_FAILED_TO_CREATE_THREAD);
} }
...@@ -2253,21 +2282,21 @@ void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc) ...@@ -2253,21 +2282,21 @@ void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc)
mal_result mal_device_start(mal_device* pDevice) mal_result mal_device_start(mal_device* pDevice)
{ {
if (pDevice == NULL) return MAL_INVALID_ARGS; if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_start() called with invalid arguments.", MAL_INVALID_ARGS);
if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return MAL_DEVICE_NOT_INITIALIZED; if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
// Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely // Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely
// a bug with the application. // a bug with the application.
if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) { if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) {
return MAL_DEVICE_ALREADY_STARTING; return mal_post_error(pDevice, "mal_device_init() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING);
} }
if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) {
return MAL_DEVICE_ALREADY_STARTED; return mal_post_error(pDevice, "mal_device_init() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED);
} }
// The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy. // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy.
if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
return MAL_DEVICE_BUSY; return mal_post_error(pDevice, "mal_device_init() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY);
} }
mal_device__set_state(pDevice, MAL_STATE_STARTING); mal_device__set_state(pDevice, MAL_STATE_STARTING);
...@@ -2281,21 +2310,21 @@ mal_result mal_device_start(mal_device* pDevice) ...@@ -2281,21 +2310,21 @@ mal_result mal_device_start(mal_device* pDevice)
mal_result mal_device_stop(mal_device* pDevice) mal_result mal_device_stop(mal_device* pDevice)
{ {
if (pDevice == NULL) return MAL_INVALID_ARGS; if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_stop() called with invalid arguments.", MAL_INVALID_ARGS);
if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return MAL_DEVICE_NOT_INITIALIZED; if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
// Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely // Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely
// a bug with the application. // a bug with the application.
if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) { if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
return MAL_DEVICE_ALREADY_STOPPING; return mal_post_error(pDevice, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING);
} }
if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) { if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) {
return MAL_DEVICE_ALREADY_STOPPED; return mal_post_error(pDevice, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED);
} }
// The device needs to be in a started state. If it's not, we just let the caller know the device is busy. // The device needs to be in a started state. If it's not, we just let the caller know the device is busy.
if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) { if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) {
return MAL_DEVICE_BUSY; return mal_post_error(pDevice, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY);
} }
mal_device__set_state(pDevice, MAL_STATE_STOPPING); mal_device__set_state(pDevice, MAL_STATE_STOPPING);
...@@ -2357,6 +2386,7 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format) ...@@ -2357,6 +2386,7 @@ mal_uint32 mal_get_sample_size_in_bytes(mal_format format)
// - Implement mmap mode for ALSA. // - Implement mmap mode for ALSA.
// - Make device initialization more robust for ALSA // - Make device initialization more robust for ALSA
// - Clamp period sizes to their min/max. // - Clamp period sizes to their min/max.
// - Look at using snd_pcm_sw/hw_params_calloc() instead of *malloc().
// - Support rewinding. This will enable applications to employ better anti-latency. // - Support rewinding. This will enable applications to employ better anti-latency.
// - Implement the null device. // - Implement the null device.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment