瀏覽代碼

sokol_audio.h: remove uwp support, replace logging with new unified logging and error reporting callback

Andre Weissflog 2 年之前
父節點
當前提交
1d81610955
共有 1 個文件被更改,包括 259 次插入249 次删除
  1. 259 249
      sokol_audio.h

+ 259 - 249
sokol_audio.h

@@ -400,27 +400,36 @@
     was called, so you don't need to worry about thread-safety.
 
 
-    LOG FUNCTION OVERRIDE
-    =====================
-    You can override the log function at initialization time like this:
-
-        void my_log(const char* message, void* user_data) {
-            printf("saudio says: \s\n", message);
+    LOGGING AND ERROR REPORTING OVERRIDE
+    ====================================
+    To override logging, first write a logging function like this:
+
+        void my_log(const char* tag,            // e.g. 'saudio'
+                    uint32_t log_level,         // 0=panic, 1=error, 2=warn, 3=info
+                    uint32_t log_item,          // SAUDIO_LOGITEM_*
+                    const char* log_item_str,   // log item as string, only in debug mode, otherwise empty string
+                    int line_nr,                // line number in sokol_audio.h
+                    const char* filename,       // debug mode only, otherwise empty string
+                    void* user_data)
+        {
+            ...
         }
 
-        ...
-            saudio_setup(&(saudio_desc){
-                // ...
-                .logger = {
-                    .log_cb = my_log,
-                    .user_data = ...,
-                }
-            });
-        ...
+    ...and then setup sokol-audio like this:
+
+        saudio_setup(&(saudio_desc){
+            .logger = {
+                .func = my_log,
+                .user_data = ...,
+            }
+        });
 
-    If no overrides are provided, puts will be used on most platforms.
-    On Android, __android_log_write will be used instead.
+    The provided logging function should be reentrant (e.g. be callable from
+    different threads).
 
+    If no custom logger is provided, verbose default logging goes to stderr
+    (this means you won't see any logging messages on Android, or on Windows
+    unless the process is attached to a terminal!).
 
     LICENSE
     =======
@@ -470,6 +479,91 @@
 extern "C" {
 #endif
 
+/*
+    saudio_log_item
+
+    Log items are defined via X-Macros, and expanded to an
+    enum 'saudio_log_item', and in debug mode only,
+    corresponding strings.
+
+    Used as parameter in the logging callback.
+*/
+#define _SAUDIO_LOG_ITEMS \
+    _SAUDIO_XMACRO(OK) \
+    _SAUDIO_XMACRO(MALLOC_FAILED) \
+    _SAUDIO_XMACRO(ALSA_SNDPCMOPEN_FAILED) \
+    _SAUDIO_XMACRO(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED) \
+    _SAUDIO_XMACRO(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED) \
+    _SAUDIO_XMACRO(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED) \
+    _SAUDIO_XMACRO(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED) \
+    _SAUDIO_XMACRO(ALSA_SND_PCM_HW_PARAMS_FAILED) \
+    _SAUDIO_XMACRO(ALSA_PTHREAD_CREATE_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_CREATE_EVENT_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_DEVICE_ACTIVATE_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED) \
+    _SAUDIO_XMACRO(WASAPI_CREATE_THREAD_FAILED) \
+    _SAUDIO_XMACRO(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED) \
+    _SAUDIO_XMACRO(AAUDIO_PTHREAD_CREATE_FAILED) \
+    _SAUDIO_XMACRO(AAUDIO_RESTARTING_STREAM_AFTER_ERROR) \
+    _SAUDIO_XMACRO(USING_AAUDIO_BACKEND) \
+    _SAUDIO_XMACRO(AAUDIO_CREATE_STREAMBUILDER_FAILED) \
+    _SAUDIO_XMACRO(USING_SLES_BACKEND) \
+    _SAUDIO_XMACRO(SLES_CREATE_ENGINE_FAILED) \
+    _SAUDIO_XMACRO(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED) \
+    _SAUDIO_XMACRO(SLES_CREATE_OUTPUT_MIX_FAILED) \
+    _SAUDIO_XMACRO(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED) \
+    _SAUDIO_XMACRO(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED) \
+    _SAUDIO_XMACRO(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED) \
+    _SAUDIO_XMACRO(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED) \
+    _SAUDIO_XMACRO(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED) \
+    _SAUDIO_XMACRO(COREAUDIO_NEW_OUTPUT_FAILED) \
+    _SAUDIO_XMACRO(COREAUDIO_ALLOCATE_BUFFER_FAILED) \
+    _SAUDIO_XMACRO(COREAUDIO_START_FAILED) \
+    _SAUDIO_XMACRO(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE) \
+
+#define _SAUDIO_XMACRO(item) SAUDIO_LOGITEM_##item,
+typedef enum saudio_log_item {
+    _SAUDIO_LOG_ITEMS
+} saudio_log_item;
+#undef _SAUDIO_XMACRO
+
+/*
+    saudio_loglevel
+
+    Used as parameter in the logging callback. Note that
+    loglevel PANIC is expected to terminate the application
+    because execution cannot continue.
+*/
+typedef enum saudio_loglevel {
+    SAUDIO_LOGLEVEL_PANIC = 0,
+    SAUDIO_LOGLEVEL_ERROR = 1,
+    SAUDIO_LOGLEVEL_WARN = 2,
+    SAUDIO_LOGLEVEL_INFO = 3,
+} saudio_loglevel;
+
+/*
+    saudio_logger
+
+    Used in saudio_desc to provide a custom logging and error reporting
+    callback to sokol-audio.
+*/
+typedef struct saudio_logger {
+    void (*func)(
+        const char* tag,            // always "saudio"
+        uint32_t log_level,         // 0=panic, 1=error, 2=warning, 3=info
+        uint32_t log_item,          // SAUDIO_LOGITEM_*
+        const char* log_item_str,   // log item as string, debug only, otherwise empty string
+        int line_nr,                // line number in sokol_audio.h
+        const char* filename,       // debug mode only, otherwise empty string
+        void* user_data);
+    void* user_data;
+} saudio_logger;
+
 /*
     saudio_allocator
 
@@ -484,17 +578,6 @@ typedef struct saudio_allocator {
     void* user_data;
 } saudio_allocator;
 
-/*
-    saudio_logger
-
-    Used in saudio_desc to provide custom log callbacks to sokol_audio.h.
-    Default behavior is SOKOL_LOG(message).
-*/
-typedef struct saudio_logger {
-    void (*log_cb)(const char* message, void* user_data);
-    void* user_data;
-} saudio_logger;
-
 typedef struct saudio_desc {
     int sample_rate;        // requested sample rate
     int num_channels;       // number of channels, default: 1 (mono)
@@ -572,21 +655,6 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
     #define SOKOL_ASSERT(c) assert(c)
 #endif
 
-#if !defined(SOKOL_DEBUG)
-    #define SAUDIO_LOG(s)
-#else
-    #define SAUDIO_LOG(s) _saudio_log(s)
-    #ifndef SOKOL_LOG
-        #if defined(__ANDROID__)
-            #include <android/log.h>
-            #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_AUDIO", s)
-        #else
-            #include <stdio.h>
-            #define SOKOL_LOG(s) puts(s)
-        #endif
-    #endif
-#endif
-
 #ifndef _SOKOL_PRIVATE
     #if defined(__GNUC__) || defined(__clang__)
         #define _SOKOL_PRIVATE __attribute__((unused)) static
@@ -616,9 +684,7 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
     #define _SAUDIO_WINDOWS (1)
     #include <winapifamily.h>
     #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP))
-        #define _SAUDIO_UWP (1)
-    #else
-        #define _SAUDIO_WIN32 (1)
+        #error "sokol_audio.h no longer supports UWP"
     #endif
 #elif defined(__ANDROID__)
     #define _SAUDIO_ANDROID (1)
@@ -644,12 +710,8 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
     #endif
     #include <windows.h>
     #include <synchapi.h>
-    #if defined(_SAUDIO_UWP)
-        #pragma comment (lib, "WindowsApp")
-    #else
-        #pragma comment (lib, "kernel32")
-        #pragma comment (lib, "ole32")
-    #endif
+    #pragma comment (lib, "kernel32")
+    #pragma comment (lib, "ole32")
     #ifndef CINTERFACE
     #define CINTERFACE
     #endif
@@ -939,15 +1001,8 @@ typedef struct {
 } _saudio_wasapi_thread_data_t;
 
 typedef struct {
-    #if defined(_SAUDIO_UWP)
-        LPOLESTR interface_activation_audio_interface_uid_string;
-        IActivateAudioInterfaceAsyncOperation* interface_activation_operation;
-        BOOL interface_activation_success;
-        HANDLE interface_activation_mutex;
-    #else
-        IMMDeviceEnumerator* device_enumerator;
-        IMMDevice* device;
-    #endif
+    IMMDeviceEnumerator* device_enumerator;
+    IMMDevice* device;
     IAudioClient* audio_client;
     IAudioRenderClient* render_client;
     _saudio_wasapi_thread_data_t thread;
@@ -1032,6 +1087,69 @@ _SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int n
     }
 }
 
+// ██╗      ██████╗  ██████╗  ██████╗ ██╗███╗   ██╗ ██████╗
+// ██║     ██╔═══██╗██╔════╝ ██╔════╝ ██║████╗  ██║██╔════╝
+// ██║     ██║   ██║██║  ███╗██║  ███╗██║██╔██╗ ██║██║  ███╗
+// ██║     ██║   ██║██║   ██║██║   ██║██║██║╚██╗██║██║   ██║
+// ███████╗╚██████╔╝╚██████╔╝╚██████╔╝██║██║ ╚████║╚██████╔╝
+// ╚══════╝ ╚═════╝  ╚═════╝  ╚═════╝ ╚═╝╚═╝  ╚═══╝ ╚═════╝
+//
+// >>logging
+#if defined(SOKOL_DEBUG)
+#define _SAUDIO_XMACRO(item) #item,
+static const char* _saudio_log_item_str[] = {
+    _SAUDIO_LOG_ITEMS
+};
+#undef _SSPINE_XMACRO
+#endif // SOKOL_DEBUG
+
+#define _SAUDIO_PANIC(code) _saudio_log(SAUDIO_LOGITEM_ ##code, SAUDIO_LOGLEVEL_PANIC, __LINE__)
+#define _SAUDIO_ERROR(code) _saudio_log(SAUDIO_LOGITEM_ ##code, SAUDIO_LOGLEVEL_ERROR, __LINE__)
+#define _SAUDIO_WARN(code) _saudio_log(SAUDIO_LOGITEM_ ##code, SAUDIO_LOGLEVEL_WARN, __LINE__)
+#define _SAUDIO_INFO(code) _saudio_log(SAUDIO_LOGITEM_ ##code, SAUDIO_LOGLEVEL_INFO, __LINE__)
+
+static void _saudio_log(saudio_log_item log_item, saudio_loglevel log_level, int line_nr) {
+    if (_saudio.desc.logger.func) {
+        #if defined(SOKOL_DEBUG)
+            const char* filename = __FILE__;
+            const char* log_item_str = _saudio_log_item_str[log_item];
+        #else
+            const char* filename = "";
+            const char* log_item_str = "";
+        #endif
+        _saudio.desc.logger.func("saudio", log_level, log_item, log_item_str, line_nr, filename, _saudio.desc.logger.user_data);
+    }
+    else {
+        // default logging function, uses printf only if debugging is enabled to save executable size
+        const char* loglevel_str;
+        switch (log_level) {
+            case SAUDIO_LOGLEVEL_PANIC: loglevel_str = "panic"; break;
+            case SAUDIO_LOGLEVEL_ERROR: loglevel_str = "error"; break;
+            case SAUDIO_LOGLEVEL_WARN:  loglevel_str = "warning"; break;
+            default: loglevel_str = "info"; break;
+        }
+        #if defined(SOKOL_DEBUG)
+            const char* log_item_str = _saudio_log_item_str[log_item];
+            #if defined(_MSC_VER)
+                // Visual Studio compiler error format
+                fprintf(stderr, "[saudio] %s(%d): %s: %s\n", __FILE__, line_nr, loglevel_str, log_item_str);
+            #else
+                // GCC error format
+                fprintf(stderr, "[saudio] %s:%d:0: %s: %s\n", __FILE__, line_nr, loglevel_str, log_item_str);
+            #endif
+        #else
+            fputs("[saudio] ", stderr);
+            fputs(loglevel_str, stderr);
+            fputs(" (build in debug mode for more info)\n", stderr);
+        #endif // SOKOL_DEBUG
+
+        // for log level PANIC it would be 'undefined behaviour' to continue
+        if (log_level == SAUDIO_LOGLEVEL_PANIC) {
+            abort();
+        }
+    }
+}
+
 // ███╗   ███╗███████╗███╗   ███╗ ██████╗ ██████╗ ██╗   ██╗
 // ████╗ ████║██╔════╝████╗ ████║██╔═══██╗██╔══██╗╚██╗ ██╔╝
 // ██╔████╔██║█████╗  ██╔████╔██║██║   ██║██████╔╝ ╚████╔╝
@@ -1054,7 +1172,9 @@ _SOKOL_PRIVATE void* _saudio_malloc(size_t size) {
     else {
         ptr = malloc(size);
     }
-    SOKOL_ASSERT(ptr);
+    if (0 == ptr) {
+        _SAUDIO_PANIC(MALLOC_FAILED);
+    }
     return ptr;
 }
 
@@ -1073,17 +1193,6 @@ _SOKOL_PRIVATE void _saudio_free(void* ptr) {
     }
 }
 
-#if defined(SOKOL_DEBUG)
-_SOKOL_PRIVATE void _saudio_log(const char* msg) {
-    SOKOL_ASSERT(msg);
-    if (_saudio.desc.logger.log_cb) {
-        _saudio.desc.logger.log_cb(msg, _saudio.desc.logger.user_data);
-    } else {
-        SOKOL_LOG(msg);
-    }
-}
-#endif
-
 // ███╗   ███╗██╗   ██╗████████╗███████╗██╗  ██╗
 // ████╗ ████║██║   ██║╚══██╔══╝██╔════╝╚██╗██╔╝
 // ██╔████╔██║██║   ██║   ██║   █████╗   ╚███╔╝
@@ -1385,7 +1494,7 @@ _SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) {
     int dir; uint32_t rate;
     int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0);
     if (rc < 0) {
-        SAUDIO_LOG("sokol_audio.h: snd_pcm_open() failed");
+        _SAUDIO_ERROR(ALSA_SND_PCM_OPEN_FAILED);
         return false;
     }
 
@@ -1398,26 +1507,26 @@ _SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) {
     snd_pcm_hw_params_any(_saudio.backend.device, params);
     snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED);
     if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) {
-        SAUDIO_LOG("sokol_audio.h: float samples not supported");
+        _SAUDIO_ERROR(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED);
         goto error;
     }
     if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) {
-        SAUDIO_LOG("sokol_audio.h: requested buffer size not supported");
+        _SAUDIO_ERROR(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED);
         goto error;
     }
     if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) {
-        SAUDIO_LOG("sokol_audio.h: requested channel count not supported");
+        _SAUDIO_ERROR(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED);
         goto error;
     }
     /* let ALSA pick a nearby sampling rate */
     rate = (uint32_t) _saudio.sample_rate;
     dir = 0;
     if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) {
-        SAUDIO_LOG("sokol_audio.h: snd_pcm_hw_params_set_rate_near() failed");
+        _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED);
         goto error;
     }
     if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) {
-        SAUDIO_LOG("sokol_audio.h: snd_pcm_hw_params() failed");
+        _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_FAILED);
         goto error;
     }
 
@@ -1432,7 +1541,7 @@ _SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) {
 
     /* create the buffer-streaming start thread */
     if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) {
-        SAUDIO_LOG("sokol_audio.h: pthread_create() failed");
+        _SAUDIO_ERROR(ALSA_PTHREAD_CREATE_FAILED);
         goto error;
     }
 
@@ -1464,48 +1573,6 @@ _SOKOL_PRIVATE void _saudio_alsa_backend_shutdown(void) {
 // >>wasapi
 #elif defined(_SAUDIO_WINDOWS)
 
-#if defined(_SAUDIO_UWP)
-/* Minimal implementation of an IActivateAudioInterfaceCompletionHandler COM object in plain C.
-   Meant to be a static singleton (always one reference when add/remove reference)
-   and implements IUnknown and IActivateAudioInterfaceCompletionHandler when queryinterface'd
-
-   Do not know why but IActivateAudioInterfaceCompletionHandler's GUID is not the one system queries for,
-   so I'm advertising the one actually requested.
-*/
-_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_interface_completion_handler_queryinterface(IActivateAudioInterfaceCompletionHandler* instance, REFIID riid, void** ppvObject) {
-    if (!ppvObject) {
-        return E_POINTER;
-    }
-
-    if (IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IActivateAudioInterface_Completion_Handler)) || IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(IID_IUnknown)))
-    {
-        *ppvObject = (void*)instance;
-        return S_OK;
-    }
-
-    *ppvObject = NULL;
-    return E_NOINTERFACE;
-}
-
-_SOKOL_PRIVATE ULONG STDMETHODCALLTYPE _saudio_interface_completion_handler_addref_release(IActivateAudioInterfaceCompletionHandler* instance) {
-    _SOKOL_UNUSED(instance);
-    return 1;
-}
-
-_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_backend_activate_audio_interface_cb(IActivateAudioInterfaceCompletionHandler* instance, IActivateAudioInterfaceAsyncOperation* activateOperation) {
-    _SOKOL_UNUSED(instance);
-    WaitForSingleObject(_saudio.backend.interface_activation_mutex, INFINITE);
-    _saudio.backend.interface_activation_success = TRUE;
-    HRESULT activation_result;
-    if (FAILED(activateOperation->lpVtbl->GetActivateResult(activateOperation, &activation_result, (IUnknown**)(&_saudio.backend.audio_client))) || FAILED(activation_result)) {
-        _saudio.backend.interface_activation_success = FALSE;
-    }
-
-    ReleaseMutex(_saudio.backend.interface_activation_mutex);
-    return S_OK;
-}
-#endif // _SAUDIO_UWP
-
 /* fill intermediate buffer with new data and reset buffer_pos */
 _SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) {
     if (_saudio_has_callback()) {
@@ -1593,25 +1660,14 @@ _SOKOL_PRIVATE void _saudio_wasapi_release(void) {
         IAudioClient_Release(_saudio.backend.audio_client);
         _saudio.backend.audio_client = 0;
     }
-    #if defined(_SAUDIO_UWP)
-        if (_saudio.backend.interface_activation_audio_interface_uid_string) {
-            CoTaskMemFree(_saudio.backend.interface_activation_audio_interface_uid_string);
-            _saudio.backend.interface_activation_audio_interface_uid_string = 0;
-        }
-        if (_saudio.backend.interface_activation_operation) {
-            IActivateAudioInterfaceAsyncOperation_Release(_saudio.backend.interface_activation_operation);
-            _saudio.backend.interface_activation_operation = 0;
-        }
-    #else
-        if (_saudio.backend.device) {
-            IMMDevice_Release(_saudio.backend.device);
-            _saudio.backend.device = 0;
-        }
-        if (_saudio.backend.device_enumerator) {
-            IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator);
-            _saudio.backend.device_enumerator = 0;
-        }
-    #endif
+    if (_saudio.backend.device) {
+        IMMDevice_Release(_saudio.backend.device);
+        _saudio.backend.device = 0;
+    }
+    if (_saudio.backend.device_enumerator) {
+        IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator);
+        _saudio.backend.device_enumerator = 0;
+    }
     if (0 != _saudio.backend.thread.buffer_end_event) {
         CloseHandle(_saudio.backend.thread.buffer_end_event);
         _saudio.backend.thread.buffer_end_event = 0;
@@ -1620,81 +1676,40 @@ _SOKOL_PRIVATE void _saudio_wasapi_release(void) {
 
 _SOKOL_PRIVATE bool _saudio_wasapi_backend_init(void) {
     REFERENCE_TIME dur;
-    /* UWP Threads are CoInitialized by default with a different threading model, and this call fails
-    See https://github.com/Microsoft/cppwinrt/issues/6#issuecomment-253930637 */
-    #if defined(_SAUDIO_WIN32)
-        /* CoInitializeEx could have been called elsewhere already, in which
-            case the function returns with S_FALSE (thus it does not make much
-            sense to check the result)
-        */
-        HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
-        _SOKOL_UNUSED(hr);
-    #endif
+    /* CoInitializeEx could have been called elsewhere already, in which
+        case the function returns with S_FALSE (thus it does not make much
+        sense to check the result)
+    */
+    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+    _SOKOL_UNUSED(hr);
     _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0);
     if (0 == _saudio.backend.thread.buffer_end_event) {
-        SAUDIO_LOG("sokol_audio wasapi: failed to create buffer_end_event");
+        _SAUDIO_ERROR(WASAPI_CREATE_EVENT_FAILED);
+        goto error;
+    }
+    if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator),
+        0, CLSCTX_ALL,
+        _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator),
+        (void**)&_saudio.backend.device_enumerator)))
+    {
+        _SAUDIO_ERROR(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED);
+        goto error;
+    }
+    if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator,
+        eRender, eConsole,
+        &_saudio.backend.device)))
+    {
+        _SOKOL_ERROR(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED);
+        goto error;
+    }
+    if (FAILED(IMMDevice_Activate(_saudio.backend.device,
+        _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient),
+        CLSCTX_ALL, 0,
+        (void**)&_saudio.backend.audio_client)))
+    {
+        _SOKOL_ERROR(WASAPI_DEVICE_ACTIVATE_FAILED);
         goto error;
     }
-    #if defined(_SAUDIO_UWP)
-        _saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex");
-        if (_saudio.backend.interface_activation_mutex == NULL) {
-            SAUDIO_LOG("sokol_audio wasapi: failed to create interface activation mutex");
-            goto error;
-        }
-        if (FAILED(StringFromIID(_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_Devinterface_Audio_Render), &_saudio.backend.interface_activation_audio_interface_uid_string))) {
-            SAUDIO_LOG("sokol_audio wasapi: failed to get default audio device ID string");
-            goto error;
-        }
-
-        /* static instance of the fake COM object */
-        static IActivateAudioInterfaceCompletionHandlerVtbl completion_handler_interface_vtable = {
-            _saudio_interface_completion_handler_queryinterface,
-            _saudio_interface_completion_handler_addref_release,
-            _saudio_interface_completion_handler_addref_release,
-            _saudio_backend_activate_audio_interface_cb
-        };
-        static IActivateAudioInterfaceCompletionHandler completion_handler_interface = { &completion_handler_interface_vtable };
-
-        if (FAILED(ActivateAudioInterfaceAsync(_saudio.backend.interface_activation_audio_interface_uid_string, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), NULL, &completion_handler_interface, &_saudio.backend.interface_activation_operation))) {
-            SAUDIO_LOG("sokol_audio wasapi: failed to get default audio device ID string");
-            goto error;
-        }
-        while (!(_saudio.backend.audio_client)) {
-            if (WaitForSingleObject(_saudio.backend.interface_activation_mutex, 10) != WAIT_TIMEOUT) {
-                ReleaseMutex(_saudio.backend.interface_activation_mutex);
-            }
-        }
-
-        if (!(_saudio.backend.interface_activation_success)) {
-            SAUDIO_LOG("sokol_audio wasapi: interface activation failed. Unable to get audio client");
-            goto error;
-        }
-
-    #else
-        if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator),
-            0, CLSCTX_ALL,
-            _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator),
-            (void**)&_saudio.backend.device_enumerator)))
-        {
-            SAUDIO_LOG("sokol_audio wasapi: failed to create device enumerator");
-            goto error;
-        }
-        if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator,
-            eRender, eConsole,
-            &_saudio.backend.device)))
-        {
-            SAUDIO_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed");
-            goto error;
-        }
-        if (FAILED(IMMDevice_Activate(_saudio.backend.device,
-            _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient),
-            CLSCTX_ALL, 0,
-            (void**)&_saudio.backend.audio_client)))
-        {
-            SAUDIO_LOG("sokol_audio wasapi: device activate failed");
-            goto error;
-        }
-    #endif
 
     WAVEFORMATEXTENSIBLE fmtex;
     _saudio_clear(&fmtex, sizeof(fmtex));
@@ -1720,22 +1735,22 @@ _SOKOL_PRIVATE bool _saudio_wasapi_backend_init(void) {
         AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
         dur, 0, (WAVEFORMATEX*)&fmtex, 0)))
     {
-        SAUDIO_LOG("sokol_audio wasapi: audio client initialize failed");
+        _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED);
         goto error;
     }
     if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) {
-        SAUDIO_LOG("sokol_audio wasapi: audio client get buffer size failed");
+        _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED);
         goto error;
     }
     if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client,
         _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient),
         (void**)&_saudio.backend.render_client)))
     {
-        SAUDIO_LOG("sokol_audio wasapi: audio client GetService failed");
+        _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED);
         goto error;
     }
     if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) {
-        SAUDIO_LOG("sokol_audio wasapi: audio client SetEventHandle failed");
+        _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED);
         goto error;
     }
     _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float);
@@ -1748,7 +1763,7 @@ _SOKOL_PRIVATE bool _saudio_wasapi_backend_init(void) {
     /* create streaming thread */
     _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0);
     if (0 == _saudio.backend.thread.thread_handle) {
-        SAUDIO_LOG("sokol_audio wasapi: CreateThread failed");
+        _SAUDIO_ERROR(WASAPI_CREATE_THREAD_FAILED);
         goto error;
     }
     return true;
@@ -1769,10 +1784,7 @@ _SOKOL_PRIVATE void _saudio_wasapi_backend_shutdown(void) {
         IAudioClient_Stop(_saudio.backend.audio_client);
     }
     _saudio_wasapi_release();
-
-    #if defined(_SAUDIO_WIN32)
-        CoUninitialize();
-    #endif
+    CoUninitialize();
 }
 
 // ██╗    ██╗███████╗██████╗  █████╗ ██╗   ██╗██████╗ ██╗ ██████╗
@@ -1961,7 +1973,7 @@ _SOKOL_PRIVATE aaudio_data_callback_result_t _saudio_aaudio_data_callback(AAudio
 
 _SOKOL_PRIVATE bool _saudio_aaudio_start_stream(void) {
     if (AAudioStreamBuilder_openStream(_saudio.backend.builder, &_saudio.backend.stream) != AAUDIO_OK) {
-        SAUDIO_LOG("sokol_audio.h aaudio: AAudioStreamBuilder_openStream failed");
+        _SAUDIO_ERROR(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED);
         return false;
     }
     AAudioStream_requestStart(_saudio.backend.stream);
@@ -1978,7 +1990,7 @@ _SOKOL_PRIVATE void _saudio_aaudio_stop_stream(void) {
 
 _SOKOL_PRIVATE void* _saudio_aaudio_restart_stream_thread_fn(void* param) {
     _SOKOL_UNUSED(param);
-    SAUDIO_LOG("sokol_audio.h aaudio: restarting stream after error");
+    _SAUDIO_WARN(AAUDIO_RESTARTING_STREAM_AFTER_ERROR);
     pthread_mutex_lock(&_saudio.backend.mutex);
     _saudio_aaudio_stop_stream();
     _saudio_aaudio_start_stream();
@@ -1991,7 +2003,7 @@ _SOKOL_PRIVATE void _saudio_aaudio_error_callback(AAudioStream* stream, void* us
     _SOKOL_UNUSED(user_data);
     if (error == AAUDIO_ERROR_DISCONNECTED) {
         if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_aaudio_restart_stream_thread_fn, 0)) {
-            SAUDIO_LOG("sokol_audio.h aaudio: pthread_create failed in _saudio_audio_error_callback");
+            _SAUDIO_ERROR(AAUDIO_PTHREAD_CREATE_FAILED);
         }
     }
 }
@@ -2008,7 +2020,7 @@ _SOKOL_PRIVATE void _saudio_aaudio_backend_shutdown(void) {
 }
 
 _SOKOL_PRIVATE bool _saudio_aaudio_backend_init(void) {
-    SAUDIO_LOG("sokol_audio.h: using AAudio backend");
+    _SAUDIO_INFO(USING_AAUDIO_BACKEND);
 
     _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float);
 
@@ -2017,7 +2029,7 @@ _SOKOL_PRIVATE bool _saudio_aaudio_backend_init(void) {
     pthread_mutex_init(&_saudio.backend.mutex, &attr);
 
     if (AAudio_createStreamBuilder(&_saudio.backend.builder) != AAUDIO_OK) {
-        SAUDIO_LOG("sokol_audio.h aaudio: AAudio_createStreamBuilder() failed!");
+        _SAUDIO_ERROR(AAUDIO_CREATE_STREAMBUILDER_FAILED);
         _saudio_aaudio_backend_shutdown();
         return false;
     }
@@ -2107,9 +2119,8 @@ _SOKOL_PRIVATE void _saudio_sles_fill_buffer(void) {
 }
 
 _SOKOL_PRIVATE void SLAPIENTRY _saudio_sles_play_cb(SLPlayItf player, void *context, SLuint32 event) {
-    (void)(context);
-    (void)(player);
-
+    _SOKOL_UNUSED(context);
+    _SOKOL_UNUSED(player);
     if (event & SL_PLAYEVENT_HEADATEND) {
         _saudio_sles_semaphore_post(&_saudio.backend.buffer_sem, 1);
     }
@@ -2163,7 +2174,7 @@ _SOKOL_PRIVATE void _saudio_sles_backend_shutdown(void) {
 }
 
 _SOKOL_PRIVATE bool _saudio_sles_backend_init(void) {
-    SAUDIO_LOG("sokol_audio.h: using OpenSLES backend");
+    _SAUDIO_INFO(USING_SLES_BACKEND);
 
     _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels;
 
@@ -2180,14 +2191,14 @@ _SOKOL_PRIVATE bool _saudio_sles_backend_init(void) {
     /* Create engine */
     const SLEngineOption opts[] = { { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } };
     if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) {
-        SAUDIO_LOG("sokol_audio opensles: slCreateEngine failed");
+        _SAUDIO_ERROR(SLES_CREATE_ENGINE_FAILED);
         _saudio_sles_backend_shutdown();
         return false;
     }
 
     (*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE);
     if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) {
-        SAUDIO_LOG("sokol_audio opensles: GetInterface->Engine failed");
+        _SAUDIO_ERROR(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED);
         _saudio_sles_backend_shutdown();
         return false;
     }
@@ -2197,16 +2208,15 @@ _SOKOL_PRIVATE bool _saudio_sles_backend_init(void) {
         const SLInterfaceID ids[] = { SL_IID_VOLUME };
         const SLboolean req[] = { SL_BOOLEAN_FALSE };
 
-        if( (*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS)
-        {
-            SAUDIO_LOG("sokol_audio opensles: CreateOutputMix failed");
+        if ((*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) {
+            _SAUDIO_ERROR(SLES_CREATE_OUTPUT_MIX_FAILED);
             _saudio_sles_backend_shutdown();
             return false;
         }
         (*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE);
 
-        if((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) {
-            SAUDIO_LOG("sokol_audio opensles: GetInterface->OutputMixVol failed");
+        if ((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) {
+            _SAUDIO_WARN(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED);
         }
     }
 
@@ -2245,24 +2255,24 @@ _SOKOL_PRIVATE bool _saudio_sles_backend_init(void) {
         const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
         const SLboolean req[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE };
 
-        if( (*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req) != SL_RESULT_SUCCESS)
+        if ((*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req) != SL_RESULT_SUCCESS)
         {
-            SAUDIO_LOG("sokol_audio opensles: CreateAudioPlayer failed");
+            _SAUDIO_ERROR(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED);
             _saudio_sles_backend_shutdown();
             return false;
         }
         (*_saudio.backend.player_obj)->Realize(_saudio.backend.player_obj, SL_BOOLEAN_FALSE);
 
-        if((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player) != SL_RESULT_SUCCESS) {
-            SAUDIO_LOG("sokol_audio opensles: GetInterface->Play failed");
+        if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player) != SL_RESULT_SUCCESS) {
+            _SAUDIO_ERROR(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED);
             _saudio_sles_backend_shutdown();
             return false;
         }
-        if((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol) != SL_RESULT_SUCCESS) {
-            SAUDIO_LOG("sokol_audio opensles: GetInterface->PlayerVol failed");
+        if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol) != SL_RESULT_SUCCESS) {
+            _SAUDIO_ERROR(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED);
         }
-        if((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue) != SL_RESULT_SUCCESS) {
-            SAUDIO_LOG("sokol_audio opensles: GetInterface->BufferQ failed");
+        if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue) != SL_RESULT_SUCCESS) {
+            _SAUDIO_ERROR(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED);
             _saudio_sles_backend_shutdown();
             return false;
         }
@@ -2418,7 +2428,7 @@ _SOKOL_PRIVATE bool _saudio_coreaudio_backend_init(void) {
     fmt.mBitsPerChannel = 32;
     _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue);
     if (0 != res) {
-        SAUDIO_LOG("sokol_audio.h: AudioQueueNewOutput() failed!\n");
+        _SAUDIO_ERROR(COREAUDIO_NEW_OUTPUT_FAILED);
         return false;
     }
     SOKOL_ASSERT(_saudio.backend.ca_audio_queue);
@@ -2429,7 +2439,7 @@ _SOKOL_PRIVATE bool _saudio_coreaudio_backend_init(void) {
         const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame;
         res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf);
         if (0 != res) {
-            SAUDIO_LOG("sokol_audio.h: AudioQueueAllocateBuffer() failed!\n");
+            _SAUDIO_ERROR(COREAUDIO_ALLOCATE_BUFFER_FAILED);
             _saudio_coreaudio_backend_shutdown();
             return false;
         }
@@ -2444,7 +2454,7 @@ _SOKOL_PRIVATE bool _saudio_coreaudio_backend_init(void) {
     /* ...and start playback */
     res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL);
     if (0 != res) {
-        SAUDIO_LOG("sokol_audio.h: AudioQueueStart() failed!\n");
+        _SAUDIO_ERROR(COREAUDIO_START_FAILED);
         _saudio_coreaudio_backend_shutdown();
         return false;
     }
@@ -2524,7 +2534,7 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
            the requested packet size
         */
         if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) {
-            SAUDIO_LOG("sokol_audio.h: actual backend buffer size isn't multiple of requested packet size");
+            _SAUDIO_ERROR(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE);
             _saudio_backend_shutdown();
             return;
         }