medium sized dollop
authorhgn <hgodden00@gmail.com>
Thu, 6 Oct 2022 04:32:49 +0000 (05:32 +0100)
committerhgn <hgodden00@gmail.com>
Thu, 6 Oct 2022 04:34:07 +0000 (05:34 +0100)
18 files changed:
dep/dr_soft/miniaudio.h
dep/dr_soft/miniaudio_impl.c
src/vg/vg.h
src/vg/vg_audio.h
src/vg/vg_console.h
src/vg/vg_input.h
src/vg/vg_io.h
src/vg/vg_lines.h
src/vg/vg_loader.h [new file with mode: 0644]
src/vg/vg_log.h [new file with mode: 0644]
src/vg/vg_m.h
src/vg/vg_platform.h
src/vg/vg_shader.h
src/vg/vg_steam.h
src/vg/vg_steam_utils.h [new file with mode: 0644]
src/vg/vg_store.h
src/vg/vg_tex.h
src/vg/vg_ui.h

index 479806f8c96c2c325314360f4fa16eef88e603b7..f774f0d5f1d5d5fe14f98c35e63d9c88d1743f8f 100644 (file)
@@ -1,6 +1,6 @@
 /*
 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
-miniaudio - v0.11.6 - 2022-01-22
+miniaudio - v0.11.9 - 2022-04-20
 
 David Reid - mackron@gmail.com
 
@@ -400,7 +400,13 @@ the be started and/or stopped at a specific time. This can be done with the foll
 The start/stop time needs to be specified based on the absolute timer which is controlled by the
 engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
 The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
-required.
+required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()`
+before anything will play:
+
+    ```c
+    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
+    ma_sound_start(&sound);
+    ```
 
 The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be
 loaded and a few options on which features should be enabled for that sound. By default, the sound
@@ -655,12 +661,6 @@ You cannot use `-std=c*` compiler flags, nor `-ansi`.
     +----------------------------------+--------------------------------------------------------------------+
     | MA_API                           | Controls how public APIs should be decorated. Default is `extern`. |
     +----------------------------------+--------------------------------------------------------------------+
-    | MA_DLL                           | If set, configures `MA_API` to either import or export APIs        |
-    |                                  | depending on whether or not the implementation is being defined.   |
-    |                                  | If defining the implementation, `MA_API` will be configured to     |
-    |                                  | export. Otherwise it will be configured to import. This has no     |
-    |                                  | effect if `MA_API` is defined externally.                          |
-    +----------------------------------+--------------------------------------------------------------------+
 
 
 3. Definitions
@@ -1399,6 +1399,9 @@ can be useful to schedule a sound to start or stop:
     ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
     ```
 
+Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
+anything will play.
+
 The time is specified in global time which is controlled by the engine. You can get the engine's
 current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as
 audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be
@@ -3637,7 +3640,7 @@ extern "C" {
 
 #define MA_VERSION_MAJOR    0
 #define MA_VERSION_MINOR    11
-#define MA_VERSION_REVISION 6
+#define MA_VERSION_REVISION 9
 #define MA_VERSION_STRING   MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
 
 #if defined(_MSC_VER) && !defined(__clang__)
@@ -3734,9 +3737,9 @@ typedef ma_uint16 wchar_t;
 /* Platform/backend detection. */
 #ifdef _WIN32
     #define MA_WIN32
-    #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
+    #if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))
         #define MA_WIN32_UWP
-    #elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
+    #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
         #define MA_WIN32_GDK
     #else
         #define MA_WIN32_DESKTOP
@@ -3794,9 +3797,15 @@ typedef ma_uint16 wchar_t;
     I am using "__inline__" only when we're compiling in strict ANSI mode.
     */
     #if defined(__STRICT_ANSI__)
-        #define MA_INLINE __inline__ __attribute__((always_inline))
+        #define MA_GNUC_INLINE_HINT __inline__
     #else
-        #define MA_INLINE inline __attribute__((always_inline))
+        #define MA_GNUC_INLINE_HINT inline
+    #endif
+
+    #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
+        #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline))
+    #else
+        #define MA_INLINE MA_GNUC_INLINE_HINT
     #endif
 #elif defined(__WATCOMC__)
     #define MA_INLINE __inline
@@ -4352,6 +4361,7 @@ MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, vo
 MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);
 MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);
 MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
+MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ);
 MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
 MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ);
 
@@ -4390,6 +4400,7 @@ MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void*
 MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF);
 MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
 MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
+MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF);
 MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
 MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF);
 
@@ -4403,6 +4414,7 @@ MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void*
 MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF);
 MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
 MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
+MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF);
 MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
 MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF);
 
@@ -4438,6 +4450,7 @@ MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pH
 MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF);
 MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
+MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF);
 MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
 MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF);
 
@@ -5077,6 +5090,7 @@ MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler
 MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);
 MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
 MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
+MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler);
 
 
 typedef struct ma_resampler_config ma_resampler_config;
@@ -5093,6 +5107,7 @@ typedef struct
     ma_uint64 (* onGetOutputLatency           )(void* pUserData, const ma_resampling_backend* pBackend);                                                            /* Optional. Latency will be reported as 0. */
     ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);   /* Optional. Latency mitigation will be disabled. */
     ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);   /* Optional. Latency mitigation will be disabled. */
+    ma_result (* onReset                      )(void* pUserData, ma_resampling_backend* pBackend);
 } ma_resampling_backend_vtable;
 
 typedef enum
@@ -5212,6 +5227,11 @@ input frames.
 */
 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
 
+/*
+Resets the resampler's timer and clears it's internal cache.
+*/
+MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
+
 
 /**************************************************************************************************************************************************************
 
@@ -5351,6 +5371,7 @@ MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_
 MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
 MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
 MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
+MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter);
 
 
 /************************************************************************************************************************************************************
@@ -5915,7 +5936,7 @@ struct ma_job
                 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
                 char* pFilePath;
                 wchar_t* pFilePathW;
-                ma_bool32 decode;                               /* When set to true, the data buffer will be decoded. Otherwise it'll be encoded and will use a decoder for the connector. */
+                ma_uint32 flags;                                /* Resource manager data source flags that were used when initializing the data buffer. */
                 ma_async_notification* pInitNotification;       /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
                 ma_async_notification* pDoneNotification;       /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */
                 ma_fence* pInitFence;                           /* Released when initialization of the decoder is complete. */
@@ -5944,6 +5965,11 @@ struct ma_job
                 ma_async_notification* pDoneNotification;       /* Signalled when the data buffer has been fully decoded. */
                 ma_fence* pInitFence;                           /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
                 ma_fence* pDoneFence;                           /* Released when the data buffer has been fully decoded. */
+                ma_uint64 rangeBegInPCMFrames;
+                ma_uint64 rangeEndInPCMFrames;
+                ma_uint64 loopPointBegInPCMFrames;
+                ma_uint64 loopPointEndInPCMFrames;
+                ma_uint32 isLooping;
             } loadDataBuffer;
             struct
             {
@@ -9309,17 +9335,17 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo
 MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor);
 MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength);
 MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping);
-MA_API ma_bool32 ma_data_source_is_looping(ma_data_source* pDataSource);
+MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource);
 MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames);
-MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
+MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
 MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames);
-MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
+MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
 MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource);
-MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource);
+MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource);
 MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource);
-MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource);
+MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource);
 MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext);
-MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource);
+MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource);
 
 
 typedef struct
@@ -9327,6 +9353,7 @@ typedef struct
     ma_data_source_base ds;
     ma_format format;
     ma_uint32 channels;
+    ma_uint32 sampleRate;
     ma_uint64 cursor;
     ma_uint64 sizeInFrames;
     const void* pData;
@@ -9350,6 +9377,7 @@ typedef struct
 {
     ma_format format;
     ma_uint32 channels;
+    ma_uint32 sampleRate;
     ma_uint64 sizeInFrames;
     const void* pData;  /* If set to NULL, will allocate a block of memory for you. */
     ma_allocation_callbacks allocationCallbacks;
@@ -9867,10 +9895,11 @@ typedef struct ma_resource_manager_data_source      ma_resource_manager_data_sou
 
 typedef enum
 {
-    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM    = 0x00000001,    /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */
-    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE    = 0x00000002,    /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */
-    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC     = 0x00000004,    /* When set, the resource manager will load the data source asynchronously. */
-    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008     /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM         = 0x00000001,   /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE         = 0x00000002,   /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC          = 0x00000004,   /* When set, the resource manager will load the data source asynchronously. */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT      = 0x00000008,   /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010    /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */
 } ma_resource_manager_data_source_flags;
 
 
@@ -10740,7 +10769,7 @@ struct ma_sound
 {
     ma_engine_node engineNode;          /* Must be the first member for compatibility with the ma_node API. */
     ma_data_source* pDataSource;
-    ma_uint64 seekTarget;               /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */
+    MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */
     MA_ATOMIC(4, ma_bool32) atEnd;
     ma_bool8 ownsDataSource;
 
@@ -11129,16 +11158,6 @@ IMPLEMENTATION
 #if defined(MA_ARM)
     #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
         #define MA_SUPPORT_NEON
-    #endif
-
-    /* Fall back to looking for the #include file. */
-    #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
-        #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
-            #define MA_SUPPORT_NEON
-        #endif
-    #endif
-
-    #if defined(MA_SUPPORT_NEON)
         #include <arm_neon.h>
     #endif
 #endif
@@ -23173,8 +23192,11 @@ static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 c
 
 static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
 {
-    /* DirectSound has a minimum period size of 20ms. */
-    ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(20, nativeSampleRate);
+    /*
+    DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for
+    reliable glitch-free processing so going to use 30ms instead.
+    */
+    ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);
     ma_uint32 periodSizeInFrames;
 
     periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
@@ -28777,6 +28799,32 @@ static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
     ma_device__on_notification_rerouted(pDevice);
 }
 
+static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
+{
+    /*
+    There have been reports from users where buffers of < ~20ms result glitches when running through
+    PipeWire. To work around this we're going to have to use a different default buffer size.
+    */
+    const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency   = 25;
+    const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
+
+    MA_ASSERT(nativeSampleRate != 0);
+
+    if (pDescriptor->periodSizeInFrames == 0) {
+        if (pDescriptor->periodSizeInMilliseconds == 0) {
+            if (performanceProfile == ma_performance_profile_low_latency) {
+                return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);
+            } else {
+                return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);
+            }
+        } else {
+            return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
+        }
+    } else {
+        return pDescriptor->periodSizeInFrames;
+    }
+}
+
 static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
 {
     /*
@@ -28882,7 +28930,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         /* We now have enough information to calculate our actual period size in frames. */
-        pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
+        pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
 
         attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
         ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames);
@@ -28944,13 +28992,33 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         /* Internal channel map. */
-        pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
-        if (pActualCMap != NULL) {
-            cmap = *pActualCMap;
-        }
 
-        for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
-            pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
+        /*
+        Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
+        the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
+        and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
+        all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
+        fixed sooner than later. I might remove this hack later.
+        */
+        if (pDescriptorCapture->channels > 2) {
+            pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
+            if (pActualCMap != NULL) {
+                cmap = *pActualCMap;
+            }
+
+            for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
+                pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
+            }
+        } else {
+            /* Hack for mono and stereo. */
+            if (pDescriptorCapture->channels == 1) {
+                pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;
+            } else if (pDescriptorCapture->channels == 2) {
+                pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
+                pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            } else {
+                MA_ASSERT(MA_FALSE);    /* Should never hit this. */
+            }
         }
 
 
@@ -28961,9 +29029,9 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         if (attr.fragsize > 0) {
-            pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
+            pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
         } else {
-            pDescriptorPlayback->periodCount = 1;
+            pDescriptorCapture->periodCount = 1;
         }
 
         pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
@@ -28998,7 +29066,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         /* We now have enough information to calculate the actual buffer size in frames. */
-        pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
+        pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
 
         attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
 
@@ -29064,13 +29132,33 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         /* Internal channel map. */
-        pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
-        if (pActualCMap != NULL) {
-            cmap = *pActualCMap;
-        }
 
-        for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
-            pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
+        /*
+        Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
+        the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
+        and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
+        all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
+        fixed sooner than later. I might remove this hack later.
+        */
+        if (pDescriptorPlayback->channels > 2) {
+            pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
+            if (pActualCMap != NULL) {
+                cmap = *pActualCMap;
+            }
+
+            for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
+                pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
+            }
+        } else {
+            /* Hack for mono and stereo. */
+            if (pDescriptorPlayback->channels == 1) {
+                pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;
+            } else if (pDescriptorPlayback->channels == 2) {
+                pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
+                pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
+            } else {
+                MA_ASSERT(MA_FALSE);    /* Should never hit this. */
+            }
         }
 
 
@@ -32329,6 +32417,12 @@ static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
 -(void)dealloc
 {
     [self remove_handler];
+
+    #if defined(__has_feature)
+        #if !__has_feature(objc_arc)
+            [super dealloc];
+        #endif
+    #endif
 }
 
 -(void)remove_handler
@@ -40297,7 +40391,7 @@ MA_API ma_result ma_device_start(ma_device* pDevice)
     }
 
     if (ma_device_get_state(pDevice) == ma_device_state_started) {
-        return MA_INVALID_OPERATION;    /* Already started. Returning an error to let the application know because it probably means they're doing something wrong. */
+        return MA_SUCCESS;  /* Already started. */
     }
 
     ma_mutex_lock(&pDevice->startStopLock);
@@ -40357,7 +40451,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice)
     }
 
     if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
-        return MA_INVALID_OPERATION;    /* Already stopped. Returning an error to let the application know because it probably means they're doing something wrong. */
+        return MA_SUCCESS;  /* Already stopped. */
     }
 
     ma_mutex_lock(&pDevice->startStopLock);
@@ -43618,6 +43712,23 @@ MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pB
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ)
+{
+    if (pBQ == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    if (pBQ->format == ma_format_f32) {
+        pBQ->pR1->f32 = 0;
+        pBQ->pR2->f32 = 0;
+    } else {
+        pBQ->pR1->s32 = 0;
+        pBQ->pR2->s32 = 0;
+    }
+
+    return MA_SUCCESS;
+}
+
 static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
 {
     ma_uint32 c;
@@ -43919,6 +44030,21 @@ MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF)
+{
+    if (pLPF == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    if (pLPF->format == ma_format_f32) {
+        pLPF->a.f32 = 0;
+    } else {
+        pLPF->a.s32 = 0;
+    }
+
+    return MA_SUCCESS;
+}
+
 static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
 {
     ma_uint32 c;
@@ -44124,6 +44250,17 @@ MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF)
+{
+    if (pLPF == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    ma_biquad_clear_cache(&pLPF->bq);
+
+    return MA_SUCCESS;
+}
+
 static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
 {
     ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
@@ -44456,6 +44593,10 @@ MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocati
     for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
         ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);
     }
+
+    if (pLPF->_ownsHeap) {
+        ma_free(pLPF->_pHeap, pAllocationCallbacks);
+    }
 }
 
 MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
@@ -44463,6 +44604,26 @@ MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
     return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE);
 }
 
+MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF)
+{
+    ma_uint32 ilpf1;
+    ma_uint32 ilpf2;
+
+    if (pLPF == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
+        ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]);
+    }
+
+    for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
+        ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]);
+    }
+
+    return MA_SUCCESS;
+}
+
 static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
 {
     ma_uint32 ilpf1;
@@ -45294,6 +45455,10 @@ MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocati
     for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
         ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);
     }
+
+    if (pHPF->_ownsHeap) {
+        ma_free(pHPF->_pHeap, pAllocationCallbacks);
+    }
 }
 
 MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
@@ -45792,6 +45957,10 @@ MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocati
     for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
         ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);
     }
+
+    if (pBPF->_ownsHeap) {
+        ma_free(pBPF->_pHeap, pAllocationCallbacks);
+    }
 }
 
 MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
@@ -49629,6 +49798,38 @@ MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_li
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
+{
+    ma_uint32 iChannel;
+
+    if (pResampler == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    /* Timers need to be cleared back to zero. */
+    pResampler->inTimeInt  = 1;  /* Set this to one to force an input sample to always be loaded for the first output frame. */
+    pResampler->inTimeFrac = 0;
+
+    /* Cached samples need to be cleared. */
+    if (pResampler->config.format == ma_format_f32) {
+        for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
+            pResampler->x0.f32[iChannel] = 0;
+            pResampler->x1.f32[iChannel] = 0;
+        }
+    } else {
+        for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
+            pResampler->x0.s16[iChannel] = 0;
+            pResampler->x1.s16[iChannel] = 0;
+        }
+    }
+
+    /* The low pass filter needs to have it's cache reset. */
+    ma_lpf_clear_cache(&pResampler->lpf);
+
+    return MA_SUCCESS;
+}
+
+
 
 /* Linear resampler backend vtable. */
 static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)
@@ -49721,6 +49922,13 @@ static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(v
     return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
 }
 
+static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)
+{
+    (void)pUserData;
+
+    return ma_linear_resampler_reset((ma_linear_resampler*)pBackend);
+}
+
 static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
 {
     ma_resampling_backend_get_heap_size__linear,
@@ -49731,7 +49939,8 @@ static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
     ma_resampling_backend_get_input_latency__linear,
     ma_resampling_backend_get_output_latency__linear,
     ma_resampling_backend_get_required_input_frame_count__linear,
-    ma_resampling_backend_get_expected_output_frame_count__linear
+    ma_resampling_backend_get_expected_output_frame_count__linear,
+    ma_resampling_backend_reset__linear
 };
 
 
@@ -50032,6 +50241,19 @@ MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler
     return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
 }
 
+MA_API ma_result ma_resampler_reset(ma_resampler* pResampler)
+{
+    if (pResampler == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) {
+        return MA_NOT_IMPLEMENTED;
+    }
+
+    return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend);
+}
+
 /**************************************************************************************************************************************************************
 
 Channel Conversion
@@ -52285,7 +52507,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co
     MA_ASSERT(pConverter != NULL);
     MA_ASSERT(pConverter->resampler.format   == pConverter->channelConverter.format);
     MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
-    MA_ASSERT(pConverter->resampler.channels <  pConverter->channelConverter.channelsIn);
+    MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
 
     frameCountIn = 0;
     if (pFrameCountIn != NULL) {
@@ -52572,6 +52794,20 @@ MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converte
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter)
+{
+    if (pConverter == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    /* There's nothing to do if we're not resampling. */
+    if (pConverter->hasResampler == MA_FALSE) {
+        return MA_SUCCESS;
+    }
+
+    return ma_resampler_reset(&pConverter->resampler);
+}
+
 
 
 /**************************************************************************************************************************************************************
@@ -54881,9 +55117,9 @@ MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool
     return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
 }
 
-MA_API ma_bool32 ma_data_source_is_looping(ma_data_source* pDataSource)
+MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource)
 {
-    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
     if (pDataSource == NULL) {
         return MA_FALSE;
@@ -54959,9 +55195,9 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou
     return MA_SUCCESS;
 }
 
-MA_API void ma_data_source_get_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
+MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
 {
-    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
     if (pDataSource == NULL) {
         return;
@@ -55003,9 +55239,9 @@ MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDa
     return MA_SUCCESS;
 }
 
-MA_API void ma_data_source_get_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
+MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
 {
-    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
     if (pDataSource == NULL) {
         return;
@@ -55033,9 +55269,9 @@ MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data
     return MA_SUCCESS;
 }
 
-MA_API ma_data_source* ma_data_source_get_current(ma_data_source* pDataSource)
+MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource)
 {
-    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
     if (pDataSource == NULL) {
         return NULL;
@@ -55057,9 +55293,9 @@ MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_so
     return MA_SUCCESS;
 }
 
-MA_API ma_data_source* ma_data_source_get_next(ma_data_source* pDataSource)
+MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource)
 {
-    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
     if (pDataSource == NULL) {
         return NULL;
@@ -55081,9 +55317,9 @@ MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, m
     return MA_SUCCESS;
 }
 
-MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(ma_data_source* pDataSource)
+MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource)
 {
-    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+    const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
     if (pDataSource == NULL) {
         return NULL;
@@ -55120,7 +55356,7 @@ static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_sou
 
     *pFormat     = pAudioBufferRef->format;
     *pChannels   = pAudioBufferRef->channels;
-    *pSampleRate = 0;   /* There is no notion of a sample rate with audio buffers. */
+    *pSampleRate = pAudioBufferRef->sampleRate;
     ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);
 
     return MA_SUCCESS;
@@ -55176,6 +55412,7 @@ MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels,
 
     pAudioBufferRef->format       = format;
     pAudioBufferRef->channels     = channels;
+    pAudioBufferRef->sampleRate   = 0;  /* TODO: Version 0.12. Set this to sampleRate. */
     pAudioBufferRef->cursor       = 0;
     pAudioBufferRef->sizeInFrames = sizeInFrames;
     pAudioBufferRef->pData        = pData;
@@ -55228,7 +55465,7 @@ MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudio
         }
 
         if (pFramesOut != NULL) {
-            ma_copy_pcm_frames(pFramesOut, ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels);
+            ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels);
         }
 
         totalFramesRead += framesToRead;
@@ -55386,10 +55623,11 @@ MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_u
     ma_audio_buffer_config config;
 
     MA_ZERO_OBJECT(&config);
-    config.format = format;
-    config.channels = channels;
+    config.format       = format;
+    config.channels     = channels;
+    config.sampleRate   = 0;    /* TODO: Version 0.12. Set this to sampleRate. */
     config.sizeInFrames = sizeInFrames;
-    config.pData = pData;
+    config.pData        = pData;
     ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
 
     return config;
@@ -55418,6 +55656,9 @@ static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig,
         return result;
     }
 
+    /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */
+    pAudioBuffer->ref.sampleRate = pConfig->sampleRate;
+
     ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
 
     if (doCopy) {
@@ -56958,7 +57199,7 @@ extern "C" {
 #define DRWAV_XSTRINGIFY(x)     DRWAV_STRINGIFY(x)
 #define DRWAV_VERSION_MAJOR     0
 #define DRWAV_VERSION_MINOR     13
-#define DRWAV_VERSION_REVISION  4
+#define DRWAV_VERSION_REVISION  6
 #define DRWAV_VERSION_STRING    DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
 #include <stddef.h>
 typedef   signed char           drwav_int8;
@@ -57493,7 +57734,7 @@ extern "C" {
 #define DRFLAC_XSTRINGIFY(x)     DRFLAC_STRINGIFY(x)
 #define DRFLAC_VERSION_MAJOR     0
 #define DRFLAC_VERSION_MINOR     12
-#define DRFLAC_VERSION_REVISION  34
+#define DRFLAC_VERSION_REVISION  38
 #define DRFLAC_VERSION_STRING    DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
 #include <stddef.h>
 typedef   signed char           drflac_int8;
@@ -57854,7 +58095,7 @@ extern "C" {
 #define DRMP3_XSTRINGIFY(x)     DRMP3_STRINGIFY(x)
 #define DRMP3_VERSION_MAJOR     0
 #define DRMP3_VERSION_MINOR     6
-#define DRMP3_VERSION_REVISION  32
+#define DRMP3_VERSION_REVISION  33
 #define DRMP3_VERSION_STRING    DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
 #include <stddef.h>
 typedef   signed char           drmp3_int8;
@@ -57978,9 +58219,14 @@ typedef drmp3_int32 drmp3_result;
     #define DRMP3_INLINE __forceinline
 #elif defined(__GNUC__)
     #if defined(__STRICT_ANSI__)
-        #define DRMP3_INLINE __inline__ __attribute__((always_inline))
+        #define DRMP3_GNUC_INLINE_HINT __inline__
     #else
-        #define DRMP3_INLINE inline __attribute__((always_inline))
+        #define DRMP3_GNUC_INLINE_HINT inline
+    #endif
+    #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
+        #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline))
+    #else
+        #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT
     #endif
 #elif defined(__WATCOMC__)
     #define DRMP3_INLINE __inline
@@ -62154,6 +62400,7 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr
         ma_result result;
         ma_uint64 internalFrameIndex;
         ma_uint32 internalSampleRate;
+        ma_uint64 currentFrameIndex;
 
         result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
         if (result != MA_SUCCESS) {
@@ -62166,9 +62413,16 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr
             internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
         }
 
-        result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
-        if (result == MA_SUCCESS) {
-            pDecoder->readPointerInPCMFrames = frameIndex;
+        /* Only seek if we're requesting a different frame to what we're currently sitting on. */
+        ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, &currentFrameIndex);
+        if (currentFrameIndex != internalFrameIndex) {
+            result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
+            if (result == MA_SUCCESS) {
+                pDecoder->readPointerInPCMFrames = frameIndex;
+            }
+
+            /* Reset the data converter so that any cached data in the resampler is cleared. */
+            ma_data_converter_reset(&pDecoder->converter);
         }
 
         return result;
@@ -64714,11 +64968,12 @@ static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource
     };
 }
 
-static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, ma_async_notification* pInitNotification, ma_fence* pInitFence)
+static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence)
 {
     ma_result result;
 
     MA_ASSERT(pDataBuffer != NULL);
+    MA_ASSERT(pConfig     != NULL);
     MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE);
 
     /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */
@@ -64772,7 +65027,9 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana
         Make sure the looping state is set before returning in order to handle the case where the
         loop state was set on the data buffer before the connector was initialized.
         */
-        ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), ma_resource_manager_data_buffer_is_looping(pDataBuffer));
+        ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
+        ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
+        ma_data_source_set_looping(pDataBuffer, pConfig->isLooping);
 
         pDataBuffer->isConnectorInitialized = MA_TRUE;
 
@@ -64858,7 +65115,7 @@ static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_res
     return MA_SUCCESS;
 }
 
-static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder** ppDecoder)
+static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder)
 {
     ma_result result = MA_SUCCESS;
     ma_decoder* pDecoder;
@@ -64888,9 +65145,13 @@ static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_res
     allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter
     is used when the length of a sound is unknown until a full decode has been performed.
     */
-    result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
-    if (result != MA_SUCCESS) {
-        return result;
+    if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
+        result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
+        if (result != MA_SUCCESS) {
+            return result;
+        }
+    } else {
+        totalFrameCount = 0;
     }
 
     if (totalFrameCount > 0) {
@@ -65132,7 +65393,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m
             job.data.resourceManager.loadDataBufferNode.pDataBufferNode   = pDataBufferNode;
             job.data.resourceManager.loadDataBufferNode.pFilePath         = pFilePathCopy;
             job.data.resourceManager.loadDataBufferNode.pFilePathW        = pFilePathWCopy;
-            job.data.resourceManager.loadDataBufferNode.decode            =  (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE   ) != 0;
+            job.data.resourceManager.loadDataBufferNode.flags             = flags;
             job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL;
             job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL;
             job.data.resourceManager.loadDataBufferNode.pInitFence        = pInitFence;
@@ -65258,7 +65519,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manage
                 } else {
                     /* Decoding. We do this the same way as we do when loading asynchronously. */
                     ma_decoder* pDecoder;
-                    result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, &pDecoder);
+                    result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);
                     if (result != MA_SUCCESS) {
                         goto done;
                     }
@@ -65460,7 +65721,15 @@ static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma
 
 static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
 {
-    return ma_resource_manager_data_buffer_set_looping((ma_resource_manager_data_buffer*)pDataSource, isLooping);
+    ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource;
+    MA_ASSERT(pDataBuffer != NULL);
+
+    c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
+
+    /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
+    ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
+
+    return MA_SUCCESS;
 }
 
 static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
@@ -65550,7 +65819,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma
         /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
         if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
             /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
-            result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL, NULL);
+            result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);
             c89atomic_exchange_i32(&pDataBuffer->result, result);
 
             ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
@@ -65576,11 +65845,16 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma
 
             job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);
             job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
-            job.data.resourceManager.loadDataBuffer.pDataBuffer       = pDataBuffer;
-            job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
-            job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
-            job.data.resourceManager.loadDataBuffer.pInitFence        = notifications.init.pFence;
-            job.data.resourceManager.loadDataBuffer.pDoneFence        = notifications.done.pFence;
+            job.data.resourceManager.loadDataBuffer.pDataBuffer             = pDataBuffer;
+            job.data.resourceManager.loadDataBuffer.pInitNotification       = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
+            job.data.resourceManager.loadDataBuffer.pDoneNotification       = notifications.done.pNotification;
+            job.data.resourceManager.loadDataBuffer.pInitFence              = notifications.init.pFence;
+            job.data.resourceManager.loadDataBuffer.pDoneFence              = notifications.done.pFence;
+            job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames     = pConfig->rangeBegInPCMFrames;
+            job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames     = pConfig->rangeEndInPCMFrames;
+            job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
+            job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
+            job.data.resourceManager.loadDataBuffer.isLooping               = pConfig->isLooping;
 
             result = ma_resource_manager_post_job(pResourceManager, &job);
             if (result != MA_SUCCESS) {
@@ -65970,25 +66244,12 @@ MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manage
 
 MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping)
 {
-    if (pDataBuffer == NULL) {
-        return MA_INVALID_ARGS;
-    }
-
-    c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
-
-    /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
-    ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
-
-    return MA_SUCCESS;
+    return ma_data_source_set_looping(pDataBuffer, isLooping);
 }
 
 MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer)
 {
-    if (pDataBuffer == NULL) {
-        return MA_FALSE;
-    }
-
-    return c89atomic_load_32((ma_bool32*)&pDataBuffer->isLooping);
+    return ma_data_source_is_looping(pDataBuffer);
 }
 
 MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames)
@@ -66174,7 +66435,12 @@ static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma
 
 static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
 {
-    return ma_resource_manager_data_stream_set_looping((ma_resource_manager_data_stream*)pDataSource, isLooping);
+    ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource;
+    MA_ASSERT(pDataStream != NULL);
+
+    c89atomic_exchange_32(&pDataStream->isLooping, isLooping);
+
+    return MA_SUCCESS;
 }
 
 static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
@@ -66643,6 +66909,14 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m
         return MA_INVALID_OPERATION;
     }
 
+    /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */
+    if (c89atomic_load_32(&pDataStream->seekCounter) == 0) {
+        if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {
+            return MA_SUCCESS;
+        }
+    }
+
+
     /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */
     c89atomic_fetch_add_32(&pDataStream->seekCounter, 1);
 
@@ -66787,13 +67061,7 @@ MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manage
 
 MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)
 {
-    if (pDataStream == NULL) {
-        return MA_INVALID_ARGS;
-    }
-
-    c89atomic_exchange_32(&pDataStream->isLooping, isLooping);
-
-    return MA_SUCCESS;
+    return ma_data_source_set_looping(pDataStream, isLooping);
 }
 
 MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream)
@@ -67155,7 +67423,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job*
     will determine that the node is available for data delivery and the data buffer connectors can be
     initialized. Therefore, it's important that it is set after the data supply has been initialized.
     */
-    if (pJob->data.resourceManager.loadDataBufferNode.decode) {
+    if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {
         /*
         Decoding. This is the complex case because we're not going to be doing the entire decoding
         process here. Instead it's going to be split of multiple jobs and loaded in pages. The
@@ -67174,7 +67442,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job*
         ma_job pageDataBufferNodeJob;
 
         /* Allocate the decoder by initializing a decoded data supply. */
-        result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pDecoder);
+        result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder);
 
         /*
         Don't ever propagate an MA_BUSY result code or else the resource manager will think the
@@ -67420,7 +67688,15 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob
 
         if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
             /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
-            result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
+            ma_resource_manager_data_source_config dataSourceConfig;    /* For setting initial looping state and range. */
+            dataSourceConfig = ma_resource_manager_data_source_config_init();
+            dataSourceConfig.rangeBegInPCMFrames     = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;
+            dataSourceConfig.rangeEndInPCMFrames     = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;
+            dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;
+            dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;
+            dataSourceConfig.isLooping               = pJob->data.resourceManager.loadDataBuffer.isLooping;
+
+            result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
             if (result != MA_SUCCESS) {
                 ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result));
                 goto done;
@@ -67543,9 +67819,13 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob
     }
 
     /* Retrieve the total length of the file before marking the decoder are loaded. */
-    result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
-    if (result != MA_SUCCESS) {
-        goto done;  /* Failed to retrieve the length. */
+    if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
+        result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
+        if (result != MA_SUCCESS) {
+            goto done;  /* Failed to retrieve the length. */
+        }
+    } else {
+        pDataStream->totalLengthInPCMFrames = 0;
     }
 
     /*
@@ -70904,6 +71184,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
     ma_uint32 dataSourceChannels;
     ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
     ma_uint32 tempCapInFrames;
+    ma_uint64 seekTarget;
 
     /* This is a data source node which means no input buses. */
     (void)ppFramesIn;
@@ -70917,13 +71198,14 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
     }
 
     /* If we're seeking, do so now before reading. */
-    if (pSound->seekTarget != MA_SEEK_TARGET_NONE) {
-        ma_data_source_seek_to_pcm_frame(pSound->pDataSource, pSound->seekTarget);
+    seekTarget = c89atomic_load_64(&pSound->seekTarget);
+    if (seekTarget != MA_SEEK_TARGET_NONE) {
+        ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);
 
         /* Any time-dependant effects need to have their times updated. */
-        ma_node_set_time(pSound, pSound->seekTarget);
+        ma_node_set_time(pSound, seekTarget);
 
-        pSound->seekTarget  = MA_SEEK_TARGET_NONE;
+        c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);
     }
 
     /*
@@ -73078,24 +73360,8 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameInd
         return MA_INVALID_OPERATION;
     }
 
-    /*
-    Resource manager data sources are thread safe which means we can just seek immediately. However, we cannot guarantee that other data sources are
-    thread safe as well so in that case we'll need to get the mixing thread to seek for us to ensure we don't try seeking at the same time as reading.
-    */
-#ifndef MA_NO_RESOURCE_MANAGER
-    if (pSound->pDataSource == pSound->pResourceManagerDataSource) {
-        ma_result result = ma_resource_manager_data_source_seek_to_pcm_frame(pSound->pResourceManagerDataSource, frameIndex);
-        if (result != MA_SUCCESS) {
-            return result;
-        }
-
-        /* Time dependant effects need to have their timers updated. */
-        return ma_node_set_time(&pSound->engineNode, frameIndex);
-    }
-#endif
-
-    /* Getting here means the data source is not a resource manager data source so we'll need to get the mixing thread to do the seeking for us. */
-    pSound->seekTarget = frameIndex;
+    /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */
+    c89atomic_exchange_64(&pSound->seekTarget, frameIndex);
 
     return MA_SUCCESS;
 }
@@ -73559,9 +73825,14 @@ code below please report the bug to the respective repository for the relevant p
     #define DRWAV_INLINE __forceinline
 #elif defined(__GNUC__)
     #if defined(__STRICT_ANSI__)
-        #define DRWAV_INLINE __inline__ __attribute__((always_inline))
+        #define DRWAV_GNUC_INLINE_HINT __inline__
+    #else
+        #define DRWAV_GNUC_INLINE_HINT inline
+    #endif
+    #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
+        #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline))
     #else
-        #define DRWAV_INLINE inline __attribute__((always_inline))
+        #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT
     #endif
 #elif defined(__WATCOMC__)
     #define DRWAV_INLINE __inline
@@ -74051,7 +74322,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_pr
     fmtOut->extendedSize       = 0;
     fmtOut->validBitsPerSample = 0;
     fmtOut->channelMask        = 0;
-    memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
+    DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat));
     if (header.sizeInBytes > 16) {
         drwav_uint8 fmt_cbSize[2];
         int bytesReadSoFar = 0;
@@ -74184,7 +74455,7 @@ DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metad
 DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
 {
     if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
-        free(pParser->pData);
+        pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
         pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
         pParser->pDataCursor = pParser->pData;
         if (pParser->pData == NULL) {
@@ -74318,6 +74589,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_pars
     }
     return bytesRead;
 }
+DRWAV_PRIVATE size_t drwav__strlen(const char* str)
+{
+    size_t result = 0;
+    while (*str++) {
+        result += 1;
+    }
+    return result;
+}
 DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead)
 {
     size_t result = 0;
@@ -74332,7 +74611,7 @@ DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser,
     if (len) {
         char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
         DRWAV_ASSERT(result != NULL);
-        memcpy(result, str, len);
+        DRWAV_COPY_MEMORY(result, str, len);
         result[len] = '\0';
         return result;
     } else {
@@ -74462,7 +74741,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars
                 pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1);
                 DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
                 bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
-                pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory);
+                pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory);
             } else {
                 pMetadata->data.bext.pCodingHistory    = NULL;
                 pMetadata->data.bext.codingHistorySize = 0;
@@ -74679,19 +74958,19 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser*
                 if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
                     return bytesRead;
                 }
-                allocSizeNeeded += strlen(buffer) + 1;
+                allocSizeNeeded += drwav__strlen(buffer) + 1;
                 buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
                 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
                 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
                     return bytesRead;
                 }
-                allocSizeNeeded += strlen(buffer) + 1;
+                allocSizeNeeded += drwav__strlen(buffer) + 1;
                 buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
                 bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
                 if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
                     return bytesRead;
                 }
-                allocSizeNeeded += strlen(buffer) + 1;
+                allocSizeNeeded += drwav__strlen(buffer) + 1;
                 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES;
                 drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
                 pParser->metadataCount += 1;
@@ -74958,7 +75237,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
     if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
         translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0);
     }
-    memset(&metadataParser, 0, sizeof(metadataParser));
+    DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
     if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
         drwav_uint64 cursorForMetadata = cursor;
         metadataParser.onRead = pWav->onRead;
@@ -75390,7 +75669,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata*
                 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
                 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
                 bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
-                memset(reservedBuf, 0, sizeof(reservedBuf));
+                DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
                 bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
                 if (pMetadata->data.bext.codingHistorySize > 0) {
                     bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
@@ -76748,8 +77027,8 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF
     if (pWav->totalPCMFrameCount == 0) {
         return DRWAV_TRUE;
     }
-    if (targetFrameIndex >= pWav->totalPCMFrameCount) {
-        targetFrameIndex  = pWav->totalPCMFrameCount - 1;
+    if (targetFrameIndex > pWav->totalPCMFrameCount) {
+        targetFrameIndex = pWav->totalPCMFrameCount;
     }
     if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
         if (targetFrameIndex < pWav->readCursorInPCMFrames) {
@@ -78621,9 +78900,14 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
     #define DRFLAC_INLINE __forceinline
 #elif defined(__GNUC__)
     #if defined(__STRICT_ANSI__)
-        #define DRFLAC_INLINE __inline__ __attribute__((always_inline))
+        #define DRFLAC_GNUC_INLINE_HINT __inline__
+    #else
+        #define DRFLAC_GNUC_INLINE_HINT inline
+    #endif
+    #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
+        #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline))
     #else
-        #define DRFLAC_INLINE inline __attribute__((always_inline))
+        #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT
     #endif
 #elif defined(__WATCOMC__)
     #define DRFLAC_INLINE __inline
@@ -78634,7 +78918,7 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
     #define DRFLAC_X64
 #elif defined(__i386) || defined(_M_IX86)
     #define DRFLAC_X86
-#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64)
+#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
     #define DRFLAC_ARM
 #endif
 #if !defined(DR_FLAC_NO_SIMD)
@@ -78671,13 +78955,6 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
     #if defined(DRFLAC_ARM)
         #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
             #define DRFLAC_SUPPORT_NEON
-        #endif
-        #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
-            #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include(<arm_neon.h>)
-                #define DRFLAC_SUPPORT_NEON
-            #endif
-        #endif
-        #if defined(DRFLAC_SUPPORT_NEON)
             #include <arm_neon.h>
         #endif
     #endif
@@ -79489,6 +79766,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i
         if (!drflac__reload_cache(bs)) {
             return DRFLAC_FALSE;
         }
+        if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+            return DRFLAC_FALSE;
+        }
         *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
         bs->consumedBits += bitCountLo;
         bs->cache <<= bitCountLo;
@@ -79830,8 +80110,18 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs,
             return DRFLAC_FALSE;
         }
     }
+    if (bs->cache == 1) {
+        *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1;
+        if (!drflac__reload_cache(bs)) {
+            return DRFLAC_FALSE;
+        }
+        return DRFLAC_TRUE;
+    }
     setBitOffsetPlus1 = drflac__clz(bs->cache);
     setBitOffsetPlus1 += 1;
+    if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+        return DRFLAC_FALSE;
+    }
     bs->consumedBits += setBitOffsetPlus1;
     bs->cache <<= setBitOffsetPlus1;
     *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
@@ -79917,6 +80207,24 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64
     *pCRCOut = crc;
     return DRFLAC_SUCCESS;
 }
+static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x)
+{
+#if 1
+    drflac_uint32 result = 0;
+    while (x > 0) {
+        result += 1;
+        x >>= 1;
+    }
+    return result;
+#endif
+}
+static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision)
+{
+    return bitsPerSample + precision + drflac__ilog2_u32(order) > 32;
+}
+#if defined(__clang__)
+__attribute__((no_sanitize("signed-integer-overflow")))
+#endif
 static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
 {
     drflac_int32 prediction = 0;
@@ -80127,7 +80435,7 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32
     return (drflac_int32)(prediction >> shift);
 }
 #if 0
-static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
 {
     drflac_uint32 i;
     DRFLAC_ASSERT(bs != NULL);
@@ -80159,10 +80467,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla
         } else {
             decodedRice =  (decodedRice >> 1);
         }
-        if (bitsPerSample+shift >= 32) {
-            pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+        if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
+            pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
         } else {
-            pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+            pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
         }
     }
     return DRFLAC_TRUE;
@@ -80241,6 +80549,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac
             if (!drflac__reload_cache(bs)) {
                 return DRFLAC_FALSE;
             }
+            if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+                return DRFLAC_FALSE;
+            }
         }
         riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
         bs->consumedBits += bitCountLo;
@@ -80288,6 +80599,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drf
                 if (!drflac__reload_cache(bs)) {
                     return DRFLAC_FALSE;
                 }
+                if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+                    return DRFLAC_FALSE;
+                }
                 bs_cache = bs->cache;
                 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
             }
@@ -80357,6 +80671,9 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac
                 if (!drflac__reload_cache(bs)) {
                     return DRFLAC_FALSE;
                 }
+                if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+                    return DRFLAC_FALSE;
+                }
                 bs_cache = bs->cache;
                 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
             }
@@ -80418,7 +80735,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorde
     }
     return DRFLAC_TRUE;
 }
-static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
 {
     drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
     drflac_uint32 zeroCountPart0 = 0;
@@ -80434,12 +80751,12 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b
     drflac_uint32 i;
     DRFLAC_ASSERT(bs != NULL);
     DRFLAC_ASSERT(pSamplesOut != NULL);
-    if (order == 0) {
-        return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+    if (lpcOrder == 0) {
+        return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
     }
     riceParamMask  = (drflac_uint32)~((~0UL) << riceParam);
     pSamplesOutEnd = pSamplesOut + (count & ~3);
-    if (bitsPerSample+shift > 32) {
+    if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
         while (pSamplesOut < pSamplesOutEnd) {
             if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
                 !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
@@ -80459,10 +80776,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b
             riceParamPart1  = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
             riceParamPart2  = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
             riceParamPart3  = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
-            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
-            pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
-            pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
-            pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
+            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
+            pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
+            pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
+            pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
             pSamplesOut += 4;
         }
     } else {
@@ -80485,10 +80802,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b
             riceParamPart1  = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
             riceParamPart2  = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
             riceParamPart3  = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
-            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
-            pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
-            pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
-            pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
+            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
+            pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
+            pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
+            pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
             pSamplesOut += 4;
         }
     }
@@ -80500,10 +80817,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b
         riceParamPart0 &= riceParamMask;
         riceParamPart0 |= (zeroCountPart0 << riceParam);
         riceParamPart0  = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
-        if (bitsPerSample+shift > 32) {
-            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+        if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
+            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
         } else {
-            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+            pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
         }
         i += 1;
         pSamplesOut += 1;
@@ -80848,18 +81165,18 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac
     }
     return DRFLAC_TRUE;
 }
-static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
 {
     DRFLAC_ASSERT(bs != NULL);
     DRFLAC_ASSERT(pSamplesOut != NULL);
-    if (order > 0 && order <= 12) {
-        if (bitsPerSample+shift > 32) {
-            return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+    if (lpcOrder > 0 && lpcOrder <= 12) {
+        if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
+            return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
         } else {
-            return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+            return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
         }
     } else {
-        return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+        return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
     }
 }
 #endif
@@ -81198,37 +81515,37 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_
     }
     return DRFLAC_TRUE;
 }
-static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
 {
     DRFLAC_ASSERT(bs != NULL);
     DRFLAC_ASSERT(pSamplesOut != NULL);
-    if (order > 0 && order <= 12) {
-        if (bitsPerSample+shift > 32) {
-            return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+    if (lpcOrder > 0 && lpcOrder <= 12) {
+        if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
+            return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
         } else {
-            return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+            return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
         }
     } else {
-        return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+        return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
     }
 }
 #endif
-static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
 {
 #if defined(DRFLAC_SUPPORT_SSE41)
     if (drflac__gIsSSE41Supported) {
-        return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+        return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
     } else
 #elif defined(DRFLAC_SUPPORT_NEON)
     if (drflac__gIsNEONSupported) {
-        return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+        return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
     } else
 #endif
     {
     #if 0
-        return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+        return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
     #else
-        return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+        return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
     #endif
     }
 }
@@ -81243,7 +81560,10 @@ static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_
     }
     return DRFLAC_TRUE;
 }
-static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+#if defined(__clang__)
+__attribute__((no_sanitize("signed-integer-overflow")))
+#endif
+static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
 {
     drflac_uint32 i;
     DRFLAC_ASSERT(bs != NULL);
@@ -81257,15 +81577,15 @@ static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs*
         } else {
             pSamplesOut[i] = 0;
         }
-        if (bitsPerSample >= 24) {
-            pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+        if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
+            pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
         } else {
-            pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+            pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
         }
     }
     return DRFLAC_TRUE;
 }
-static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
 {
     drflac_uint8 residualMethod;
     drflac_uint8 partitionOrder;
@@ -81280,17 +81600,17 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_
     if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
         return DRFLAC_FALSE;
     }
-    pDecodedSamples += order;
+    pDecodedSamples += lpcOrder;
     if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
         return DRFLAC_FALSE;
     }
     if (partitionOrder > 8) {
         return DRFLAC_FALSE;
     }
-    if ((blockSize / (1 << partitionOrder)) < order) {
+    if ((blockSize / (1 << partitionOrder)) < lpcOrder) {
         return DRFLAC_FALSE;
     }
-    samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+    samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;
     partitionsRemaining = (1 << partitionOrder);
     for (;;) {
         drflac_uint8 riceParam = 0;
@@ -81310,7 +81630,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_
             }
         }
         if (riceParam != 0xFF) {
-            if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
+            if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
                 return DRFLAC_FALSE;
             }
         } else {
@@ -81318,7 +81638,7 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_
             if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
                 return DRFLAC_FALSE;
             }
-            if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) {
+            if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
                 return DRFLAC_FALSE;
             }
         }
@@ -81438,7 +81758,7 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32
         }
         pDecodedSamples[i] = sample;
     }
-    if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
+    if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
         return DRFLAC_FALSE;
     }
     return DRFLAC_TRUE;
@@ -81475,7 +81795,7 @@ static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 bl
             return DRFLAC_FALSE;
         }
     }
-    if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) {
+    if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
         return DRFLAC_FALSE;
     }
     return DRFLAC_TRUE;
@@ -81584,6 +81904,9 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u
                 return DRFLAC_FALSE;
             }
             crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16);
+            if (header->blockSizeInPCMFrames == 0xFFFF) {
+                return DRFLAC_FALSE;
+            }
             header->blockSizeInPCMFrames += 1;
         } else {
             DRFLAC_ASSERT(blockSize >= 8);
@@ -81616,6 +81939,9 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u
         if (header->bitsPerSample == 0) {
             header->bitsPerSample = streaminfoBitsPerSample;
         }
+        if (header->bitsPerSample != streaminfoBitsPerSample) {
+            return DRFLAC_FALSE;
+        }
         if (!drflac__read_uint8(bs, 8, &header->crc8)) {
             return DRFLAC_FALSE;
         }
@@ -81686,6 +82012,9 @@ static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame,
     } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
         subframeBitsPerSample += 1;
     }
+    if (subframeBitsPerSample > 32) {
+        return DRFLAC_FALSE;
+    }
     if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
         return DRFLAC_FALSE;
     }
@@ -83520,7 +83849,7 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac
 #ifndef DR_FLAC_NO_OGG
     if (init.container == drflac_container_ogg) {
         drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
-        *pInternalOggbs = oggbs;
+        DRFLAC_COPY_MEMORY(pInternalOggbs, &oggbs, sizeof(oggbs));
         pFlac->bs.onRead = drflac__on_read_ogg;
         pFlac->bs.onSeek = drflac__on_seek_ogg;
         pFlac->bs.pUserData = (void*)pInternalOggbs;
@@ -88258,7 +88587,11 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
             vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
 #endif
 #else
+        #if DRMP3_HAVE_SSE
             static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
+        #else
+            const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
+        #endif
             a = DRMP3_VMUL(a, g_scale);
             b = DRMP3_VMUL(b, g_scale);
 #if DRMP3_HAVE_SSE
@@ -88532,7 +88865,6 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num
         }
     }
 }
-#include <math.h>
 #if defined(SIZE_MAX)
     #define DRMP3_SIZE_MAX  SIZE_MAX
 #else
@@ -88578,18 +88910,6 @@ static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
     }
     return a;
 }
-static DRMP3_INLINE double drmp3_sin(double x)
-{
-    return sin(x);
-}
-static DRMP3_INLINE double drmp3_exp(double x)
-{
-    return exp(x);
-}
-static DRMP3_INLINE double drmp3_cos(double x)
-{
-    return drmp3_sin((DRMP3_PI_D*0.5) - x);
-}
 static void* drmp3__malloc_default(size_t sz, void* pUserData)
 {
     (void)pUserData;
index fa303286fa7bdcc1eb93c4bc7fbaf198dcb1c9a0..4e86fab2c4a6388792bc53e831ceb3d0f8d4624e 100644 (file)
@@ -1,5 +1,11 @@
 #define MA_NO_GENERATION
 #define MA_NO_DECODING
 #define MA_NO_ENCODING
+#define MA_NO_WAV
+#define MA_NO_FLAC
+#define MA_NO_MP3
+#define MA_NO_ENGINE
+#define MA_NO_NODE_GRAPH
+#define MA_NO_RESOURCE_MANAGER
 #define MINIAUDIO_IMPLEMENTATION
 #include "miniaudio.h"
index 25771ff8d142fc9ff7b180a0048172691feb78d3..b076091d1c627ba252d32ccf81d38165b94895f9 100644 (file)
@@ -1,7 +1,4 @@
-/* Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved */
-
-static void vg_exiterr( const char *err );
-static void vg_exit(void);
+/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
 
 #ifndef VG_HEADER_H
 #define VG_HEADER_H
@@ -36,6 +33,7 @@ void vg_register_exit( void( *funcptr )(void), const char *name );
 
 #include "vg_m.h"
 #include "vg_io.h"
+#include "vg_log.h"
 
 #ifdef VG_STEAM
 //#include "vg_steamworks.h"
@@ -48,14 +46,7 @@ void vg_register_exit( void( *funcptr )(void), const char *name );
 
 #ifndef VG_NON_CLIENT
 
-/* Engine globals */
-GLFWwindow* vg_window;
-
-#ifdef VG_3D
- m4x4f vg_pv;
-#else
- m3x3f vg_pv;
-#endif
+m4x4f vg_pv;
 
 #ifdef VG_CAPTURE_MODE
 int vg_window_x = 1920;
@@ -73,61 +64,171 @@ double vg_time,
        vg_time_last,
        vg_time_delta;
 
-#include "vg_audio.h"
-#include "vg_shader.h"
-#include "vg_tex.h"
-#include "vg_input.h"
-#include "vg_ui.h"
-#include "vg_console.h"
-#include "vg_lines.h"
-#include "vg_debug.h"
 
-#ifndef VG_RELEASE
-void vg_checkgl( const char *src_info )
+struct vg
 {
-   GLenum err;
-   while( (err = glGetError()) != GL_NO_ERROR )
-   {
-      vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
-   }
+   /* Engine sync */
+   GLFWwindow* window;
+
+   vg_mutex     mux_context;
+   vg_semaphore sem_allow_exec,
+                sem_exec_finished;
+   int exec_context;
+
+   vg_mutex     mux_engine_status;
+   int engine_running;
+
+
+   /* Gamepad */
+   GLFWgamepadstate  gamepad;
+   int                               gamepad_ready;
+   const char       *gamepad_name;
+   int                               gamepad_id;
 }
+static vg;
+
 
- #define VG_STRINGIT( X ) #X
- #define VG_CHECK_GL() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
+//#define VG_SYNC_DEBUG
+
+#ifdef VG_SYNC_DEBUG
+  #define VG_SYNC_LOG(...) vg_info(__VA_ARGS__)
 #else
#define VG_CHECK_GL()
 #define VG_SYNC_LOG(...)
 #endif
 
+/*
+ * Sync execution so that the OpenGL context is switched onto this thread.
+ * Anything after this call will be in a valid context.
+ */
+static int vg_acquire_thread_sync( int id )
+{
+   if( id == 0 )
+   {
+      /* no action */
+      return 1;
+   }
+   else
+   {
+      /* Check if the engine is no longer running */
+      vg_mutex_lock( &vg.mux_engine_status );
+      if( !vg.engine_running )
+      {
+         VG_SYNC_LOG( "[%d] Engine is no longer running\n", id );
+         vg_mutex_unlock( &vg.mux_engine_status );
+         return 0;
+      }
+      vg_mutex_unlock( &vg.mux_engine_status );
 
-#define VG_GAMELOOP
 
-void( *vg_on_exit[16] )(void);
-u32 vg_exit_count = 0;
+      vg_mutex_lock( &vg.mux_context );
+      VG_SYNC_LOG( "[%d] Signal to sync.\n", id );
+      vg.exec_context = id;
+      vg_mutex_unlock( &vg.mux_context );
+      
+      /* wait until told we can go */
+      VG_SYNC_LOG( "[%d] Waiting to acuire sync.\n", id );
+      vg_semaphore_wait( &vg.sem_allow_exec );
+      glfwMakeContextCurrent( vg.window );
 
-void vg_register_exit( void( *funcptr )(void), const char *name )
-{
-   vg_info( "exit registered: (%u)'%s'\n", vg_exit_count, name );
-   vg_on_exit[ vg_exit_count ++ ] = funcptr;
+      /* context now valid to work in while we hold up main thread */
+      VG_SYNC_LOG( "[%d] Context acquired.\n", id );
+
+      return 1;
+   }
 }
 
-static void vg_exit(void)
+/*
+ * Signify that we are done with the OpenGL context in this thread.
+ * Anything after this call will be in an undefined context.
+ */
+static void vg_release_thread_sync( int id )
 {
-   for( int i = vg_exit_count-1; i >= 0; i -- )
+   if( id == 0 )
+   {
+      vg_mutex_lock( &vg.mux_context );
+
+      if( vg.exec_context != 0 )
+      {
+         VG_SYNC_LOG( "[%d] Allowing content (%d).\n", id, vg.exec_context );
+
+         /* allow operations to go */
+         glfwMakeContextCurrent( NULL );
+         vg_semaphore_post( &vg.sem_allow_exec );
+
+         /* wait for operations to complete */
+         VG_SYNC_LOG( "[%d] Waiting for content (%d).\n", id, vg.exec_context );
+         vg_semaphore_wait( &vg.sem_exec_finished );
+         
+         /* re-engage main thread */
+         VG_SYNC_LOG( "[%d] Re-engaging.\n", id );
+         vg.exec_context = 0;
+         glfwMakeContextCurrent( vg.window );
+      }
+
+      vg_mutex_unlock( &vg.mux_context );
+   }
+   else
    {
-      vg_info( "engine_exit[%d]()\n", i );
-      vg_on_exit[i]();
+      /* signal that we are done */
+      VG_SYNC_LOG( "[%d] Releasing context.\n", id );
+      glfwMakeContextCurrent( NULL );
+      vg_semaphore_post( &vg.sem_exec_finished );
    }
-   
-   vg_info( "done\n" );
-   exit(0);
 }
 
-static void vg_exiterr( const char *err )
+static void vg_opengl_sync_init(void)
 {
-   vg_error( "Engine Fatal: %s\n", err );
-   vg_exit();
+   vg_semaphore_init( &vg.sem_allow_exec, 0 );
+   vg_semaphore_init( &vg.sem_exec_finished, 0 );
+   vg_mutex_init( &vg.mux_context );
 }
 
+static void vg_opengl_sync_free(void)
+{
+   vg_semaphore_free( &vg.sem_allow_exec );
+   vg_semaphore_free( &vg.sem_exec_finished );
+   vg_mutex_free( &vg.mux_context );
+}
+
+
+int vg_checkgl( const char *src_info );
+#define VG_STRINGIT( X ) #X
+#define VG_CHECK_GL_ERR() vg_checkgl( __FILE__ ":L" VG_STRINGIT(__LINE__) )
+
+#include "vg_audio.h"
+#include "vg_shader.h"
+#include "vg_tex.h"
+#include "vg_input.h"
+#include "vg_ui.h"
+#include "vg_console.h"
+#include "vg_lines.h"
+#include "vg_debug.h"
+#include "vg_loader.h"
+
+#define VG_GAMELOOP
+static void vg_register(void) VG_GAMELOOP;
+static void vg_start(void) VG_GAMELOOP;
+static void vg_update(int loaded) VG_GAMELOOP;
+static void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
+static void vg_render(void) VG_GAMELOOP;
+static void vg_ui(void) VG_GAMELOOP;
+
+int vg_checkgl( const char *src_info )
+{
+   int fail = 0;
+
+   GLenum err;
+   while( (err = glGetError()) != GL_NO_ERROR )
+   {
+      vg_error( "(%s) OpenGL Error: #%d\n", src_info, err );
+      fail = 1;
+   }
+
+   return fail;
+}
+
+
+
 void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
 {
    vg_mouse[0] = xpos;
@@ -140,44 +241,71 @@ void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
    vg_mouse_wheel[1] += yoffset;
 }
 
-
-static void vg_register(void) VG_GAMELOOP;
-static void vg_start(void) VG_GAMELOOP;
-static void vg_update(void) VG_GAMELOOP;
-static void vg_framebuffer_resize(int w, int h) VG_GAMELOOP;
-static void vg_render(void) VG_GAMELOOP;
-static void vg_ui(void) VG_GAMELOOP;
-static void vg_free(void) VG_GAMELOOP;
-
 void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
 {
    vg_window_x = w;
    vg_window_y = h;
 
-#ifdef VG_FRAMEBUFFER_RESIZE
    vg_framebuffer_resize(w,h);
-#endif
 }
 
-static void vg_init( int argc, char *argv[], const char *window_name )
+static int vg_bake_shaders(void)
 {
-#ifdef VG_STEAM
-   if( !sw_init() )
-      return;
-#endif
-   
+   if( vg_acquire_thread_sync(1) )
+   {
+      if( !vg_shaders_recompile() )
+      {
+         vg_shaders_free(NULL);
+         vg_release_thread_sync(1);
+         return 0;
+      }
+      else
+      {
+         vg_release_thread_sync(1);
+
+         if( !vg_loader_highwater( vg_shaders_free, NULL ) )         return 0;
+         else return 1;
+      }
+   }
+
+   return 0;
+}
+
+int vg_preload(void);
+int vg_load(void);
+static int vg_load_full(void)
+{
+   if( !vg_preload() )                                               return 0;
+
+   /* internal */
+   if( !vg_gamepad_init() )                                          return 0;
+   if( !vg_loader_highwater( NULL, NULL ) )                          return 0;
+
+   if( !vg_lines_init() )                                            return 0;
+   if( !vg_loader_highwater( vg_lines_free, NULL ) )                 return 0;
+
+   if( !vg_audio_init() )                                            return 0;
+   if( !vg_loader_highwater( vg_audio_free, NULL ) )                 return 0;
+
+   /* client */
+   if( !vg_load() )                                                  return 0;
+
+   return 1;
+}
+
+static void vg_enter( int argc, char *argv[], const char *window_name )
+{
+   vg_log_init();
+   vg_console_init();
+
    glfwInit();
    glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
    glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
    glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
    glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE );
    
-#ifdef VG_CAPTURE_MODE
-   glfwWindowHint( GLFW_RESIZABLE, GLFW_FALSE );
-#else
    glfwWindowHint( GLFW_RESIZABLE, GLFW_TRUE );
-#endif
-   glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
+   glfwWindowHint( GLFW_DOUBLEBUFFER, GLFW_TRUE );
    
 #if 0
    glfwWindowHint(GLFW_SAMPLES,4);
@@ -190,130 +318,151 @@ static void vg_init( int argc, char *argv[], const char *window_name )
    glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
    glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
    
+   /* This is set like this because of an OS issue */
    int refresh_rate = mode->refreshRate;
-
    if( refresh_rate < 28 || refresh_rate >= 144 )
       refresh_rate = 60;
-
    glfwWindowHint( GLFW_REFRESH_RATE, refresh_rate );
 
-   if( !(vg_window = glfwCreateWindow( vg_window_x, vg_window_y, 
+   if( !(vg.window = glfwCreateWindow( vg_window_x, vg_window_y, 
                                        window_name, NULL, NULL)) )
-      vg_exiterr( "GLFW Failed to initialize" );
-   else
-      vg_register_exit( &glfwTerminate, "glfwTerminate" );
+   {
+      vg_error( "GLFW Failed to initialize\n" );
+      return;
+   }
    
-   glfwMakeContextCurrent( vg_window );
+   glfwMakeContextCurrent( vg.window );
    glfwSwapInterval( 1 );
 
-   glfwSetWindowSizeLimits( vg_window, 800, 600, GLFW_DONT_CARE,GLFW_DONT_CARE);
-   glfwSetFramebufferSizeCallback( vg_window, vg_framebuffer_resize_callback );
+   glfwSetWindowSizeLimits( vg.window, 800, 600, GLFW_DONT_CARE,GLFW_DONT_CARE);
+   glfwSetFramebufferSizeCallback( vg.window, vg_framebuffer_resize_callback );
 
-   glfwSetCursorPosCallback( vg_window, vg_mouse_callback );
-   glfwSetScrollCallback( vg_window, vg_scroll_callback );
+   glfwSetCursorPosCallback( vg.window, vg_mouse_callback );
+   glfwSetScrollCallback( vg.window, vg_scroll_callback );
    
-   glfwSetCharCallback( vg_window, console_proc_wchar );
-   glfwSetKeyCallback( vg_window, console_proc_key );
+   glfwSetCharCallback( vg.window, console_proc_wchar );
+   glfwSetKeyCallback( vg.window, console_proc_key );
 #if 0
    glfwSetInputMode(vg_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
 #endif
 
    if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) ) 
-      vg_exiterr( "Glad failed to initialize" );
+   {
+      vg_error( "Glad Failed to initialize\n" );
+      glfwTerminate();
+      return;
+   }
 
    const unsigned char* glver = glGetString( GL_VERSION );
    vg_success( "Load setup complete, OpenGL version: %s\n", glver );
-   
    vg_run_gfx_diagnostics();
-   
-   vg_gamepad_init();
-   vg_lines_init();
-   vg_register_exit( &vg_lines_free, "vg_lines_free" );
-   ui_default_init();
-   vg_register_exit( &ui_default_free, "UI" );
-      
-   vg_register();
-   vg_register_exit( &vg_free, "vg_free" );
 
+   if( !ui_default_init() )
+      goto il_exit_ui;
 
-   vg_shaders_recompile(0,NULL);
-   vg_register_exit( &vg_shaders_free, "vg_shaders_free" );
-       vg_function_push( (struct vg_cmd){
-               .name = "shaders",
-               .function = vg_shaders_recompile
-       });
+   if( !vg_loader_init() )
+      goto il_exit_loader;
 
-   if( !vg_audio_init() )
-      vg_exit();
-   vg_register_exit( &vg_audio_free, "vg_audio_free" );
+   vg_mutex_init( &vg.mux_engine_status );
+   vg.engine_running = 1;
 
-   vg_start();
+   vg_opengl_sync_init();
+   vg_loader_start();
 
-   vg_console_init();
-   vg_register_exit( &vg_console_free, "Console" );
-   
-   /* 
-    * Main gameloop
-    */
-   while( !glfwWindowShouldClose( vg_window ) )
+   int loaded = 0;
+
+   while(1)
    {
-      v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
+      vg_acquire_thread_sync( 0 );
 
+      if( glfwWindowShouldClose( vg.window ) )
+      {
+         break;
+      }
+
+      v2_copy( (v2f){ 0.0f, 0.0f }, vg_mouse_wheel );
       glfwPollEvents();
-      
-      #ifdef VG_STEAM
-      sw_event_loop();
-      #endif
-      
+
       vg_time_last = vg_time;
       vg_time = glfwGetTime();
       vg_time_delta = vg_minf( vg_time - vg_time_last, 0.1f );
       
-      vg_update_inputs();
-      vg_update();
-      vg_render();
-      
-      vg_lines_drawall((float*)vg_pv);
-      
+      enum loader_status load_status = vg_loader_status();
+
+      if( load_status == k_loader_status_complete )
       {
-         ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
-         ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1], 
-               vg_get_button_state( "primary" ) );
+         glClearColor( 0.0f,sinf(vg_time*20.0)*0.5f+0.5f,0.0f,1.0f );
+         glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
          
-         audio_debug_ui( vg_pv );
-         vg_ui();
-         vg_console_draw();
-         
-         ui_resolve( &ui_global_ctx );
-         ui_draw( &ui_global_ctx, NULL );
+         if( !loaded )
+         {
+            vg_start();
+            loaded = 1;
+         }
       }
-      
-      glfwSwapBuffers( vg_window );
-      VG_CHECK_GL();
+      else
+      {
+         if( load_status == k_loader_status_fail )
+         {
+            break;
+         }
+         else
+         {
+            vg_loader_render();
+         }
+      }
+
+      vg_update_inputs();
+      vg_update( loaded );
+
+      if( loaded )
+      {
+         vg_render();
+
+         /* ui */
+         {
+            ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
+            ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1], 
+                  vg_get_button_state( "primary" ) );
+            
+            audio_debug_ui( vg_pv );
+            vg_ui();
+            vg_console_draw();
+            
+            ui_resolve( &ui_global_ctx );
+            ui_draw( &ui_global_ctx, NULL );
+         }
+      }
+
+
+
+
+      glfwSwapBuffers( vg.window );
+
+      if( VG_CHECK_GL_ERR() )
+         break;
+
+      vg_release_thread_sync( 0 );
    }
-   
-   vg_exit();
-}
 
-#ifndef VG_3D
-void vg_projection_update(void)
-{
-   /*
-    * Reproject screenspace mouse into world
-    */
+   vg_console_write_persistent();
 
-   vg_mouse_ws[0] = vg_mouse[0];
-   vg_mouse_ws[1] = vg_mouse[1];
-   vg_mouse_ws[2] = 1.0f;
-   
-   vg_mouse_ws[0] =   (2.0f * vg_mouse_ws[0]) / ((float)vg_window_x) - 1.0f;
-   vg_mouse_ws[1] = -((2.0f * vg_mouse_ws[1]) / ((float)vg_window_y) - 1.0f);
-   
-   m3x3f inverse;
-   m3x3_inv( vg_pv, inverse ); 
-   m3x3_mulv( inverse, vg_mouse_ws, vg_mouse_ws );
+   vg_mutex_lock( &vg.mux_engine_status );
+   vg.engine_running = 0;
+   vg_mutex_unlock( &vg.mux_engine_status );
+
+
+   vg_loader_free();
+   vg_opengl_sync_free();
+
+   vg_mutex_free( &vg.mux_engine_status );
+
+il_exit_loader:
+   ui_default_free();
+
+il_exit_ui:
+   glfwTerminate();
 }
-#endif
 
 #endif
 
index 2171359eda030edc572e0e1d5fafdd0af589d1d8..8cdaa4acb744e1c165de6f95e0d663dbbec96b0f 100644 (file)
@@ -6,7 +6,16 @@
 #define MA_NO_GENERATION
 #define MA_NO_DECODING
 #define MA_NO_ENCODING
+#define MA_NO_WAV
+#define MA_NO_FLAC
+#define MA_NO_MP3
+#define MA_NO_ENGINE
+#define MA_NO_NODE_GRAPH
+#define MA_NO_RESOURCE_MANAGER
+
 #include "dr_soft/miniaudio.h"
+
+
 #include "vg/vg.h"
 #include "vg/vg_stdint.h"
 #include "vg/vg_platform.h"
 
 #include <time.h>
 
+#ifdef __GNUC__
+  #pragma GCC push_options
+  #pragma GCC optimize ("O3")
+#endif
+
 #define STB_VORBIS_MAX_CHANNELS 2
 #include "stb/stb_vorbis.h"
 
+#ifdef __GNUC__
+  #pragma GCC pop_options
+#endif
+
 #define SFX_MAX_SYSTEMS       32
 #define AUDIO_FLAG_LOOP       0x1
 #define AUDIO_FLAG_ONESHOT    0x2
@@ -102,8 +120,9 @@ static struct vg_audio_system
 
    /* synchro */
    int               sync_locked;
-   MUTEX_TYPE        mutex_checker;
-   MUTEX_TYPE        mutex_sync;
+
+   vg_mutex          mux_checker,
+                     mux_sync;
 
    /* Audio engine, thread 1 */
    struct active_audio_player
@@ -167,17 +186,17 @@ static void *audio_alloc( u32 size )
 static int audio_lock_checker_load(void)
 {
    int value;
-   MUTEX_LOCK( vg_audio.mutex_checker );
+   vg_mutex_lock( &vg_audio.mux_checker );
    value = vg_audio.sync_locked;
-   MUTEX_UNLOCK( vg_audio.mutex_checker );
+   vg_mutex_unlock( &vg_audio.mux_checker );
    return value;
 }
 
 static void audio_lock_checker_store( int value )
 {
-   MUTEX_LOCK( vg_audio.mutex_checker );
+   vg_mutex_lock( &vg_audio.mux_checker );
    vg_audio.sync_locked = value;
-   MUTEX_UNLOCK( vg_audio.mutex_checker );
+   vg_mutex_unlock( &vg_audio.mux_checker );
 }
 
 static void audio_require_lock(void)
@@ -185,19 +204,20 @@ static void audio_require_lock(void)
    if( audio_lock_checker_load() )
       return;
 
-   vg_exiterr( "Modifying sound effects systems requires locking\n" );
+   vg_error( "Modifying sound effects systems requires locking\n" );
+   abort();
 }
 
 static void audio_lock(void)
 {
-   MUTEX_LOCK( vg_audio.mutex_sync );
+   vg_mutex_lock( &vg_audio.mux_sync );
    audio_lock_checker_store(1);
 }
 
 static void audio_unlock(void)
 {
    audio_lock_checker_store(0);
-   MUTEX_UNLOCK( vg_audio.mutex_sync );
+   vg_mutex_unlock( &vg_audio.mux_sync );
 }
 
 
@@ -206,6 +226,9 @@ static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf,
 
 static int vg_audio_init(void)
 {
+   vg_mutex_init( &vg_audio.mux_checker );
+   vg_mutex_init( &vg_audio.mux_sync );
+
    vg_convar_push( (struct vg_convar){
       .name = "debug_audio",
       .data = &vg_audio.debug_ui,
@@ -231,7 +254,7 @@ static int vg_audio_init(void)
    ma_device_config *dconf  = &vg_audio.miniaudio_dconfig;
    ma_device        *device = &vg_audio.miniaudio_device;
 
-    *dconf = ma_device_config_init( ma_device_type_playback );
+   *dconf = ma_device_config_init( ma_device_type_playback );
    dconf->playback.format    = ma_format_f32;
    dconf->playback.channels  = 2;
    dconf->sampleRate         = 44100;
@@ -256,16 +279,20 @@ static int vg_audio_init(void)
          return 0;
       }
    }
-
+   
+   vg_success( "Ready\n" );
    return 1;
 }
 
-static void vg_audio_free(void)
+static void vg_audio_free(void * nothing)
 {
    ma_device        *device = &vg_audio.miniaudio_device;
    ma_device_uninit( device );
 
    free( vg_audio.mem );
+
+   vg_mutex_free( &vg_audio.mux_checker );
+   vg_mutex_free( &vg_audio.mux_sync );
 }
 
 /* 
@@ -461,16 +488,32 @@ static void audio_system_cleanup(void)
  */
 static void audio_entity_spacialize( audio_entity *ent, float *vol, float *pan )
 {
+   if( ent->info.vol < 0.01f )
+   {
+      *vol = ent->info.vol;
+      *pan = 0.0f;
+      return;
+   }
+
    v3f delta;
    v3_sub( ent->info.world_position, vg_audio.listener_pos, delta );
 
-   float dist = v3_length( delta ),
-         attn = (dist / ent->info.vol) +1.0f;
+   float dist2 = v3_length2( delta );
 
-   v3_muls( delta, 1.0f/dist, delta );
+   if( dist2 < 0.0001f )
+   {
+      *pan = 0.0f;
+      *vol = 1.0f;
+   }
+   else
+   {
+      float dist = sqrtf( dist2 ),
+            attn = (dist / ent->info.vol) +1.0f;
 
-   *pan = v3_dot( vg_audio.listener_ears, delta );
-   *vol = 1.0f/(attn*attn);
+      v3_muls( delta, 1.0f/dist, delta );
+      *pan = v3_dot( vg_audio.listener_ears, delta );
+      *vol = 1.0f/(attn*attn);
+   }
 }
 
 static void audio_decode_uncompressed_mono( float *src, u32 count, float *dst )
@@ -664,7 +707,9 @@ static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf,
       struct active_audio_player *aap = &vg_audio.active_players[i];
 
       if( aap->active )
+      {
          audio_entity_mix( i, pOut32F, frame_count );
+      }
    }
    
 #if 0
@@ -730,7 +775,8 @@ static float *audio_decompress_vorbis( const unsigned char *data, int len,
    if( !buffer )
    {
       stb_vorbis_close( pv );
-      vg_exit();
+      vg_error( "Failed to allocated memory for audio\n" );
+      return NULL;
    }
    
    int read_samples = stb_vorbis_get_samples_float_interleaved( 
@@ -762,7 +808,7 @@ static int audio_clip_load( audio_clip *clip )
 
    if( clip->source_mode == k_audio_source_mono )
    {
-      u32 samples;
+      u32 samples = 0;
       float *sound = audio_decompress_vorbis( filedata, file_len, 1, &samples );
       clip->data = sound;
       clip->len = samples;
@@ -833,9 +879,8 @@ static void audio_require_init( audio_player *player )
    if( player->init )
       return;
 
-   char err[128];
-   snprintf( err, 127, "Must init before playing! (%s)\n", player->name );
-   vg_exiterr( err );
+   vg_error( "Must init before playing! (%s)\n", player->name );
+   abort();
 }
 
 /* Play a clip using player. If its already playing something, it will 
@@ -890,6 +935,16 @@ static void audio_player_init( audio_player *player )
  * Safety enforced Get/set attributes
  */
 
+static int audio_player_is_playing( audio_player *sys )
+{
+   audio_require_lock();
+
+   if( sys->active_entity != AATREE_PTR_NIL )
+      return 1;
+   else 
+      return 0;
+}
+
 static void audio_player_set_position( audio_player *sys, v3f pos )
 {
    audio_require_lock();
index 8f9020cfcf0a4d60c94933f4f664aec870483d1f..6ef2d7837774fc01f29331915e0a148e46fbd6a2 100644 (file)
@@ -3,6 +3,10 @@
 #ifndef VG_CONSOLE_H
 #define VG_CONSOLE_H
 
+#include "vg/vg_ui.h"
+#include "vg/vg_log.h"
+
+
 struct vg_console
 {
        struct vg_convar
@@ -46,13 +50,10 @@ struct vg_console
        }
        *functions;
        
-       char lines[16][512];
-       u32 current, len;
-       
-       char input[512];
+       char input[96];
        int cursor_user, cursor_pos, string_length;
        
-       char history[32][512];
+       char history[32][96];
        int history_last, history_pos, history_count;
        
        int enabled;
@@ -111,14 +112,15 @@ static void vg_console_draw( void )
        if( !vg_console.enabled )
                return;
 
-       int ptr = vg_console.current-1;
+   vg_mutex_lock( &log_print_mutex );
 
+       int ptr = vg_log.buffer_line_current;
    int const fh = 14;
+   int console_lines = VG_MIN( 16, vg_log.buffer_line_count );
        
        ui_global_ctx.cursor[0] = 0;
        ui_global_ctx.cursor[1] = 0;
-       ui_global_ctx.cursor[3] = 
-      vg_list_size( vg_console.lines )*fh*vg_console.scale;
+       ui_global_ctx.cursor[3] = 16*fh*vg_console.scale;
        ui_fill_x( &ui_global_ctx );
        
        ui_new_node( &ui_global_ctx );
@@ -127,18 +129,19 @@ static void vg_console_draw( void )
        
                ui_global_ctx.cursor[3] = fh*vg_console.scale;
                ui_align_bottom( &ui_global_ctx ); 
-               
-               for( int i = 0; i < vg_console.len; i ++ )
+
+               for( int i=0; i<console_lines; i ++ )
                {
+                       ptr --;
+
                        if( ptr < 0 )
-                               ptr = vg_list_size( vg_console.lines )-1;
+                               ptr = vg_list_size( vg_log.buffer )-1;
          
                        ui_text( &ui_global_ctx, ui_global_ctx.cursor, 
-               vg_console.lines[ptr], vg_console.scale, 0 );
+               vg_log.buffer[ptr], vg_console.scale, 0 );
                        ui_global_ctx.cursor[1] -= fh*vg_console.scale;
-               
-                       ptr --;
                }
+
        }
        ui_end_down( &ui_global_ctx );
        
@@ -162,17 +165,7 @@ static void vg_console_draw( void )
                ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x66ffffff );
        }
        ui_end_down( &ui_global_ctx );
-}
-
-void vg_console_println( const char *str )
-{
-       if( vg_console.len < vg_list_size( vg_console.lines ) )
-               vg_console.len ++;
-       
-       strcpy( vg_console.lines[ vg_console.current ++ ], str );
-       
-       if( vg_console.current >= vg_list_size( vg_console.lines ) )
-               vg_console.current = 0;
+   vg_mutex_unlock( &log_print_mutex );
 }
 
 static int vg_console_list( int argc, char const *argv[] )
@@ -196,6 +189,14 @@ static int vg_console_list( int argc, char const *argv[] )
 
 static int vg_console_chartest( int argc, char const *argv[] )
 {
+vg_info(" Copyright  .        . .       -----, ,----- ,---.   .---.  " );
+vg_info(" 2021-2022  |\\      /| |           /  |      |    | |    /| " );
+vg_info("            | \\    / | +--        /   +----- +---'  |   / | " );
+vg_info("            |  \\  /  | |         /    |      |   \\  |  /  | " );
+vg_info("            |   \\/   | |        /     |      |    \\ | /   | " );
+vg_info("            '        ' '--' [] '----- '----- '     ' '---'  " 
+        "SOFTWARE" );
+
    vg_info( "\"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG\"\n" );
    vg_info( "'the quick brown fox jumps over the lazy dog'\n" );
    vg_info( ":;!@#$%^& 0123456789 +-*/=~ ()[]{}<>\n" );
@@ -204,25 +205,29 @@ static int vg_console_chartest( int argc, char const *argv[] )
 
 static void vg_console_init(void)
 {
-       vg_log_callback = vg_console_println;
-       
        vg_convar_push( (struct vg_convar)
-       { .name = "console_scale", .data = &vg_console.scale, 
+       { 
+      .name = "console_scale", .data = &vg_console.scale, 
       .data_type = k_convar_dtype_i32, 
                .opt_i32 = { .clamp = 1, .min = 1, .max = 7 },
       .update = NULL
    });
        
-       vg_function_push( (struct vg_cmd){
+       vg_function_push( (struct vg_cmd)
+   {
                .name = "list",
                .function = vg_console_list
        });
 
-   vg_function_push( (struct vg_cmd){
+   vg_function_push( (struct vg_cmd)
+   {
       .name = "chartest",
       .function = vg_console_chartest
    });
+}
 
+static void vg_console_load_autos(void)
+{
        /* Read and exec persistent commands */
        FILE *fp = fopen( "cfg/auto.conf", "r" );
        if( fp )
index 76b3b95e7781934783363c6cb3345f0cf526661e..3985f3c26975dcc89acb5dcfeb1776e078a58898 100644 (file)
@@ -2,6 +2,9 @@
 #ifndef VG_INPUT_H
 #define VG_INPUT_H
 
+#include "common.h"
+#include "vg/vg_loader.h"
+
 static inline float vg_get_axis( const char *axis );
 static inline int vg_get_button( const char *button );
 static inline int vg_get_button_down( const char *button );
@@ -15,11 +18,6 @@ enum vg_button_state
        k_button_state_none = 0
 };
 
-GLFWgamepadstate vg_gamepad;
-int                    vg_gamepad_ready = 0;
-const char *vg_gamepad_name = NULL;
-int                    vg_gamepad_id;
-
 /* TODO: Fix this... */
 enum EInputMode
 {
@@ -147,17 +145,15 @@ static inline int key_is_keyboard( int const id )
 int get_button_cross_device( int const id )
 {
        if( key_is_keyboard( id ) )
-               return glfwGetKey( vg_window, id );
+               return glfwGetKey( vg.window, id );
        else
-               return glfwGetMouseButton( vg_window, id ) == GLFW_PRESS;
+               return glfwGetMouseButton( vg.window, id ) == GLFW_PRESS;
 }
 
 void vg_update_inputs(void)
 {
-   if( !glfwGetGamepadState( GLFW_JOYSTICK_1, &vg_gamepad) )
-   {
-      vg_gamepad_ready = 0;
-   }
+   if( !glfwGetGamepadState( GLFW_JOYSTICK_1, &vg.gamepad) )
+      vg.gamepad_ready = 0;
 
        /* Update button inputs */
        for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ )
@@ -171,37 +167,44 @@ void vg_update_inputs(void)
    {
                struct button_binding *binding = vg_controller_binds + i;
                binding->prev = binding->value;
-      binding->value = vg_gamepad.buttons[ binding->bind ];
+      binding->value = vg.gamepad.buttons[ binding->bind ];
    }
        
        /* Update axis inputs */
        for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ )
        {
                struct axis_binding *binding = vg_axis_binds + i;
-      binding->value = vg_gamepad.axes[ binding->bind ];
+      binding->value = vg.gamepad.axes[ binding->bind ];
        }
 }
 
-static void vg_gamepad_init(void)
+static int vg_gamepad_init(void)
 {
-   for( int id = 0; id <= GLFW_JOYSTICK_LAST; id ++ )
+   if( vg_acquire_thread_sync(1) )
    {
-      if( glfwJoystickPresent( id ) )
+      for( int id=0; id<=GLFW_JOYSTICK_LAST; id ++ )
       {
-         vg_info( "Joystick found: '%s'\n", glfwGetJoystickName(id) );
+         if( glfwJoystickPresent( id ) )
+         {
+            vg_info( "Joystick found: '%s'\n", glfwGetJoystickName(id) );
+         }
+
+         if( glfwJoystickIsGamepad( id ) )
+         {
+            vg.gamepad_name = glfwGetGamepadName( id );
+            vg_success( "Gamepad mapping registered: %s\n", vg.gamepad_name );
+            
+            vg.gamepad_ready = 1;
+            vg.gamepad_id = id;
+            break;
+         }
       }
 
-      if( glfwJoystickIsGamepad( id ) )
-      {
-         vg_gamepad_name = glfwGetGamepadName( id );
-         vg_success( "Gamepad with mapping registered: %s\n", vg_gamepad_name );
-         
-         vg_gamepad_ready = 1;
-         vg_gamepad_id = id;
-         
-         break;
-      }
+      vg_release_thread_sync(1);
+      return 1;
    }
+
+   return 0;
 }
 
 #endif
index 2650b3fd9f7e5ac6faa2f33cf4a70a888b7a0f5e..19d468b8e1ce39634cdbe31dcdac7e1447c6a363 100644 (file)
@@ -6,58 +6,6 @@
 #include "vg_stdint.h"
 #include "vg_platform.h"
 #include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <malloc.h>
-
-#define KNRM  "\x1B[0m"
-#define KRED  "\x1B[31m"
-#define KGRN  "\x1B[32m"
-#define KYEL  "\x1B[33m"
-#define KBLU  "\x1B[34m"
-#define KMAG  "\x1B[35m"
-#define KCYN  "\x1B[36m"
-#define KWHT  "\x1B[37m"
-
-void (*vg_log_callback)( const char *str ) = NULL;
-
-static void vg_log_write( FILE *file, const char *prefix, 
-      const char *fmt, va_list args )
-{
-       char buffer[512];
-       int i, j;
-       
-       for( i = 0; i < vg_list_size( buffer ); i ++ )
-       {
-               if( prefix[i] )
-                       buffer[i] = prefix[i];
-               else 
-                       break;
-       }
-       
-       j = i + vsnprintf( buffer + i, vg_list_size( buffer ) - i -12, fmt, args );
-       strcpy( buffer + j, KNRM );
-       
-       fputs( buffer, file );
-       
-       if( vg_log_callback )
-               vg_log_callback( buffer );
-}
-
-#define VG_LOGX( NAME, PIPE, PFX )           \
-static void NAME(const char *fmt, ...)       \
-{                                            \
-   va_list args;                             \
-   va_start( args, fmt );                    \
-   vg_log_write( PIPE, (PFX), fmt, args );   \
-   va_end( args );                           \
-}
-
-VG_LOGX( vg_success, stdout, (KGRN "success" KWHT "| " KGRN) )
-VG_LOGX( vg_info,    stdout, (KNRM "   info" KWHT "| " KNRM) )
-VG_LOGX( vg_log,     stdout, (KWHT "    log" KWHT "| " KWHT) )
-VG_LOGX( vg_warn,    stdout, (KYEL "   warn" KWHT "| " KYEL) )
-VG_LOGX( vg_error,   stderr, (KRED "  error" KWHT "| " KRED) )
 
 /*
  * FIle I/O
index e14d3381d3e4bf65942601afcb79a53ee052bb45..0c02bb19fbd2faaaa6e5adeb74d1258ae411dd59 100644 (file)
@@ -1,5 +1,10 @@
 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
 
+#ifndef VG_LINES_H
+#define VG_LINES_H
+
+#include "vg/vg.h"
+
 static int debug_lines_enable = 1;
 
 #ifdef VG_3D
@@ -74,8 +79,10 @@ struct
 }
 vg_lines;
 
-static void vg_lines_init(void)
+static int vg_lines_init(void)
 {
+   vg_info( "vg_lines_init\n" );
+
    vg_convar_push( (struct vg_convar){
       .name = "debug_lines",
       .data = &debug_lines_enable,
@@ -86,45 +93,74 @@ static void vg_lines_init(void)
    
    vg_shader_register( &_shader_lines );
        
-       glGenVertexArrays( 1, &vg_lines.vao );
-       glGenBuffers( 1, &vg_lines.vbo );
-       glBindVertexArray( vg_lines.vao );
-       
-       glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo );
-       
-       vg_lines.cap = 50000;
-       vg_lines.buffer_size = vg_lines.cap * sizeof( struct vg_lines_vert );
-       
-       glBufferData( GL_ARRAY_BUFFER, vg_lines.buffer_size, NULL, GL_DYNAMIC_DRAW );
-       glBindVertexArray( vg_lines.vao );
-       
-       glVertexAttribPointer( 
-               0, 
-               sizeof( vg_lines.buffer[0].co ) / sizeof(float), 
-               GL_FLOAT, 
-               GL_FALSE, 
-               sizeof( struct vg_lines_vert ), 
-               (void *)0 
-       );
-       glEnableVertexAttribArray( 0 );
-       
-       glVertexAttribPointer( 
-               1, 
-               4, 
-               GL_UNSIGNED_BYTE, 
-               GL_TRUE, 
-               sizeof( struct vg_lines_vert ), 
-               (void*)(offsetof( struct vg_lines_vert, colour ))
-       );
-       glEnableVertexAttribArray( 1 );
-       vg_lines.buffer = malloc( vg_lines.buffer_size );
+
+   if( vg_acquire_thread_sync(1) )
+   {
+      glGenVertexArrays( 1, &vg_lines.vao );
+      glGenBuffers( 1, &vg_lines.vbo );
+      glBindVertexArray( vg_lines.vao );
+      glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo );
+      
+      vg_lines.cap = 50000;
+      vg_lines.buffer_size = vg_lines.cap * sizeof( struct vg_lines_vert );
+      
+      glBufferData( GL_ARRAY_BUFFER, vg_lines.buffer_size, 
+                    NULL, GL_DYNAMIC_DRAW );
+      glBindVertexArray( vg_lines.vao );
+
+      if( VG_CHECK_GL_ERR() )
+      {
+         vg_release_thread_sync(1);
+         vg_error( "Failed\n" );
+      }
+
+      /* Pointers */
+      glVertexAttribPointer( 
+         0, 
+         sizeof( vg_lines.buffer[0].co ) / sizeof(float), 
+         GL_FLOAT, 
+         GL_FALSE, 
+         sizeof( struct vg_lines_vert ), 
+         (void *)0 
+      );
+      glEnableVertexAttribArray( 0 );
+      
+      glVertexAttribPointer( 
+         1, 
+         4, 
+         GL_UNSIGNED_BYTE, 
+         GL_TRUE, 
+         sizeof( struct vg_lines_vert ), 
+         (void*)(offsetof( struct vg_lines_vert, colour ))
+      );
+      glEnableVertexAttribArray( 1 );
+
+      if( VG_CHECK_GL_ERR() )
+         goto il_delete_buffers_and_fail;
+
+      /* Alloc RAM */
+      vg_lines.buffer = malloc( vg_lines.buffer_size );
+      if( vg_lines.buffer )
+      {
+         vg_success( "done\n" );
+         vg_release_thread_sync(1);
+         return 1;
+      }
+
+il_delete_buffers_and_fail:
+      glDeleteBuffers( 1, &vg_lines.vbo );
+      glDeleteVertexArrays( 1, &vg_lines.vao );
+      vg_release_thread_sync(1);
+      return 0;
+   }
+
+   return 0;
 }
 
-static void vg_lines_free(void)
+static void vg_lines_free(void *nothing)
 {
        glDeleteVertexArrays( 1, &vg_lines.vao );
        glDeleteBuffers( 1, &vg_lines.vbo );
-       
        free( vg_lines.buffer );
 }
 
@@ -263,6 +299,20 @@ static void vg_line_boxf_transformed( m4x3f m, boxf box, u32 colour )
    vg_line( p100, p010, colour );
 }
 
+static void vg_line_cross(v3f pos,u32 colour, float scale)
+{
+   v3f p0, p1;
+   v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
+   v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
+   vg_line( p0, p1, colour );
+   v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
+   v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
+   vg_line( p0, p1, colour );
+   v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
+   v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
+   vg_line( p0, p1, colour );
+}
+
 static void vg_line_pt3( v3f pt, float size, u32 colour )
 {
    boxf box =
@@ -273,3 +323,5 @@ static void vg_line_pt3( v3f pt, float size, u32 colour )
 
    vg_line_boxf( box, colour );
 }
+
+#endif /* VG_LINES_H */
diff --git a/src/vg/vg_loader.h b/src/vg/vg_loader.h
new file mode 100644 (file)
index 0000000..2ba707e
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2021-2022 (C) Mount0 Software, Harry Godden - All Rights Reserved
+ * -----------------------------------------------------------------------------
+ *
+ * Splash / load screen
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#ifndef VG_LOADER_H
+#define VG_LOADER_H
+
+#include "common.h"
+
+static struct vg_loader
+{
+   /* Synchronization */
+   vg_semaphore sem_loading;
+   vg_mutex mux_status;
+
+   enum loader_status
+   {
+      k_loader_status_loading,
+      k_loader_status_complete,
+      k_loader_status_fail
+   }
+   status;
+
+   /* Shutdown steps */
+   struct loader_free_step
+   {
+      void (*fn_free)(void *);
+      void *data;
+   }
+   *step_buffer;
+   u32 step_count, step_cap, step_action;
+}
+vg_loader;
+
+static int vg_loader_init(void)
+{
+   vg_semaphore_init( &vg_loader.sem_loading, 0 );
+   vg_mutex_init( &vg_loader.mux_status );
+   return 1;
+}
+
+static void vg_loader_free(void)
+{
+   vg_semaphore_wait( &vg_loader.sem_loading );
+   vg_semaphore_free( &vg_loader.sem_loading );
+   vg_mutex_free( &vg_loader.mux_status );
+
+   for( int i=0; i<vg_loader.step_count; i++ )
+   {
+      struct loader_free_step *step = 
+         &vg_loader.step_buffer[vg_loader.step_count -1 -i];
+
+      step->fn_free( step->data );
+   }
+
+   free( vg_loader.step_buffer );
+}
+
+static enum loader_status vg_loader_status(void)
+{
+   enum loader_status answer;
+
+   vg_mutex_lock( &vg_loader.mux_status );
+   answer = vg_loader.status;
+   vg_mutex_unlock( &vg_loader.mux_status );
+
+   return answer;
+}
+
+static float hue_to_rgb( float p, float q, float t )
+{
+   if(t < 0.0f) t += 1.0f;
+   if(t > 1.0f) t -= 1.0f;
+   if(t < 1.0f/6.0f) return p + (q - p) * 6.0f * t;
+   if(t < 1.0f/2.0f) return q;
+   if(t < 2.0f/3.0f) return p + (q - p) * (2.0f/3.0f - t) * 6.0f;
+   return p;
+}
+
+static void vg_loader_render(void)
+{
+   float h = vg_fractf(vg_time*0.1),
+         s = 0.2f,
+         l = 0.1f, //* (0.5f+vg_fractf(vg_time*40.0)*0.5f),
+         q = l < 0.5f ? l * (1.0f + s) : l + s - l * s,
+         p = 2.0f * l - q,
+         r = hue_to_rgb( p, q, h + 1.0f/3.0f ),
+         g = hue_to_rgb( p, q, h ),
+         b = hue_to_rgb( p, q, h - 1.0f/3.0f );
+
+   glClearColor( r, g, b, 1.0f );
+   glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
+
+
+
+   ui_begin( &ui_global_ctx, vg_window_x, vg_window_y );
+
+
+   int const fh = 14;
+   int lines_screen_max = ((vg_window_y/fh)-2),
+       lines_max_draw = VG_MIN( lines_screen_max, vg_list_size(vg_log.buffer) ),
+       lines_to_draw  = VG_MIN( lines_max_draw, vg_log.buffer_line_count );
+
+       int ptr = vg_log.buffer_line_current;
+       
+       ui_global_ctx.cursor[0] = 0;
+       ui_global_ctx.cursor[1] = lines_to_draw*fh;
+   ui_global_ctx.cursor[3] = fh;
+       ui_fill_x( &ui_global_ctx );
+
+   for( int i=0; i<lines_to_draw; i ++ )
+   {
+      ptr --;
+
+      if( ptr < 0 )
+         ptr = vg_list_size( vg_log.buffer )-1;
+      
+      ui_text( &ui_global_ctx, ui_global_ctx.cursor, 
+            vg_log.buffer[ptr], vg_console.scale, 0 );
+
+      ui_global_ctx.cursor[1] -= fh*vg_console.scale;
+   }
+
+   ui_resolve( &ui_global_ctx );
+   ui_draw( &ui_global_ctx, NULL );
+}
+
+
+static int vg_load_full(void);
+
+static void vg_loader_thread(void * nothing)
+{
+   /* Run client loader */
+   int res = vg_load_full();
+
+   /* Propogate status */
+   vg_mutex_lock( &vg_loader.mux_status );
+   if( res )
+   {
+      vg_loader.status = k_loader_status_complete;
+   }
+   else
+   {
+      vg_loader.status = k_loader_status_fail;
+   }
+   vg_mutex_unlock( &vg_loader.mux_status );
+
+   vg_semaphore_post( &vg_loader.sem_loading );
+}
+
+static void vg_loader_start(void)
+{
+   vg_thread_run( vg_loader_thread, NULL );
+}
+
+static void vg_free_libc_malloced( void *data )
+{
+   free( data );
+}
+
+static int vg_loader_push_free_step( struct loader_free_step step )
+{
+   void *buf = buffer_reserve( vg_loader.step_buffer, vg_loader.step_count, 
+                              &vg_loader.step_cap, 1, 
+                              sizeof( struct loader_free_step ) );
+
+   if( !buf )
+      return 0;
+
+   vg_loader.step_buffer = buf;
+   vg_loader.step_buffer[ vg_loader.step_count ++ ] = step;
+   return 1;
+}
+/*
+ * Schedule something to be freed
+ */
+__attribute__((warn_unused_result))
+static int vg_loader_highwater( void( *fn_free )(void *), void *data )
+{
+   if( fn_free )
+   {
+      struct loader_free_step step;
+      step.data = data;
+      step.fn_free = fn_free;
+
+      if( !vg_loader_push_free_step( step ) )
+         return 0;
+   }
+   else
+   {
+      if( data )
+      {
+         struct loader_free_step step;
+         step.data = data;
+         step.fn_free = vg_free_libc_malloced;
+
+         if( !vg_loader_push_free_step( step ) )
+            return 0;
+      }
+   }
+
+   vg_mutex_lock( &vg.mux_engine_status );
+
+   if( !vg.engine_running )
+   {
+      vg_mutex_unlock( &vg.mux_engine_status );
+      return 0;
+   }
+
+   vg_mutex_unlock( &vg.mux_engine_status );
+   return 1;
+}
+
+#endif /* VG_LOADER_H */
+
+#if 0
+#ifndef LOADER_H
+#define LOADER_H
+
+#include "common.h"
+
+static struct loader
+{
+   MUTEX_TYPE mux;
+
+   struct loader_step
+   {
+      int (*fn_load)(void);
+      void (*fn_free)(void);
+
+      int require_opengl;
+      const char *name;
+   }
+   *step_buffer;
+   u32 step_count, step_cap, step_action, 
+       low_water_mark; /* What is the minumum number of systems we can have? */
+
+   enum loader_status
+   {
+      k_loader_status_loading,
+      k_loader_status_complete,
+      k_loader_status_fail
+   }
+   status;
+
+   int cancel;
+}
+loader;
+
+static void loader_add_stage( struct loader_step step )
+{
+   loader.step_buffer = buffer_reserve( loader.step_buffer, loader.step_count, 
+                                        &loader.step_cap,
+                                        1, sizeof( struct loader_step ) );
+
+   loader.step_buffer[ loader.step_count ++ ] = step;
+}
+
+static void loader_insert_stage( struct loader_step step )
+{
+   
+}
+
+static void loader_cancel(void)
+{
+   MUTEX_LOCK( loader.mux );
+   loader.cancel = 1;
+   MUTEX_UNLOCK( loader.mux );
+}
+
+static void loader_worker_thread( void *nothing )
+{
+   while(1)
+   {
+      vg_sleep_ms( 1000.0 );
+      vg_info( "... loader ....\n" );
+
+      if( loader.cancel )
+      {
+         return;
+      }
+   }
+}
+
+static void loader_begin(void)
+{
+   if( loader.step_count == 0 )
+   {
+      loader.status = k_loader_status_complete;
+      return;
+   }
+
+   loader.status = k_loader_status_loading;
+   vg_thread_run( loader_worker_thread, NULL );
+}
+
+static void loader_free(void)
+{
+   /* TODO */
+   for( int i=0; i<loader.step_count; i++ )
+   {
+      struct loader_step *step = &loader.step_buffer[ loader.step_count -i -1 ];
+      if( step->fn_free )
+         step->fn_free();
+   }
+}
+
+/*
+ * Returns 0 if loading is not happening
+ * Returns 1 if we are loading something
+ */
+static int loader_update(void)
+{
+   MUTEX_LOCK( loader.mux );
+
+   if( loader.status == k_loader_status_complete )
+   {
+      MUTEX_UNLOCK( loader.mux );
+      return 0;
+   }
+   else
+   {
+      struct loader_step *cstep = &loader.step_buffer[ loader.step_action ];
+
+      if( cstep->require_opengl )
+      {
+         if( !cstep->fn_load() )
+         {
+            loader.cancel = 1;
+            MUTEX_UNLOCK( loader.mux );
+            
+            loader_free();
+            vg_exit();
+         }
+
+         loader.step_action ++;
+      }
+
+      MUTEX_UNLOCK( loader.mux );
+      return 1;
+   }
+}
+
+#endif /* LOADER_H */
+#endif
diff --git a/src/vg/vg_log.h b/src/vg/vg_log.h
new file mode 100644 (file)
index 0000000..54ee188
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef VG_LOG_H
+#define VG_LOG_H
+
+#include <stdarg.h>
+#include <string.h>
+#include <malloc.h>
+#include "vg/vg_stdint.h"
+#include "vg/vg_platform.h"
+
+#define KNRM  "\x1B[0m"
+#define KRED  "\x1B[31m"
+#define KGRN  "\x1B[32m"
+#define KYEL  "\x1B[33m"
+#define KBLU  "\x1B[34m"
+#define KMAG  "\x1B[35m"
+#define KCYN  "\x1B[36m"
+#define KWHT  "\x1B[37m"
+
+vg_mutex log_print_mutex;
+
+struct vg_log
+{
+   char buffer[64][96];
+   u32  buffer_line_count, buffer_line_current;
+}
+static vg_log;
+
+void vg_console_append_to_log( const char *str )
+{
+   if( vg_log.buffer_line_count < vg_list_size( vg_log.buffer ) )
+      vg_log.buffer_line_count ++;
+
+   char *dest = vg_log.buffer[ vg_log.buffer_line_current ++ ];
+
+   for( int i=0; i<vg_list_size( vg_log.buffer[0] ); i++ )
+   {
+      if( !str[i] || ( i == vg_list_size(vg_log.buffer[0])-1 ) )
+      {
+         dest[i] = '\0';
+         break;
+      }
+
+      dest[i] = str[i];
+   }
+   
+   if( vg_log.buffer_line_current >= vg_list_size( vg_log.buffer ) )
+      vg_log.buffer_line_current = 0;
+}
+
+static void vg_log_write( FILE *file, const char *prefix, 
+      const char *fmt, va_list args )
+{
+   vg_mutex_lock( &log_print_mutex );
+
+       char buffer[512];
+       int i, j;
+       
+       for( i = 0; i < vg_list_size( buffer ); i ++ )
+       {
+               if( prefix[i] )
+                       buffer[i] = prefix[i];
+               else 
+                       break;
+       }
+       
+       j = i + vsnprintf( buffer + i, vg_list_size( buffer ) - i -12, fmt, args );
+       strcpy( buffer + j, KNRM );
+       
+       fputs( buffer, file );
+   
+   vg_console_append_to_log( buffer );
+   vg_mutex_unlock( &log_print_mutex );
+}
+
+static void vg_log_init(void)
+{
+   vg_mutex_init( &log_print_mutex );
+}
+
+#define VG_LOGX( NAME, PIPE, PFX )           \
+static void NAME(const char *fmt, ...)       \
+{                                            \
+   va_list args;                             \
+   va_start( args, fmt );                    \
+   vg_log_write( PIPE, (PFX), fmt, args );   \
+   va_end( args );                           \
+}
+
+VG_LOGX( vg_success, stdout, (KGRN "success" KWHT "| " KGRN) )
+VG_LOGX( vg_info,    stdout, (KNRM "   info" KWHT "| " KNRM) )
+VG_LOGX( vg_warn,    stdout, (KYEL "   warn" KWHT "| " KYEL) )
+VG_LOGX( vg_error,   stderr, (KRED "  error" KWHT "| " KRED) )
+VG_LOGX( vg_low,     stdout, (KWHT "    log" KWHT "| " KWHT) )
+
+#endif /* VG_LOG_H */
index 5bbb46733bbb0482d532d36d885d35ad4effecad..417ca32607d392bd48bf8b8785b8e1a1b70a42d9 100644 (file)
@@ -45,9 +45,6 @@ static float stable_force( float current, float diff )
    return fnew;
 }
 
-#define VG_MIN( A, B ) ((A)<(B)?(A):(B))
-#define VG_MAX( A, B ) ((A)>(B)?(A):(B))
-
 static inline int vg_min( int a, int b )
 {
        return a < b? a: b;
index be9a225bf8fc56f97d3481f7f1a5bf5f39ff529a..d7c0a0cff6ee13d0a08ff5ef1e09bcbef45ba449 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef VG_PLATFORM_H
 #define VG_PLATFORM_H
 
+#include "vg.h"
+
 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
 
 typedef unsigned int uint;
@@ -29,36 +31,74 @@ struct vg_achievement
 #define vg_static_assert _Static_assert
 #define vg_list_size( A ) (sizeof(A)/sizeof(A[0]))
 
-/* Pthred emulation for windows */
 #ifdef _WIN32
        #include <windows.h>
-       #define MUTEX_TYPE             HANDLE
-       #define MUTEX_INITIALIZER      NULL
-       #define MUTEX_SETUP(x)         (x) = CreateMutex(NULL, FALSE, NULL)
-
-   /* TODO: Why is this defined but never used? */
-       #define MUTEX_CLEANUP(x)       (CloseHandle(x)) 
-       #define MUTEX_LOCK(x)          emulate_pthread_mutex_lock(&(x))
-       #define MUTEX_UNLOCK(x)        (ReleaseMutex(x))
-
-       int emulate_pthread_mutex_lock( volatile MUTEX_TYPE *mx )
-       { 
-               if( *mx == NULL ) /* static initializer? */
-               { 
-                       HANDLE p = CreateMutex( NULL, FALSE, NULL );
-                       if( InterlockedCompareExchangePointer( (PVOID*)mx, (PVOID)p, NULL ) 
-               != NULL )
-                               CloseHandle(p);
-               }
-               
-               return WaitForSingleObject( *mx, INFINITE ) == WAIT_FAILED;
-       }
+
+/* TODO */
+
 #else
        #include <pthread.h>
-       #define MUTEX_LOCK(x)                   pthread_mutex_lock(&(x))
-       #define MUTEX_UNLOCK(x)                 pthread_mutex_unlock(&(x))
-       #define MUTEX_TYPE                              pthread_mutex_t
-       #define MUTEX_INITIALIZER               {0}
+   #include <semaphore.h>
+
+   typedef sem_t                 vg_semaphore;
+   typedef pthread_mutex_t       vg_mutex;
+
+static int vg_semaphore_init( vg_semaphore *sem, u32 value )
+{
+   if( !sem_init( sem, 0, value ) )
+      return 1;
+   else
+      return 0;
+}
+
+static int vg_semaphore_wait( vg_semaphore *sem )
+{
+   if( !sem_wait( sem ) )
+      return 1;
+   else
+      return 0;
+}
+
+static int vg_semaphore_post( vg_semaphore *sem )
+{
+   if( !sem_post( sem ) )
+      return 1;
+   else
+      return 0;
+}
+
+static void vg_semaphore_free( vg_semaphore *sem )
+{
+   sem_destroy( sem );
+}
+
+static int vg_mutex_init( vg_mutex *mutex )
+{
+   memset( mutex, 0, sizeof(vg_mutex) );
+   return 1;
+}
+
+static int vg_mutex_lock( vg_mutex *mutex )
+{
+   if( !pthread_mutex_lock( mutex ) )
+      return 1;
+   else
+      return 0;
+}
+
+static int vg_mutex_unlock( vg_mutex *mutex )
+{
+   if( !pthread_mutex_unlock( mutex ) )
+      return 1;
+   else
+      return 0;
+}
+
+static void vg_mutex_free( vg_mutex *mutex )
+{
+
+}
+
 #endif
 
 
@@ -120,4 +160,26 @@ static double vg_time_diff( struct timespec start, struct timespec end )
    return elapsed;
 }
 
+#define VG_MIN( A, B ) ((A)<(B)?(A):(B))
+#define VG_MAX( A, B ) ((A)>(B)?(A):(B))
+
+static void *buffer_reserve( void *buffer, u32 count, u32 *cap, u32 amount, 
+      size_t emsize )
+{
+   if( count+amount > *cap )
+   {
+      *cap = VG_MAX( (*cap)*2, (*cap)+amount );
+      
+      return realloc( buffer, (*cap) * emsize );
+   }
+
+   return buffer;
+}
+
+static void *buffer_fix( void *buffer, u32 count, u32 *cap, size_t emsize )
+{
+   *cap = count;
+   return realloc( buffer, (*cap) * emsize );
+}
+
 #endif
index 3cad2356b1d35077027c1d983f87bda576ae5b4c..a6c7a0b13ff4723e86927dd3d15e04513225fb4c 100644 (file)
@@ -3,6 +3,9 @@
 #ifndef VG_SHADER_H
 #define VG_SHADER_H
 
+#include "vg/vg.h"
+#include "vg/vg_platform.h"
+
 #define STB_INCLUDE_IMPLEMENTATION
 #define STB_INCLUDE_LINE_GLSL
 #include "stb/stb_include.h"
@@ -151,7 +154,16 @@ static int vg_shader_compile( struct vg_shader *shader )
        return 1;
 }
 
-static void vg_shaders_free(void)
+static void vg_free_shader( struct vg_shader *shader )
+{
+   if( shader->compiled )
+   {
+      glDeleteProgram( shader->id );
+      shader->compiled = 0;
+   }
+}
+
+static void vg_shaders_free(void *nothing)
 {
        for( int i = 0; i < arrlen( vg_shaders_active ); i ++ )
        {
@@ -164,16 +176,19 @@ static void vg_shaders_free(void)
        arrfree( vg_shaders_active );
 }
 
-static int vg_shaders_recompile(int argc, const char *argv[])
+static int vg_shaders_recompile(void)
 {
        vg_info( "Compiling shaders\n" );
-       for( int i = 0; i < arrlen( vg_shaders_active ); i ++ )
+
+       for( int i=0; i<arrlen( vg_shaders_active ); i ++ )
        {
                struct vg_shader *shader = vg_shaders_active[i];
-               vg_shader_compile( shader );
+
+               if( !vg_shader_compile( shader ) )
+         return 0;
        }
 
-   return 0;
+   return 1;
 }
 
 static void vg_shader_register( struct vg_shader *shader )
index d1e88be3af60ec5f35c7db01a836cf53c78d6a40..05fe1acaeea72e69968d8b7261e6fa48cb4e6d1b 100644 (file)
@@ -529,7 +529,7 @@ static void steamworks_event_loop( HSteamPipe pipe )
        
        while( SteamAPI_ManualDispatch_GetNextCallback( pipe, &callback ) )
        {
-               vg_log( "steamworks_event::callback( %i )\n", callback.m_iCallback );
+               vg_low( "steamworks_event::callback( %i )\n", callback.m_iCallback );
        
                /* Check for dispatching API call results */
                if( callback.m_iCallback == k_iSteamAPICallCompleted )
diff --git a/src/vg/vg_steam_utils.h b/src/vg/vg_steam_utils.h
new file mode 100644 (file)
index 0000000..282fe74
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef VG_STEAM_UTILS_H
+#define VG_STEAM_UTILS_H
+
+#include "vg_steam.h"
+
+#if defined( VALVE_CALLBACK_PACK_SMALL )
+ #pragma pack( push, 4 )
+#elif defined( VALVE_CALLBACK_PACK_LARGE )
+ #pragma pack( push, 8 )
+#endif 
+
+/* ... */
+
+#pragma pack(pop)
+
+typedef void ISteamUtils;
+ISteamUtils *SteamAPI_SteamUtils_v010(void);
+ISteamUtils *SteamAPI_SteamUtils(void)
+{
+   return SteamAPI_SteamUtils_v010();
+}
+
+int SteamAPI_ISteamUtils_SetWarningMessageHook( 
+      ISteamUtils *self, void( *fn_print )(int, const char *) );
+
+
+#endif /* VG_STEAM_UTILS_H */
index 00a8540568996fb2933f2d21e6f114a20832bd9c..ee0e91acf04d3e94579b1f62c7dfafda8d322a9d 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "vg_stdint.h"
 #include "vg_io.h"
+#include "vg_log.h"
 
 /* 
  * Anderson tree implementation with extensions:
@@ -562,10 +563,10 @@ static void aatree_show_r( aatree *tree, aatree_ptr t, int lvl,
 
       for( int i=0; i<lvl; i++ )
       {
-         vg_log( "  " );
+         vg_info( "  " );
       }
       p_show( data );
-      vg_log( " (%d) \n", t );
+      vg_info( " (%d) \n", t );
 
       aatree_show_r( tree, ptnode->right, lvl+1, p_show );
    }
@@ -581,10 +582,10 @@ static void aatree_show( aatree *tree, aatree_ptr t, void(*p_show)(void *data))
 
       for( int i=0; i<ptnode->level; i++ )
       {
-         vg_log( "  " );
+         vg_info( "  " );
       }
       p_show( data );
-      vg_log( " (%d) \n", t );
+      vg_info( " (%d) \n", t );
 
       aatree_show( tree, ptnode->right, p_show );
    }
@@ -603,7 +604,7 @@ static void aatree_show_counts( aatree *tree, aatree_ptr t, int lvl, int *ln,
 
    aatree_show_counts( tree, ptnode->left, lvl+1, ln, err, p_show, show );
 
-   if( show ) vg_log( "%03d| ", *ln );
+   if( show ) vg_info( "%03d| ", *ln );
    *ln = *ln +1;
 
    if( show ) 
@@ -629,7 +630,7 @@ static void aatree_show_counts( aatree *tree, aatree_ptr t, int lvl, int *ln,
    if( !aatree_verify( tree, t ) )
    {
       if( show )
-         vg_log( "error\n" );
+         vg_info( "error\n" );
       *err = 1;
    }
 
index 71c79c0105a422ff0233d601e7c69d143f08f493..9e0c56f6e18bebb8551edaa7add1ab586759dd3b 100644 (file)
@@ -2,10 +2,14 @@
 #ifndef VG_TEX_H
 #define VG_TEX_H
 
+#include "vg/vg.h"
+#include "vg/vg_log.h"
+
 #define VG_TEXTURE_NO_MIP      0x1
 #define VG_TEXTURE_REPEAT      0x2
 #define VG_TEXTURE_CLAMP       0x4
 #define VG_TEXTURE_NEAREST     0x8
+#define VG_TEXTURE_ALLOCATED_INTERNAL 0x10
 
 struct vg_tex2d
 {
@@ -21,6 +25,12 @@ struct vg_sprite
 
 static void vg_tex2d_bind( vg_tex2d *tex, u32 id )
 {
+   if( !(tex->flags & VG_TEXTURE_ALLOCATED_INTERNAL) )
+   {
+      vg_error( "Tried to use '%s' while unloaded!\n", tex->path );
+      return;
+   }
+
        glActiveTexture( GL_TEXTURE0 + id );
        glBindTexture( GL_TEXTURE_2D, tex->name );
 }
@@ -101,7 +111,7 @@ static GLuint vg_tex2d_rgba( const char *path )
 
 static void vg_tex2d_init( vg_tex2d *textures[], int num )
 {
-       for( int i = 0; i < num; i ++ )
+       for( int i=0; i<num; i ++ )
        {
                vg_tex2d *tex = textures[i];
                tex->name = vg_tex2d_rgba( tex->path );
@@ -127,6 +137,8 @@ static void vg_tex2d_init( vg_tex2d *textures[], int num )
                        vg_tex2d_clamp();
                else
                        vg_tex2d_repeat();
+
+      tex->flags |= VG_TEXTURE_ALLOCATED_INTERNAL;
        }
 }
 
index fa4d9cd1e149fde4880041cbd01b9ea3789d5873..844194e73867e9e468c70d9b9fcb732d983e9e2d 100644 (file)
@@ -1,5 +1,9 @@
 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
 
+/*
+ * TODO: Get rid of many context design
+ */
+
 #ifndef VG_UI_H
 #define VG_UI_H
 
@@ -171,62 +175,6 @@ static ui_colourset ui_default_colours = {
 
 static ui_ctx ui_global_ctx;
 
-static void ui_init_context( ui_ctx *ctx, int index_buffer_size )
-{
-       u32 vertex_buffer_size = (index_buffer_size+(index_buffer_size/2));
-       
-       /* Generate the buffer we are gonna be drawing to */
-       {
-               glGenVertexArrays(1, &ctx->vao);
-               glGenBuffers( 1, &ctx->vbo );
-               glGenBuffers( 1, &ctx->ebo );
-               glBindVertexArray( ctx->vao );
-               
-               glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo );
-               
-               glBufferData( GL_ARRAY_BUFFER, 
-            vertex_buffer_size * sizeof( struct ui_vert ), 
-            NULL, GL_DYNAMIC_DRAW );
-               glBindVertexArray( ctx->vao );
-               
-               glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo );
-               glBufferData( GL_ELEMENT_ARRAY_BUFFER, 
-            index_buffer_size * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
-               
-               u32 const stride = sizeof( struct ui_vert );
-               
-               /* XY */
-               glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, 
-            stride, (void *)offsetof( struct ui_vert, co ) );
-               glEnableVertexAttribArray( 0 );
-               
-               /* UV */
-               glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, 
-            stride, (void *)offsetof( struct ui_vert, uv ) );
-               glEnableVertexAttribArray( 1 );
-               
-               /* COLOUR */
-               glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, 
-            (void *)offsetof( struct ui_vert, colour ) );
-               glEnableVertexAttribArray( 2 );
-               
-               /* CLIPPING */
-               glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride, 
-            (void *)offsetof( struct ui_vert, clip ) );
-               glEnableVertexAttribArray( 3 );
-       }
-       
-       /* Initialize default context */
-       {
-               ctx->verts = (struct ui_vert *)malloc( 
-            vertex_buffer_size * sizeof(struct ui_vert) );
-               ctx->indices = (u16*)malloc( index_buffer_size * sizeof(u16) );
-
-      if( !ctx->colours )
-         ctx->colours = &ui_default_colours;
-       }
-}
-
 static void ui_context_free( ui_ctx *ctx )
 {
        glDeleteVertexArrays( 1, &ctx->vao );
@@ -237,7 +185,88 @@ static void ui_context_free( ui_ctx *ctx )
        free( ctx->indices );
 }
 
-static void ui_default_init(void)
+static int ui_init_context( ui_ctx *ctx, int index_buffer_size )
+{
+       u32 vertex_buffer_size = (index_buffer_size+(index_buffer_size/2));
+       
+       /* Generate the buffer we are gonna be drawing to */
+   glGenVertexArrays( 1, &ctx->vao );
+   glGenBuffers( 1, &ctx->vbo );
+   glGenBuffers( 1, &ctx->ebo );
+
+   glBindVertexArray( ctx->vao );
+   glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo );
+   
+   glBufferData( GL_ARRAY_BUFFER, 
+         vertex_buffer_size * sizeof( struct ui_vert ), 
+         NULL, GL_DYNAMIC_DRAW );
+   glBindVertexArray( ctx->vao );
+   
+   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo );
+   glBufferData( GL_ELEMENT_ARRAY_BUFFER, 
+         index_buffer_size * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW );
+
+   if( VG_CHECK_GL_ERR() )
+      goto il_fail_alloc_gpu;
+
+   /* Set pointers */
+   u32 const stride = sizeof( struct ui_vert );
+   
+   /* XY */
+   glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, 
+         stride, (void *)offsetof( struct ui_vert, co ) );
+   glEnableVertexAttribArray( 0 );
+   
+   /* UV */
+   glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, 
+         stride, (void *)offsetof( struct ui_vert, uv ) );
+   glEnableVertexAttribArray( 1 );
+   
+   /* COLOUR */
+   glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, 
+         (void *)offsetof( struct ui_vert, colour ) );
+   glEnableVertexAttribArray( 2 );
+   
+   /* CLIPPING */
+   glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride, 
+         (void *)offsetof( struct ui_vert, clip ) );
+   glEnableVertexAttribArray( 3 );
+   
+   if( VG_CHECK_GL_ERR() )
+      goto il_fail_attributes;
+       
+
+       /* Alloc RAM default context */
+   ctx->verts = (struct ui_vert *)malloc( 
+         vertex_buffer_size * sizeof(struct ui_vert) );
+   if( !ctx->verts )
+      goto il_fail_alloc_verts;
+
+   ctx->indices = (u16*)malloc( index_buffer_size * sizeof(u16) );
+   if( !ctx->indices )
+      goto il_fail_alloc_indices;
+
+   if( !ctx->colours )
+      ctx->colours = &ui_default_colours;
+
+   return 1;
+
+                           free( ctx->indices );
+                           ctx->indices = NULL;
+il_fail_alloc_indices:
+                           free( ctx->verts );
+                           ctx->verts = NULL;
+il_fail_alloc_verts:
+il_fail_attributes:
+il_fail_alloc_gpu:
+                           glDeleteBuffers( 1, &ctx->ebo );
+                           glDeleteBuffers( 1, &ctx->vbo );
+                           glDeleteVertexArrays( 1, &ctx->vao );
+                           VG_CHECK_GL_ERR();
+                           return 0;
+}
+
+static int ui_default_init(void)
 {
        /* Load default font */
    u32 compressed[] = {
@@ -246,6 +275,7 @@ static void ui_default_init(void)
 
    u32 pixels = 0, total = 256*256, data = 0;
    u8 *image = malloc( total );
+   if( !image ) goto il_fail_alloc_image;
    
    while( pixels < total )
    {
@@ -263,22 +293,39 @@ static void ui_default_init(void)
    }
    
    glGenTextures( 1, &ui_glyph_texture );
+   if( !ui_glyph_texture ) goto il_fail_gen_texture;
+
    glBindTexture( GL_TEXTURE_2D, ui_glyph_texture );
-   
    glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, 256, 256, 0, 
          GL_RED, GL_UNSIGNED_BYTE, image );
+
+   if( VG_CHECK_GL_ERR() ) goto il_fail_alloc_gpu;
    
    vg_tex2d_clamp();
    vg_tex2d_nearest();
    
    free( image );
        
-   vg_shader_register( &_shader_ui );
-       ui_init_context( &ui_global_ctx, 20000 );
+       if( !ui_init_context( &ui_global_ctx, 20000 ) ) goto il_generic_fail;
+   if( !vg_shader_compile( &_shader_ui ) ) goto il_shader_fail;
+
+   return 1;
+
+il_shader_fail:
+il_fail_alloc_gpu:
+il_generic_fail:
+   glDeleteTextures( 1, &ui_glyph_texture );
+
+il_fail_gen_texture:
+   free( image );
+
+il_fail_alloc_image:
+   return 0;
 }
 
 static void ui_default_free(void)
 {
+   vg_free_shader( &_shader_ui );
    glDeleteTextures( 1, &ui_glyph_texture );
        ui_context_free( &ui_global_ctx );
 }
@@ -378,7 +425,10 @@ static struct ui_qnode *ui_current( ui_ctx *ctx )
 static void ui_new_node( ui_ctx *ctx )
 {
        if( ctx->stack_count == vg_list_size( ctx->stack ) )
-               vg_exiterr( "[UI] Stack overflow while creating box!" );
+   {
+      vg_error( "[UI] Stack overflow while creating box!" );
+      return;
+   }
        
        struct ui_qnode *parent = &ctx->stack[ ctx->stack_count-1 ];
        struct ui_qnode *node = &ctx->stack[ ctx->stack_count++ ];
@@ -690,7 +740,10 @@ static void ui_begin( ui_ctx *ctx, ui_px res_x, ui_px res_y )
 static void ui_resolve( ui_ctx *ctx )
 {
        if( ctx->stack_count-1 )
-               vg_exiterr( "[UI] Mismatched node create/drestroy!" );
+   {
+      vg_error( "[UI] Mismatched node create/drestroy!" );
+      return;
+   }
        
        if( ctx->click_state == 3 || ctx->click_state == 0 )
        {