Browse Source

Merge pull request #1755 from bkrypt/update_miniaudio

Update `vendor/miniaudio` to v0.11.9
gingerBill 3 years ago
parent
commit
836c325021

+ 27 - 82
vendor/miniaudio/common.odin

@@ -13,13 +13,15 @@ when ODIN_OS == .Windows {
 handle :: distinct rawptr
 
 
-/* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
-SIMD_ALIGNMENT :: 64
-
-LOG_LEVEL_DEBUG   :: 4
-LOG_LEVEL_INFO    :: 3
-LOG_LEVEL_WARNING :: 2
-LOG_LEVEL_ERROR   :: 1
+/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */
+SIMD_ALIGNMENT :: 32
+
+log_level :: enum c.int {
+	LOG_LEVEL_DEBUG   = 4,
+	LOG_LEVEL_INFO    = 3,
+	LOG_LEVEL_WARNING = 2,
+	LOG_LEVEL_ERROR   = 1,	
+}
 
 
 channel :: enum u8 {
@@ -158,13 +160,13 @@ result :: enum c.int {
 	FAILED_TO_STOP_BACKEND_DEVICE               = -303,
 } 
 
+
 MIN_CHANNELS :: 1
-MAX_CHANNELS :: 32
+MAX_CHANNELS :: 254
 
 MAX_FILTER_ORDER :: 8
 
 
-
 stream_format :: enum c.int {
 	pcm = 0,
 }
@@ -175,9 +177,9 @@ stream_layout :: enum c.int {
 }
 
 dither_mode :: enum c.int {
-    	none = 0,
-    	rectangle,
-    	triangle,
+	none = 0,
+	rectangle,
+	triangle,
 }
 
 format :: enum c.int {
@@ -224,7 +226,6 @@ channel_mix_mode :: enum c.int {
 	rectangular = 0,   /* Simple averaging based on the plane(s) the channel is sitting on. */
 	simple,            /* Drop excess channels; zeroed out extra channels. */
 	custom_weights,    /* Use custom weights specified in ma_channel_router_config. */
-	planar_blend = rectangular,
 	default = rectangular,
 }
 
@@ -257,6 +258,10 @@ lcg :: struct {
 	state: i32,
 }
 
+
+/* Spinlocks are 32-bit for compatibility reasons. */
+spinlock :: distinct u32
+
 NO_THREADING :: false
 
 when !NO_THREADING {
@@ -272,8 +277,6 @@ thread_priority :: enum c.int {
 	default  =  0,
 }
 
-/* Spinlocks are 32-bit for compatibility reasons. */
-spinlock :: distinct u32
 
 when ODIN_OS == .Windows {
 	thread    :: distinct rawptr
@@ -297,69 +300,6 @@ when ODIN_OS == .Windows {
 	}
 }
 
-	
-@(default_calling_convention="c", link_prefix="ma_")
-foreign lib {
-	/*
-	Locks a spinlock.
-	*/
-	spinlock_lock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result ---
-
-	/*
-	Locks a spinlock, but does not yield() when looping.
-	*/
-	spinlock_lock_noyield :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result ---
-
-	/*
-	Unlocks a spinlock.
-	*/
-	spinlock_unlock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result ---
-
-
-	/*
-	Creates a mutex.
-
-	A mutex must be created from a valid context. A mutex is initially unlocked.
-	*/
-	mutex_init :: proc(pMutex: ^mutex) -> result ---
-
-	/*
-	Deletes a mutex.
-	*/
-	mutex_uninit :: proc(pMutex: ^mutex) ---
-
-	/*
-	Locks a mutex with an infinite timeout.
-	*/
-	mutex_lock :: proc(pMutex: ^mutex) ---
-
-	/*
-	Unlocks a mutex.
-	*/
-	mutex_unlock :: proc(pMutex: ^mutex) ---
-
-
-	/*
-	Initializes an auto-reset event.
-	*/
-	event_init :: proc(pEvent: ^event) -> result ---
-
-	/*
-	Uninitializes an auto-reset event.
-	*/
-	event_uninit :: proc(pEvent: ^event) ---
-
-	/*
-	Waits for the specified auto-reset event to become signalled.
-	*/
-	event_wait :: proc(pEvent: ^event) -> result ---
-
-	/*
-	Signals the specified auto-reset event.
-	*/
-	event_signal :: proc(pEvent: ^event) -> result ---
-}
-
 }  /* NO_THREADING */
 
 
@@ -385,17 +325,22 @@ foreign lib {
 	result_description :: proc(result: result) -> cstring ---
 
 	/*
-	malloc(). Calls MA_MALLOC().
+	malloc()
 	*/
 	malloc :: proc(sz: c.size_t, pAllocationCallbacks: ^allocation_callbacks) -> rawptr ---
 
 	/*
-	realloc(). Calls MA_REALLOC().
+	calloc()
+	*/
+	calloc :: proc(sz: c.size_t, pAllocationCallbacks: ^allocation_callbacks) -> rawptr ---
+
+	/*
+	realloc()
 	*/
 	realloc :: proc(p: rawptr, sz: c.size_t, pAllocationCallbacks: ^allocation_callbacks) -> rawptr ---
 
 	/*
-	free(). Calls MA_FREE().
+	free()
 	*/
 	free :: proc(p: rawptr, pAllocationCallbacks: ^allocation_callbacks) ---
 
@@ -417,7 +362,7 @@ foreign lib {
 	/*
 	Blends two frames in floating point format.
 	*/
-	blend_f32 :: proc(pOut, pInA, pInB: ^f32, factor: f32, channels: u32) ---
+	blend_f32 :: proc(pOut, pInA, pInB: [^]f32, factor: f32, channels: u32) ---
 
 	/*
 	Retrieves the size of a sample in bytes for the given format.

+ 173 - 106
vendor/miniaudio/data_conversion.odin

@@ -36,77 +36,106 @@ linear_resampler_config :: struct {
 }
 
 linear_resampler :: struct {
-	config: linear_resampler_config,
+	config:        linear_resampler_config,
 	inAdvanceInt:  u32,
 	inAdvanceFrac: u32,
 	inTimeInt:     u32,
 	inTimeFrac:    u32,
 	x0: struct #raw_union {
-		f32: [MAX_CHANNELS]f32,
-		s16: [MAX_CHANNELS]i16,
+		f32: [^]f32,
+		s16: [^]i16,
 	}, /* The previous input frame. */
 	x1: struct #raw_union {
-		f32: [MAX_CHANNELS]f32,
-		s16: [MAX_CHANNELS]i16,
+		f32: [^]f32,
+		s16: [^]i16,
 	}, /* The next input frame. */
 	lpf: lpf,
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
+}
+
+resampling_backend :: struct {}
+resampling_backend_vtable :: struct {
+	onGetHeapSize:                 proc "c" (pUserData: rawptr, pConfig: ^resampler_config, pHeapSizeInBytes: ^c.size_t) -> result,
+	onInit:                        proc "c" (pUserData: rawptr, pConfig: ^resampler_config, pHeap: rawptr, ppBackend: ^^resampling_backend) -> result,
+	onUninit:                      proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, pAllocationCallbacks: ^allocation_callbacks),
+	onProcess:                     proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, pFramesIn: rawptr, pFrameCountIn: ^u64, pFramesOut: rawptr, pFrameCountOut: ^u64) -> result,
+	onSetRate:                     proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, sampleRateIn: u32, sampleRateOut: u32) -> result,           /* Optional. Rate changes will be disabled. */
+	onGetInputLatency:             proc "c" (pUserData: rawptr, pBackend: ^resampling_backend) -> u64,                                                     /* Optional. Latency will be reported as 0. */
+	onGetOutputLatency:            proc "c" (pUserData: rawptr, pBackend: ^resampling_backend) -> u64,                                                     /* Optional. Latency will be reported as 0. */
+	onGetRequiredInputFrameCount:  proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, outputFrameCount: u64, pInputFrameCount: ^u64) -> result,   /* Optional. Latency mitigation will be disabled. */
+	onGetExpectedOutputFrameCount: proc "c" (pUserData: rawptr, pBackend: ^resampling_backend, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result,   /* Optional. Latency mitigation will be disabled. */
+	onReset:                       proc "c" (pUserData: rawptr, pBackend: ^resampling_backend) -> result,
 }
 
 resample_algorithm :: enum {
 	linear = 0,   /* Fastest, lowest quality. Optional low-pass filtering. Default. */
-	speex,
+	custom,
 }
 
 resampler_config :: struct {
-	format:        format, /* Must be either ma_format_f32 or ma_format_s16. */
-	channels:      u32,
-	sampleRateIn:  u32,
-	sampleRateOut: u32,
-	algorithm:     resample_algorithm,
+	format:           format, /* Must be either ma_format_f32 or ma_format_s16. */
+	channels:         u32,
+	sampleRateIn:     u32,
+	sampleRateOut:    u32,
+	algorithm:        resample_algorithm, /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */
+	pBackendVTable:   ^resampling_backend_vtable,
+	pBackendUserData: rawptr,
 	linear: struct {
 		lpfOrder:         u32,
-		lpfNyquistFactor: f64,
-	},
-	speex: struct {
-		quality: c.int, /* 0 to 10. Defaults to 3. */
 	},
 }
 
 resampler :: struct {
-	config: resampler_config,
+	pBackend:         ^resampling_backend,
+	pBackendVTable:   ^resampling_backend_vtable,
+	pBackendUserData: rawptr,
+	format:           format,
+	channels:         u32,
+	sampleRateIn:     u32,
+	sampleRateOut:    u32,
 	state: struct #raw_union {
 		linear: linear_resampler,
-		speex: struct {
-			pSpeexResamplerState: rawptr, /* SpeexResamplerState* */
-		},
-	},
+	},    /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
 }
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
 	linear_resampler_config_init :: proc(format: format, channels: u32, sampleRateIn, sampleRateOut: u32) -> linear_resampler_config ---
 
-	linear_resampler_init                            :: proc(pConfig: ^linear_resampler_config, pResampler: ^linear_resampler) -> result ---
-	linear_resampler_uninit                          :: proc(pResampler: ^linear_resampler) ---
+	linear_resampler_get_heap_size                   :: proc(pConfig: ^linear_resampler_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	linear_resampler_init_preallocated               :: proc(pConfig: ^linear_resampler_config, pHeap: rawptr, pResampler: ^linear_resampler) -> result ---
+	linear_resampler_init                            :: proc(pConfig: ^linear_resampler_config, pAllocationCallbacks: ^allocation_callbacks, pResampler: ^linear_resampler) -> result ---
+	linear_resampler_uninit                          :: proc(pResampler: ^linear_resampler, pAllocationCallbacks: ^allocation_callbacks) ---
 	linear_resampler_process_pcm_frames              :: proc(pResampler: ^linear_resampler, pFramesIn: rawptr, pFrameCountIn: ^u64, pFramesOut: rawptr, pFrameCountOut: ^u64) -> result ---
 	linear_resampler_set_rate                        :: proc(pResampler: ^linear_resampler, sampleRateIn, sampleRateOut: u32) -> result ---
 	linear_resampler_set_rate_ratio                  :: proc(pResampler: ^linear_resampler, ratioInOut: f32) -> result ---
-	linear_resampler_get_required_input_frame_count  :: proc(pResampler: ^linear_resampler, outputFrameCount: u64) -> u64 ---
-	linear_resampler_get_expected_output_frame_count :: proc(pResampler: ^linear_resampler, inputFrameCount:  u64) -> u64 ---
 	linear_resampler_get_input_latency               :: proc(pResampler: ^linear_resampler) -> u64 ---
 	linear_resampler_get_output_latency              :: proc(pResampler: ^linear_resampler) -> u64 ---
+	linear_resampler_get_required_input_frame_count  :: proc(pResampler: ^linear_resampler, outputFrameCount: u64, pInputFrameCount: ^u64) -> result ---
+	linear_resampler_get_expected_output_frame_count :: proc(pResampler: ^linear_resampler, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result ---
+	linear_resampler_reset                           :: proc(pResampler: ^linear_resampler) -> result ---
 
 	resampler_config_init :: proc(format: format, channels: u32, sampleRateIn, sampleRateOut: u32, algorithm: resample_algorithm) -> resampler_config ---
 
+	resampler_get_heap_size     :: proc(pConfig: ^resampler_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	resampler_init_preallocated :: proc(pConfig: ^resampler_config, pHeap: rawptr, pResampler: ^resampler) -> result ---
+
 	/*
 	Initializes a new resampler object from a config.
 	*/
-	resampler_init :: proc(pConfig: ^resampler_config, pResampler: ^resampler) -> result ---
+	resampler_init :: proc(pConfig: ^resampler_config, pAllocationCallbacks: ^allocation_callbacks, pResampler: ^resampler) -> result ---
 
 	/*
 	Uninitializes a resampler.
 	*/
-	resampler_uninit :: proc(pResampler: ^resampler) ---
+	resampler_uninit :: proc(pResampler: ^resampler, pAllocationCallbacks: ^allocation_callbacks) ---
 
 	/*
 	Converts the given input data.
@@ -145,6 +174,15 @@ foreign lib {
 	*/
 	resampler_set_rate_ratio :: proc(pResampler: ^resampler, ratio: f32) -> result ---
 
+	/*
+	Retrieves the latency introduced by the resampler in input frames.
+	*/
+	resampler_get_input_latency :: proc(pResampler: ^resampler) -> u64 ---
+
+	/*
+	Retrieves the latency introduced by the resampler in output frames.
+	*/
+	resampler_get_output_latency :: proc(pResampler: ^resampler) -> u64 ---
 
 	/*
 	Calculates the number of whole input frames that would need to be read from the client in order to output the specified
@@ -153,24 +191,18 @@ foreign lib {
 	The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
 	read from the input buffer in order to output the specified number of output frames.
 	*/
-	resampler_get_required_input_frame_count :: proc(pResampler: ^resampler, outputFrameCount: u64) -> u64 ---
+	resampler_get_required_input_frame_count :: proc(pResampler: ^resampler, outputFrameCount: u64, pInputFrameCount: ^u64) -> result ---
 
 	/*
 	Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
 	input frames.
 	*/
-	resampler_get_expected_output_frame_count :: proc(pResampler: ^resampler, inputFrameCount: u64) -> u64 ---
-
+	resampler_get_expected_output_frame_count :: proc(pResampler: ^resampler, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result ---
 
 	/*
-	Retrieves the latency introduced by the resampler in input frames.
+	Resets the resampler's timer and clears it's internal cache.
 	*/
-	resampler_get_input_latency :: proc(pResampler: ^resampler) -> u64 ---
-
-	/*
-	Retrieves the latency introduced by the resampler in output frames.
-	*/
-	resampler_get_output_latency :: proc(pResampler: ^resampler) -> u64 ---
+	resampler_reset :: proc(pResampler: ^resampler) -> result ---
 }
 
 
@@ -179,42 +211,63 @@ foreign lib {
 Channel Conversion
 
 **************************************************************************************************************************************************************/
+channel_conversion_path :: enum c.int {
+	unknown,
+	passthrough,
+	mono_out,    /* Converting to mono. */
+	mono_in,     /* Converting from mono. */
+	shuffle,     /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */
+	weights,     /* Blended based on weights. */
+}
+
+mono_expansion_mode :: enum c.int {
+	duplicate = 0,   /* The default. */
+	average,         /* Average the mono channel across all channels. */
+	stereo_only,     /* Duplicate to the left and right channels only and ignore the others. */
+	default = duplicate,
+}
+
 channel_converter_config :: struct {
-	format:        format,
-	channelsIn:    u32,
-	channelsOut:   u32,
-	channelMapIn:  [MAX_CHANNELS]channel,
-	channelMapOut: [MAX_CHANNELS]channel,
-	mixingMode:    channel_mix_mode,
-	weights:       [MAX_CHANNELS][MAX_CHANNELS]f32,  /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
+	format:         format,
+	channelsIn:     u32,
+	channelsOut:    u32,
+	pChannelMapIn:  [^]channel,
+	pChannelMapOut: [^]channel,
+	mixingMode:     channel_mix_mode,
+	ppWeights:      ^[^]f32,  /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
 }
 
 channel_converter :: struct {
-	format:        format,
-	channelsIn:    u32,
-	channelsOut:   u32,
-	channelMapIn:  [MAX_CHANNELS]channel,
-	channelMapOut: [MAX_CHANNELS]channel,
-	mixingMode:    channel_mix_mode,
-	weights: struct #raw_union {
-		f32: [MAX_CHANNELS][MAX_CHANNELS]f32,
-		s16: [MAX_CHANNELS][MAX_CHANNELS]i32,
+	format:         format,
+	channelsIn:     u32,
+	channelsOut:    u32,
+	mixingMode:     channel_mix_mode,
+	conversionPath: channel_conversion_path,
+	pChannelMapIn:  [^]channel,
+	pChannelMapOut: [^]channel,
+	pShuffleTable:  [^]u8,
+	weights: struct #raw_union { /* [in][out] */
+		f32: ^[^]f32,
+		s16: ^[^]i32,
 	},
-	isPassthrough:         b8,
-	isSimpleShuffle:       b8,
-	isSimpleMonoExpansion: b8,
-	isStereoToMono:        b8,
-	shuffleTable:          [MAX_CHANNELS]u8,
+
+	/* Memory management. */
+	_pHeap:   rawptr,
+	_ownsHeap: b32,
 }
 
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
-	channel_converter_config_init :: proc(format: format, channelsIn: u32, pChannelMapIn: ^channel, channelsOut: u32, pChannelMapOut: ^channel, mixingMode: channel_mix_mode) -> channel_converter_config ---
-
-	channel_converter_init               :: proc(pConfig: ^channel_converter_config, pConverter: ^channel_converter) -> result ---
-	channel_converter_uninit             :: proc(pConverter: ^channel_converter) ---
-	channel_converter_process_pcm_frames :: proc(pConverter: ^channel_converter, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
+	channel_converter_config_init :: proc(format: format, channelsIn: u32, pChannelMapIn: [^]channel, channelsOut: u32, pChannelMapOut: [^]channel, mixingMode: channel_mix_mode) -> channel_converter_config ---
+
+	channel_converter_get_heap_size          :: proc(pConfig: ^channel_converter_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	channel_converter_init_preallocated      :: proc(pConfig: ^channel_converter_config, pHeap: rawptr, pConverter: ^channel_converter) -> result ---
+	channel_converter_init                   :: proc(pConfig: ^channel_converter_config, pAllocationCallbacks: ^allocation_callbacks, pConverter: ^channel_converter) -> result ---
+	channel_converter_uninit                 :: proc(pConverter: ^channel_converter, pAllocationCallbacks: ^allocation_callbacks) ---
+	channel_converter_process_pcm_frames     :: proc(pConverter: ^channel_converter, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result ---
+	channel_converter_get_input_channel_map  :: proc(pConverter: ^channel_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	channel_converter_get_output_channel_map :: proc(pConverter: ^channel_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
 }
 
 
@@ -224,32 +277,39 @@ Data Conversion
 
 **************************************************************************************************************************************************************/
 data_converter_config :: struct {
-	formatIn:       format,
-	formatOut:      format,
-	channelsIn:     u32,
-	channelsOut:    u32,
-	sampleRateIn:   u32,
-	sampleRateOut:  u32,
-	channelMapIn:   [MAX_CHANNELS]channel,
-	channelMapOut:  [MAX_CHANNELS]channel,
-	ditherMode:     dither_mode,
-	channelMixMode: channel_mix_mode,
-	channelWeights: [MAX_CHANNELS][MAX_CHANNELS]f32, /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */
-	resampling: struct {
-		algorithm: resample_algorithm,
-		allowDynamicSampleRate: b32,
-		linear: struct {
-			lpfOrderL:        u32,
-			lpfNyquistFactor: f64,
-		},
-		speex: struct {
-			quality: c.int,
-		},
-	},
+	formatIn:               format,
+	formatOut:              format,
+	channelsIn:             u32,
+	channelsOut:            u32,
+	sampleRateIn:           u32,
+	sampleRateOut:          u32,
+	pChannelMapIn:          [^]channel,
+	pChannelMapOut:         [^]channel,
+	ditherMode:             dither_mode,
+	channelMixMode:         channel_mix_mode,
+	ppChannelWeights:       ^[^]f32, /* [in][out]. Only used when channelMixMode is set to ma_channel_mix_mode_custom_weights. */
+	allowDynamicSampleRate: b32,
+	resampling:             resampler_config,
+}
+
+data_converter_execution_path :: enum c.int {
+	passthrough,       /* No conversion. */
+	format_only,       /* Only format conversion. */
+	channels_only,     /* Only channel conversion. */
+	resample_only,     /* Only resampling. */
+	resample_first,    /* All conversions, but resample as the first step. */
+	channels_first,    /* All conversions, but channels as the first step. */
 }
 
 data_converter :: struct {
-	config:                  data_converter_config,
+	formatIn:                format,
+	formatOut:               format,
+	channelsIn:              u32,
+	channelsOut:             u32,
+	sampleRateIn:            u32,
+	sampleRateOut:           u32,
+	ditherMode:              dither_mode,
+	executionPath:           data_converter_execution_path, /* The execution path the data converter will follow when processing. */
 	channelConverter:        channel_converter,
 	resampler:               resampler,
 	hasPreFormatConversion:  b8,
@@ -257,6 +317,10 @@ data_converter :: struct {
 	hasChannelConverter:     b8,
 	hasResampler:            b8,
 	isPassthrough:           b8,
+
+	/* Memory management. */
+	_ownsHeap: b8,
+	_pHeap:    rawptr,
 }
 
 
@@ -265,15 +329,20 @@ foreign lib {
 	data_converter_config_init_default :: proc() -> data_converter_config ---
 	data_converter_config_init :: proc(formatIn, formatOut: format, channelsIn, channelsOut: u32, sampleRateIn, sampleRateOut: u32) -> data_converter_config ---
 
-	data_converter_init                            :: proc(pConfig: ^data_converter_config, pConverter: ^data_converter) -> result ---
-	data_converter_uninit                          :: proc(pConverter: ^data_converter) ---
+	data_converter_get_heap_size                   :: proc(pConfig: ^data_converter_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	data_converter_init_preallocated               :: proc(pConfig: ^data_converter_config, pHeap: rawptr, pConverter: ^data_converter) -> result ---
+	data_converter_init                            :: proc(pConfig: ^data_converter_config, pAllocationCallbacks: ^allocation_callbacks, pConverter: ^data_converter) -> result ---
+	data_converter_uninit                          :: proc(pConverter: ^data_converter, pAllocationCallbacks: ^allocation_callbacks) ---
 	data_converter_process_pcm_frames              :: proc(pConverter: ^data_converter, pFramesIn: rawptr, pFrameCountIn: ^u64, pFramesOut: rawptr, pFrameCountOut: ^u64) -> result ---
 	data_converter_set_rate                        :: proc(pConverter: ^data_converter, sampleRateIn, sampleRateOut: u32) -> result ---
 	data_converter_set_rate_ratio                  :: proc(pConverter: ^data_converter, ratioInOut: f32) -> result ---
-	data_converter_get_required_input_frame_count  :: proc(pConverter: ^data_converter, outputFrameCount: u64) -> u64 ---
-	data_converter_get_expected_output_frame_count :: proc(pConverter: ^data_converter, inputFrameCount:  u64) -> u64 ---
 	data_converter_get_input_latency               :: proc(pConverter: ^data_converter) -> u64 ---
 	data_converter_get_output_latency              :: proc(pConverter: ^data_converter) -> u64 ---
+	data_converter_get_required_input_frame_count  :: proc(pConverter: ^data_converter, outputFrameCount: u64, pInputFrameCount: ^u64) -> result ---
+	data_converter_get_expected_output_frame_count :: proc(pConverter: ^data_converter, inputFrameCount: u64, pOutputFrameCount: ^u64) -> result ---
+	data_converter_get_input_channel_map           :: proc(pConverter: ^data_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	data_converter_get_output_channel_map          :: proc(pConverter: ^data_converter, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	data_converter_reset                           :: proc(pConverter: ^data_converter) -> result ---
 }
 
 /************************************************************************************************************************************************************
@@ -332,43 +401,40 @@ CHANNEL_INDEX_NULL :: 255
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
-	/* Retrieves the channel position of the specified channel based on miniaudio's default channel map. */
-	channel_map_get_default_channel :: proc(channelCount: u32, channelIndex: u32) -> channel ---
-
 	/*
 	Retrieves the channel position of the specified channel in the given channel map.
 
 	The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed.
 	*/
-	channel_map_get_channel :: proc(pChannelMap: ^channel, channelCount: u32, channelIndex: u32) -> channel ---
+	channel_map_get_channel :: proc(pChannelMap: [^]channel, channelCount: u32, channelIndex: u32) -> channel ---
 
 	/*
 	Initializes a blank channel map.
 
 	When a blank channel map is specified anywhere it indicates that the native channel map should be used.
 	*/
-	channel_map_init_blank :: proc(channels: u32, pChannelMap: ^channel) ---
+	channel_map_init_blank :: proc(pChannelMap: [^]channel, channels: u32) ---
 
 	/*
 	Helper for retrieving a standard channel map.
 
-	The output channel map buffer must have a capacity of at least `channels`.
+	The output channel map buffer must have a capacity of at least `channelMapCap`.
 	*/
-	get_standard_channel_map :: proc(standardChannelMap: standard_channel_map, channels: u32, pChannelMap: ^channel) ---
+	channel_map_init_standard :: proc(standardChannelMap: standard_channel_map, pChannelMap: [^]channel, channelMapCap: c.size_t, channels: u32) ---
 
 	/*
 	Copies a channel map.
 
 	Both input and output channel map buffers must have a capacity of at at least `channels`.
 	*/
-	channel_map_copy :: proc(pOut: ^channel, pIn: ^channel, channels: u32) ---
+	channel_map_copy :: proc(pOut: [^]channel, pIn: [^]channel, channels: u32) ---
 
 	/*
 	Copies a channel map if one is specified, otherwise copies the default channel map.
 
 	The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`.
 	*/
-	channel_map_copy_or_default :: proc(pOut: ^channel, pIn: ^channel, channels: u32) ---
+	channel_map_copy_or_default :: proc(pOut: [^]channel, channelMapCapOut: c.size_t, pIn: [^]channel, channels: u32) ---
 
 
 	/*
@@ -378,12 +444,12 @@ foreign lib {
 	is usually treated as a passthrough.
 
 	Invalid channel maps:
-	  - A channel map with no channels
-	  - A channel map with more than one channel and a mono channel
+		- A channel map with no channels
+		- A channel map with more than one channel and a mono channel
 
 	The channel map buffer must have a capacity of at least `channels`.
 	*/
-	channel_map_valid :: proc(channels: u32, pChannelMap: ^channel) -> b32 ---
+	channel_map_is_valid :: proc(pChannelMap: [^]channel, channels: u32) -> b32 ---
 
 	/*
 	Helper for comparing two channel maps for equality.
@@ -392,23 +458,24 @@ foreign lib {
 
 	Both channels map buffers must have a capacity of at least `channels`.
 	*/
-	channel_map_equal :: proc(channels: u32, pChannelMapA, pChannelMapB: ^channel) -> b32 ---
+	channel_map_is_equal :: proc(pChannelMapA, pChannelMapB: [^]channel, channels: u32) -> b32 ---
 
 	/*
 	Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
 
 	The channel map buffer must have a capacity of at least `channels`.
 	*/
-	channel_map_blank :: proc(channels: u32, pChannelMap: ^channel) -> b32 ---
+	channel_map_is_blank :: proc(pChannelMap: [^]channel, channels: u32) -> b32 ---
 
 	/*
 	Helper for determining whether or not a channel is present in the given channel map.
 
 	The channel map buffer must have a capacity of at least `channels`.
 	*/
-	channel_map_contains_channel_position :: proc(channels: u32, pChannelMap: ^channel, channelPosition: channel) -> b32 ---
+	channel_map_contains_channel_position :: proc(channels: u32, pChannelMap: [^]channel, channelPosition: channel) -> b32 ---
 }
 
+
 /************************************************************************************************************************************************************
 
 Conversion Helpers
@@ -461,9 +528,9 @@ foreign lib {
 	rb_uninit               :: proc(pRB: ^rb) ---
 	rb_reset                :: proc(pRB: ^rb) ---
 	rb_acquire_read         :: proc(pRB: ^rb, pSizeInBytes: ^c.size_t, ppBufferOut: ^rawptr) -> result ---
-	rb_commit_read          :: proc(pRB: ^rb, sizeInBytes: c.size_t, pBufferOut: rawptr) -> result ---
+	rb_commit_read          :: proc(pRB: ^rb, sizeInBytes: c.size_t) -> result ---
 	rb_acquire_write        :: proc(pRB: ^rb, pSizeInBytes: ^c.size_t, ppBufferOut: ^rawptr) -> result ---
-	rb_commit_write         :: proc(pRB: ^rb, sizeInBytes: c.size_t, pBufferOut: rawptr) -> result ---
+	rb_commit_write         :: proc(pRB: ^rb, sizeInBytes: c.size_t) -> result ---
 	rb_seek_read            :: proc(pRB: ^rb, offsetInBytes: c.size_t) -> result ---
 	rb_seek_write           :: proc(pRB: ^rb, offsetInBytes: c.size_t) -> result ---
 	rb_pointer_distance     :: proc(pRB: ^rb) -> i32 ---    /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */

+ 51 - 51
vendor/miniaudio/decoding.odin

@@ -22,68 +22,63 @@ you do your own synchronization.
 
 decoding_backend_config :: struct {
 	preferredFormat: format,
+	seekPointCount: u32,   /* Set to > 0 to generate a seektable if the decoding backend supports it. */
 }
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
-	decoding_backend_config_init :: proc(preferredFormat: format) -> decoding_backend_config ---
+	decoding_backend_config_init :: proc(preferredFormat: format, seekPointCount: u32) -> decoding_backend_config ---
 }
 
 
 decoding_backend_vtable :: struct {
 	onInit:          proc "c" (pUserData: rawptr, onRead: decoder_read_proc, onSeek: decoder_seek_proc, onTell: decoder_tell_proc, pReadSeekTellUserData: rawptr, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result,
-	onInitFile:      proc "c" (pUserData: rawptr, pFilePath: cstring, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result,               /* Optional. */
+	onInitFile:      proc "c" (pUserData: rawptr, pFilePath: cstring, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result,               	 /* Optional. */
 	onInitFileW:     proc "c" (pUserData: rawptr, pFilePath: [^]c.wchar_t, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result,            /* Optional. */
 	onInitMemory:    proc "c" (pUserData: rawptr, pData: rawptr, dataSize: c.size_t, pConfig: ^decoding_backend_config, pAllocationCallbacks: ^allocation_callbacks, ppBackend: ^^data_source) -> result,  /* Optional. */
 	onUninit:        proc "c" (pUserData: rawptr, pBackend: ^data_source, pAllocationCallbacks: ^allocation_callbacks),
-	onGetChannelMap: proc "c" (pUserData: rawptr, pBackend: ^data_source, pChannelMap: ^channel, channelMapCap: c.size_t) -> result,
 }
 
 
-/* TODO: Convert read and seek to be consistent with the VFS API (ma_result return value, bytes read moved to an output parameter). */
-decoder_read_proc :: proc "c" (pDecoder: ^decoder, pBufferOut: rawptr, bytesToRead: c.size_t) -> c.size_t         /* Returns the number of bytes read. */
-decoder_seek_proc :: proc "c" (pDecoder: ^decoder, byteOffset: i64, origin: seek_origin) -> b32
+decoder_read_proc :: proc "c" (pDecoder: ^decoder, pBufferOut: rawptr, bytesToRead: c.size_t, pBytesRead: ^c.size_t) -> result         /* Returns the number of bytes read. */
+decoder_seek_proc :: proc "c" (pDecoder: ^decoder, byteOffset: i64, origin: seek_origin) -> result
 decoder_tell_proc :: proc "c" (pDecoder: ^decoder, pCursor: ^i64) -> result
 
 decoder_config :: struct {
-	format:     format, /* Set to 0 or ma_format_unknown to use the stream's internal format. */
-	channels:   u32,    /* Set to 0 to use the stream's internal channels. */
-	sampleRate: u32,    /* Set to 0 to use the stream's internal sample rate. */
-	channelMap: [MAX_CHANNELS]channel,
-	channelMixMode: channel_mix_mode,
-	ditherMode: dither_mode,
-	resampling: struct {
-		algorithm: resample_algorithm,
-		linear: struct {
-			lpfOrder: u32,
-		},
-		speex: struct {
-			quality: c.int,
-		},
-	},
+	format:                 format, /* Set to 0 or ma_format_unknown to use the stream's internal format. */
+	channels:               u32,    /* Set to 0 to use the stream's internal channels. */
+	sampleRate:             u32,    /* Set to 0 to use the stream's internal sample rate. */
+	channelMap:             [^]channel,
+	channelMixMode:         channel_mix_mode,
+	ditherMode:             dither_mode,
+	resampling:             resampler_config,
 	allocationCallbacks:    allocation_callbacks,
 	encodingFormat:         encoding_format,
-	ppCustomBackendVTables: ^^decoding_backend_vtable,
+	seekPointCount:         u32,   /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */
+	ppCustomBackendVTables: ^[^]decoding_backend_vtable,
 	customBackendCount:     u32,
 	pCustomBackendUserData: rawptr,
 }
 
 decoder :: struct  {
-	ds: data_source_base,
-	pBackend: ^data_source,                   /* The decoding backend we'll be pulling data from. */
-	pBackendVTable: ^^decoding_backend_vtable, /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
-	pBackendUserData: rawptr,
-	onRead: decoder_read_proc,
-	onSeek: decoder_seek_proc,
-	onTell: decoder_tell_proc,
-	pUserData: rawptr,
+	ds:                     data_source_base,
+	pBackend:               ^data_source,               /* The decoding backend we'll be pulling data from. */
+	pBackendVTable:         ^decoding_backend_vtable,   /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
+	pBackendUserData:       rawptr,
+	onRead:                 decoder_read_proc,
+	onSeek:                 decoder_seek_proc,
+	onTell:                 decoder_tell_proc,
+	pUserData:              rawptr,
 	readPointerInPCMFrames: u64,      /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
-	outputFormat: format,
-	outputChannels: u32,
-	outputSampleRate: u32,
-	outputChannelMap: [MAX_CHANNELS]channel,
-	converter: data_converter,   /* <-- Data conversion is achieved by running frames through this. */
-	allocationCallbacks: allocation_callbacks,
+	outputFormat:           format,
+	outputChannels:         u32,
+	outputSampleRate:       u32,
+	converter:              data_converter,    /* <-- Data conversion is achieved by running frames through this. */
+	pInputCache:            rawptr,            /* In input format. Can be null if it's not needed. */
+  inputCacheCap:          u64,               /* The capacity of the input cache. */
+  inputCacheConsumed:     u64,               /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
+  inputCacheRemaining:    u64,               /* The number of valid frames remaining in the cahce. */
+	allocationCallbacks:    allocation_callbacks,
 	data: struct #raw_union {
 		vfs: struct {
 			pVFS: ^vfs,
@@ -114,6 +109,25 @@ foreign lib {
 	*/
 	decoder_uninit :: proc(pDecoder: ^decoder) -> result ---
 
+	/*
+	Reads PCM frames from the given decoder.
+
+	This is not thread safe without your own synchronization.
+	*/
+	decoder_read_pcm_frames :: proc(pDecoder: ^decoder, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+
+	/*
+	Seeks to a PCM frame based on it's absolute index.
+
+	This is not thread safe without your own synchronization.
+	*/
+	decoder_seek_to_pcm_frame :: proc(pDecoder: ^decoder, frameIndex: u64) -> result ---
+
+	/*
+	Retrieves the decoder's output data format.
+	*/
+	decoder_get_data_format :: proc(pDecoder: ^decoder, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: ^channel, channelMapCap: c.size_t) -> result ---
+
 	/*
 	Retrieves the current position of the read cursor in PCM frames.
 	*/
@@ -133,21 +147,7 @@ foreign lib {
 
 	This function is not thread safe without your own synchronization.
 	*/
-	decoder_get_length_in_pcm_frames :: proc(pDecoder: ^decoder) -> u64 ---
-
-	/*
-	Reads PCM frames from the given decoder.
-
-	This is not thread safe without your own synchronization.
-	*/
-	decoder_read_pcm_frames :: proc(pDecoder: ^decoder, pFramesOut: rawptr, frameCount: u64) -> u64 ---
-
-	/*
-	Seeks to a PCM frame based on it's absolute index.
-
-	This is not thread safe without your own synchronization.
-	*/
-	decoder_seek_to_pcm_frame :: proc(pDecoder: ^decoder, frameIndex: u64) -> result ---
+	decoder_get_length_in_pcm_frames :: proc(pDecoder: ^decoder, pLength: ^u64) -> result ---
 
 	/*
 	Retrieves the number of frames that can be read before reaching the end.

File diff suppressed because it is too large
+ 472 - 280
vendor/miniaudio/device_io_procs.odin


+ 219 - 146
vendor/miniaudio/device_io_types.odin

@@ -18,12 +18,13 @@ SUPPORT_WEBAUDIO   :: false // ODIN_OS == .Emscripten
 SUPPORT_CUSTOM     :: true
 SUPPORT_NULL       :: true // ODIN_OS != .Emscripten
 
-STATE_UNINITIALIZED :: 0
-STATE_STOPPED       :: 1   /* The device's default state after initialization. */
-STATE_STARTED       :: 2   /* The device is started and is requesting and/or delivering audio data. */
-STATE_STARTING      :: 3   /* Transitioning from a stopped state to started. */
-STATE_STOPPING      :: 4   /* Transitioning from a started state to stopped. */
-
+device_state :: enum c.int {
+	uninitialized = 0,
+	stopped       = 1,  /* The device's default state after initialization. */
+	started       = 2,  /* The device is started and is requesting and/or delivering audio data. */
+	starting      = 3,  /* Transitioning from a stopped state to started. */
+	stopping      = 4,  /* Transitioning from a started state to stopped. */
+}
 
 
 when SUPPORT_WASAPI {
@@ -56,6 +57,96 @@ backend :: enum c.int {
 BACKEND_COUNT :: len(backend)
 
 
+/*
+Device job thread. This is used by backends that require asynchronous processing of certain
+operations. It is not used by all backends.
+
+The device job thread is made up of a thread and a job queue. You can post a job to the thread with
+ma_device_job_thread_post(). The thread will do the processing of the job.
+*/
+device_job_thread_config :: struct {
+	noThread:         b32, /* Set this to true if you want to process jobs yourself. */
+	jobQueueCapacity: u32,
+	jobQueueFlags:    u32,
+}
+
+device_job_thread :: struct {
+	thread:     thread,
+	jobQueue:   job_queue,
+	_hasThread: b32,
+}
+
+
+/* Device notification types. */
+device_notification_type :: enum c.int {
+	started,
+	stopped,
+	rerouted,
+	interruption_began,
+	interruption_ended,
+}
+
+device_notification :: struct {
+	pDevice: ^device,
+	type:    device_notification_type,
+	data: struct #raw_union {
+		started: struct {
+			_unused: c.int,
+		},
+		stopped: struct {
+			_unused: c.int,
+		},
+		rerouted: struct {
+			_unused: c.int,
+		},
+		interruption: struct {
+			_unused: c.int,
+		},
+	},
+}
+
+/*
+The notification callback for when the application should be notified of a change to the device.
+
+This callback is used for notifying the application of changes such as when the device has started,
+stopped, rerouted or an interruption has occurred. Note that not all backends will post all
+notification types. For example, some backends will perform automatic stream routing without any
+kind of notification to the host program which means miniaudio will never know about it and will
+never be able to fire the rerouted notification. You should keep this in mind when designing your
+program.
+
+The stopped notification will *not* get fired when a device is rerouted.
+
+
+Parameters
+----------
+pNotification (in)
+    A pointer to a structure containing information about the event. Use the `pDevice` member of
+    this object to retrieve the relevant device. The `type` member can be used to discriminate
+    against each of the notification types.
+
+
+Remarks
+-------
+Do not restart or uninitialize the device from the callback.
+
+Not all notifications will be triggered by all backends, however the started and stopped events
+should be reliable for all backends. Some backends do not have a good way to detect device
+stoppages due to unplugging the device which may result in the stopped callback not getting
+fired. This has been observed with at least one BSD variant.
+
+The rerouted notification is fired *after* the reroute has occurred. The stopped notification will
+*not* get fired when a device is rerouted. The following backends are known to do automatic stream
+rerouting, but do not have a way to be notified of the change:
+
+  * DirectSound
+
+The interruption notifications are used on mobile platforms for detecting when audio is interrupted
+due to things like an incoming phone call. Currently this is only implemented on iOS. None of the
+Android backends will report this notification.
+*/
+device_notification_proc :: proc "c" (pNotification: ^device_notification)
+
 /*
 The callback for processing audio data from the device.
 
@@ -96,9 +187,11 @@ callback. The following APIs cannot be called from inside the callback:
 
 The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
 */
-device_callback_proc :: proc "c" (pDevice: ^device, pOutput: rawptr, pInput: rawptr, frameCount: u32)
+device_data_proc :: proc "c" (pDevice: ^device, pOutput, pInput: rawptr, frameCount: u32)
 
 /*
+DEPRECATED. Use ma_device_notification_proc instead.
+
 The callback for when the device has been stopped.
 
 This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces
@@ -108,48 +201,15 @@ such as being unplugged or an internal error occuring.
 Parameters
 ----------
 pDevice (in)
-	A pointer to the device that has just stopped.
+    A pointer to the device that has just stopped.
 
 
 Remarks
 -------
 Do not restart or uninitialize the device from the callback.
 */
-stop_proc :: proc "c" (pDevice: ^device)
-
-/*
-The callback for handling log messages.
-
-
-Parameters
-----------
-pContext (in)
-	A pointer to the context the log message originated from.
-
-pDevice (in)
-	A pointer to the device the log message originate from, if any. This can be null, in which case the message came from the context.
+stop_proc :: proc "c" (pDevice: ^device)  /* DEPRECATED. Use ma_device_notification_proc instead. */
 
-logLevel (in)
-	The log level. This can be one of the following:
-
-	+----------------------+
-	| Log Level            |
-	+----------------------+
-	| MA_LOG_LEVEL_DEBUG   |
-	| MA_LOG_LEVEL_INFO    |
-	| MA_LOG_LEVEL_WARNING |
-	| MA_LOG_LEVEL_ERROR   |
-	+----------------------+
-
-message (in)
-	The log message.
-
-
-Remarks
--------
-Do not modify the state of the device from inside the callback.
-*/
-log_proc :: proc "c" (pContext: context_type, pDevice: ^device, logLevel: u32, message: cstring)
 
 device_type :: enum c.int {
 	playback = 1,
@@ -279,29 +339,14 @@ device_id :: struct #raw_union {
 
 DATA_FORMAT_FLAG_EXCLUSIVE_MODE :: 1 << 1    /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
 
+MAX_DEVICE_NAME_LENGTH :: 255
+
 device_info :: struct {
 	/* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
 	id:        device_id,
-	name:      [256]byte,
+	name:      [MAX_DEVICE_NAME_LENGTH + 1]c.char, /* +1 for null terminator. */
 	isDefault: b32,
 
-	/*
-	Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
-	a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
-	pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
-	here mainly for informational purposes or in the rare case that someone might find it useful.
-
-	These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
-	*/
-	formatCount:   u32,
-	formats:       [format]format,
-	minChannels:   u32,
-	maxChannels:   u32,
-	minSampleRate: u32,
-	maxSampleRate: u32,
-
-
-	/* Experimental. Don't use these right now. */
 	nativeDataFormatCount: u32,
 	nativeDataFormats: [/*len(format_count) * standard_sample_rate.rate_count * MAX_CHANNELS*/ 64]struct { /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */
 		format:     format, /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
@@ -312,31 +357,26 @@ device_info :: struct {
 }
 
 device_config :: struct {
-	deviceType:               device_type,
-	sampleRate:               u32,
-	periodSizeInFrames:       u32,
-	periodSizeInMilliseconds: u32,
-	periods:                  u32,
-	performanceProfile:       performance_profile,
-	noPreZeroedOutputBuffer:  b8,   /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */
-	noClip:                   b8,                    /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */
-	dataCallback:             device_callback_proc,
-	stopCallback:             stop_proc,
-	pUserData: rawptr,
-	resampling: struct {
-		algorithm: resample_algorithm,
-		linear: struct {
-			lpfOrder: u32,
-		},
-		speex: struct {
-			quality: c.int,
-		},
-	},
+	deviceType:                 device_type,
+	sampleRate:                 u32,
+	periodSizeInFrames:         u32,
+	periodSizeInMilliseconds:   u32,
+	periods:                    u32,
+	performanceProfile:         performance_profile,
+	noPreSilencedOutputBuffer:  b8,   /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */
+	noClip:                     b8,   /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */
+	noDisableDenormals:         b8,   /* Do not disable denormals when firing the data callback. */
+  noFixedSizedCallback:       b8,   /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */
+	dataCallback:               device_data_proc,
+	notificationCallback:       device_notification_proc,
+	stopCallback:               stop_proc,
+	pUserData:                  rawptr,
+	resampling:                 resampler_config,
 	playback: struct {
 		pDeviceID:      ^device_id,
 		format:         format,
 		channels:       u32,
-		channelMap:     [MAX_CHANNELS]channel,
+		channelMap:     [^]channel,
 		channelMixMode: channel_mix_mode,
 		shareMode:      share_mode,
 	},
@@ -344,7 +384,7 @@ device_config :: struct {
 		pDeviceID:      ^device_id,
 		format:         format,
 		channels:       u32,
-		channelMap:     [MAX_CHANNELS]channel,
+		channelMap:     [^]channel,
 		channelMixMode: channel_mix_mode,
 		shareMode:      share_mode,
 	},
@@ -373,9 +413,10 @@ device_config :: struct {
 		recordingPreset: opensl_recording_preset,
 	},
 	aaudio: struct {
-		usage:       aaudio_usage,
-		contentType: aaudio_content_type,
-		inputPreset: aaudio_input_preset,
+		usage:                   aaudio_usage,
+		contentType:             aaudio_content_type,
+		inputPreset:             aaudio_input_preset,
+		noAutoStartAfterReroute: b32,
 	},
 }
 
@@ -425,14 +466,14 @@ to many devices. A device is created from a context.
 The general flow goes like this:
 
   1) A context is created with `onContextInit()`
-	 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
-	 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
+     1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
+     1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
   2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
-	 selected from device enumeration via `onContextEnumerateDevices()`.
+     selected from device enumeration via `onContextEnumerateDevices()`.
   3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
   4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call
-	 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
-	 miniaudio internally.
+     to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
+     miniaudio internally.
 
 Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
 callbacks defined in this structure.
@@ -440,7 +481,7 @@ callbacks defined in this structure.
 Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
 physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
 given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
-needs to stop and the `onContextEnumerateDevices()` function return with a success code.
+needs to stop and the `onContextEnumerateDevices()` function returns with a success code.
 
 Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
 and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
@@ -455,7 +496,7 @@ internally by miniaudio.
 
 On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's
 supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for
-sample rate. For the channel map, the default should be used when `ma_channel_map_blank()` returns true (all channels set to
+sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to
 `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
 inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
 size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
@@ -474,14 +515,17 @@ This allows miniaudio to then process any necessary data conversion and then pas
 If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
 which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
 
-The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been
+The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been
 encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
 
 The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
-callback. When the device is stopped, the `ma_device_get_state() == MA_STATE_STARTED` condition will fail and the loop will be terminated
+callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated
 which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
 look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
 wake up the audio thread.
+
+If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the
+`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient.
 */
 backend_callbacks :: struct {
 	onContextInit:              proc "c" (pContext: ^context_type, pConfig: ^context_config, pCallbacks: ^backend_callbacks) -> result,
@@ -496,10 +540,10 @@ backend_callbacks :: struct {
 	onDeviceWrite:              proc "c" (pDevice: ^device, pFrames: rawptr, frameCount: u32, pFramesWritten: ^u32) -> result,
 	onDeviceDataLoop:           proc "c" (pDevice: ^device) -> result,
 	onDeviceDataLoopWakeup:     proc "c" (pDevice: ^device) -> result,
+	onDeviceGetInfo:            proc "c" (pDevice: ^device, type: device_type, pDeviceInfo: ^device_info) -> result,
 }
 
 context_config :: struct {
-	logCallback: log_proc, /* Legacy logging callback. Will be removed in version 0.11. */
 	pLog: ^log,
 	threadPriority: thread_priority,
 	threadStackSize: c.size_t,
@@ -538,7 +582,7 @@ context_command__wasapi :: struct {
 			deviceType:           device_type,
 			pAudioClient:         rawptr,
 			ppAudioClientService: ^rawptr,
-			pResult:              ^rawptr, /* The result from creating the audio client service. */
+			pResult:              ^result, /* The result from creating the audio client service. */
 		},
 		releaseAudioClient: struct {
 			pDevice:    ^device,
@@ -548,21 +592,20 @@ context_command__wasapi :: struct {
 }
 
 context_type :: struct {
-	callbacks: backend_callbacks,
-	backend: backend,                 /* DirectSound, ALSA, etc. */
-	pLog: ^log,
-	log: log,                         /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
-	logCallback: log_proc,            /* Legacy callback. Will be removed in version 0.11. */
-	threadPriority: thread_priority,
-	threadStackSize: c.size_t,
-	pUserData: rawptr,
-	allocationCallbacks: allocation_callbacks,
-	deviceEnumLock: mutex,            /* Used to make ma_context_get_devices() thread safe. */
-	deviceInfoLock: mutex,            /* Used to make ma_context_get_device_info() thread safe. */
-	deviceInfoCapacity: u32,          /* Total capacity of pDeviceInfos. */
+	callbacks:               backend_callbacks,
+	backend:                 backend,          /* DirectSound, ALSA, etc. */
+	pLog:                    ^log,
+	log:                     log,              /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
+	threadPriority:          thread_priority,
+	threadStackSize:         c.size_t,
+	pUserData:               rawptr,
+	allocationCallbacks:     allocation_callbacks,
+	deviceEnumLock:          mutex,            /* Used to make ma_context_get_devices() thread safe. */
+	deviceInfoLock:          mutex,            /* Used to make ma_context_get_device_info() thread safe. */
+	deviceInfoCapacity:      u32,          		 /* Total capacity of pDeviceInfos. */
 	playbackDeviceInfoCount: u32,
-	captureDeviceInfoCount: u32,
-	pDeviceInfos: [^]device_info,     /* Playback devices first, then capture. */
+	captureDeviceInfoCount:  u32,
+	pDeviceInfos:            [^]device_info,   /* Playback devices first, then capture. */
 
 	using _: struct #raw_union {
 		wasapi: (struct {
@@ -575,7 +618,7 @@ context_type :: struct {
 		} when SUPPORT_WASAPI else struct {}),
 		
 		dsound: (struct {
-			DSoundDLL:                    handle,
+			hDSoundDLL:                   handle,
 			DirectSoundCreate:            proc "system" (),
 			DirectSoundEnumerateA:        proc "system" (),
 			DirectSoundCaptureCreate:     proc "system" (),
@@ -741,6 +784,8 @@ context_type :: struct {
 
 			/*pa_mainloop**/ pMainLoop:     rawptr,
 			/*pa_context**/  pPulseContext: rawptr,
+			pApplicationName:               cstring, /* Set when the context is initialized. Used by devices for their local pa_context objects. */
+      pServerName:                    cstring, /* Set when the context is initialized. Used by devices for their local pa_context objects. */
 		} when SUPPORT_PULSEAUDIO else struct {}),
 		
 		jack: (struct {
@@ -762,7 +807,7 @@ context_type :: struct {
 			jack_port_get_buffer:          proc "system" (),
 			jack_free:                     proc "system" (),
 
-			pClientName:    [^]c.char,
+			pClientName:    cstring,
 			tryStartServer: b32,
 		} when SUPPORT_JACK else struct {}),
 
@@ -817,7 +862,7 @@ context_type :: struct {
 		} when SUPPORT_SNDIO else struct {}),
 
 		audio4: (struct {
-			_unused: cint,
+			_unused: c.int,
 		} when SUPPORT_AUDIO4 else struct {}),
 
 		oss: (struct {
@@ -855,6 +900,7 @@ context_type :: struct {
 			AAudioStream_getFramesPerBurst:                proc "system" (),
 			AAudioStream_requestStart:                     proc "system" (),
 			AAudioStream_requestStop:                      proc "system" (),
+			jobThread:                                     device_job_thread, /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */
 		} when SUPPORT_AAUDIO else struct {}),
 
 		opensl: (struct {
@@ -921,37 +967,40 @@ context_type :: struct {
 }
 
 device :: struct {
-	pContext:                ^context_type,
-	type:                    device_type,
-	sampleRate:              u32,
-	state:                   u32, /*atomic*/        /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */
-	onData:                  device_callback_proc,  /* Set once at initialization time and should not be changed after. */
-	onStop:                  stop_proc,             /* Set once at initialization time and should not be changed after. */
-	pUserData:               rawptr,                /* Application defined data. */
-	startStopLock:           mutex,
-	wakeupEvent:             event,
-	startEvent:              event,
-	stopEvent:               event,
-	device_thread:           thread,
-	workResult:              result,                /* This is set by the worker thread after it's finished doing a job. */
-	isOwnerOfContext:        b8,                    /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */
-	noPreZeroedOutputBuffer: b8,
-	noClip:                  b8,
-	masterVolumeFactor:      f32, /*atomic*/        /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
-	duplexRB:                duplex_rb,             /* Intermediary buffer for duplex device on asynchronous backends. */
+	pContext:                  ^context_type,
+	type:                      device_type,
+	sampleRate:                u32,
+	state:                     u32, /*atomic*/            /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */
+	onData:                    device_data_proc,          /* Set once at initialization time and should not be changed after. */
+	onNotification:            device_notification_proc,  /* Set once at initialization time and should not be changed after. */
+	onStop:                    stop_proc,                 /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */
+	pUserData:                 rawptr,                    /* Application defined data. */
+	startStopLock:             mutex,
+	wakeupEvent:               event,
+	startEvent:                event,
+	stopEvent:                 event,
+	device_thread:             thread,
+	workResult:                result,                /* This is set by the worker thread after it's finished doing a job. */
+	isOwnerOfContext:          b8,                    /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */
+	noPreSilencedOutputBuffer: b8,
+	noClip:                    b8,
+	noDisableDenormals:        b8,
+	noFixedSizedCallback:      b8,
+	masterVolumeFactor:        f32, /*atomic*/        /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
+	duplexRB:                  duplex_rb,             /* Intermediary buffer for duplex device on asynchronous backends. */
 	resampling: struct {
-		algorithm: resample_algorithm,
+		algorithm:        resample_algorithm,
+		pBackendVTable:   ^resampling_backend_vtable,
+		pBackendUserData: rawptr,
 		linear: struct {
 			lpfOrder: u32,
 		},
-		speex: struct {
-			quality: c.int,
-		},
 	},
 	playback: struct {
-		id:                         device_id,             /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
-		name:                       [256]byte,             /* Maybe temporary. Likely to be replaced with a query API. */
-		shareMode:                  share_mode,            /* Set to whatever was passed in when the device was initialized. */
+		pID:                        ^device_id,                           /* Set to NULL if using default ID, otherwise set to the address of "id". */
+		id:                         device_id,                            /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
+		name:                       [MAX_DEVICE_NAME_LENGTH + 1]c.char,   /* Maybe temporary. Likely to be replaced with a query API. */
+		shareMode:                  share_mode,                           /* Set to whatever was passed in when the device was initialized. */
 		playback_format:            format,
 		channels:                   u32,
 		channelMap:                 [MAX_CHANNELS]channel,
@@ -963,11 +1012,19 @@ device :: struct {
 		internalPeriods:            u32,
 		channelMixMode:             channel_mix_mode,
 		converter:                  data_converter,
+		pIntermediaryBuffer:        rawptr,  /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
+		intermediaryBufferCap:      u32,
+		intermediaryBufferLen:      u32,     /* How many valid frames are sitting in the intermediary buffer. */
+		pInputCache:                rawptr,  /* In external format. Can be null. */
+		inputCacheCap:              u64,
+		inputCacheConsumed:         u64,
+		inputCacheRemaining:        u64,
 	},
 	capture: struct {
-		id:                         device_id,             /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
-		name:                       [256]byte,             /* Maybe temporary. Likely to be replaced with a query API. */
-		shareMode:                  share_mode,            /* Set to whatever was passed in when the device was initialized. */
+		pID:                        ^device_id,                           /* Set to NULL if using default ID, otherwise set to the address of "id". */
+		id:                         device_id,                            /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */
+		name:                       [MAX_DEVICE_NAME_LENGTH + 1]c.char,   /* Maybe temporary. Likely to be replaced with a query API. */
+		shareMode:                  share_mode,                           /* Set to whatever was passed in when the device was initialized. */
 		capture_format:             format,
 		channels:                   u32,
 		channelMap:                 [MAX_CHANNELS]channel,
@@ -979,6 +1036,9 @@ device :: struct {
 		internalPeriods:            u32,
 		channelMixMode:             channel_mix_mode,
 		converter:                  data_converter,
+		pIntermediaryBuffer:        rawptr,  /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
+		intermediaryBufferCap:      u32,
+		intermediaryBufferLen:      u32,     /* How many valid frames are sitting in the intermediary buffer. */
 	},
 
 	using _: struct #raw_union {
@@ -991,7 +1051,7 @@ device :: struct {
 			notificationClient: IMMNotificationClient,
 			/*HANDLE*/ hEventPlayback: handle,                    /* Auto reset. Initialized to signaled. */
 			/*HANDLE*/ hEventCapture: handle,                     /* Auto reset. Initialized to unsignaled. */
-			actualPeriodSizeInFramesPlayback: u32,             /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
+			actualPeriodSizeInFramesPlayback: u32,                /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
 			actualPeriodSizeInFramesCapture: u32,
 			originalPeriodSizeInFrames: u32,
 			originalPeriodSizeInMilliseconds: u32,
@@ -999,8 +1059,14 @@ device :: struct {
 			originalPerformanceProfile: performance_profile,
 			periodSizeInFramesPlayback: u32,
 			periodSizeInFramesCapture: u32,
-			isStartedCapture: b32, /*atomic*/                   /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
-			isStartedPlayback: b32, /*atomic*/                  /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
+			pMappedBufferCapture: rawptr,
+			mappedBufferCaptureCap: u32,
+			mappedBufferCaptureLen: u32,
+			pMappedBufferPlayback: rawptr,
+			mappedBufferPlaybackCap: u32,
+			mappedBufferPlaybackLen: u32,
+			isStartedCapture: b32, /*atomic*/                  /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
+			isStartedPlayback: b32, /*atomic*/                 /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
 			noAutoConvertSRC: b8,                              /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
 			noDefaultQualitySRC: b8,                           /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
 			noHardwareOffloading: b8,
@@ -1049,14 +1115,16 @@ device :: struct {
 		} when SUPPORT_ALSA else struct {}),
 
 		pulse: (struct {
+			/*pa_mainloop**/ pMainLoop: rawptr,
+    	/*pa_context**/ pPulseContext: rawptr,
 			/*pa_stream**/ pStreamPlayback: rawptr,
 			/*pa_stream**/ pStreamCapture: rawptr,
 		} when SUPPORT_PULSEAUDIO else struct {}),
 		
 		jack: (struct {
 			/*jack_client_t**/ pClient: rawptr,
-			/*jack_port_t**/ pPortsPlayback: [MAX_CHANNELS]rawptr,
-			/*jack_port_t**/ pPortsCapture:  [MAX_CHANNELS]rawptr,
+			/*jack_port_t**/ pPortsPlayback: [^]rawptr,
+			/*jack_port_t**/ pPortsCapture:  [^]rawptr,
 			pIntermediaryBufferPlayback: [^]f32, /* Typed as a float because JACK is always floating point. */
 			pIntermediaryBufferCapture: [^]f32,
 		} when SUPPORT_JACK else struct {}),
@@ -1079,6 +1147,7 @@ device :: struct {
 			isSwitchingCaptureDevice: b32,    /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
 			pRouteChangeHandler: rawptr,             /* Only used on mobile platforms. Obj-C object for handling route changes. */
 		} when SUPPORT_COREAUDIO else struct {}),
+
 		sndio: (struct {
 			handlePlayback: rawptr,
 			handleCapture: rawptr,
@@ -1099,6 +1168,10 @@ device :: struct {
 		aaudio: (struct {
 			/*AAudioStream**/ pStreamPlayback: rawptr,
 			/*AAudioStream**/ pStreamCapture: rawptr,
+			usage: aaudio_usage,
+			contentType: aaudio_content_type,
+			inputPreset: aaudio_input_preset,
+			noAutoStartAfterReroute: b32,
 		} when SUPPORT_AAUDIO else struct {}),
 
 		opensl: (struct {

File diff suppressed because it is too large
+ 2204 - 274
vendor/miniaudio/doc.odin


+ 300 - 0
vendor/miniaudio/effects.odin

@@ -0,0 +1,300 @@
+package miniaudio
+
+import c "core:c/libc"
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
+
+/*
+Delay
+*/
+delay_config :: struct {
+	channels:      u32,
+	sampleRate:    u32,
+	delayInFrames: u32,
+	delayStart:    b32,    /* Set to true to delay the start of the output; false otherwise. */
+	wet:           f32,    /* 0..1. Default = 1. */
+	dry:           f32,    /* 0..1. Default = 1. */
+	decay:         f32,    /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */
+}
+
+delay :: struct {
+	config: delay_config,
+	cursor: u32,               /* Feedback is written to this cursor. Always equal or in front of the read cursor. */
+	bufferSizeInFrames: u32,   /* The maximum of config.startDelayInFrames and config.feedbackDelayInFrames. */
+	pBuffer: [^]f32,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	delay_config_init :: proc(channels, sampleRate, delayInFrames: u32, decay: f32) -> delay_config ---
+
+	delay_init               :: proc(pConfig: ^delay_config, pAllocationCallbacks: ^allocation_callbacks, pDelay: ^delay) -> result ---
+	delay_uninit             :: proc(pDelay: ^delay, pAllocationCallbacks: ^allocation_callbacks) ---
+	delay_process_pcm_frames :: proc(pDelay: ^delay, pFramesOut, pFramesIn: rawptr, frameCount: u32) -> result ---
+	delay_set_wet            :: proc(pDelay: ^delay, value: f32) ---
+	delay_get_wet            :: proc(pDelay: ^delay) -> f32 ---
+	delay_set_dry            :: proc(pDelay: ^delay, value: f32) ---
+	delay_get_dry            :: proc(pDelay: ^delay) -> f32 ---
+	delay_set_decay          :: proc(pDelay: ^delay, value: f32) ---
+	delay_get_decay          :: proc(pDelay: ^delay) -> f32 ---
+}
+
+
+/* Gainer for smooth volume changes. */
+gainer_config :: struct {
+	channels: u32,
+	smoothTimeInFrames: u32,
+}
+
+gainer :: struct {
+	config:    gainer_config,
+	t:         u32,
+	pOldGains: [^]f32,
+	pNewGains: [^]f32,
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	gainer_config_init :: proc(channels, smoothTimeInFrames: u32) -> gainer_config ---
+
+	gainer_get_heap_size      :: proc(pConfig: ^gainer_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	gainer_init_preallocated  :: proc(pConfig: ^gainer_config, pHeap: rawptr, pGainer: ^gainer) -> result ---
+	gainer_init               :: proc(pConfig: ^gainer_config, pAllocationCallbacks: ^allocation_callbacks, pGainer: ^gainer) -> result ---
+	gainer_uninit             :: proc(pGainer: ^gainer, pAllocationCallbacks: ^allocation_callbacks) ---
+	gainer_process_pcm_frames :: proc(pGainer: ^gainer, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
+	gainer_set_gain           :: proc(pGainer: ^gainer, newGain: f32) -> result ---
+	gainer_set_gains          :: proc(pGainer: ^gainer, pNewGains: [^]f32) -> result ---
+}
+
+
+/* Stereo panner. */
+pan_mode :: enum c.int {
+	balance = 0,    /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */
+	pan,            /* A true pan. The sound from one side will "move" to the other side and blend with it. */
+}
+
+panner_config :: struct {
+	format:   format,
+	channels: u32,
+	mode:     pan_mode,
+	pan:      f32,
+}
+
+panner :: struct {
+	format:   format,
+	channels: u32,
+	mode:     pan_mode,
+	pan:      f32,  /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	panner_config_init :: proc(format: format, channels: u32) -> panner_config ---
+
+	panner_init               :: proc(pConfig: ^panner_config, pPanner: ^panner) -> result ---
+	panner_process_pcm_frames :: proc(pPanner: ^panner, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result ---
+	panner_set_mode           :: proc(pPanner: ^panner, mode: pan_mode) ---
+	panner_get_mode           :: proc(pPanner: ^panner) -> pan_mode ---
+	panner_set_pan            :: proc(pPanner: ^panner, pan: f32) ---
+	panner_get_pan            :: proc(pPanner: ^panner) -> f32 ---
+}
+
+
+/* Fader. */
+fader_config :: struct {
+	format:     format,
+	channels:   u32,
+	sampleRate: u32,
+}
+
+fader :: struct {
+	config:         fader_config,
+	volumeBeg:      f32,    /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */
+	volumeEnd:      f32,
+	lengthInFrames: u64,    /* The total length of the fade. */
+	cursorInFrames: u64,    /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	fader_config_init :: proc(format: format, channels, sampleRate: u32) -> fader_config ---
+
+	fader_init               :: proc(pConfig: ^fader_config, pFader: ^fader) -> result ---
+	fader_process_pcm_frames :: proc(pFader: ^fader, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result ---
+	fader_get_data_format    :: proc(pFader: ^fader, pFormat: ^format, pChannels, pSampleRate: ^u32) ---
+	fader_set_fade           :: proc(pFader: ^fader, volumeBeg, volumeEnd: f32, lengthInFrames: u64) ---
+	fader_get_current_volume :: proc(pFader: ^fader) -> f32 ---
+}
+
+
+/* Spatializer. */
+vec3f :: struct {
+	x: f32,
+	y: f32,
+	z: f32,
+}
+
+attenuation_model :: enum c.int {
+	none,          /* No distance attenuation and no spatialization. */
+	inverse,       /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
+	linear,        /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */
+	exponential,   /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */
+}
+
+positioning :: enum c.int {
+	absolute,
+	relative,
+}
+
+handedness :: enum c.int {
+	right,
+	left,
+}
+
+spatializer_listener_config :: struct {
+	channelsOut:             u32,
+	pChannelMapOut:          [^]channel,
+	handedness:              handedness,   /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
+	coneInnerAngleInRadians: f32,
+	coneOuterAngleInRadians: f32,
+	coneOuterGain:           f32,
+	speedOfSound:            f32,
+	worldUp:                 vec3f,
+}
+
+spatializer_listener :: struct {
+		config:    spatializer_listener_config,
+		position:  vec3f,  /* The absolute position of the listener. */
+		direction: vec3f,  /* The direction the listener is facing. The world up vector is config.worldUp. */
+		velocity:  vec3f,
+		isEnabled: b32,
+
+		/* Memory management. */
+		_ownsHeap: b32,
+		_pHeap:    rawptr,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	spatializer_listener_config_init :: proc(channelsOut: u32) -> spatializer_listener_config ---
+
+	spatializer_listener_get_heap_size      :: proc(pConfig: ^spatializer_listener_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	spatializer_listener_init_preallocated  :: proc(pConfig: ^spatializer_listener_config, pHeap: rawptr, pListener: ^spatializer_listener) -> result ---
+	spatializer_listener_init               :: proc(pConfig: ^spatializer_listener_config, pAllocationCallbacks: ^allocation_callbacks, pListener: ^spatializer_listener) -> result ---
+	spatializer_listener_uninit             :: proc(pListener: ^spatializer_listener, pAllocationCallbacks: ^allocation_callbacks) ---
+	spatializer_listener_get_channel_map    :: proc(pListener: ^spatializer_listener) -> ^channel ---
+	spatializer_listener_set_cone           :: proc(pListener: ^spatializer_listener, innerAngleInRadians, outerAngleInRadians, outerGain: f32) ---
+	spatializer_listener_get_cone           :: proc(pListener: ^spatializer_listener, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) ---
+	spatializer_listener_set_position       :: proc(pListener: ^spatializer_listener, x, y, z: f32) ---
+	spatializer_listener_get_position       :: proc(pListener: ^spatializer_listener) -> vec3f ---
+	spatializer_listener_set_direction      :: proc(pListener: ^spatializer_listener, x, y, z: f32) ---
+	spatializer_listener_get_direction      :: proc(pListener: ^spatializer_listener) -> vec3f ---
+	spatializer_listener_set_velocity       :: proc(pListener: ^spatializer_listener, x, y, z: f32) ---
+	spatializer_listener_get_velocity       :: proc(pListener: ^spatializer_listener) -> vec3f ---
+	spatializer_listener_set_speed_of_sound :: proc(pListener: ^spatializer_listener, speedOfSound: f32) ---
+	spatializer_listener_get_speed_of_sound :: proc(pListener: ^spatializer_listener) -> f32 ---
+	spatializer_listener_set_world_up       :: proc(pListener: ^spatializer_listener, x, y, z: f32) ---
+	spatializer_listener_get_world_up       :: proc(pListener: ^spatializer_listener) -> vec3f ---
+	spatializer_listener_set_enabled        :: proc(pListener: ^spatializer_listener, isEnabled: b32) ---
+	spatializer_listener_is_enabled         :: proc(pListener: ^spatializer_listener) -> b32 ---
+}
+
+spatializer_config :: struct {
+	channelsIn:                   u32,
+	channelsOut:                  u32,
+	pChannelMapIn:                [^]channel,
+	attenuationModel:             attenuation_model,
+	positioning:                  positioning,
+	handedness:                   handedness,    /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
+	minGain:                      f32,
+	maxGain:                      f32,
+	minDistance:                  f32,
+	maxDistance:                  f32,
+	rolloff:                      f32,
+	coneInnerAngleInRadians:      f32,
+	coneOuterAngleInRadians:      f32,
+	coneOuterGain:                f32,
+	dopplerFactor:                f32,    /* Set to 0 to disable doppler effect. */
+	directionalAttenuationFactor: f32,    /* Set to 0 to disable directional attenuation. */
+	gainSmoothTimeInFrames:       u32,    /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
+}
+
+spatializer :: struct {
+		channelsIn:                   u32,
+		channelsOut:                  u32,
+		pChannelMapIn:                [^]channel,
+		attenuationModel:             attenuation_model,
+		positioning:                  positioning,
+		handedness:                   handedness,    /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */
+		minGain:                      f32,
+		maxGain:                      f32,
+		minDistance:                  f32,
+		maxDistance:                  f32,
+		rolloff:                      f32,
+		coneInnerAngleInRadians:      f32,
+		coneOuterAngleInRadians:      f32,
+		coneOuterGain:                f32,
+		dopplerFactor:                f32,      /* Set to 0 to disable doppler effect. */
+		directionalAttenuationFactor: f32,      /* Set to 0 to disable directional attenuation. */
+		gainSmoothTimeInFrames:       u32,      /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
+		position:                     vec3f,
+		direction:                    vec3f,
+		velocity:                     vec3f,    /* For doppler effect. */
+		dopplerPitch:                 f32,      /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */
+		gainer:                       gainer,   /* For smooth gain transitions. */
+		pNewChannelGainsOut:          [^]f32,     /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */
+
+		/* Memory management. */
+		_pHeap:    rawptr,
+		_ownsHeap: b32,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	spatializer_config_init :: proc(channelsIn, channelsOut: u32) -> spatializer_config ---
+
+	spatializer_get_heap_size                       :: proc(pConfig: ^spatializer_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	spatializer_init_preallocated                   :: proc(pConfig: ^spatializer_config, pHeap: rawptr, pSpatializer: ^spatializer) -> result ---
+	spatializer_init                                :: proc(pConfig: ^spatializer_config, pAllocationCallbacks: ^allocation_callbacks, pSpatializer: ^spatializer) -> result ---
+	spatializer_uninit                              :: proc(pSpatializer: ^spatializer, pAllocationCallbacks: ^allocation_callbacks) ---
+	spatializer_process_pcm_frames                  :: proc(pSpatializer: ^spatializer, pListener: ^spatializer_listener, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result ---
+	spatializer_get_input_channels                  :: proc(pSpatializer: ^spatializer) -> u32 ---
+	spatializer_get_output_channels                 :: proc(pSpatializer: ^spatializer) -> u32 ---
+	spatializer_set_attenuation_model               :: proc(pSpatializer: ^spatializer, attenuationModel: attenuation_model) ---
+	spatializer_get_attenuation_model               :: proc(pSpatializer: ^spatializer) -> attenuation_model ---
+	spatializer_set_positioning                     :: proc(pSpatializer: ^spatializer, positioning: positioning) ---
+	spatializer_get_positioning                     :: proc(pSpatializer: ^spatializer) -> positioning ---
+	spatializer_set_rolloff                         :: proc(pSpatializer: ^spatializer, rolloff: f32) ---
+	spatializer_get_rolloff                         :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_min_gain                        :: proc(pSpatializer: ^spatializer, minGain: f32) ---
+	spatializer_get_min_gain                        :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_max_gain                        :: proc(pSpatializer: ^spatializer, maxGain: f32) ---
+	spatializer_get_max_gain                        :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_min_distance                    :: proc(pSpatializer: ^spatializer, minDistance: f32) ---
+	spatializer_get_min_distance                    :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_max_distance                    :: proc(pSpatializer: ^spatializer, maxDistance: f32) ---
+	spatializer_get_max_distance                    :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_cone                            :: proc(pSpatializer: ^spatializer, innerAngleInRadians, outerAngleInRadians, outerGain: f32) ---
+	spatializer_get_cone                            :: proc(pSpatializer: ^spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) ---
+	spatializer_set_doppler_factor                  :: proc(pSpatializer: ^spatializer, dopplerFactor: f32) ---
+	spatializer_get_doppler_factor                  :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_directional_attenuation_factor  :: proc(pSpatializer: ^spatializer, directionalAttenuationFactor: f32) ---
+	spatializer_get_directional_attenuation_factor  :: proc(pSpatializer: ^spatializer) -> f32 ---
+	spatializer_set_position                        :: proc(pSpatializer: ^spatializer, x, y, z: f32) ---
+	spatializer_get_position                        :: proc(pSpatializer: ^spatializer) -> vec3f ---
+	spatializer_set_direction                       :: proc(pSpatializer: ^spatializer, x, y, z: f32) ---
+	spatializer_get_direction                       :: proc(pSpatializer: ^spatializer) -> vec3f ---
+	spatializer_set_velocity                        :: proc(pSpatializer: ^spatializer, x, y, z: f32) ---
+	spatializer_get_velocity                        :: proc(pSpatializer: ^spatializer) -> vec3f ---
+	spatializer_get_relative_position_and_direction :: proc(pSpatializer: ^spatializer, pListener: ^spatializer_listener, pRelativePos, pRelativeDir: ^vec3f) ---
+}

+ 15 - 8
vendor/miniaudio/encoding.odin

@@ -19,14 +19,14 @@ Encoders do not perform any format conversion for you. If your target format doe
 
 ************************************************************************************************************************************************************/
 
-encoder_write_proc            :: proc "c" (pEncoder: ^encoder, pBufferIn: rawptr, bytesToWrite: c.size_t) -> c.size_t        /* Returns the number of bytes written. */
-encoder_seek_proc             :: proc "c" (pEncoder: ^encoder, byteOffset: c.int, origin: seek_origin) -> b32
+encoder_write_proc            :: proc "c" (pEncoder: ^encoder, pBufferIn: rawptr, bytesToWrite: c.size_t, pBytesWritten: ^c.size_t) -> result
+encoder_seek_proc             :: proc "c" (pEncoder: ^encoder, offset: i64, origin: seek_origin) -> result
 encoder_init_proc             :: proc "c" (pEncoder: ^encoder) -> result
-encoder_uninit_proc           :: proc "c" (pEncoder: ^encoder)      
-encoder_write_pcm_frames_proc :: proc "c" (pEncoder: ^encoder, pFramesIn: rawptr, frameCount: u64) -> u64
+encoder_uninit_proc           :: proc "c" (pEncoder: ^encoder)
+encoder_write_pcm_frames_proc :: proc "c" (pEncoder: ^encoder, pFramesIn: rawptr, frameCount: u64, pFramesWritten: ^u64) -> result
 
 encoder_config :: struct {
-	resourceFormat:      resource_format,
+	encodingFormat:      encoding_format,
 	format:              format,
 	channels:            u32,
 	sampleRate:          u32,
@@ -42,16 +42,23 @@ encoder :: struct {
 	onWritePCMFrames: encoder_write_pcm_frames_proc,
 	pUserData:        rawptr,
 	pInternalEncoder: rawptr, /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
-	pFile:            rawptr,    /* FILE*. Only used when initialized with ma_encoder_init_file(). */
+	data: struct #raw_union {
+		vfs: struct {
+			pVFS: ^vfs,
+			file: vfs_file,
+		},
+	},
 }
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
-	encoder_config_init      :: proc(resourceFormat: resource_format, format: format, channels: u32, sampleRate: u32) -> encoder_config ---
+	encoder_config_init      :: proc(encodingFormat: encoding_format, format: format, channels: u32, sampleRate: u32) -> encoder_config ---
 
 	encoder_init             :: proc(onWrite: encoder_write_proc, onSeek: encoder_seek_proc, pUserData: rawptr, pConfig: ^encoder_config, pEncoder: ^encoder) -> result ---
+	encoder_init_vfs         :: proc(pVFS: ^vfs, pFilePath: cstring, pConfig: ^encoder_config, pEncoder: ^encoder) -> result ---
+	encoder_init_vfs_w       :: proc(pVFS: ^vfs, pFilePath: [^]c.wchar_t, pConfig: ^encoder_config, pEncoder: ^encoder) -> result ---
 	encoder_init_file        :: proc(pFilePath: cstring, pConfig: ^encoder_config, pEncoder: ^encoder) -> result ---
 	encoder_init_file_w      :: proc(pFilePath: [^]c.wchar_t, pConfig: ^encoder_config, pEncoder: ^encoder) -> result ---
 	encoder_uninit           :: proc(pEncoder: ^encoder) ---
-	encoder_write_pcm_frames :: proc(pEncoder: ^encoder, FramesIn: rawptr, frameCount: u64) -> u64 ---
+	encoder_write_pcm_frames :: proc(pEncoder: ^encoder, FramesIn: rawptr, frameCount: u64, pFramesWritten: ^u64) -> result ---
 }

+ 341 - 0
vendor/miniaudio/engine.odin

@@ -0,0 +1,341 @@
+package miniaudio
+
+import "core:c"
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
+
+/************************************************************************************************************************************************************
+
+Engine
+
+************************************************************************************************************************************************************/
+
+/* Sound flags. */
+sound_flags :: enum c.int {
+	STREAM                = 0x00000001,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
+	DECODE                = 0x00000002,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
+	ASYNC                 = 0x00000004,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
+	WAIT_INIT             = 0x00000008,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
+	NO_DEFAULT_ATTACHMENT = 0x00000010,   /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
+	NO_PITCH              = 0x00000020,   /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
+	NO_SPATIALIZATION     = 0x00000040,   /* Disable spatialization. */
+}
+
+ENGINE_MAX_LISTENERS :: 4
+
+LISTENER_INDEX_CLOSEST :: 255
+
+engine_node_type :: enum c.int {
+	sound,
+	group,
+}
+
+engine_node_config :: struct {
+	pEngine:                  ^engine,
+	type:                     engine_node_type,
+	channelsIn:               u32,
+	channelsOut:              u32,
+	sampleRate:               u32,     /* Only used when the type is set to ma_engine_node_type_sound. */
+	isPitchDisabled:          b8,      /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */
+	isSpatializationDisabled: b8,      /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */
+	pinnedListenerIndex:      u8,      /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */
+}
+
+/* Base node object for both ma_sound and ma_sound_group. */
+engine_node :: struct {
+	baseNode:                 node_base,           /* Must be the first member for compatiblity with the ma_node API. */
+	pEngine:                  ^engine,             /* A pointer to the engine. Set based on the value from the config. */
+	sampleRate:               u32,                 /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */
+	fader:                    fader,
+	resampler:                linear_resampler,    /* For pitch shift. */
+	spatializer:              spatializer,
+	panner:                   panner,
+	pitch:                    f32, /*atomic*/
+	oldPitch:                 f32,                 /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */
+	oldDopplerPitch:          f32,                 /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */
+	isPitchDisabled:          b32, /*atomic*/      /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */
+	isSpatializationDisabled: b32, /*atomic*/      /* Set to false by default. When set to false, will not have spatialisation applied. */
+	pinnedListenerIndex:      u32, /*atomic*/      /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */
+
+	/* Memory management. */
+	_ownsHeap: b8,
+	_pHeap:    rawptr,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	engine_node_config_init :: proc(pEngine: ^engine, type: engine_node_type, flags: u32) -> engine_node_config ---
+
+	engine_node_get_heap_size     :: proc(pConfig: ^engine_node_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	engine_node_init_preallocated :: proc(pConfig: ^engine_node_config, pHeap: rawptr, pEngineNode: ^engine_node) -> result ---
+	engine_node_init              :: proc(pConfig: ^engine_node_config, pAllocationCallbacks: ^allocation_callbacks, pEngineNode: ^engine_node) -> result ---
+	engine_node_uninit            :: proc(pEngineNode: ^engine_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+SOUND_SOURCE_CHANNEL_COUNT :: 0xFFFFFFFF
+
+sound_config :: struct {
+	pFilePath:                      cstring,          /* Set this to load from the resource manager. */
+	pFilePathW:                     [^]c.wchar_t,     /* Set this to load from the resource manager. */
+	pDataSource:                    ^data_source,     /* Set this to load from an existing data source. */
+	pInitialAttachment:             ^node,            /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */
+	initialAttachmentInputBusIndex: u32,              /* The index of the input bus of pInitialAttachment to attach the sound to. */
+	channelsIn:                     u32,              /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */
+	channelsOut:                    u32,              /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */
+	flags:                          u32,              /* A combination of MA_SOUND_FLAG_* flags. */
+	initialSeekPointInPCMFrames:    u64,              /* Initializes the sound such that it's seeked to this location by default. */
+	rangeBegInPCMFrames:            u64,
+	rangeEndInPCMFrames:            u64,
+	loopPointBegInPCMFrames:        u64,
+	loopPointEndInPCMFrames:        u64,
+	isLooping:                      b32,
+	pDoneFence:                     ^fence,           /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */
+}
+
+sound :: struct {
+	engineNode:     engine_node,       /* Must be the first member for compatibility with the ma_node API. */
+	pDataSource:    ^data_source,
+	seekTarget:     u64, /*atomic*/    /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */
+	atEnd:          b32, /*atomic*/
+	ownsDataSource: b8,
+
+	/*
+	We're declaring a resource manager data source object here to save us a malloc when loading a
+	sound via the resource manager, which I *think* will be the most common scenario.
+	*/
+	pResourceManagerDataSource: ^resource_manager_data_source,
+}
+
+/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */
+sound_inlined :: struct {
+	sound: sound,
+	pNext: ^sound_inlined,
+	pPrev: ^sound_inlined,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	sound_config_init :: proc() -> sound_config ---
+
+	sound_init_from_file                     :: proc(pEngine: ^engine, pFilePath: cstring, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result ---
+	sound_init_from_file_w                   :: proc(pEngine: ^engine, pFilePath: [^]c.wchar_t, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result ---
+	sound_init_copy                          :: proc(pEngine: ^engine, pExistingSound: ^sound, flags: u32, pGroup: ^sound_group, pSound: ^sound) -> result ---
+	sound_init_from_data_source              :: proc(pEngine: ^engine, pDataSource: ^data_source, flags: u32, pGroup: ^sound_group, pSound: ^sound) -> result ---
+	sound_init_ex                            :: proc(pEngine: ^engine, pConfig: ^sound_config, pSound: ^sound) -> result ---
+	sound_uninit                             :: proc(pSound: ^sound) ---
+	sound_get_engine                         :: proc(pSound: ^sound) -> ^engine ---
+	sound_get_data_source                    :: proc(pSound: ^sound) -> ^data_source ---
+	sound_start                              :: proc(pSound: ^sound) -> result ---
+	sound_stop                               :: proc(pSound: ^sound) -> result ---
+	sound_set_volume                         :: proc(pSound: ^sound, volume: f32) ---
+	sound_get_volume                         :: proc(pSound: ^sound) -> f32 ---
+	sound_set_pan                            :: proc(pSound: ^sound, pan: f32) ---
+	sound_get_pan                            :: proc(pSound: ^sound) -> f32 ---
+	sound_set_pan_mode                       :: proc(pSound: ^sound, panMode: pan_mode) ---
+	sound_get_pan_mode                       :: proc(pSound: ^sound) -> pan_mode ---
+	sound_set_pitch                          :: proc(pSound: ^sound, pitch: f32) ---
+	sound_get_pitch                          :: proc(pSound: ^sound) -> f32 ---
+	sound_set_spatialization_enabled         :: proc(pSound: ^sound, enabled: b32) ---
+	sound_is_spatialization_enabled          :: proc(pSound: ^sound) -> b32 ---
+	sound_set_pinned_listener_index          :: proc(pSound: ^sound, listenerIndex: u32) ---
+	sound_get_pinned_listener_index          :: proc(pSound: ^sound) -> u32 ---
+	sound_get_listener_index                 :: proc(pSound: ^sound) -> u32 ---
+	sound_get_direction_to_listener          :: proc(pSound: ^sound) -> vec3f ---
+	sound_set_position                       :: proc(pSound: ^sound, x, y, z: f32) ---
+	sound_get_position                       :: proc(pSound: ^sound) -> vec3f ---
+	sound_set_direction                      :: proc(pSound: ^sound, x, y, z: f32) ---
+	sound_get_direction                      :: proc(pSound: ^sound) -> vec3f ---
+	sound_set_velocity                       :: proc(pSound: ^sound, x, y, z: f32) ---
+	sound_get_velocity                       :: proc(pSound: ^sound) -> vec3f ---
+	sound_set_attenuation_model              :: proc(pSound: ^sound, attenuationModel: attenuation_model) ---
+	sound_get_attenuation_model              :: proc(pSound: ^sound) -> attenuation_model ---
+	sound_set_positioning                    :: proc(pSound: ^sound, positioning: positioning) ---
+	sound_get_positioning                    :: proc(pSound: ^sound) -> positioning ---
+	sound_set_rolloff                        :: proc(pSound: ^sound, rolloff: f32) ---
+	sound_get_rolloff                        :: proc(pSound: ^sound) -> f32 ---
+	sound_set_min_gain                       :: proc(pSound: ^sound, minGain: f32) ---
+	sound_get_min_gain                       :: proc(pSound: ^sound) -> f32 ---
+	sound_set_max_gain                       :: proc(pSound: ^sound, maxGain: f32) ---
+	sound_get_max_gain                       :: proc(pSound: ^sound) -> f32 ---
+	sound_set_min_distance                   :: proc(pSound: ^sound, minDistance: f32) ---
+	sound_get_min_distance                   :: proc(pSound: ^sound) -> f32 ---
+	sound_set_max_distance                   :: proc(pSound: ^sound, maxDistance: f32) ---
+	sound_get_max_distance                   :: proc(pSound: ^sound) -> f32 ---
+	sound_set_cone                           :: proc(pSound: ^sound, innerAngleInRadians, outerAngleInRadians, outerGain: f32) ---
+	sound_get_cone                           :: proc(pSound: ^sound, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) ---
+	sound_set_doppler_factor                 :: proc(pSound: ^sound, dopplerFactor: f32) ---
+	sound_get_doppler_factor                 :: proc(pSound: ^sound) -> f32 ---
+	sound_set_directional_attenuation_factor :: proc(pSound: ^sound, directionalAttenuationFactor: f32) ---
+	sound_get_directional_attenuation_factor :: proc(pSound: ^sound) -> f32 ---
+	sound_set_fade_in_pcm_frames             :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) ---
+	sound_set_fade_in_milliseconds           :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) ---
+	sound_get_current_fade_volume            :: proc(pSound: ^sound) -> f32 ---
+	sound_set_start_time_in_pcm_frames       :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) ---
+	sound_set_start_time_in_milliseconds     :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) ---
+	sound_set_stop_time_in_pcm_frames        :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) ---
+	sound_set_stop_time_in_milliseconds      :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) ---
+	sound_is_playing                         :: proc(pSound: ^sound) -> b32 ---
+	sound_get_time_in_pcm_frames             :: proc(pSound: ^sound) -> u64 ---
+	sound_set_looping                        :: proc(pSound: ^sound, isLooping: b32) ---
+	sound_is_looping                         :: proc(pSound: ^sound) -> b32 ---
+	sound_at_end                             :: proc(pSound: ^sound) -> b32 ---
+	sound_seek_to_pcm_frame                  :: proc(pSound: ^sound, frameIndex: u64) -> result --- /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
+	sound_get_data_format                    :: proc(pSound: ^sound, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: ^channel, channelMapCap: c.size_t) -> result ---
+	sound_get_cursor_in_pcm_frames           :: proc(pSound: ^sound, pCursor: ^u64) -> result ---
+	sound_get_length_in_pcm_frames           :: proc(pSound: ^sound, pLength: ^u64) -> result ---
+	sound_get_cursor_in_seconds              :: proc(pSound: ^sound, pCursor: ^f32) -> result ---
+	sound_get_length_in_seconds              :: proc(pSound: ^sound, pLength: ^f32) -> result ---
+}
+
+
+/* A sound group is just a sound. */
+sound_group_config :: distinct sound_config
+sound_group        :: distinct sound
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	sound_group_config_init :: proc() -> sound_group_config ---
+
+	sound_group_init                               :: proc(pEngine: ^engine, flags: u32, pParentGroup, pGroup: ^sound_group) -> result ---
+	sound_group_init_ex                            :: proc(pEngine: ^engine, pConfig: ^sound_group_config, pGroup: ^sound_group) -> result ---
+	sound_group_uninit                             :: proc(pGroup: ^sound_group) ---
+	sound_group_get_engine                         :: proc(pGroup: ^sound_group) -> ^engine ---
+	sound_group_start                              :: proc(pGroup: ^sound_group) -> result ---
+	sound_group_stop                               :: proc(pGroup: ^sound_group) -> result ---
+	sound_group_set_volume                         :: proc(pGroup: ^sound_group, volume: f32) ---
+	sound_group_get_volume                         :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_pan                            :: proc(pGroup: ^sound_group, pan: f32) ---
+	sound_group_get_pan                            :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_pan_mode                       :: proc(pGroup: ^sound_group, panMode: pan_mode) ---
+	sound_group_get_pan_mode                       :: proc(pGroup: ^sound_group) -> pan_mode ---
+	sound_group_set_pitch                          :: proc(pGroup: ^sound_group, pitch: f32) ---
+	sound_group_get_pitch                          :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_spatialization_enabled         :: proc(pGroup: ^sound_group, enabled: b32) ---
+	sound_group_is_spatialization_enabled          :: proc(pGroup: ^sound_group) -> b32 ---
+	sound_group_set_pinned_listener_index          :: proc(pGroup: ^sound_group, listenerIndex: u32) ---
+	sound_group_get_pinned_listener_index          :: proc(pGroup: ^sound_group) -> u32 ---
+	sound_group_get_listener_index                 :: proc(pGroup: ^sound_group) -> u32 ---
+	sound_group_get_direction_to_listener          :: proc(pGroup: ^sound_group) -> vec3f ---
+	sound_group_set_position                       :: proc(pGroup: ^sound_group, x, y, z: f32) ---
+	sound_group_get_position                       :: proc(pGroup: ^sound_group) -> vec3f ---
+	sound_group_set_direction                      :: proc(pGroup: ^sound_group, x, y, z: f32) ---
+	sound_group_get_direction                      :: proc(pGroup: ^sound_group) -> vec3f ---
+	sound_group_set_velocity                       :: proc(pGroup: ^sound_group, x, y, z: f32) ---
+	sound_group_get_velocity                       :: proc(pGroup: ^sound_group) -> vec3f ---
+	sound_group_set_attenuation_model              :: proc(pGroup: ^sound_group, attenuationModel: attenuation_model) ---
+	sound_group_get_attenuation_model              :: proc(pGroup: ^sound_group) -> attenuation_model ---
+	sound_group_set_positioning                    :: proc(pGroup: ^sound_group, positioning: positioning) ---
+	sound_group_get_positioning                    :: proc(pGroup: ^sound_group) -> positioning ---
+	sound_group_set_rolloff                        :: proc(pGroup: ^sound_group, rolloff: f32) ---
+	sound_group_get_rolloff                        :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_min_gain                       :: proc(pGroup: ^sound_group, minGain: f32) ---
+	sound_group_get_min_gain                       :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_max_gain                       :: proc(pGroup: ^sound_group, maxGain: f32) ---
+	sound_group_get_max_gain                       :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_min_distance                   :: proc(pGroup: ^sound_group, minDistance: f32) ---
+	sound_group_get_min_distance                   :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_max_distance                   :: proc(pGroup: ^sound_group, maxDistance: f32) ---
+	sound_group_get_max_distance                   :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_cone                           :: proc(pGroup: ^sound_group, innerAngleInRadians, outerAngleInRadians, outerGain: f32) ---
+	sound_group_get_cone                           :: proc(pGroup: ^sound_group, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) ---
+	sound_group_set_doppler_factor                 :: proc(pGroup: ^sound_group, dopplerFactor: f32) ---
+	sound_group_get_doppler_factor                 :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_directional_attenuation_factor :: proc(pGroup: ^sound_group, directionalAttenuationFactor: f32) ---
+	sound_group_get_directional_attenuation_factor :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_fade_in_pcm_frames             :: proc(pGroup: ^sound_group, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) ---
+	sound_group_set_fade_in_milliseconds           :: proc(pGroup: ^sound_group, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) ---
+	sound_group_get_current_fade_volume            :: proc(pGroup: ^sound_group) -> f32 ---
+	sound_group_set_start_time_in_pcm_frames       :: proc(pGroup: ^sound_group, absoluteGlobalTimeInFrames: u64) ---
+	sound_group_set_start_time_in_milliseconds     :: proc(pGroup: ^sound_group, absoluteGlobalTimeInMilliseconds: u64) ---
+	sound_group_set_stop_time_in_pcm_frames        :: proc(pGroup: ^sound_group, absoluteGlobalTimeInFrames: u64) ---
+	sound_group_set_stop_time_in_milliseconds      :: proc(pGroup: ^sound_group, absoluteGlobalTimeInMilliseconds: u64) ---
+	sound_group_is_playing                         :: proc(pGroup: ^sound_group) -> b32 ---
+	sound_group_get_time_in_pcm_frames             :: proc(pGroup: ^sound_group) -> u64 ---
+}
+
+
+engine_config :: struct {
+	pResourceManager:             ^resource_manager,      /* Can be null in which case a resource manager will be created for you. */
+	pContext:                     ^context_type,
+	pDevice:                      ^device,                /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
+	pPlaybackDeviceID:            ^device_id,             /* The ID of the playback device to use with the default listener. */
+	pLog:                         ^log,                   /* When set to NULL, will use the context's log. */
+	listenerCount:                u32,                    /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
+	channels:                     u32,                    /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */
+	sampleRate:                   u32,                    /* The sample rate. When set to 0 will use the native channel count of the device. */
+	periodSizeInFrames:           u32,                    /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/
+	periodSizeInMilliseconds:     u32,                    /* Used if periodSizeInFrames is unset. */
+	gainSmoothTimeInFrames:       u32,                    /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
+	gainSmoothTimeInMilliseconds: u32,                    /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
+	allocationCallbacks:          allocation_callbacks,
+	noAutoStart:                  b32,                    /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */
+	noDevice:                     b32,                    /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */
+	monoExpansionMode:            mono_expansion_mode,    /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
+	pResourceManagerVFS:          ^vfs,                   /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */
+}
+
+engine :: struct {
+	nodeGraph:              node_graph,                   /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */
+	pResourceManager:       ^resource_manager,
+	pDevice:                ^device,                      /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
+	pLog:                   ^log,
+	sampleRate:             u32,
+	listenerCount:          u32,
+	listeners:              [ENGINE_MAX_LISTENERS]spatializer_listener,
+	allocationCallbacks:    allocation_callbacks,
+	ownsResourceManager:    b8,
+	ownsDevice:             b8,
+	inlinedSoundLock:       spinlock,                     /* For synchronizing access so the inlined sound list. */
+	pInlinedSoundHead:      ^sound_inlined,               /* The first inlined sound. Inlined sounds are tracked in a linked list. */
+	inlinedSoundCount:      u32, /*atomic*/               /* The total number of allocated inlined sound objects. Used for debugging. */
+	gainSmoothTimeInFrames: u32,                          /* The number of frames to interpolate the gain of spatialized sounds across. */
+	monoExpansionMode:      mono_expansion_mode,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	engine_config_init :: proc() -> engine_config ---
+
+	engine_init                 :: proc(pConfig: ^engine_config, pEngine: ^engine) -> result ---
+	engine_uninit               :: proc(pEngine: ^engine) ---
+	engine_read_pcm_frames      :: proc(pEngine: ^engine, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	engine_get_node_graph       :: proc(pEngine: ^engine) -> ^node_graph ---
+	engine_get_resource_manager :: proc(pEngine: ^engine) -> ^resource_manager ---
+	engine_get_device           :: proc(pEngine: ^engine) -> ^device ---
+	engine_get_log              :: proc(pEngine: ^engine) -> ^log ---
+	engine_get_endpoint         :: proc(pEngine: ^engine) -> ^node ---
+	engine_get_time             :: proc(pEngine: ^engine) -> u64 ---
+	engine_set_time             :: proc(pEngine: ^engine, globalTime: u64) -> result ---
+	engine_get_channels         :: proc(pEngine: ^engine) -> u32 ---
+	engine_get_sample_rate      :: proc(pEngine: ^engine) -> u32 ---
+	
+	engine_start       :: proc(pEngine: ^engine) -> result ---
+	engine_stop        :: proc(pEngine: ^engine) -> result ---
+	engine_set_volume  :: proc(pEngine: ^engine, volume: f32) -> result ---
+	engine_set_gain_db :: proc(pEngine: ^engine, gainDB: f32) -> result ---
+	
+	engine_get_listener_count     :: proc(pEngine: ^engine) -> u32 ---
+	engine_find_closest_listener  :: proc(pEngine: ^engine, absolutePosX, absolutePosY, absolutePosZ: f32) -> u32 ---
+	engine_listener_set_position  :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) ---
+	engine_listener_get_position  :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f ---
+	engine_listener_set_direction :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) ---
+	engine_listener_get_direction :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f ---
+	engine_listener_set_velocity  :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) ---
+	engine_listener_get_velocity  :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f ---
+	engine_listener_set_cone      :: proc(pEngine: ^engine, listenerIndex: u32, innerAngleInRadians, outerAngleInRadians, outerGain: f32) ---
+	engine_listener_get_cone      :: proc(pEngine: ^engine, listenerIndex: u32, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) ---
+	engine_listener_set_world_up  :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) ---
+	engine_listener_get_world_up  :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f ---
+	engine_listener_set_enabled   :: proc(pEngine: ^engine, listenerIndex: u32, isEnabled: b32) ---
+	engine_listener_is_enabled    :: proc(pEngine: ^engine, listenerIndex: u32) -> b32 ---
+	
+	engine_play_sound_ex :: proc(pEngine: ^engine, pFilePath: cstring, pNode: ^node, nodeInputBusIndex: u32) -> result ---
+	engine_play_sound    :: proc(pEngine: ^engine, pFilePath: cstring, pGroup: ^sound_group) -> result ---   /* Fire and forget. */
+}

+ 99 - 30
vendor/miniaudio/filtering.odin

@@ -1,5 +1,7 @@
 package miniaudio
 
+import c "core:c/libc"
+
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 } else when ODIN_OS == .Linux {
@@ -19,14 +21,14 @@ biquad_coefficient :: struct #raw_union {
 } 
 
 biquad_config :: struct {
-	format: format,
+	format:   format,
 	channels: u32,
-	b0: f64,
-	b1: f64,
-	b2: f64,
-	a0: f64,
-	a1: f64,
-	a2: f64,
+	b0:       f64,
+	b1:       f64,
+	b2:       f64,
+	a0:       f64,
+	a1:       f64,
+	a2:       f64,
 }
 
 biquad :: struct {
@@ -37,17 +39,25 @@ biquad :: struct {
 	b2:       biquad_coefficient,
 	a1:       biquad_coefficient,
 	a2:       biquad_coefficient,
-	r1:       [MAX_CHANNELS]biquad_coefficient,
-	r2:       [MAX_CHANNELS]biquad_coefficient,
+	pR1:      ^biquad_coefficient,
+	pR2:      ^biquad_coefficient,
+
+	/* Memory management. */
+	_pHeap: 	 rawptr,
+	_ownsHeap: b32,
 } 
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
 	biquad_config_init :: proc(format: format, channels: u32, b0, b1, b2, a0, a1, a2: f64) -> biquad_config ---
 
-	biquad_init               :: proc(pConfig: ^biquad_config, pBQ: ^biquad) -> result ---
+	biquad_get_heap_size      :: proc(pConfig: ^biquad_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	biquad_init_preallocated  :: proc(pConfig: ^biquad_config, pHeap: rawptr, pBQ: ^biquad) -> result ---
+	biquad_init               :: proc(pConfig: ^biquad_config, pAllocationCallbacks: ^allocation_callbacks, pBQ: ^biquad) -> result ---
+	biquad_uninit             :: proc(pBQ: ^biquad, pAllocationCallbacks: ^allocation_callbacks) ---
 	biquad_reinit             :: proc(pConfig: ^biquad_config, pBQ: ^biquad) -> result ---
-	biquad_process_pcm_frames :: proc(pBQ: ^biquad, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
+	biquad_clear_cache        :: proc(pBQ: ^biquad) -> result ---
+	biquad_process_pcm_frames :: proc(pBQ: ^biquad, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result ---
 	biquad_get_latency        :: proc(pBQ: ^biquad) -> u32 ---
 }
 
@@ -70,7 +80,11 @@ lpf1 :: struct {
 	format:   format,
 	channels: u32,
 	a:        biquad_coefficient,
-	r1:       [MAX_CHANNELS]biquad_coefficient,
+	pR1:      ^biquad_coefficient,
+
+	/* Memory management. */
+	_pHeap: 	 rawptr,
+	_ownsHeap: b32,
 }
 
 lpf2 :: struct {
@@ -91,8 +105,12 @@ lpf :: struct {
 	sampleRate: u32,
 	lpf1Count:  u32,
 	lpf2Count:  u32,
-	lpf1:       [1]lpf1,
-	lpf2:       [MAX_FILTER_ORDER/2]lpf2,
+	pLPF1:      ^lpf1,
+	pLPF2:      ^lpf2,
+
+	/* Memory management. */
+	_pHeap: 	 rawptr,
+	_ownsHeap: b32,
 }
 
 
@@ -101,20 +119,32 @@ foreign lib {
 	lpf1_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64) -> lpf1_config ---
 	lpf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency, q: f64) -> lpf2_config ---
 
-	lpf1_init               :: proc(pConfig: ^lpf1_config, pLPF: ^lpf1) -> result ---
+	lpf1_get_heap_size      :: proc(pConfig: ^lpf1_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	lpf1_init_preallocated  :: proc(pConfig: ^lpf1_config, pHeap: rawptr, pLPF: ^lpf1) -> result ---
+	lpf1_init               :: proc(pConfig: ^lpf1_config, pAllocationCallbacks: ^allocation_callbacks, pLPF: ^lpf1) -> result ---
+	lpf1_uninit             :: proc(pLPF: ^lpf1, pAllocationCallbacks: ^allocation_callbacks) ---
 	lpf1_reinit             :: proc(pConfig: ^lpf1_config, pLPF: ^lpf1) -> result ---
+	lpf1_clear_cache        :: proc(pLPF: ^lpf1) -> result ---
 	lpf1_process_pcm_frames :: proc(pLPF: ^lpf1, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	lpf1_get_latency        :: proc(pLPF: ^lpf1) -> u32 ---
 
-	lpf2_init               :: proc(pConfig: ^lpf2_config, pLPF: ^lpf2) -> result ---
+	lpf2_get_heap_size      :: proc(pConfig: ^lpf2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	lpf2_init_preallocated  :: proc(pConfig: ^lpf2_config, pHeap: rawptr, pHPF: ^lpf2) -> result ---
+	lpf2_init               :: proc(pConfig: ^lpf2_config, pAllocationCallbacks: ^allocation_callbacks, pLPF: ^lpf2) -> result ---
+	lpf2_uninit             :: proc(pLPF: ^lpf2, pAllocationCallbacks: ^allocation_callbacks) ---
 	lpf2_reinit             :: proc(pConfig: ^lpf2_config, pLPF: ^lpf2) -> result ---
+	lpf2_clear_cache        :: proc(pLPF: ^lpf2) -> result ---
 	lpf2_process_pcm_frames :: proc(pLPF: ^lpf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	lpf2_get_latency        :: proc(pLPF: ^lpf2) -> u32 ---
 
 	lpf_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, order: u32) -> lpf_config ---
 
-	lpf_init                :: proc(pConfig: ^lpf_config, pLPF: ^lpf) -> result ---
+	lpf_get_heap_size       :: proc(pConfig: ^lpf_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	lpf_init_preallocated   :: proc(pConfig: ^lpf_config, pHeap: rawptr, pLPF: ^lpf) -> result ---
+	lpf_init                :: proc(pConfig: ^lpf_config, pAllocationCallbacks: ^allocation_callbacks, pLPF: ^lpf) -> result ---
+	lpf_uninit              :: proc(pLPF: ^lpf, pAllocationCallbacks: ^allocation_callbacks) ---
 	lpf_reinit              :: proc(pConfig: ^lpf_config, pLPF: ^lpf) -> result ---
+	lpf_clear_cache         :: proc(pLPF: ^lpf) -> result ---
 	lpf_process_pcm_frames  :: proc(pLPF: ^lpf, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	lpf_get_latency         :: proc(pLPF: ^lpf) -> u32 ---
 }
@@ -138,7 +168,11 @@ hpf1 :: struct {
 	format:   format,
 	channels: u32,
 	a:        biquad_coefficient,
-	r1:       [MAX_CHANNELS]biquad_coefficient,
+	pR1:      ^biquad_coefficient,
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
 }
 
 hpf2 :: struct {
@@ -159,8 +193,12 @@ hpf :: struct {
 	sampleRate: u32,
 	hpf1Count:  u32,
 	hpf2Count:  u32,
-	hpf1:       [1]hpf1,
-	hpf2:       [MAX_FILTER_ORDER/2]hpf2,
+	pHPF1:      ^hpf1,
+	pHPF2:      ^hpf2,
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
 }
 
 
@@ -169,19 +207,28 @@ foreign lib {
 	hpf1_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64) -> hpf1_config ---
 	hpf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency, q: f64) -> hpf2_config ---
 
-	hpf1_init               :: proc(pConfig: ^hpf1_config, pHPF: ^hpf1) -> result ---
+	hpf1_get_heap_size      :: proc(pConfig: ^hpf1_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	hpf1_init_preallocated  :: proc(pConfig: ^hpf1_config, pHeap: rawptr, pLPF: ^hpf1) -> result ---
+	hpf1_init               :: proc(pConfig: ^hpf1_config, pAllocationCallbacks: ^allocation_callbacks, pHPF: ^hpf1) -> result ---
+	hpf1_uninit             :: proc(pHPF: ^hpf1, pAllocationCallbacks: ^allocation_callbacks) ---
 	hpf1_reinit             :: proc(pConfig: ^hpf1_config, pHPF: ^hpf1) -> result ---
 	hpf1_process_pcm_frames :: proc(pHPF: ^hpf1, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	hpf1_get_latency        :: proc(pHPF: ^hpf1) -> u32 ---
 
-	hpf2_init               :: proc(pConfig: ^hpf2_config, pHPF: ^hpf2) -> result ---
+	hpf2_get_heap_size      :: proc(pConfig: ^hpf2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	hpf2_init_preallocated  :: proc(pConfig: ^hpf2_config, pHeap: rawptr, pHPF: ^hpf2) -> result ---
+	hpf2_init               :: proc(pConfig: ^hpf2_config, pAllocationCallbacks: ^allocation_callbacks, pHPF: ^hpf2) -> result ---
+	hpf2_uninit             :: proc(pHPF: ^hpf2, pAllocationCallbacks: ^allocation_callbacks) ---
 	hpf2_reinit             :: proc(pConfig: ^hpf2_config, pHPF: ^hpf2) -> result ---
 	hpf2_process_pcm_frames :: proc(pHPF: ^hpf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	hpf2_get_latency        :: proc(pHPF: ^hpf2) -> u32 ---
 
 	hpf_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, order: u32) -> hpf_config ---
 
-	hpf_init                :: proc(pConfig: ^hpf_config, pHPF: ^hpf) -> result ---
+	hpf_get_heap_size       :: proc(pConfig: ^hpf_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	hpf_init_preallocated   :: proc(pConfig: ^hpf_config, pHeap: rawptr, pLPF: ^hpf) -> result ---
+	hpf_init                :: proc(pConfig: ^hpf_config, pAllocationCallbacks: ^allocation_callbacks, pHPF: ^hpf) -> result ---
+	hpf_uninit              :: proc(pHPF: ^hpf, pAllocationCallbacks: ^allocation_callbacks) ---
 	hpf_reinit              :: proc(pConfig: ^hpf_config, pHPF: ^hpf) -> result ---
 	hpf_process_pcm_frames  :: proc(pHPF: ^hpf, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	hpf_get_latency         :: proc(pHPF: ^hpf) -> u32 ---
@@ -217,21 +264,31 @@ bpf :: struct {
 	format:    format,
 	channels:  u32,
 	bpf2Count: u32,
-	bpf2:      [MAX_FILTER_ORDER/2]bpf2,
+	pBPF2:     ^bpf2,
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
 }
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
 	bpf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, q: f64) -> bpf2_config ---
 
-	bpf2_init               :: proc(pConfig: ^bpf2_config, pBPF: ^bpf2) -> result ---
+	bpf2_get_heap_size      :: proc(pConfig: ^bpf2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	bpf2_init_preallocated  :: proc(pConfig: ^bpf2_config, pHeap: rawptr, pBPF: ^bpf2) -> result ---
+	bpf2_init               :: proc(pConfig: ^bpf2_config, pAllocationCallbacks: ^allocation_callbacks, pBPF: ^bpf2) -> result ---
+	bpf2_uninit             :: proc(pBPF: ^bpf2, pAllocationCallbacks: ^allocation_callbacks) ---
 	bpf2_reinit             :: proc(pConfig: ^bpf2_config, pBPF: ^bpf2) -> result ---
 	bpf2_process_pcm_frames :: proc(pBPF: ^bpf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	bpf2_get_latency        :: proc(pBPF: ^bpf2) -> u32 ---
 
 	bpf_config_init :: proc(format: format, channels: u32, sampleRate: u32, cutoffFrequency: f64, order: u32) -> bpf_config ---
 
-	bpf_init               :: proc(pConfig: ^bpf_config, pBPF: ^bpf) -> result ---
+	bpf_get_heap_size      :: proc(pConfig: ^bpf_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	bpf_init_preallocated  :: proc(pConfig: ^bpf_config, pHeap: rawptr, pBPF: ^bpf) -> result ---
+	bpf_init               :: proc(pConfig: ^bpf_config, pAllocationCallbacks: ^allocation_callbacks, pBPF: ^bpf) -> result ---
+	bpf_uninit             :: proc(pBPF: ^bpf, pAllocationCallbacks: ^allocation_callbacks) ---
 	bpf_reinit             :: proc(pConfig: ^bpf_config, pBPF: ^bpf) -> result ---
 	bpf_process_pcm_frames :: proc(pBPF: ^bpf, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	bpf_get_latency        :: proc(pBPF: ^bpf) -> u32 ---
@@ -260,7 +317,10 @@ notch2 :: struct {
 foreign lib {
 	notch2_config_init :: proc(format: format, channels: u32, sampleRate: u32, q: f64, frequency: f64) -> notch2_config ---
 
-	notch2_init               :: proc(pConfig: ^notch2_config, pFilter: ^notch2) -> result ---
+	notch2_get_heap_size      :: proc(pConfig: ^notch2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	notch2_init_preallocated  :: proc(pConfig: ^notch2_config, pHeap: rawptr, pFilter: ^notch2) -> result ---
+	notch2_init               :: proc(pConfig: ^notch2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^notch2) -> result ---
+	notch2_uninit             :: proc(pFilter: ^notch2, pAllocationCallbacks: ^allocation_callbacks) ---
 	notch2_reinit             :: proc(pConfig: ^notch2_config, pFilter: ^notch2) -> result ---
 	notch2_process_pcm_frames :: proc(pFilter: ^notch2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	notch2_get_latency        :: proc(pFilter: ^notch2) -> u32 ---
@@ -290,7 +350,10 @@ peak2 :: struct {
 foreign lib {
 	peak2_config_init :: proc(format: format, channels: u32, sampleRate: u32, gainDB, q, frequency: f64) -> peak2_config ---
 
-	peak2_init               :: proc(pConfig: ^peak2_config, pFilter: ^peak2) -> result ---
+	peak2_get_heap_size      :: proc(pConfig: ^peak2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	peak2_init_preallocated  :: proc(pConfig: ^peak2_config, pHeap: rawptr, pFilter: ^peak2) -> result ---
+	peak2_init               :: proc(pConfig: ^peak2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^peak2) -> result ---
+	peak2_uninit             :: proc(pFilter: ^peak2, pAllocationCallbacks: ^allocation_callbacks) ---
 	peak2_reinit             :: proc(pConfig: ^peak2_config, pFilter: ^peak2) -> result ---
 	peak2_process_pcm_frames :: proc(pFilter: ^peak2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	peak2_get_latency        :: proc(pFilter: ^peak2) -> u32 ---
@@ -320,7 +383,10 @@ loshelf2 :: struct {
 foreign lib {
 	loshelf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, gainDB, shelfSlope, frequency: f64) -> loshelf2_config ---
 
-	loshelf2_init               :: proc(pConfig: ^loshelf2_config, pFilter: ^loshelf2) -> result ---
+	loshelf2_get_heap_size      :: proc(pConfig: ^loshelf2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	loshelf2_init_preallocated  :: proc(pConfig: ^loshelf2_config, pHeap: rawptr, pFilter: ^loshelf2) -> result ---
+	loshelf2_init               :: proc(pConfig: ^loshelf2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^loshelf2) -> result ---
+	loshelf2_uninit             :: proc(pFilter: ^loshelf2, pAllocationCallbacks: ^allocation_callbacks) ---
 	loshelf2_reinit             :: proc(pConfig: ^loshelf2_config, pFilter: ^loshelf2) -> result ---
 	loshelf2_process_pcm_frames :: proc(pFilter: ^loshelf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	loshelf2_get_latency        :: proc(pFilter: ^loshelf2) -> u32 ---
@@ -350,7 +416,10 @@ hishelf2 :: struct {
 foreign lib {
 	hishelf2_config_init :: proc(format: format, channels: u32, sampleRate: u32, gainDB, shelfSlope, frequency: f64) -> hishelf2_config ---
 
-	hishelf2_init               :: proc(pConfig: ^hishelf2_config, pFilter: ^hishelf2) -> result ---
+	hishelf2_get_heap_size      :: proc(pConfig: ^hishelf2_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	hishelf2_init_preallocated  :: proc(pConfig: ^hishelf2_config, pHeap: rawptr, pFilter: ^hishelf2) -> result ---
+	hishelf2_init               :: proc(pConfig: ^hishelf2_config, pAllocationCallbacks: ^allocation_callbacks, pFilter: ^hishelf2) -> result ---
+	hishelf2_uninit             :: proc(pFilter: ^hishelf2, pAllocationCallbacks: ^allocation_callbacks) ---
 	hishelf2_reinit             :: proc(pConfig: ^hishelf2_config, pFilter: ^hishelf2) -> result ---
 	hishelf2_process_pcm_frames :: proc(pFilter: ^hishelf2, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	hishelf2_get_latency        :: proc(pFilter: ^hishelf2) -> u32 ---

+ 17 - 11
vendor/miniaudio/generation.odin

@@ -56,14 +56,18 @@ noise :: struct {
 	lcg:    lcg,
 	state: struct #raw_union {
 		pink: struct {
-			bin:          [MAX_CHANNELS][16]f64,
-			accumulation: [MAX_CHANNELS]f64,
-			counter:      [MAX_CHANNELS]u32,
+			bin:          ^[^]f64,
+			accumulation: [^]f64,
+			counter:      [^]u32,
 		},
 		brownian: struct {
-			accumulation: [MAX_CHANNELS]f64,
+			accumulation: [^]f64,
 		},
 	},
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
 }
 
 @(default_calling_convention="c", link_prefix="ma_")
@@ -72,7 +76,7 @@ foreign lib {
 
 	waveform_init              :: proc(pConfig: ^waveform_config, pWaveform: ^waveform) -> result ---
 	waveform_uninit            :: proc(pWaveform: ^waveform) ---
-	waveform_read_pcm_frames   :: proc(pWaveform: ^waveform, pFramesOut: rawptr, frameCount: u64) -> u64 ---
+	waveform_read_pcm_frames   :: proc(pWaveform: ^waveform, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
 	waveform_seek_to_pcm_frame :: proc(pWaveform: ^waveform, frameIndex: u64) -> result ---
 	waveform_set_amplitude     :: proc(pWaveform: ^waveform, amplitude: f64) -> result ---
 	waveform_set_frequency     :: proc(pWaveform: ^waveform, frequency: f64) -> result ---
@@ -81,10 +85,12 @@ foreign lib {
 
 	noise_config_init :: proc(format: format, channels: u32, type: noise_type, seed: i32, amplitude: f64) -> noise_config ---
 
-	noise_init            :: proc(pConfig: ^noise_config, pNoise: ^noise) -> result ---
-	noise_uninit          :: proc(pNoise: ^noise) ---
-	noise_read_pcm_frames :: proc(pNoise: ^noise, pFramesOut: rawptr, frameCount: u64) -> u64 ---
-	noise_set_amplitude   :: proc(pNoise: ^noise, amplitude: f64) -> result ---
-	noise_set_seed        :: proc(pNoise: ^noise, seed: i32) -> result ---
-	noise_set_type        :: proc(pNoise: ^noise, type: noise_type) -> result ---
+	noise_get_heap_size     :: proc(pConfig: ^noise_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	noise_init_preallocated :: proc(pConfig: ^noise_config, pHeap: rawptr, pNoise: ^noise) -> result ---
+	noise_init              :: proc(pConfig: ^noise_config, pAllocationCallbacks: ^allocation_callbacks, pNoise: ^noise) -> result ---
+	noise_uninit            :: proc(pNoise: ^noise, pAllocationCallbacks: ^allocation_callbacks) ---
+	noise_read_pcm_frames   :: proc(pNoise: ^noise, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	noise_set_amplitude     :: proc(pNoise: ^noise, amplitude: f64) -> result ---
+	noise_set_seed          :: proc(pNoise: ^noise, seed: i32) -> result ---
+	noise_set_type          :: proc(pNoise: ^noise, type: noise_type) -> result ---
 }

+ 239 - 0
vendor/miniaudio/job_queue.odin

@@ -0,0 +1,239 @@
+package miniaudio
+
+import c "core:c/libc"
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
+
+/*
+Slot Allocator
+--------------
+The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used
+as the insertion point for an object.
+
+Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
+
+The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:
+
+		+-----------------+-----------------+
+		| 32 Bits         | 32 Bits         |
+		+-----------------+-----------------+
+		| Reference Count | Slot Index      |
+		+-----------------+-----------------+
+*/
+slot_allocator_config :: struct {
+	capacity: u32,    /* The number of slots to make available. */
+}
+
+slot_allocator_group :: struct {
+	bitfield: u32, /*atomic*/   /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */
+}
+
+slot_allocator :: struct {
+	pGroups:  [^]slot_allocator_group,   /* Slots are grouped in chunks of 32. */
+	pSlots:   [^]u32,                    /* 32 bits for reference counting for ABA mitigation. */
+	count:    u32,                       /* Allocation count. */
+	capacity: u32,
+
+	/* Memory management. */
+	_ownsHeap: b32,
+	_pHeap:    rawptr,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	slot_allocator_config_init :: proc(capacity: u32) -> slot_allocator_config ---
+
+	slot_allocator_get_heap_size     :: proc(pConfig: ^slot_allocator_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	slot_allocator_init_preallocated :: proc(pConfig: ^slot_allocator_config, pHeap: rawptr, pAllocator: ^slot_allocator) -> result ---
+	slot_allocator_init              :: proc(pConfig: ^slot_allocator_config, pAllocationCallbacks: ^allocation_callbacks, pAllocator: ^slot_allocator) -> result ---
+	slot_allocator_uninit            :: proc(pAllocator: ^slot_allocator, pAllocationCallbacks: ^allocation_callbacks) ---
+	slot_allocator_alloc             :: proc(pAllocator: ^slot_allocator, pSlot: ^u64) -> result ---
+	slot_allocator_free              :: proc(pAllocator: ^slot_allocator, slot: u64) -> result ---
+}
+
+/*
+Callback for processing a job. Each job type will have their own processing callback which will be
+called by ma_job_process().
+*/
+job_proc :: proc "c" (pJob: ^job)
+
+/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */
+job_type :: enum c.int {
+	/* Miscellaneous. */
+	QUIT = 0,
+	CUSTOM,
+
+	/* Resource Manager. */
+	RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE,
+	RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE,
+	RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE,
+	RESOURCE_MANAGER_LOAD_DATA_BUFFER,
+	RESOURCE_MANAGER_FREE_DATA_BUFFER,
+	RESOURCE_MANAGER_LOAD_DATA_STREAM,
+	RESOURCE_MANAGER_FREE_DATA_STREAM,
+	RESOURCE_MANAGER_PAGE_DATA_STREAM,
+	RESOURCE_MANAGER_SEEK_DATA_STREAM,
+
+	/* Device. */
+	DEVICE_AAUDIO_REROUTE,
+
+	/* Count. Must always be last. */
+	COUNT,
+}
+
+job :: struct {
+	toc: struct #raw_union {   /* 8 bytes. We encode the job code into the slot allocation data to save space. */
+		breakup: struct {
+			code:     u16,         /* Job type. */
+			slot:     u16,         /* Index into a ma_slot_allocator. */
+			refcount: u32,
+		},
+		allocation: u64,
+	},
+	next:  u64, /*atomic*/    /* refcount + slot for the next item. Does not include the job code. */
+	order: u32,               /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */
+
+	data: struct #raw_union {
+		/* Miscellaneous. */
+		custom: struct {
+			proc_: job_proc,
+			data0: uintptr,
+			data1: uintptr,
+		},
+
+		/* Resource Manager */
+		resourceManager: struct #raw_union {
+			loadDataBufferNode: struct {
+				pResourceManager:  rawptr /*ma_resource_manager**/,
+				pDataBufferNode:   rawptr /*ma_resource_manager_data_buffer_node**/,
+				pFilePath:         cstring,
+				pFilePathW:        [^]c.wchar_t,
+				flags:             u32,                       /* Resource manager data source flags that were used when initializing the data buffer. */
+				pInitNotification: ^async_notification,       /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
+				pDoneNotification: ^async_notification,       /* 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. */
+				pInitFence:        ^fence,                    /* Released when initialization of the decoder is complete. */
+				pDoneFence:        ^fence,                    /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
+			},
+			freeDataBufferNode: struct {
+				pResourceManager:  rawptr /*ma_resource_manager**/,
+				pDataBufferNode:   rawptr /*ma_resource_manager_data_buffer_node**/,
+				pDoneNotification: ^async_notification,
+				pDoneFence:        ^fence,
+			},
+			pageDataBufferNode: struct {
+				pResourceManager:  rawptr /*ma_resource_manager**/,
+				pDataBufferNode:   rawptr /*ma_resource_manager_data_buffer_node**/,
+				pDecoder:          rawptr /*ma_decoder**/,
+				pDoneNotification: ^async_notification,       /* Signalled when the data buffer has been fully decoded. */
+				pDoneFence:        ^fence,                    /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
+			},
+
+			loadDataBuffer: struct {
+				pDataBuffer:             rawptr /*ma_resource_manager_data_buffer**/,
+				pInitNotification:       ^async_notification,       /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
+				pDoneNotification:       ^async_notification,       /* Signalled when the data buffer has been fully decoded. */
+				pInitFence:              ^fence,                    /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
+				pDoneFence:              ^fence,                    /* Released when the data buffer has been fully decoded. */
+				rangeBegInPCMFrames:     u64,
+				rangeEndInPCMFrames:     u64,
+				loopPointBegInPCMFrames: u64,
+				loopPointEndInPCMFrames: u64,
+				isLooping:               u32,
+			},
+			freeDataBuffer: struct {
+				pDataBuffer:       rawptr /*ma_resource_manager_data_buffer**/,
+				pDoneNotification: ^async_notification,
+				pDoneFence:        ^fence,
+			},
+
+			loadDataStream: struct {
+				pDataStream:       rawptr /*ma_resource_manager_data_stream**/,
+				pFilePath:         cstring,               /* Allocated when the job is posted, freed by the job thread after loading. */
+				pFilePathW:        [^]c.wchar_t,          /* ^ As above ^. Only used if pFilePath is NULL. */
+				initialSeekPoint:  u64,
+				pInitNotification: ^async_notification,   /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
+				pInitFence:        ^fence,
+			},
+			freeDataStream: struct {
+				pDataStream:       rawptr /*ma_resource_manager_data_stream**/,
+				pDoneNotification: ^async_notification,
+				pDoneFence:        ^fence,
+			},
+			pageDataStream: struct {
+				pDataStream: rawptr /*ma_resource_manager_data_stream**/,
+				pageIndex:   u32,   /* The index of the page to decode into. */
+			},
+			seekDataStream: struct {
+				pDataStream: rawptr /*ma_resource_manager_data_stream**/,
+				frameIndex:  u64,
+			},
+		},
+
+		/* Device. */
+		device: struct #raw_union {
+			aaudio: struct #raw_union {
+				reroute: struct {
+					pDevice:    rawptr /*ma_device**/,
+					deviceType: u32 /*ma_device_type*/,
+				},
+			},
+		},
+	},
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	job_init    :: proc(code: u16) -> job ---
+	job_process :: proc(pJob: ^job) -> result ---
+}
+
+
+/*
+When set, ma_job_queue_next() will not wait and no semaphore will be signaled in
+ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.
+
+This flag should always be used for platforms that do not support multithreading.
+*/
+job_queue_flags :: enum c.int {
+	NON_BLOCKING = 0x00000001,
+}
+
+job_queue_config :: struct {
+	flags:    u32,
+	capacity: u32, /* The maximum number of jobs that can fit in the queue at a time. */
+}
+
+USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE :: false
+
+job_queue :: struct {
+	flags:     u32,                                          /* Flags passed in at initialization time. */
+	capacity:  u32,                                          /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */
+	head:      u64, /*atomic*/                               /* The first item in the list. Required for removing from the top of the list. */
+	tail:      u64, /*atomic*/                               /* The last item in the list. Required for appending to the end of the list. */
+	sem:       (struct {} when NO_THREADING else semaphore), /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */
+	allocator: slot_allocator,
+	pJobs:     [^]job,
+	lock:      (struct {} when USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE else spinlock),
+
+	/* Memory management. */
+	_pHeap:    rawptr,
+	_ownsHeap: b32,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	job_queue_config_init :: proc(flags, capacity: u32) -> job_queue_config ---
+
+	job_queue_get_heap_size     :: proc(pConfig: ^job_queue_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	job_queue_init_preallocated :: proc(pConfig: ^job_queue_config, pHeap: rawptr, pQueue: ^job_queue) -> result ---
+	job_queue_init              :: proc(pConfig: ^job_queue_config, pAllocationCallbacks: ^allocation_callbacks, pQueue: ^job_queue) -> result ---
+	job_queue_uninit            :: proc(pQueue: ^job_queue, pAllocationCallbacks: ^allocation_callbacks) ---
+	job_queue_post              :: proc(pQueue: ^job_queue, pJob: ^job) -> result ---
+	job_queue_next              :: proc(pQueue: ^job_queue, pJob: ^job) -> result --- /* Returns MA_CANCELLED if the next job is a quit job. */
+}

BIN
vendor/miniaudio/lib/miniaudio.lib


+ 30 - 0
vendor/miniaudio/logging.odin

@@ -12,6 +12,36 @@ when ODIN_OS == .Windows {
 
 MAX_LOG_CALLBACKS :: 4
 
+
+/*
+The callback for handling log messages.
+
+
+Parameters
+----------
+pUserData (in)
+    The user data pointer that was passed into ma_log_register_callback().
+
+logLevel (in)
+    The log level. This can be one of the following:
+
+    +----------------------+
+    | Log Level            |
+    +----------------------+
+    | MA_LOG_LEVEL_DEBUG   |
+    | MA_LOG_LEVEL_INFO    |
+    | MA_LOG_LEVEL_WARNING |
+    | MA_LOG_LEVEL_ERROR   |
+    +----------------------+
+
+pMessage (in)
+    The log message.
+
+
+Remarks
+-------
+Do not modify the state of the device from inside the callback.
+*/
 log_callback_proc :: proc "c" (pUserData: rawptr, level: u32, pMessage: cstring)
 
 log_callback :: struct {

+ 469 - 0
vendor/miniaudio/node_graph.odin

@@ -0,0 +1,469 @@
+package miniaudio
+
+import "core:c"
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
+
+/************************************************************************************************************************************************************
+
+Node Graph
+
+************************************************************************************************************************************************************/
+
+/* Must never exceed 254. */
+MAX_NODE_BUS_COUNT :: 254
+
+/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */
+MAX_NODE_LOCAL_BUS_COUNT :: 2
+
+/* Use this when the bus count is determined by the node instance rather than the vtable. */
+NODE_BUS_COUNT_UNKNOWN :: 255
+
+node :: struct {}
+
+/* Node flags. */
+node_flags :: enum c.int {
+	PASSTHROUGH                = 0x00000001,
+	CONTINUOUS_PROCESSING      = 0x00000002,
+	ALLOW_NULL_INPUT           = 0x00000004,
+	DIFFERENT_PROCESSING_RATES = 0x00000008,
+	SILENT_OUTPUT              = 0x00000010,
+}
+
+/* The playback state of a node. Either started or stopped. */
+node_state :: enum c.int {
+	started = 0,
+	stopped = 1,
+}
+
+node_vtable :: struct {
+	/*
+	Extended processing callback. This callback is used for effects that process input and output
+	at different rates (i.e. they perform resampling). This is similar to the simple version, only
+	they take two seperate frame counts: one for input, and one for output.
+
+	On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas
+	`pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
+
+	On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
+	`pFrameCountIn` to the number of input frames that were consumed.
+	*/
+	onProcess: proc "c" (pNode: ^node, ppFramesIn: ^[^]f32, pFrameCountIn: ^u32, ppFramesOut: ^[^]f32, pFrameCountOut: ^u32),
+
+	/*
+	A callback for retrieving the number of a input frames that are required to output the
+	specified number of output frames. You would only want to implement this when the node performs
+	resampling. This is optional, even for nodes that perform resampling, but it does offer a
+	small reduction in latency as it allows miniaudio to calculate the exact number of input frames
+	to read at a time instead of having to estimate.
+	*/
+	onGetRequiredInputFrameCount: proc "c" (pNode: ^node, outputFrameCount: u32, pInputFrameCount: ^u32) -> result,
+
+	/*
+	The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
+	parameters of the callbacks above.
+	*/
+	inputBusCount: u8,
+
+	/*
+	The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
+	parameters of the callbacks above.
+	*/
+	outputBusCount: u8,
+
+	/*
+	Flags describing characteristics of the node. This is currently just a placeholder for some
+	ideas for later on.
+	*/
+	flags: u32,
+}
+
+node_config :: struct {
+	vtable:          ^node_vtable,  /* Should never be null. Initialization of the node will fail if so. */
+	initialState:    node_state,    /* Defaults to ma_node_state_started. */
+	inputBusCount:   u32,           /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
+	outputBusCount:  u32,           /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise  be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */
+	pInputChannels:  ^u32,          /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
+	pOutputChannels: ^u32,          /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */
+}
+
+/*
+A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
+list. Think of the input bus as a linked list, with the output bus being an item in that list.
+*/
+node_output_bus :: struct {
+	/* Immutable. */
+	pNode:          ^node,                  /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
+	outputBusIndex: u8,                     /* The index of the output bus on pNode that this output bus represents. */
+	channels:       u8,                     /* The number of channels in the audio stream for this bus. */
+
+	/* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
+	inputNodeInputBusIndex: u8, /*atomic*/                  /* The index of the input bus on the input. Required for detaching. */
+	flags:                  u32, /*atomic*/                 /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */
+	refCount:               u32, /*atomic*/                 /* Reference count for some thread-safety when detaching. */
+	isAttached:             b32, /*atomic*/                 /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */
+	lock:                   spinlock, /*atomic*/            /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
+	volume:                 f32, /*atomic*/                 /* Linear. */
+	pNext:                  ^node_output_bus, /*atomic*/    /* If null, it's the tail node or detached. */
+	pPrev:                  ^node_output_bus, /*atomic*/    /* If null, it's the head node or detached. */
+	pInputNode:             ^node, /*atomic*/               /* The node that this output bus is attached to. Required for detaching. */
+}
+
+/*
+A node has multiple input buses. The output buses of a node are connecting to the input busses of
+another. An input bus is essentially just a linked list of output buses.
+*/
+node_input_bus :: struct {
+	/* Mutable via multiple threads. */
+	head:        node_output_bus,             /* Dummy head node for simplifying some lock-free thread-safety stuff. */
+	nextCounter: u32, /*atomic*/              /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */
+	lock:        spinlock, /*atomic*/         /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
+
+	/* Set once at startup. */
+	channels: u8,                             /* The number of channels in the audio stream for this bus. */
+}
+
+
+node_base :: struct {
+	/* These variables are set once at startup. */
+	pNodeGraph:                  ^node_graph,     /* The graph this node belongs to. */
+	vtable:                      ^node_vtable,
+	pCachedData:                 [^]f32,            /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
+	cachedDataCapInFramesPerBus: u16,               /* The capacity of the input data cache in frames, per bus. */
+
+	/* These variables are read and written only from the audio thread. */
+	cachedFrameCountOut:  u16,
+	cachedFrameCountIn:   u16,
+	consumedFrameCountIn: u16,
+
+	/* These variables are read and written between different threads. */
+	state:          node_state, /*atomic*/      /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
+	stateTimes:     [2]u64, /*atomic*/          /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */
+	localTime:      u64, /*atomic*/             /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */
+	inputBusCount:  u32,
+	outputBusCount: u32,
+	pInputBuses:    [^]node_input_bus,
+	pOutputBuses:   [^]node_output_bus,
+
+	/* Memory management. */
+	_inputBuses:  [MAX_NODE_LOCAL_BUS_COUNT]node_input_bus,
+	_outputBuses: [MAX_NODE_LOCAL_BUS_COUNT]node_output_bus,
+	_pHeap:       rawptr,   /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */
+	_ownsHeap:    b32,      /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	node_config_init :: proc() -> node_config ---
+
+	node_get_heap_size           :: proc(pNodeGraph: ^node_graph, pConfig: ^node_config, pHeapSizeInBytes: ^c.size_t) -> result ---
+	node_init_preallocated       :: proc(pNodeGraph: ^node_graph, pConfig: ^node_config, pHeap: rawptr, pNode: ^node) -> result ---
+	node_init                    :: proc(pNodeGraph: ^node_graph, pConfig: ^node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^node) -> result ---
+	node_uninit                  :: proc(pNode: ^node, pAllocationCallbacks: ^allocation_callbacks) ---
+	node_get_node_graph          :: proc(pNode: ^node) -> ^node_graph ---
+	node_get_input_bus_count     :: proc(pNode: ^node) -> u32 ---
+	node_get_output_bus_count    :: proc(pNode: ^node) -> u32 ---
+	node_get_input_channels      :: proc(pNode: ^node, inputBusIndex: u32) -> u32 ---
+	node_get_output_channels     :: proc(pNode: ^node, outputBusIndex: u32) -> u32 ---
+	node_attach_output_bus       :: proc(pNode: ^node, outputBusIndex: u32, pOtherNode: ^node, otherNodeInputBusIndex: u32) -> result ---
+	node_detach_output_bus       :: proc(pNode: ^node, outputBusIndex: u32) -> result ---
+	node_detach_all_output_buses :: proc(pNode: ^node) -> result ---
+	node_set_output_bus_volume   :: proc(pNode: ^node, outputBusIndex: u32, volume: f32) -> result ---
+	node_get_output_bus_volume   :: proc(pNode: ^node, outputBusIndex: u32) -> f32 ---
+	node_set_state               :: proc(pNode: ^node, state: node_state) -> result ---
+	node_get_state               :: proc(pNode: ^node) -> node_state ---
+	node_set_state_time          :: proc(pNode: ^node, state: node_state, globalTime: u64) -> result ---
+	node_get_state_time          :: proc(pNode: ^node, state: node_state) -> u64 ---
+	node_get_state_by_time       :: proc(pNode: ^node, globalTime: u64) -> node_state ---
+	node_get_state_by_time_range :: proc(pNode: ^node, globalTimeBeg: u64, globalTimeEnd: u64) -> node_state ---
+	node_get_time                :: proc(pNode: ^node) -> u64 ---
+	node_set_time                :: proc(pNode: ^node, localTime: u64) -> result ---
+}
+
+node_graph_config :: struct {
+	channels:             u32,
+	nodeCacheCapInFrames: u16,
+}
+
+node_graph :: struct {
+	/* Immutable. */
+	base:                 node_base,       /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */
+	endpoint:             node_base,       /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */
+	nodeCacheCapInFrames: u16,
+
+	/* Read and written by multiple threads. */
+	isReading:            b32, /*atomic*/
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	node_graph_config_init :: proc(channels: u32) -> node_graph_config ---
+
+	node_graph_init            :: proc(pConfig: ^node_graph_config, pAllocationCallbacks: ^allocation_callbacks, pNodeGraph: ^node_graph) -> result ---
+	node_graph_uninit          :: proc(pNodeGraph: ^node_graph, pAllocationCallbacks: ^allocation_callbacks) ---
+	node_graph_get_endpoint    :: proc(pNodeGraph: ^node_graph) -> ^node ---
+	node_graph_read_pcm_frames :: proc(pNodeGraph: ^node_graph, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	node_graph_get_channels    :: proc(pNodeGraph: ^node_graph) -> u32 ---
+	node_graph_get_time        :: proc(pNodeGraph: ^node_graph) -> u64 ---
+	node_graph_set_time        :: proc(pNodeGraph: ^node_graph, globalTime: u64) -> result ---
+}
+
+
+
+/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */
+data_source_node_config :: struct {
+	nodeConfig:  node_config,
+	pDataSource: ^data_source,
+}
+
+data_source_node :: struct {
+	base:        node_base,
+	pDataSource: ^data_source,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	data_source_node_config_init :: proc(pDataSource: ^data_source) -> data_source_node_config ---
+
+	data_source_node_init        :: proc(pNodeGraph: ^node_graph, pConfig: ^data_source_node_config, pAllocationCallbacks: ^allocation_callbacks, pDataSourceNode: ^data_source_node) -> result ---
+	data_source_node_uninit      :: proc(pDataSourceNode: ^data_source_node, pAllocationCallbacks: ^allocation_callbacks) ---
+	data_source_node_set_looping :: proc(pDataSourceNode: ^data_source_node, isLooping: b32) -> result ---
+	data_source_node_is_looping  :: proc(pDataSourceNode: ^data_source_node) -> b32 ---
+}
+
+
+/* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
+splitter_node_config :: struct {
+	nodeConfig: node_config,
+	channels:   u32,
+}
+
+splitter_node :: struct {
+	base: node_base,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	splitter_node_config_init :: proc(channels: u32) -> splitter_node_config ---
+
+	splitter_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^splitter_node_config, pAllocationCallbacks: ^allocation_callbacks, pSplitterNode: ^splitter_node) -> result ---
+	splitter_node_uninit :: proc(pSplitterNode: ^splitter_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Biquad Node
+*/
+biquad_node_config :: struct {
+	nodeConfig: node_config,
+	biquad:     biquad_config,
+}
+
+biquad_node :: struct {
+	baseNode: node_base,
+	biquad:   biquad,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	biquad_node_config_init :: proc(channels: u32, b0, b1, b2, a0, a1, a2: f32) -> biquad_node_config ---
+
+	biquad_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^biquad_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^biquad_node) -> result ---
+	biquad_node_reinit :: proc(pConfig: ^biquad_config, pNode: ^biquad_node) -> result ---
+	biquad_node_uninit :: proc(pNode: ^biquad_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Low Pass Filter Node
+*/
+lpf_node_config :: struct {
+	nodeConfig: node_config,
+	lpf:        lpf_config,
+}
+
+lpf_node :: struct {
+	baseNode: node_base,
+	lpf:      lpf,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	lpf_node_config_init :: proc(channels, sampleRate: u32, cutoffFrequency: f64, order: u32) -> lpf_node_config ---
+
+	lpf_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^lpf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^lpf_node) -> result ---
+	lpf_node_reinit :: proc(pConfig: ^lpf_config, pNode: ^lpf_node) -> result ---
+	lpf_node_uninit :: proc(pNode: ^lpf_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+High Pass Filter Node
+*/
+hpf_node_config :: struct {
+	nodeConfig: node_config,
+	hpf:        hpf_config,
+}
+
+hpf_node :: struct {
+	baseNode: node_base,
+	hpf:      hpf,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	hpf_node_config_init :: proc(channels, sampleRate: u32, cutoffFrequency: f64, order: u32) -> hpf_node_config ---
+
+	hpf_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^hpf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^hpf_node) -> result ---
+	hpf_node_reinit :: proc(pConfig: ^hpf_config, pNode: ^hpf_node) -> result ---
+	hpf_node_uninit :: proc(pNode: ^hpf_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Band Pass Filter Node
+*/
+bpf_node_config :: struct {
+	nodeConfig: node_config,
+	bpf:        bpf_config,
+}
+
+bpf_node :: struct {
+	baseNode: node_base,
+	bpf:      bpf,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	bpf_node_config_init :: proc(channels, sampleRate: u32, cutoffFrequency: f64, order: u32) -> bpf_node_config ---
+
+	bpf_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^bpf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^bpf_node) -> result ---
+	bpf_node_reinit :: proc(pConfig: ^bpf_config, pNode: ^bpf_node) -> result ---
+	bpf_node_uninit :: proc(pNode: ^bpf_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Notching Filter Node
+*/
+notch_node_config :: struct {
+	nodeConfig: node_config,
+	notch:      notch_config,
+}
+
+notch_node :: struct {
+	baseNode: node_base,
+	notch:    notch2,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	notch_node_config_init :: proc(channels, sampleRate: u32, q, frequency: f64) -> notch_node_config ---
+
+	notch_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^notch_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^notch_node) -> result ---
+	notch_node_reinit :: proc(pConfig: ^notch_config, pNode: ^notch_node) -> result ---
+	notch_node_uninit :: proc(pNode: ^notch_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Peaking Filter Node
+*/
+peak_node_config :: struct {
+	nodeConfig: node_config,
+	peak:       peak_config,
+}
+
+peak_node :: struct {
+	baseNode: node_base,
+	peak:     peak2,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	peak_node_config_init :: proc(channels, sampleRate: u32, gainDB, q, frequency: f64) -> peak_node_config ---
+
+	peak_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^peak_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^peak_node) -> result ---
+	peak_node_reinit :: proc(pConfig: ^peak_config, pNode: ^peak_node) -> result ---
+	peak_node_uninit :: proc(pNode: ^peak_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Low Shelf Filter Node
+*/
+loshelf_node_config :: struct {
+	nodeConfig: node_config,
+	loshelf:    loshelf_config,
+}
+
+loshelf_node :: struct {
+	baseNode: node_base,
+	loshelf:  loshelf2,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	loshelf_node_config_init :: proc(channels, sampleRate: u32, gainDB, q, frequency: f64) -> loshelf_node_config ---
+
+	loshelf_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^loshelf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^loshelf_node) -> result ---
+	loshelf_node_reinit :: proc(pConfig: ^loshelf_config, pNode: ^loshelf_node) -> result ---
+	loshelf_node_uninit :: proc(pNode: ^loshelf_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+High Shelf Filter Node
+*/
+hishelf_node_config :: struct {
+	nodeConfig: node_config,
+	hishelf:    hishelf_config,
+}
+
+hishelf_node :: struct {
+	baseNode: node_base,
+	hishelf:  hishelf2,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	hishelf_node_config_init :: proc(channels, sampleRate: u32, gainDB, q, frequency: f64) -> hishelf_node_config ---
+
+	hishelf_node_init   :: proc(pNodeGraph: ^node_graph, pConfig: ^hishelf_node_config, pAllocationCallbacks: ^allocation_callbacks, pNode: ^hishelf_node) -> result ---
+	hishelf_node_reinit :: proc(pConfig: ^hishelf_config, pNode: ^hishelf_node) -> result ---
+	hishelf_node_uninit :: proc(pNode: ^hishelf_node, pAllocationCallbacks: ^allocation_callbacks) ---
+}
+
+
+/*
+Delay Filter Node
+*/
+delay_node_config :: struct {
+	nodeConfig: node_config,
+	delay:      delay_config,
+}
+
+delay_node :: struct {
+	baseNode: node_base,
+	delay:    delay,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	delay_node_config_init :: proc(channels, sampleRate, delayInFrames: u32, decay: f32) -> delay_node_config ---
+
+	delay_node_init      :: proc(pNodeGraph: ^node_graph, pConfig: ^delay_node_config, pAllocationCallbacks: ^allocation_callbacks, pDelayNode: ^delay_node) -> result ---
+	delay_node_uninit    :: proc(pDelayNode: ^delay_node, pAllocationCallbacks: ^allocation_callbacks) ---
+	delay_node_set_wet   :: proc(pDelayNode: ^delay_node, value: f32) ---
+	delay_node_get_wet   :: proc(pDelayNode: ^delay_node) -> f32 ---
+	delay_node_set_dry   :: proc(pDelayNode: ^delay_node, value: f32) ---
+	delay_node_get_dry   :: proc(pDelayNode: ^delay_node) -> f32 ---
+	delay_node_set_decay :: proc(pDelayNode: ^delay_node, value: f32) ---
+	delay_node_get_decay :: proc(pDelayNode: ^delay_node) -> f32 ---
+}

+ 288 - 0
vendor/miniaudio/resource_manager.odin

@@ -0,0 +1,288 @@
+package miniaudio
+
+import "core:c"
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
+
+/************************************************************************************************************************************************************
+
+Resource Manager
+
+************************************************************************************************************************************************************/
+
+resource_manager_data_source_flags :: enum c.int {
+	STREAM         = 0x00000001,   /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */
+	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. */
+	ASYNC          = 0x00000004,   /* When set, the resource manager will load the data source asynchronously. */
+	WAIT_INIT      = 0x00000008,   /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
+	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. */
+}
+
+/*
+Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional.
+*/
+resource_manager_pipeline_stage_notification :: struct {
+	pNotification: ^async_notification,
+	pFence:        ^fence,
+}
+
+resource_manager_pipeline_notifications :: struct {
+	init: resource_manager_pipeline_stage_notification,    /* Initialization of the decoder. */
+	done: resource_manager_pipeline_stage_notification,    /* Decoding fully completed. */
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	resource_manager_pipeline_notifications_init :: proc() -> resource_manager_pipeline_notifications ---
+}
+
+
+/* BEGIN BACKWARDS COMPATIBILITY */
+/* TODO: Remove this block in version 0.12. */
+resource_manager_job                              :: job
+resource_manager_job_init                         :: job_init
+JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING :: job_queue_flags.NON_BLOCKING
+resource_manager_job_queue_config                 :: job_queue_config
+resource_manager_job_queue_config_init            :: job_queue_config_init
+resource_manager_job_queue                        :: job_queue
+resource_manager_job_queue_get_heap_size          :: job_queue_get_heap_size
+resource_manager_job_queue_init_preallocated      :: job_queue_init_preallocated
+resource_manager_job_queue_init                   :: job_queue_init
+resource_manager_job_queue_uninit                 :: job_queue_uninit
+resource_manager_job_queue_post                   :: job_queue_post
+resource_manager_job_queue_next                   :: job_queue_next
+/* END BACKWARDS COMPATIBILITY */
+
+
+
+/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */
+RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT :: 64
+
+resource_manager_flags :: enum c.int {
+	/* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */
+	NON_BLOCKING = 0x00000001,
+
+	/* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */
+	NO_THREADING = 0x00000002,
+}
+
+resource_manager_data_source_config :: struct {
+	pFilePath:                   cstring,
+	pFilePathW:                  [^]c.wchar_t,
+	pNotifications:              ^resource_manager_pipeline_notifications,
+	initialSeekPointInPCMFrames: u64,
+	rangeBegInPCMFrames:         u64,
+	rangeEndInPCMFrames:         u64,
+	loopPointBegInPCMFrames:     u64,
+	loopPointEndInPCMFrames:     u64,
+	isLooping:                   b32,
+	flags:                       u32,
+}
+
+resource_manager_data_supply_type :: enum c.int {
+	unknown = 0,   /* Used for determining whether or the data supply has been initialized. */
+	encoded,       /* Data supply is an encoded buffer. Connector is ma_decoder. */
+	decoded,       /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */
+	decoded_paged, /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */
+}
+
+resource_manager_data_supply :: struct {
+	type: resource_manager_data_supply_type, /*atomic*/    /* Read and written from different threads so needs to be accessed atomically. */
+	backend: struct #raw_union {
+		encoded: struct {
+			pData:       rawptr,
+			sizeInBytes: c.size_t,
+		},
+		decoded: struct {
+			pData:             rawptr,
+			totalFrameCount:   u64,
+			decodedFrameCount: u64,
+			format:            format,
+			channels:          u32,
+			sampleRate:        u32,
+		},
+		decodedPaged: struct {
+			data:              paged_audio_buffer_data,
+			decodedFrameCount: u64,
+			sampleRate:        u32,
+		},
+	},
+}
+
+resource_manager_data_buffer_node :: struct {
+	hashedName32:                 u32,                  /* The hashed name. This is the key. */
+	refCount:                     u32,
+	result:                       result, /*atomic*/    /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */
+	executionCounter:             u32, /*atomic*/       /* For allocating execution orders for jobs. */
+	executionPointer:             u32, /*atomic*/       /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
+	isDataOwnedByResourceManager: b32,                  /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */
+	data:                         resource_manager_data_supply,
+	pParent:                      ^resource_manager_data_buffer_node,
+	pChildLo:                     ^resource_manager_data_buffer_node,
+	pChildHi:                     ^resource_manager_data_buffer_node,
+}
+
+resource_manager_data_buffer :: struct {
+	ds:                     data_source_base,                      /* Base data source. A data buffer is a data source. */
+	pResourceManager:       ^resource_manager,                     /* A pointer to the resource manager that owns this buffer. */
+	pNode:                  ^resource_manager_data_buffer_node,    /* The data node. This is reference counted and is what supplies the data. */
+	flags:                  u32,                                   /* The flags that were passed used to initialize the buffer. */
+	executionCounter:       u32, /*atomic*/                        /* For allocating execution orders for jobs. */
+	executionPointer:       u32, /*atomic*/                        /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
+	seekTargetInPCMFrames:  u64,                                   /* Only updated by the public API. Never written nor read from the job thread. */
+	seekToCursorOnNextRead: b32,                                   /* On the next read we need to seek to the frame cursor. */
+	result:                 result, /*atomic*/                     /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */
+	isLooping:              b32, /*atomic*/                        /* Can be read and written by different threads at the same time. Must be used atomically. */
+	isConnectorInitialized: b32,                                   /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */
+	connector: struct #raw_union {
+		decoder:     decoder,             /* Supply type is ma_resource_manager_data_supply_type_encoded */
+		buffer:      audio_buffer,        /* Supply type is ma_resource_manager_data_supply_type_decoded */
+		pagedBuffer: paged_audio_buffer,  /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */
+	},  /* Connects this object to the node's data supply. */
+}
+
+resource_manager_data_stream :: struct {
+	ds:                     data_source_base,     /* Base data source. A data stream is a data source. */
+	pResourceManager:       ^resource_manager,    /* A pointer to the resource manager that owns this data stream. */
+	flags:                  u32,                  /* The flags that were passed used to initialize the stream. */
+	decoder:                decoder,              /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */
+	isDecoderInitialized:   b32,                  /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */
+	totalLengthInPCMFrames: u64,                  /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */
+	relativeCursor:         u32,                  /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */
+	absoluteCursor:         u64, /*atomic*/       /* The playback cursor, in absolute position starting from the start of the file. */
+	currentPageIndex:       u32,                  /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */
+	executionCounter:       u32, /*atomic*/       /* For allocating execution orders for jobs. */
+	executionPointer:       u32, /*atomic*/       /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
+
+	/* Written by the public API, read by the job thread. */
+	isLooping:              b32, /*atomic*/       /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */
+
+	/* Written by the job thread, read by the public API. */
+	pPageData:              rawptr,               /* Buffer containing the decoded data of each page. Allocated once at initialization time. */
+	pageFrameCount:         [2]u32, /*atomic*/    /* The number of valid PCM frames in each page. Used to determine the last valid frame. */
+
+	/* Written and read by both the public API and the job thread. These must be atomic. */
+	result:                 result, /*atomic*/    /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */
+	isDecoderAtEnd:         b32, /*atomic*/       /* Whether or not the decoder has reached the end. */
+	isPageValid:            [2]b32, /*atomic*/    /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */
+	seekCounter:            b32, /*atomic*/       /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */
+}
+
+resource_manager_data_source :: struct {
+	backend: struct #raw_union {
+		buffer: resource_manager_data_buffer,
+		stream: resource_manager_data_stream,
+	},  /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */
+
+	flags:            u32,                /* The flags that were passed in to ma_resource_manager_data_source_init(). */
+	executionCounter: u32, /*atomic*/     /* For allocating execution orders for jobs. */
+	executionPointer: u32, /*atomic*/     /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
+}
+
+resource_manager_config :: struct {
+	allocationCallbacks:            allocation_callbacks,
+	pLog:                           ^log,
+	decodedFormat:                  format,    /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */
+	decodedChannels:                u32,       /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
+	decodedSampleRate:              u32,       /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */
+	jobThreadCount:                 u32,       /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
+	jobQueueCapacity:               u32,       /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */
+	flags:                          u32,
+	pVFS:                           ^vfs,      /* Can be NULL in which case defaults will be used. */
+	ppCustomDecodingBackendVTables: ^[^]decoding_backend_vtable,
+	customDecodingBackendCount:     u32,
+	pCustomDecodingBackendUserData: rawptr,
+}
+
+resource_manager :: struct {
+	config:              resource_manager_config,
+	pRootDataBufferNode: ^resource_manager_data_buffer_node,                                               /* The root buffer in the binary tree. */
+	dataBufferBSTLock:   (struct {} when NO_THREADING else mutex),                                         /* For synchronizing access to the data buffer binary tree. */
+	jobThreads:          (struct {} when NO_THREADING else [RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]thread), /* The threads for executing jobs. */
+	jobQueue:            job_queue,                                                                        /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */
+	defaultVFS:          default_vfs,                                                                      /* Only used if a custom VFS is not specified. */
+	log:                 log,                                                                              /* Only used if no log was specified in the config. */
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	resource_manager_data_source_config_init :: proc() -> resource_manager_data_source_config ---
+	resource_manager_config_init             :: proc() -> resource_manager_config ---
+
+	/* Init. */
+	resource_manager_init    :: proc(pConfig: ^resource_manager_config, pResourceManager: ^resource_manager) -> result ---
+	resource_manager_uninit  :: proc(pResourceManager: ^resource_manager) ---
+	resource_manager_get_log :: proc(pResourceManager: ^resource_manager) -> ^log ---
+
+	/* Registration. */
+	resource_manager_register_file           :: proc(pResourceManager: ^resource_manager, pFilePath: cstring, flags: u32) -> result ---
+	resource_manager_register_file_w         :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t, flags: u32) -> result ---
+	resource_manager_register_decoded_data   :: proc(pResourceManager: ^resource_manager, pName: cstring, pData: rawptr, frameCount: u64, format: format, channels: u32, sampleRate: u32) -> result ---  /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */
+	resource_manager_register_decoded_data_w :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t, pData: rawptr, frameCount: u64, format: format, channels: u32, sampleRate: u32) -> result ---
+	resource_manager_register_encoded_data   :: proc(pResourceManager: ^resource_manager, pName: cstring, pData: rawptr, sizeInBytes: c.size_t) -> result ---    /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */
+	resource_manager_register_encoded_data_w :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t, pData: rawptr, sizeInBytes: c.size_t) -> result ---
+	resource_manager_unregister_file         :: proc(pResourceManager: ^resource_manager, pFilePath: cstring) -> result ---
+	resource_manager_unregister_file_w       :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t) -> result ---
+	resource_manager_unregister_data         :: proc(pResourceManager: ^resource_manager, pName: cstring) -> result ---
+	resource_manager_unregister_data_w       :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t) -> result ---
+
+	/* Data Buffers. */
+	resource_manager_data_buffer_init_ex                  :: proc(pResourceManager: ^resource_manager, pConfig: ^resource_manager_data_source_config, pDataBuffer: ^resource_manager_data_buffer) -> result ---
+	resource_manager_data_buffer_init                     :: proc(pResourceManager: ^resource_manager, pFilePath: cstring, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataBuffer: ^resource_manager_data_buffer) -> result ---
+	resource_manager_data_buffer_init_w                   :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataBuffer: ^resource_manager_data_buffer) -> result ---
+	resource_manager_data_buffer_init_copy                :: proc(pResourceManager: ^resource_manager, pExistingDataBuffer, pDataBuffer: ^resource_manager_data_buffer) -> result ---
+	resource_manager_data_buffer_uninit                   :: proc(pDataBuffer: ^resource_manager_data_buffer) -> result ---
+	resource_manager_data_buffer_read_pcm_frames          :: proc(pDataBuffer: ^resource_manager_data_buffer, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	resource_manager_data_buffer_seek_to_pcm_frame        :: proc(pDataBuffer: ^resource_manager_data_buffer, frameIndex: u64) -> result ---
+	resource_manager_data_buffer_get_data_format          :: proc(pDataBuffer: ^resource_manager_data_buffer, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	resource_manager_data_buffer_get_cursor_in_pcm_frames :: proc(pDataBuffer: ^resource_manager_data_buffer, pCursor: ^u64) -> result ---
+	resource_manager_data_buffer_get_length_in_pcm_frames :: proc(pDataBuffer: ^resource_manager_data_buffer, pLength: ^u64) -> result ---
+	resource_manager_data_buffer_result                   :: proc(pDataBuffer: ^resource_manager_data_buffer) -> result ---
+	resource_manager_data_buffer_set_looping              :: proc(pDataBuffer: ^resource_manager_data_buffer, isLooping: b32) -> result ---
+	resource_manager_data_buffer_is_looping               :: proc(pDataBuffer: ^resource_manager_data_buffer) -> b32 ---
+	resource_manager_data_buffer_get_available_frames     :: proc(pDataBuffer: ^resource_manager_data_buffer, pAvailableFrames: ^u64) -> result ---
+
+	/* Data Streams. */
+	resource_manager_data_stream_init_ex                  :: proc(pResourceManager: ^resource_manager, pConfig: ^resource_manager_data_source_config, pDataStream: ^resource_manager_data_stream) -> result ---
+	resource_manager_data_stream_init                     :: proc(pResourceManager: ^resource_manager, pFilePath: cstring, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataStream: ^resource_manager_data_stream) -> result ---
+	resource_manager_data_stream_init_w                   :: proc(pResourceManager: ^resource_manager, pFilePath: [^]c.wchar_t, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataStream: ^resource_manager_data_stream) -> result ---
+	resource_manager_data_stream_uninit                   :: proc(pDataStream: ^resource_manager_data_stream) -> result ---
+	resource_manager_data_stream_read_pcm_frames          :: proc(pDataStream: ^resource_manager_data_stream, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	resource_manager_data_stream_seek_to_pcm_frame        :: proc(pDataStream: ^resource_manager_data_stream, frameIndex: u64) -> result ---
+	resource_manager_data_stream_get_data_format          :: proc(pDataStream: ^resource_manager_data_stream, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	resource_manager_data_stream_get_cursor_in_pcm_frames :: proc(pDataStream: ^resource_manager_data_stream, pCursor: ^u64) -> result ---
+	resource_manager_data_stream_get_length_in_pcm_frames :: proc(pDataStream: ^resource_manager_data_stream, pLength: ^u64) -> result ---
+	resource_manager_data_stream_result                   :: proc(pDataStream: ^resource_manager_data_stream) -> result ---
+	resource_manager_data_stream_set_looping              :: proc(pDataStream: ^resource_manager_data_stream, isLooping: b32) -> result ---
+	resource_manager_data_stream_is_looping               :: proc(pDataStream: ^resource_manager_data_stream) -> b32 ---
+	resource_manager_data_stream_get_available_frames     :: proc(pDataStream: ^resource_manager_data_stream, pAvailableFrames: ^u64) -> result ---
+
+	/* Data Sources. */
+	resource_manager_data_source_init_ex                  :: proc(pResourceManager: ^resource_manager, pConfig: ^resource_manager_data_source_config, pDataSource: ^resource_manager_data_source) -> result ---
+	resource_manager_data_source_init                     :: proc(pResourceManager: ^resource_manager, pName: cstring, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataSource: ^resource_manager_data_source) -> result ---
+	resource_manager_data_source_init_w                   :: proc(pResourceManager: ^resource_manager, pName: [^]c.wchar_t, flags: u32, pNotifications: ^resource_manager_pipeline_notifications, pDataSource: ^resource_manager_data_source) -> result ---
+	resource_manager_data_source_init_copy                :: proc(pResourceManager: ^resource_manager, pExistingDataSource, pDataSource: ^resource_manager_data_source) -> result ---
+	resource_manager_data_source_uninit                   :: proc(pDataSource: ^resource_manager_data_source) -> result ---
+	resource_manager_data_source_read_pcm_frames          :: proc(pDataSource: ^resource_manager_data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	resource_manager_data_source_seek_to_pcm_frame        :: proc(pDataSource: ^resource_manager_data_source, frameIndex: u64) -> result ---
+	resource_manager_data_source_get_data_format          :: proc(pDataSource: ^resource_manager_data_source, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	resource_manager_data_source_get_cursor_in_pcm_frames :: proc(pDataSource: ^resource_manager_data_source, pCursor: ^u64) -> result ---
+	resource_manager_data_source_get_length_in_pcm_frames :: proc(pDataSource: ^resource_manager_data_source, pLength: ^u64) -> result ---
+	resource_manager_data_source_result                   :: proc(pDataSource: ^resource_manager_data_source) -> result ---
+	resource_manager_data_source_set_looping              :: proc(pDataSource: ^resource_manager_data_source, isLooping: b32) -> result ---
+	resource_manager_data_source_is_looping               :: proc(pDataSource: ^resource_manager_data_source) -> b32 ---
+	resource_manager_data_source_get_available_frames     :: proc(pDataSource: ^resource_manager_data_source, pAvailableFrames: ^u64) -> result ---
+
+	/* Job management. */
+	resource_manager_post_job         :: proc(pResourceManager: ^resource_manager, pJob: ^job) -> result ---
+	resource_manager_post_job_quit    :: proc(pResourceManager: ^resource_manager) -> result ---  /* Helper for posting a quit job. */
+	resource_manager_next_job         :: proc(pResourceManager: ^resource_manager, pJob: ^job) -> result ---
+	resource_manager_process_job      :: proc(pResourceManager: ^resource_manager, pJob: ^job) -> result ---  /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */
+	resource_manager_process_next_job :: proc(pResourceManager: ^resource_manager) -> result ---   /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */
+}

File diff suppressed because it is too large
+ 61631 - 40671
vendor/miniaudio/src/miniaudio.h


+ 152 - 0
vendor/miniaudio/synchronization.odin

@@ -0,0 +1,152 @@
+package miniaudio
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	/*
+	Locks a spinlock.
+	*/
+	spinlock_lock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result ---
+
+	/*
+	Locks a spinlock, but does not yield() when looping.
+	*/
+	spinlock_lock_noyield :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result ---
+
+	/*
+	Unlocks a spinlock.
+	*/
+	spinlock_unlock :: proc(/*volatile*/ pSpinlock: ^spinlock) -> result ---
+
+when NO_THREADING {
+	/*
+	Creates a mutex.
+
+	A mutex must be created from a valid context. A mutex is initially unlocked.
+	*/
+	mutex_init :: proc(pMutex: ^mutex) -> result ---
+
+	/*
+	Deletes a mutex.
+	*/
+	mutex_uninit :: proc(pMutex: ^mutex) ---
+
+	/*
+	Locks a mutex with an infinite timeout.
+	*/
+	mutex_lock :: proc(pMutex: ^mutex) ---
+
+	/*
+	Unlocks a mutex.
+	*/
+	mutex_unlock :: proc(pMutex: ^mutex) ---
+
+
+	/*
+	Initializes an auto-reset event.
+	*/
+	event_init :: proc(pEvent: ^event) -> result ---
+
+	/*
+	Uninitializes an auto-reset event.
+	*/
+	event_uninit :: proc(pEvent: ^event) ---
+
+	/*
+	Waits for the specified auto-reset event to become signalled.
+	*/
+	event_wait :: proc(pEvent: ^event) -> result ---
+
+	/*
+	Signals the specified auto-reset event.
+	*/
+	event_signal :: proc(pEvent: ^event) -> result ---
+} /* NO_THREADING */
+
+}
+
+/*
+Fence
+=====
+This locks while the counter is larger than 0. Counter can be incremented and decremented by any
+thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
+fence just as another thread returns from ma_fence_wait().
+
+The idea behind a fence is to allow you to wait for a group of operations to complete. When an
+operation starts, the counter is incremented which locks the fence. When the operation completes,
+the fence will be released which decrements the counter. ma_fence_wait() will block until the
+counter hits zero.
+
+If threading is disabled, ma_fence_wait() will spin on the counter.
+*/
+fence :: struct {
+	e:       (struct {} when NO_THREADING else event),
+	counter: (u32 when NO_THREADING else struct {}),
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	fence_init    :: proc(pFence: ^fence) -> result ---
+	fence_uninit  :: proc(pFence: ^fence) ---
+	fence_acquire :: proc(pFence: ^fence) -> result ---    /* Increment counter. */
+	fence_release :: proc(pFence: ^fence) -> result ---    /* Decrement counter. */
+	fence_wait    :: proc(pFence: ^fence) -> result ---    /* Wait for counter to reach 0. */
+}
+
+
+/*
+Notification callback for asynchronous operations.
+*/
+async_notification :: struct {}
+
+async_notification_callbacks :: struct {
+	onSignal: proc "c" (pNotification: ^async_notification),
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	async_notification_signal :: proc(pNotification: ^async_notification) -> result ---
+}
+
+
+/*
+Simple polling notification.
+
+This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()
+*/
+async_notification_poll :: struct {
+		cb:        async_notification_callbacks,
+		signalled: b32,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	async_notification_poll_init         :: proc(pNotificationPoll: ^async_notification_poll) -> result ---
+	async_notification_poll_is_signalled :: proc(pNotificationPoll: ^async_notification_poll) -> b32 ---
+}
+
+
+/*
+Event Notification
+
+This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail.
+*/
+async_notification_event :: struct {
+	cb: async_notification_callbacks,
+	e:  (struct {} when NO_THREADING else event),
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	async_notification_event_init   :: proc(pNotificationEvent: ^async_notification_event) -> result ---
+	async_notification_event_uninit :: proc(pNotificationEvent: ^async_notification_event) -> result ---
+	async_notification_event_wait   :: proc(pNotificationEvent: ^async_notification_event) -> result ---
+	async_notification_event_signal :: proc(pNotificationEvent: ^async_notification_event) -> result ---
+}

+ 120 - 55
vendor/miniaudio/utilities.odin

@@ -1,5 +1,7 @@
 package miniaudio
 
+import c "core:c/libc"
+
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 } else when ODIN_OS == .Linux {
@@ -10,13 +12,6 @@ when ODIN_OS == .Windows {
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
-	/*
-	Adjust buffer size based on a scaling factor.
-
-	This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
-	*/
-	scale_buffer_size :: proc(baseBufferSize: u32, scale: f32) -> u32 ---
-
 	/*
 	Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
 	*/
@@ -51,9 +46,14 @@ foreign lib {
 
 
 	/*
-	Clips f32 samples.
+	Clips samples.
 	*/
-	clip_samples_f32 :: proc(p: [^]f32, sampleCount: u64) ---
+	clip_samples_u8  :: proc(pDst: [^]u8,  pSrc: [^]i16, count: u64) ---
+	clip_samples_s16 :: proc(pDst: [^]i16, pSrc: [^]i32, count: u64) ---
+	clip_samples_s24 :: proc(pDst: [^]u8,  pSrc: [^]i64, count: u64) ---
+	clip_samples_s32 :: proc(pDst: [^]i32, pSrc: [^]i64, count: u64) ---
+	clip_samples_f32 :: proc(pDst,         pSrc: [^]f32, count: u64) ---
+	clip_pcm_frames	 :: proc(pDst,         pSrc: rawptr, frameCount: u64, format: format, channels: u32) ---
 
 	/*
 	Helper for applying a volume factor to samples.
@@ -86,20 +86,26 @@ foreign lib {
 	apply_volume_factor_pcm_frames_f32 :: proc(pFrames: [^]f32, frameCount: u64, channels: u32, factor: f32) ---
 	apply_volume_factor_pcm_frames     :: proc(pFrames: rawptr, frameCount: u64, format: format, channels: u32, factor: f32) ---
 
+	copy_and_apply_volume_factor_per_channel_f32 :: proc(pFramesOut, pFramesIn: [^]f32, frameCount: u64, channels: u32, pChannelGains: [^]f32) ---
+
+
+	ma_copy_and_apply_volume_and_clip_samples_u8  :: proc(pDst: [^]u8,  pSrc: [^]i16, count: u64, volume: f32) ---
+	ma_copy_and_apply_volume_and_clip_samples_s16 :: proc(pDst: [^]i16, pSrc: [^]i32, count: u64, volume: f32) ---
+	ma_copy_and_apply_volume_and_clip_samples_s24 :: proc(pDst: [^]u8,  pSrc: [^]i64, count: u64, volume: f32) ---
+	ma_copy_and_apply_volume_and_clip_samples_s32 :: proc(pDst: [^]i32, pSrc: [^]i64, count: u64, volume: f32) ---
+	ma_copy_and_apply_volume_and_clip_samples_f32 :: proc(pDst,         pSrc: [^]f32, count: u64, volume: f32) ---
+	ma_copy_and_apply_volume_and_clip_pcm_frames 	:: proc(pDst,         pSrc: rawptr, frameCount: u64, format: format, channels: u32, volume: f32) ---
+
 
 	/*
 	Helper for converting a linear factor to gain in decibels.
 	*/
-	factor_to_gain_db :: proc(factor: f32) -> f32 ---
+	volume_linear_to_db :: proc(factor: f32) -> f32 ---
 
 	/*
 	Helper for converting gain in decibels to a linear factor.
 	*/
-	gain_db_to_factor :: proc(gain: f32) -> f32 ---
-}
-
-zero_pcm_frames :: #force_inline proc "c" (p: rawptr, frameCount: u64, format: format, channels: u32) { 
-	silence_pcm_frames(p, frameCount, format, channels)
+	volume_db_to_linear :: proc(gain: f32) -> f32 ---
 }
 
 offset_pcm_frames_ptr_f32 :: #force_inline proc "c" (p: [^]f32, offsetInFrames: u64, channels: u32) -> [^]f32 {
@@ -109,23 +115,20 @@ offset_pcm_frames_const_ptr_f32 :: #force_inline proc "c" (p: [^]f32, offsetInFr
 	return cast([^]f32)offset_pcm_frames_ptr(p, offsetInFrames, .f32, channels)
 }
 
-clip_pcm_frames_f32 :: #force_inline proc "c" (p: [^]f32, frameCount: u64, channels: u32) { 
-	clip_samples_f32(p, frameCount*u64(channels)) 
-}
-
 
 data_source :: struct {}
 
+DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT :: 0x00000001
+
 data_source_vtable :: struct {
 	onRead:          proc "c" (pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result,
 	onSeek:          proc "c" (pDataSource: ^data_source, frameIndex: u64) -> result,
-	onMap:           proc "c" (pDataSource: ^data_source, ppFramesOut: ^rawptr, pFrameCount: ^u64) -> result,   /* Returns MA_AT_END if the end has been reached. This should be considered successful. */
-	onUnmap:         proc "c" (pDataSource: ^data_source, frameCount: u64) -> result,
-	onGetDataFormat: proc "c" (pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32) -> result,
+	onGetDataFormat: proc "c" (pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result,
 	onGetCursor:     proc "c" (pDataSource: ^data_source, pCursor: ^u64) -> result,
 	onGetLength:     proc "c" (pDataSource: ^data_source, pLength: ^u64) -> result,
+	onSetLooping:    proc "c" (pDataSource: ^data_source, isLooping: b32) -> result,
+	flags:           u32,
 } 
-data_source_callbacks :: data_source_vtable  /* TODO: Remove ma_data_source_callbacks in version 0.11. */
 
 data_source_get_next_proc :: proc "c" (pDataSource: ^data_source) -> ^data_source
 
@@ -134,45 +137,43 @@ data_source_config :: struct {
 }
 
 data_source_base :: struct {
-	cb: data_source_callbacks,    /* TODO: Remove this. */
-
-	/* Variables below are placeholder and not yet used. */
 	vtable:           ^data_source_vtable,
 	rangeBegInFrames: u64,
-	rangeEndInFrames: u64,                   /* Set to -1 for unranged (default). */
-	loopBegInFrames:  u64,                   /* Relative to rangeBegInFrames. */
-	loopEndInFrames:  u64,                   /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
-	pCurrent:         ^data_source,                  /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */
-	pNext:            ^data_source,                  /* When set to NULL, onGetNext will be used. */
-	onGetNext:        ^data_source_get_next_proc,   /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
+	rangeEndInFrames: u64,                        /* Set to -1 for unranged (default). */
+	loopBegInFrames:  u64,                        /* Relative to rangeBegInFrames. */
+	loopEndInFrames:  u64,                        /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
+	pCurrent:         ^data_source,               /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */
+	pNext:            ^data_source,               /* When set to NULL, onGetNext will be used. */
+	onGetNext:        data_source_get_next_proc,  /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
+	isLooping:        b32, /*atomic*/
 }
 
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
-	ma_data_source_config_init :: proc() -> data_source_config ---
+	data_source_config_init :: proc() -> data_source_config ---
 	
-	data_source_init                     :: proc(pConfig: ^data_source_config, pDataSource: ^data_source) -> result ---
-	data_source_uninit                   :: proc(pDataSource: ^data_source) ---
-	data_source_read_pcm_frames          :: proc(pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64, loop: b32) -> result ---   /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
-	data_source_seek_pcm_frames          :: proc(pDataSource: ^data_source, frameCount: u64, pFramesSeeked: ^u64, loop: b32) -> result --- /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */
-	data_source_seek_to_pcm_frame        :: proc(pDataSource: ^data_source, frameIndex: u64) -> result ---
-	data_source_map                      :: proc(pDataSource: ^data_source, ppFramesOut: ^rawptr, pFrameCount: ^u64) -> result ---   /* Returns MA_NOT_IMPLEMENTED if mapping is not supported. */
-	data_source_unmap                    :: proc(pDataSource: ^data_source, frameCount: u64) -> result ---       /* Returns MA_AT_END if the end has been reached. */
-	data_source_get_data_format          :: proc(pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32) -> result ---
-	data_source_get_cursor_in_pcm_frames :: proc(pDataSource: ^data_source, pCursor: ^u64) -> result ---
-	data_source_get_length_in_pcm_frames :: proc(pDataSource: ^data_source, pLength: ^u64) -> result ---    /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
-	// #if defined(MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING)
-	// 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 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 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_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_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);
-	// #endif
+	data_source_init                         :: proc(pConfig: ^data_source_config, pDataSource: ^data_source) -> result ---
+	data_source_uninit                       :: proc(pDataSource: ^data_source) ---
+	data_source_read_pcm_frames              :: proc(pDataSource: ^data_source, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---   /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
+	data_source_seek_pcm_frames              :: proc(pDataSource: ^data_source, frameCount: u64, pFramesSeeked: ^u64) -> result --- /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */
+	data_source_seek_to_pcm_frame            :: proc(pDataSource: ^data_source, frameIndex: u64) -> result ---
+	data_source_get_data_format              :: proc(pDataSource: ^data_source, pFormat: ^format, pChannels: ^u32, pSampleRate: ^u32, pChannelMap: [^]channel, channelMapCap: c.size_t) -> result ---
+	data_source_get_cursor_in_pcm_frames     :: proc(pDataSource: ^data_source, pCursor: ^u64) -> result ---
+	data_source_get_length_in_pcm_frames     :: proc(pDataSource: ^data_source, pLength: ^u64) -> result ---    /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
+	data_source_get_cursor_in_seconds        :: proc(pDataSource: ^data_source, pCursor: ^f32) -> result ---
+	data_source_get_length_in_seconds        :: proc(pDataSource: ^data_source, pLength: ^f32) -> result ---
+	data_source_set_looping                  :: proc(pDataSource: ^data_source, isLooping: b32) -> result ---
+	data_source_is_looping                   :: proc(pDataSource: ^data_source) -> b32 ---
+	data_source_set_range_in_pcm_frames      :: proc(pDataSource: ^data_source, rangeBegInFrames: u64, rangeEndInFrames: u64) -> result ---
+	data_source_get_range_in_pcm_frames      :: proc(pDataSource: ^data_source, pRangeBegInFrames: ^u64, pRangeEndInFrames: ^u64) ---
+	data_source_set_loop_point_in_pcm_frames :: proc(pDataSource: ^data_source, loopBegInFrames: u64, loopEndInFrames: u64) -> result ---
+	data_source_get_loop_point_in_pcm_frames :: proc(pDataSource: ^data_source, pLoopBegInFrames: ^u64, pLoopEndInFrames: ^u64) ---
+	data_source_set_current                  :: proc(pDataSource: ^data_source, pCurrentDataSource: ^data_source) -> result ---
+	data_source_get_current                  :: proc(pDataSource: ^data_source) -> ^data_source ---
+	data_source_set_next                     :: proc(pDataSource: ^data_source, pNextDataSource: ^data_source) -> result ---
+	data_source_get_next                     :: proc(pDataSource: ^data_source) -> ^data_source ---
+	data_source_set_next_callback            :: proc(pDataSource: ^data_source, onGetNext: ^data_source_get_next_proc) -> result ---
+	data_source_get_next_callback            :: proc(pDataSource: ^data_source) -> ^data_source_get_next_proc ---
 }
 
 
@@ -180,6 +181,7 @@ audio_buffer_ref :: struct {
 	ds:           data_source_base,
 	format:       format,
 	channels:     u32,
+	sampleRate:   u32,
 	cursor:       u64,
 	sizeInFrames: u64,
 	pData:        rawptr,
@@ -204,6 +206,7 @@ foreign lib {
 audio_buffer_config :: struct {
 	format:              format,
 	channels:            u32,
+	sampleRate:          u32,
 	sizeInFrames:        u64,
 	pData:               rawptr,  /* If set to NULL, will allocate a block of memory for you. */
 	allocationCallbacks: allocation_callbacks,
@@ -234,3 +237,65 @@ foreign lib {
 	audio_buffer_get_length_in_pcm_frames :: proc(pAudioBuffer: ^audio_buffer, pLength: ^u64) -> result ---
 	audio_buffer_get_available_frames     :: proc(pAudioBuffer: ^audio_buffer, pAvailableFrames: ^u64) -> result ---
 }
+
+/*
+Paged Audio Buffer
+==================
+A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
+can be used for cases where audio data is streamed in asynchronously while allowing data to be read
+at the same time.
+
+This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
+simultaneously across different threads, however only one thread at a time can append, and only one
+thread at a time can read and seek.
+*/
+paged_audio_buffer_page :: struct {
+	pNext:        ^paged_audio_buffer_page, /*atomic*/
+	sizeInFrames: u64,
+	pAudioData:   [1]u8,
+}
+
+paged_audio_buffer_data :: struct {
+	format:   format,
+	channels: u32,
+	head:     paged_audio_buffer_page,                /* Dummy head for the lock-free algorithm. Always has a size of 0. */
+	pTail:    ^paged_audio_buffer_page, /*atomic*/    /* Never null. Initially set to &head. */
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	paged_audio_buffer_data_init                     :: proc(format: format, channels: u32, pData: ^paged_audio_buffer_data) -> result ---
+	paged_audio_buffer_data_uninit                   :: proc(pData: ^paged_audio_buffer_data, pAllocationCallbacks: ^allocation_callbacks) ---
+	paged_audio_buffer_data_get_head                 :: proc(pData: ^paged_audio_buffer_data) -> ^paged_audio_buffer_page ---
+	paged_audio_buffer_data_get_tail                 :: proc(pData: ^paged_audio_buffer_data) -> ^paged_audio_buffer_page ---
+	paged_audio_buffer_data_get_length_in_pcm_frames :: proc(pData: ^paged_audio_buffer_data, pLength: ^u64) -> result ---
+	paged_audio_buffer_data_allocate_page            :: proc(pData: ^paged_audio_buffer_data, pageSizeInFrames: u64, pInitialData: rawptr, pAllocationCallbacks: ^allocation_callbacks, ppPage: ^^paged_audio_buffer_page) -> result ---
+	paged_audio_buffer_data_free_page                :: proc(pData: ^paged_audio_buffer_data, pPage: ^paged_audio_buffer_page, pAllocationCallbacks: ^allocation_callbacks) -> result ---
+	paged_audio_buffer_data_append_page              :: proc(pData: ^paged_audio_buffer_data, pPage: ^paged_audio_buffer_page) -> result ---
+	paged_audio_buffer_data_allocate_and_append_page :: proc(pData: ^paged_audio_buffer_data, pageSizeInFrames: u32, pInitialData: rawptr, pAllocationCallbacks: ^allocation_callbacks) -> result ---
+}
+
+
+paged_audio_buffer_config :: struct {
+	pData: ^paged_audio_buffer_data,  /* Must not be null. */
+}
+
+paged_audio_buffer :: struct {
+	ds:             data_source_base,
+	pData:          ^paged_audio_buffer_data,  /* Audio data is read from here. Cannot be null. */
+	pCurrent:       ^paged_audio_buffer_page,
+	relativeCursor: u64,                       /* Relative to the current page. */
+	absoluteCursor: u64,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	paged_audio_buffer_config_init :: proc(pData: ^paged_audio_buffer_data) -> paged_audio_buffer_config ---
+
+	paged_audio_buffer_init                     :: proc(pConfig: ^paged_audio_buffer_config, pPagedAudioBuffer: ^paged_audio_buffer) -> result ---
+	paged_audio_buffer_uninit                   :: proc(pPagedAudioBuffer: ^paged_audio_buffer) ---
+	paged_audio_buffer_read_pcm_frames          :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---   /* Returns MA_AT_END if no more pages available. */
+	paged_audio_buffer_seek_to_pcm_frame        :: proc(pPagedAudioBuffer: ^paged_audio_buffer, frameIndex: u64) -> result ---
+	paged_audio_buffer_get_cursor_in_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pCursor: ^u64) -> result ---
+	paged_audio_buffer_get_length_in_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pLength: ^u64) -> result ---
+}

+ 4 - 6
vendor/miniaudio/vfs.odin

@@ -22,8 +22,10 @@ appropriate for a given situation.
 vfs :: struct {}
 vfs_file :: distinct handle
 
-OPEN_MODE_READ  :: 0x00000001
-OPEN_MODE_WRITE :: 0x00000002
+open_mode_flags :: enum c.int {
+	READ  = 0x00000001,
+	WRITE = 0x00000002,
+}
 
 seek_origin :: enum c.int {
 	start,
@@ -71,10 +73,6 @@ foreign lib {
 	default_vfs_init       :: proc(pVFS: ^default_vfs, pAllocationCallbacks: ^allocation_callbacks) -> result ---
 }
 
-resource_format :: enum c.int {
-	wav,
-}
-
 encoding_format :: enum c.int {
 	unknown = 0,
 	wav,

Some files were not shown because too many files changed in this diff