Browse Source

miniaudio: update to 0.11.21

Laytan Laats 1 year ago
parent
commit
16584779fb

+ 23 - 19
vendor/miniaudio/common.odin

@@ -141,28 +141,32 @@ result :: enum c.int {
 	CANCELLED                                   = -51,
 	CANCELLED                                   = -51,
 	MEMORY_ALREADY_MAPPED                       = -52,
 	MEMORY_ALREADY_MAPPED                       = -52,
 
 
+	/* General non-standard errors. */
+	CRC_MISMATCH                                = -100,
+
 	/* General miniaudio-specific errors. */
 	/* General miniaudio-specific errors. */
-	FORMAT_NOT_SUPPORTED                        = -100,
-	DEVICE_TYPE_NOT_SUPPORTED                   = -101,
-	SHARE_MODE_NOT_SUPPORTED                    = -102,
-	NO_BACKEND                                  = -103,
-	NO_DEVICE                                   = -104,
-	API_NOT_FOUND                               = -105,
-	INVALID_DEVICE_CONFIG                       = -106,
-	LOOP                                        = -107,
+	FORMAT_NOT_SUPPORTED                        = -200,
+	DEVICE_TYPE_NOT_SUPPORTED                   = -201,
+	SHARE_MODE_NOT_SUPPORTED                    = -202,
+	NO_BACKEND                                  = -203,
+	NO_DEVICE                                   = -204,
+	API_NOT_FOUND                               = -205,
+	INVALID_DEVICE_CONFIG                       = -206,
+	LOOP                                        = -207,
+	BACKEND_NOT_ENABLED                         = -208,
 
 
 	/* State errors. */
 	/* State errors. */
-	DEVICE_NOT_INITIALIZED                      = -200,
-	DEVICE_ALREADY_INITIALIZED                  = -201,
-	DEVICE_NOT_STARTED                          = -202,
-	DEVICE_NOT_STOPPED                          = -203,
+	DEVICE_NOT_INITIALIZED                      = -300,
+	DEVICE_ALREADY_INITIALIZED                  = -301,
+	DEVICE_NOT_STARTED                          = -302,
+	DEVICE_NOT_STOPPED                          = -303,
 
 
 	/* Operation errors. */
 	/* Operation errors. */
-	FAILED_TO_INIT_BACKEND                      = -300,
-	FAILED_TO_OPEN_BACKEND_DEVICE               = -301,
-	FAILED_TO_START_BACKEND_DEVICE              = -302,
-	FAILED_TO_STOP_BACKEND_DEVICE               = -303,
-} 
+	FAILED_TO_INIT_BACKEND                      = -400,
+	FAILED_TO_OPEN_BACKEND_DEVICE               = -401,
+	FAILED_TO_START_BACKEND_DEVICE              = -402,
+	FAILED_TO_STOP_BACKEND_DEVICE               = -403,
+}
 
 
 
 
 MIN_CHANNELS :: 1
 MIN_CHANNELS :: 1
@@ -214,7 +218,7 @@ standard_sample_rate :: enum u32 {
 	rate_192000 = 192000,
 	rate_192000 = 192000,
 
 
 	rate_16000  = 16000,     /* Extreme lows */
 	rate_16000  = 16000,     /* Extreme lows */
-	rate_11025  = 11250,
+	rate_11025  = 11025,
 	rate_8000   = 8000,
 	rate_8000   = 8000,
 
 
 	rate_352800 = 352800,    /* Extreme highs */
 	rate_352800 = 352800,    /* Extreme highs */
@@ -229,7 +233,7 @@ standard_sample_rate :: enum u32 {
 channel_mix_mode :: enum c.int {
 channel_mix_mode :: enum c.int {
 	rectangular = 0,   /* Simple averaging based on the plane(s) the channel is sitting on. */
 	rectangular = 0,   /* Simple averaging based on the plane(s) the channel is sitting on. */
 	simple,            /* Drop excess channels; zeroed out extra channels. */
 	simple,            /* Drop excess channels; zeroed out extra channels. */
-	custom_weights,    /* Use custom weights specified in ma_channel_router_config. */
+	custom_weights,    /* Use custom weights specified in ma_channel_converter_config. */
 	default = rectangular,
 	default = rectangular,
 }
 }
 
 

+ 55 - 25
vendor/miniaudio/data_conversion.odin

@@ -138,7 +138,7 @@ foreign lib {
 	/*
 	/*
 	Converts the given input data.
 	Converts the given input data.
 
 
-	Both the input and output frames must be in the format specified in the config when the resampler was initilized.
+	Both the input and output frames must be in the format specified in the config when the resampler was initialized.
 
 
 	On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
 	On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
 	were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
 	were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
@@ -161,7 +161,7 @@ foreign lib {
 
 
 
 
 	/*
 	/*
-	Sets the input and output sample sample rate.
+	Sets the input and output sample rate.
 	*/
 	*/
 	resampler_set_rate :: proc(pResampler: ^resampler, sampleRateIn, sampleRateOut: u32) -> result ---
 	resampler_set_rate :: proc(pResampler: ^resampler, sampleRateIn, sampleRateOut: u32) -> result ---
 
 
@@ -226,13 +226,14 @@ mono_expansion_mode :: enum c.int {
 }
 }
 
 
 channel_converter_config :: struct {
 channel_converter_config :: struct {
-	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. */
+	format:                          format,
+	channelsIn:                      u32,
+	channelsOut:                     u32,
+	pChannelMapIn:                   [^]channel,
+	pChannelMapOut:                  [^]channel,
+	mixingMode:                      channel_mix_mode,
+	calculateLFEFromSpatialChannels: b32, /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */
+	ppWeights:                       ^[^]f32, /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
 }
 }
 
 
 channel_converter :: struct {
 channel_converter :: struct {
@@ -275,19 +276,20 @@ Data Conversion
 
 
 **************************************************************************************************************************************************************/
 **************************************************************************************************************************************************************/
 data_converter_config :: struct {
 data_converter_config :: struct {
-	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,
+	formatIn:                        format,
+	formatOut:                       format,
+	channelsIn:                      u32,
+	channelsOut:                     u32,
+	sampleRateIn:                    u32,
+	sampleRateOut:                   u32,
+	pChannelMapIn:                   [^]channel,
+	pChannelMapOut:                  [^]channel,
+	ditherMode:                      dither_mode,
+	channelMixMode:                  channel_mix_mode,
+	calculateLFEFromSpatialChannels: b32, /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */
+	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 {
 data_converter_execution_path :: enum c.int {
@@ -471,6 +473,28 @@ foreign lib {
 	The channel map buffer must have a capacity of at least `channels`.
 	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 ---
+
+	/*
+	Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The
+	index of the channel is output to `pChannelIndex`.
+	
+	The channel map buffer must have a capacity of at least `channels`.
+	*/
+	channel_map_find_channel_position :: proc(channels: u32, pChannelMap: [^]channel, channelPosition: channel, pChannelIndex: ^u32) -> b32 ---
+
+	/*
+	Generates a string representing the given channel map.
+	
+	This is for printing and debugging purposes, not serialization/deserialization.
+	
+	Returns the length of the string, not including the null terminator.
+	*/
+	channel_map_to_string :: proc(pChannelMap: [^]channel, channels: u32, pBufferOut: [^]u8, bufferCap: uint) -> uint ---
+	
+	/*
+	Retrieves a human readable version of a channel position.
+	*/
+	channel_position_to_string :: proc(channel: channel) -> cstring ---
 }
 }
 
 
 
 
@@ -514,9 +538,11 @@ rb :: struct {
 }
 }
 
 
 pcm_rb :: struct {
 pcm_rb :: struct {
-	rb:       rb,
-	format:   format,
-	channels: u32,
+	ds:         data_source_base,
+	rb:         rb,
+	format:     format,
+	channels:   u32,
+	sampleRate: u32, /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */
 }
 }
 
 
 @(default_calling_convention="c", link_prefix="ma_")
 @(default_calling_convention="c", link_prefix="ma_")
@@ -556,6 +582,10 @@ foreign lib {
 	pcm_rb_get_subbuffer_stride :: proc(pRB: ^pcm_rb) -> u32 ---
 	pcm_rb_get_subbuffer_stride :: proc(pRB: ^pcm_rb) -> u32 ---
 	pcm_rb_get_subbuffer_offset :: proc(pRB: ^pcm_rb, subbufferIndex: u32) -> u32 ---
 	pcm_rb_get_subbuffer_offset :: proc(pRB: ^pcm_rb, subbufferIndex: u32) -> u32 ---
 	pcm_rb_get_subbuffer_ptr    :: proc(pRB: ^pcm_rb, subbufferIndex: u32, pBuffer: rawptr) -> rawptr ---
 	pcm_rb_get_subbuffer_ptr    :: proc(pRB: ^pcm_rb, subbufferIndex: u32, pBuffer: rawptr) -> rawptr ---
+	pcm_rb_get_format           :: proc(pRB: ^pcm_rb) -> format ---
+	pcm_rb_get_channels         :: proc(pRB: ^pcm_rb) -> u32 ---
+	pcm_rb_get_sample_rate      :: proc(pRB: ^pcm_rb) -> u32 ---
+	pcm_rb_set_sample_rate      :: proc(pRB: ^pcm_rb, sampleRate: u32) ---
 }
 }
 
 
 /*
 /*

+ 11 - 8
vendor/miniaudio/device_io_procs.odin

@@ -636,17 +636,17 @@ foreign lib {
 					callback will write to every sample in the output buffer, or if you are doing your own clearing.
 					callback will write to every sample in the output buffer, or if you are doing your own clearing.
 
 
 			noClip
 			noClip
-					When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the
-					contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only
+        			When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or
+        			not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only
 					applies when the playback sample format is f32.
 					applies when the playback sample format is f32.
 
 
 			noDisableDenormals
 			noDisableDenormals
 					By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals.
 					By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals.
 
 
 			noFixedSizedCallback
 			noFixedSizedCallback
-					Allows miniaudio to fire the data callback with any frame count. When this is set to true, the data callback will be fired with a consistent frame
-					count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to false, miniaudio will fire the callback with whatever the
-					backend requests, which could be anything.
+        			Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a
+        			consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with
+        			whatever the backend requests, which could be anything.
 
 
 			dataCallback
 			dataCallback
 					The callback to fire whenever data is ready to be delivered to or from the device.
 					The callback to fire whenever data is ready to be delivered to or from the device.
@@ -668,7 +668,7 @@ foreign lib {
 					A pointer that will passed to callbacks in pBackendVTable.
 					A pointer that will passed to callbacks in pBackendVTable.
 
 
 			resampling.linear.lpfOrder
 			resampling.linear.lpfOrder
-					The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher
+					The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher
 					the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
 					the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
 					`MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
 					`MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
 
 
@@ -1150,8 +1150,6 @@ foreign lib {
 
 
 	Do not call this in any callback.
 	Do not call this in any callback.
 
 
-	This will be called implicitly by `ma_device_uninit()`.
-
 
 
 	See Also
 	See Also
 	--------
 	--------
@@ -1586,6 +1584,11 @@ foreign lib {
 	*/
 	*/
 	get_backend_name :: proc(backend: backend) -> cstring ---
 	get_backend_name :: proc(backend: backend) -> cstring ---
 
 
+	/*
+	Retrieves the backend enum from the given name.
+	*/
+	get_backend_from_name :: proc(pBackendName: cstring, pBackend: ^backend) -> result ---
+
 	/*
 	/*
 	Determines whether or not the given backend is available by the compilation environment.
 	Determines whether or not the given backend is available by the compilation environment.
 	*/
 	*/

+ 137 - 107
vendor/miniaudio/device_io_types.odin

@@ -84,6 +84,7 @@ device_notification_type :: enum c.int {
 	rerouted,
 	rerouted,
 	interruption_began,
 	interruption_began,
 	interruption_ended,
 	interruption_ended,
+	unlocked,
 }
 }
 
 
 device_notification :: struct {
 device_notification :: struct {
@@ -195,7 +196,7 @@ DEPRECATED. Use ma_device_notification_proc instead.
 The callback for when the device has been stopped.
 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
 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
-such as being unplugged or an internal error occuring.
+such as being unplugged or an internal error occurring.
 
 
 
 
 Parameters
 Parameters
@@ -225,7 +226,7 @@ share_mode :: enum c.int {
 
 
 /* iOS/tvOS/watchOS session categories. */
 /* iOS/tvOS/watchOS session categories. */
 ios_session_category :: enum c.int {
 ios_session_category :: enum c.int {
-	default = 0,        /* AVAudioSessionCategoryPlayAndRecord with AVAudioSessionCategoryOptionDefaultToSpeaker. */
+	default = 0,        /* AVAudioSessionCategoryPlayAndRecord. */
 	none,               /* Leave the session category unchanged. */
 	none,               /* Leave the session category unchanged. */
 	ambient,            /* AVAudioSessionCategoryAmbient */
 	ambient,            /* AVAudioSessionCategoryAmbient */
 	solo_ambient,       /* AVAudioSessionCategorySoloAmbient */
 	solo_ambient,       /* AVAudioSessionCategorySoloAmbient */
@@ -267,34 +268,41 @@ opensl_recording_preset :: enum c.int {
 	voice_unprocessed,   /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
 	voice_unprocessed,   /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
 }
 }
 
 
+/* WASAPI audio thread priority characteristics. */
+wasapi_usage :: enum c.int {
+	default = 0,
+	games,
+	pro_audio,
+}
+
 /* AAudio usage types. */
 /* AAudio usage types. */
 aaudio_usage :: enum c.int {
 aaudio_usage :: enum c.int {
 	default = 0,                    /* Leaves the usage type unset. */
 	default = 0,                    /* Leaves the usage type unset. */
-	announcement,                   /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
-	emergency,                      /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
-	safety,                         /* AAUDIO_SYSTEM_USAGE_SAFETY */
-	vehicle_status,                 /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
+	media,                          /* AAUDIO_USAGE_MEDIA */
+	voice_communication,            /* AAUDIO_USAGE_VOICE_COMMUNICATION */
+	voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
 	alarm,                          /* AAUDIO_USAGE_ALARM */
 	alarm,                          /* AAUDIO_USAGE_ALARM */
+	notification,                   /* AAUDIO_USAGE_NOTIFICATION */
+	notification_ringtone,          /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
+	notification_event,             /* AAUDIO_USAGE_NOTIFICATION_EVENT */
 	assistance_accessibility,       /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
 	assistance_accessibility,       /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
 	assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
 	assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
 	assistance_sonification,        /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
 	assistance_sonification,        /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
-	assitant,                       /* AAUDIO_USAGE_ASSISTANT */
 	game,                           /* AAUDIO_USAGE_GAME */
 	game,                           /* AAUDIO_USAGE_GAME */
-	media,                          /* AAUDIO_USAGE_MEDIA */
-	notification,                   /* AAUDIO_USAGE_NOTIFICATION */
-	notification_event,             /* AAUDIO_USAGE_NOTIFICATION_EVENT */
-	notification_ringtone,          /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
-	voice_communication,            /* AAUDIO_USAGE_VOICE_COMMUNICATION */
-	voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
+	assitant,                       /* AAUDIO_USAGE_ASSISTANT */
+	emergency,                      /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
+	safety,                         /* AAUDIO_SYSTEM_USAGE_SAFETY */
+	vehicle_status,                 /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
+	announcement,                   /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
 }
 }
 
 
 /* AAudio content types. */
 /* AAudio content types. */
 aaudio_content_type :: enum c.int {
 aaudio_content_type :: enum c.int {
 	default = 0,             /* Leaves the content type unset. */
 	default = 0,             /* Leaves the content type unset. */
-	movie,                   /* AAUDIO_CONTENT_TYPE_MOVIE */
+	speech,                  /* AAUDIO_CONTENT_TYPE_SPEECH */
 	music,                   /* AAUDIO_CONTENT_TYPE_MUSIC */
 	music,                   /* AAUDIO_CONTENT_TYPE_MUSIC */
+	movie,                   /* AAUDIO_CONTENT_TYPE_MOVIE */
 	sonification,            /* AAUDIO_CONTENT_TYPE_SONIFICATION */
 	sonification,            /* AAUDIO_CONTENT_TYPE_SONIFICATION */
-	speech,                  /* AAUDIO_CONTENT_TYPE_SPEECH */
 }
 }
 
 
 /* AAudio input presets. */
 /* AAudio input presets. */
@@ -302,12 +310,19 @@ aaudio_input_preset :: enum c.int {
 	default = 0,             /* Leaves the input preset unset. */
 	default = 0,             /* Leaves the input preset unset. */
 	generic,                 /* AAUDIO_INPUT_PRESET_GENERIC */
 	generic,                 /* AAUDIO_INPUT_PRESET_GENERIC */
 	camcorder,               /* AAUDIO_INPUT_PRESET_CAMCORDER */
 	camcorder,               /* AAUDIO_INPUT_PRESET_CAMCORDER */
-	unprocessed,             /* AAUDIO_INPUT_PRESET_UNPROCESSED */
 	voice_recognition,       /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
 	voice_recognition,       /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
 	voice_communication,     /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
 	voice_communication,     /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
+	unprocessed,             /* AAUDIO_INPUT_PRESET_UNPROCESSED */
 	voice_performance,       /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
 	voice_performance,       /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
 }
 }
 
 
+aaudio_allowed_capture_policy :: enum c.int {
+	default = 0, /* Leaves the allowed capture policy unset. */
+	all,         /* AAUDIO_ALLOW_CAPTURE_BY_ALL */
+	system,      /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */
+	none,        /* AAUDIO_ALLOW_CAPTURE_BY_NONE */
+}
+
 
 
 timer :: struct #raw_union {
 timer :: struct #raw_union {
 	counter:  i64,
 	counter:  i64,
@@ -364,36 +379,41 @@ device_config :: struct {
 	periods:                    u32,
 	periods:                    u32,
 	performanceProfile:         performance_profile,
 	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. */
 	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. */
+	noClip:                     b8,   /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */
 	noDisableDenormals:         b8,   /* Do not disable denormals when firing the data callback. */
 	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. */
+  	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,
 	dataCallback:               device_data_proc,
 	notificationCallback:       device_notification_proc,
 	notificationCallback:       device_notification_proc,
 	stopCallback:               stop_proc,
 	stopCallback:               stop_proc,
 	pUserData:                  rawptr,
 	pUserData:                  rawptr,
 	resampling:                 resampler_config,
 	resampling:                 resampler_config,
 	playback: struct {
 	playback: struct {
-		pDeviceID:      ^device_id,
-		format:         format,
-		channels:       u32,
-		channelMap:     [^]channel,
-		channelMixMode: channel_mix_mode,
-		shareMode:      share_mode,
+		pDeviceID:                       ^device_id,
+		format:                          format,
+		channels:                        u32,
+		channelMap:                      [^]channel,
+		channelMixMode:                  channel_mix_mode,
+		calculateLFEFromSpatialChannels: b32, /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */
+		shareMode:                       share_mode,
 	},
 	},
 	capture: struct {
 	capture: struct {
-		pDeviceID:      ^device_id,
-		format:         format,
-		channels:       u32,
-		channelMap:     [^]channel,
-		channelMixMode: channel_mix_mode,
-		shareMode:      share_mode,
+		pDeviceID:                       ^device_id,
+		format:                          format,
+		channels:                        u32,
+		channelMap:                      [^]channel,
+		channelMixMode:                  channel_mix_mode,
+		calculateLFEFromSpatialChannels: b32, /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */
+		shareMode:                       share_mode,
 	},
 	},
 
 
 	wasapi: struct {
 	wasapi: struct {
-		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. */
-		noAutoStreamRouting:  b8, /* Disables automatic stream routing. */
-		noHardwareOffloading: b8, /* Disables WASAPI's hardware offloading feature. */
+		usage:                  wasapi_usage, /* When configured, uses Avrt APIs to set the thread characteristics. */
+		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. */
+		noAutoStreamRouting:    b8, /* Disables automatic stream routing. */
+		noHardwareOffloading:   b8, /* Disables WASAPI's hardware offloading feature. */
+		loopbackProcessID:      u32, /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */
+		loopbackProcessExclude: b8, /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */
 	},
 	},
 	alsa: struct {
 	alsa: struct {
 		noMMap:         b32, /* Disables MMap mode. */
 		noMMap:         b32, /* Disables MMap mode. */
@@ -409,20 +429,23 @@ device_config :: struct {
 		allowNominalSampleRateChange: b32, /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
 		allowNominalSampleRateChange: b32, /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
 	},
 	},
 	opensl: struct {
 	opensl: struct {
-		streamType:      opensl_stream_type,
-		recordingPreset: opensl_recording_preset,
+		streamType:                     opensl_stream_type,
+		recordingPreset:                opensl_recording_preset,
+		enableCompatibilityWorkarounds: b32,
 	},
 	},
 	aaudio: struct {
 	aaudio: struct {
-		usage:                   aaudio_usage,
-		contentType:             aaudio_content_type,
-		inputPreset:             aaudio_input_preset,
-		noAutoStartAfterReroute: b32,
+		usage:                          aaudio_usage,
+		contentType:                    aaudio_content_type,
+		inputPreset:                    aaudio_input_preset,
+		allowedCapturePolicy:           aaudio_allowed_capture_policy,
+		noAutoStartAfterReroute:        b32,
+		enableCompatibilityWorkarounds: b32,
 	},
 	},
 }
 }
 
 
 
 
 /*
 /*
-The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`.
+The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`.
 
 
 
 
 Parameters
 Parameters
@@ -500,7 +523,7 @@ sample rate. For the channel map, the default should be used when `ma_channel_ma
 `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
 `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
 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
 size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
-sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_data_format`
+sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor`
 object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
 object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
 
 
 Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
 Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
@@ -516,7 +539,7 @@ If the backend requires absolute flexibility with it's data delivery, it can opt
 which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
 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_device_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.
+encountered. 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
 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_device_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
@@ -609,12 +632,17 @@ context_type :: struct {
 
 
 	using _: struct #raw_union {
 	using _: struct #raw_union {
 		wasapi: (struct {
 		wasapi: (struct {
-			commandThread: thread,
-			commandLock:   mutex,
-			commandSem:    semaphore,
-			commandIndex:  u32,
-			commandCount:  u32,
-			commands:      [4]context_command__wasapi,
+			commandThread:                   thread,
+			commandLock:                     mutex,
+			commandSem:                      semaphore,
+			commandIndex:                    u32,
+			commandCount:                    u32,
+			commands:                        [4]context_command__wasapi,
+			hAvrt:                           handle,
+			AvSetMmThreadCharacteristicsA:   proc "system" (),
+			AvRevertMmThreadCharacteristics: proc "system" (),
+			hMMDevapi:                       handle,
+			ActivateAudioInterfaceAsync:     proc "system" (),
 		} when SUPPORT_WASAPI else struct {}),
 		} when SUPPORT_WASAPI else struct {}),
 		
 		
 		dsound: (struct {
 		dsound: (struct {
@@ -888,6 +916,7 @@ context_type :: struct {
 			AAudioStreamBuilder_setUsage:                  proc "system" (),
 			AAudioStreamBuilder_setUsage:                  proc "system" (),
 			AAudioStreamBuilder_setContentType:            proc "system" (),
 			AAudioStreamBuilder_setContentType:            proc "system" (),
 			AAudioStreamBuilder_setInputPreset:            proc "system" (),
 			AAudioStreamBuilder_setInputPreset:            proc "system" (),
+			AAudioStreamBuilder_setAllowedCapturePolicy:   proc "system" (),
 			AAudioStreamBuilder_openStream:                proc "system" (),
 			AAudioStreamBuilder_openStream:                proc "system" (),
 			AAudioStream_close:                            proc "system" (),
 			AAudioStream_close:                            proc "system" (),
 			AAudioStream_getState:                         proc "system" (),
 			AAudioStream_getState:                         proc "system" (),
@@ -926,6 +955,7 @@ context_type :: struct {
 	using _: struct #raw_union {
 	using _: struct #raw_union {
 		win32: (struct {
 		win32: (struct {
 			/*HMODULE*/ hOle32DLL:       handle,
 			/*HMODULE*/ hOle32DLL:       handle,
+			CoInitialize:                proc "system" (),
 			CoInitializeEx:              proc "system" (),
 			CoInitializeEx:              proc "system" (),
 			CoUninitialize:              proc "system" (),
 			CoUninitialize:              proc "system" (),
 			CoCreateInstance:            proc "system" (),
 			CoCreateInstance:            proc "system" (),
@@ -941,25 +971,12 @@ context_type :: struct {
 			RegOpenKeyExA:               proc "system" (),
 			RegOpenKeyExA:               proc "system" (),
 			RegCloseKey:                 proc "system" (),
 			RegCloseKey:                 proc "system" (),
 			RegQueryValueExA:            proc "system" (),
 			RegQueryValueExA:            proc "system" (),
+
+			/*HRESULT*/ CoInitializeResult: c.long,
 		} when ODIN_OS == .Windows else struct {}),
 		} when ODIN_OS == .Windows else struct {}),
 		
 		
 		posix: (struct {
 		posix: (struct {
-			pthreadSO:                   handle,
-			pthread_create:              proc "system" (),
-			pthread_join:                proc "system" (),
-			pthread_mutex_init:          proc "system" (),
-			pthread_mutex_destroy:       proc "system" (),
-			pthread_mutex_lock:          proc "system" (),
-			pthread_mutex_unlock:        proc "system" (),
-			pthread_cond_init:           proc "system" (),
-			pthread_cond_destroy:        proc "system" (),
-			pthread_cond_wait:           proc "system" (),
-			pthread_cond_signal:         proc "system" (),
-			pthread_attr_init:           proc "system" (),
-			pthread_attr_destroy:        proc "system" (),
-			pthread_attr_setschedpolicy: proc "system" (),
-			pthread_attr_getschedparam:  proc "system" (),
-			pthread_attr_setschedparam:  proc "system" (),
+			_unused: c.int,
 		} when ODIN_OS != .Windows else struct {}),
 		} when ODIN_OS != .Windows else struct {}),
 		
 		
 		_unused: c.int,
 		_unused: c.int,
@@ -997,48 +1014,50 @@ device :: struct {
 		},
 		},
 	},
 	},
 	playback: struct {
 	playback: struct {
-		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,
-		internalFormat:             format,
-		internalChannels:           u32,
-		internalSampleRate:         u32,
-		internalChannelMap:         [MAX_CHANNELS]channel,
-		internalPeriodSizeInFrames: u32,
-		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,
+		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,
+		internalFormat:                  format,
+		internalChannels:                u32,
+		internalSampleRate:              u32,
+		internalChannelMap:              [MAX_CHANNELS]channel,
+		internalPeriodSizeInFrames:      u32,
+		internalPeriods:                 u32,
+		channelMixMode:                  channel_mix_mode,
+		calculateLFEFromSpatialChannels: b32,
+		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 {
 	capture: struct {
-		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,
-		internalFormat:             format,
-		internalChannels:           u32,
-		internalSampleRate:         u32,
-		internalChannelMap:         [MAX_CHANNELS]channel,
-		internalPeriodSizeInFrames: u32,
-		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. */
+		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,
+		internalFormat:                  format,
+		internalChannels:                u32,
+		internalSampleRate:              u32,
+		internalChannelMap:              [MAX_CHANNELS]channel,
+		internalPeriodSizeInFrames:      u32,
+		internalPeriods:                 u32,
+		channelMixMode:                  channel_mix_mode,
+		calculateLFEFromSpatialChannels: b32,
+		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 {
 	using _: struct #raw_union {
@@ -1067,6 +1086,8 @@ device :: struct {
 			mappedBufferPlaybackLen: u32,
 			mappedBufferPlaybackLen: u32,
 			isStartedCapture: b32, /*atomic*/                  /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
 			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. */
 			isStartedPlayback: b32, /*atomic*/                 /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
+			loopbackProcessID: u32,
+			loopbackProcessExclude: b8,
 			noAutoConvertSRC: b8,                              /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
 			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. */
 			noDefaultQualitySRC: b8,                           /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
 			noHardwareOffloading: b8,
 			noHardwareOffloading: b8,
@@ -1074,6 +1095,9 @@ device :: struct {
 			allowPlaybackAutoStreamRouting: b8,
 			allowPlaybackAutoStreamRouting: b8,
 			isDetachedPlayback: b8,
 			isDetachedPlayback: b8,
 			isDetachedCapture: b8,
 			isDetachedCapture: b8,
+			usage: wasapi_usage,
+			hAvrtHandle: rawptr,
+			rerouteLock: mutex,
 		} when SUPPORT_WASAPI else struct {}),
 		} when SUPPORT_WASAPI else struct {}),
 		
 		
 		dsound: (struct {
 		dsound: (struct {
@@ -1171,6 +1195,7 @@ device :: struct {
 			usage: aaudio_usage,
 			usage: aaudio_usage,
 			contentType: aaudio_content_type,
 			contentType: aaudio_content_type,
 			inputPreset: aaudio_input_preset,
 			inputPreset: aaudio_input_preset,
+			allowedCapturePolicy: aaudio_allowed_capture_policy,
 			noAutoStartAfterReroute: b32,
 			noAutoStartAfterReroute: b32,
 		} when SUPPORT_AAUDIO else struct {}),
 		} when SUPPORT_AAUDIO else struct {}),
 
 
@@ -1192,8 +1217,13 @@ device :: struct {
 		} when SUPPORT_OPENSL else struct {}),
 		} when SUPPORT_OPENSL else struct {}),
 
 
 		webaudio: (struct {
 		webaudio: (struct {
-			indexPlayback: c.int,              /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
-			indexCapture: c.int,
+			/* audioWorklets path. */
+			/* EMSCRIPTEN_WEBAUDIO_T */ audioContext: c.int,
+			/* EMSCRIPTEN_WEBAUDIO_T */ audioWorklet: c.int,
+			pIntermediaryBuffer: ^f32,
+			pStackBuffer: rawptr,
+			initResult: result, /* Set to MA_BUSY while initializing is in progress. */
+			deviceIndex: c.int, /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */
 		} when SUPPORT_WEBAUDIO else struct {}),
 		} when SUPPORT_WEBAUDIO else struct {}),
 
 
 		null_device: (struct {
 		null_device: (struct {

+ 203 - 120
vendor/miniaudio/doc.odin

@@ -2,7 +2,7 @@ package miniaudio
 
 
 /*
 /*
 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
-miniaudio - v0.11.9 - 2022-04-20
+miniaudio - v0.11.21 - 2023-11-15
 
 
 David Reid - [email protected]
 David Reid - [email protected]
 
 
@@ -40,7 +40,7 @@ A config/init pattern is used throughout the entire library. The idea is that yo
 object and pass that into the initialization routine. The advantage to this system is that the
 object and pass that into the initialization routine. The advantage to this system is that the
 config object can be initialized with logical defaults and new properties added to it without
 config object can be initialized with logical defaults and new properties added to it without
 breaking the API. The config object can be allocated on the stack and does not need to be
 breaking the API. The config object can be allocated on the stack and does not need to be
-maintained after initialization of the corresponding object. 
+maintained after initialization of the corresponding object.
 
 
 
 
 1.1. Low Level API
 1.1. Low Level API
@@ -89,7 +89,7 @@ device on the stack, but you could allocate it on the heap if that suits your si
 
 
         // Do something here. Probably your program's main loop.
         // Do something here. Probably your program's main loop.
 
 
-        ma_device_uninit(&device);    // This will stop the device so no need to do that manually.
+        ma_device_uninit(&device);
         return 0;
         return 0;
     }
     }
     ```
     ```
@@ -365,7 +365,7 @@ initialized. The easiest but least flexible way of playing a sound is like so:
 This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
 This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
 internal sound up for recycling. The last parameter is used to specify which sound group the sound
 internal sound up for recycling. The last parameter is used to specify which sound group the sound
 should be associated with which will be explained later. This particular way of playing a sound is
 should be associated with which will be explained later. This particular way of playing a sound is
-simple, but lacks flexibility and features. A more flexible way of playing a sound is to first 
+simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
 initialize a sound:
 initialize a sound:
 
 
     ```c
     ```c
@@ -388,7 +388,7 @@ Sounds should be uninitialized with `ma_sound_uninit()`.
 
 
 Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
 Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
 `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
 `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
-`ma_sound_seek_to_pcm_frames(&sound, 0)` to seek back to the start of a sound. By default, starting
+`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting
 and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
 and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
 the be started and/or stopped at a specific time. This can be done with the following functions:
 the be started and/or stopped at a specific time. This can be done with the following functions:
 
 
@@ -400,13 +400,13 @@ the be started and/or stopped at a specific time. This can be done with the foll
     ```
     ```
 
 
 The start/stop time needs to be specified based on the absolute timer which is controlled by the
 The start/stop time needs to be specified based on the absolute timer which is controlled by the
-engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
-The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
-required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()`
-before anything will play:
+engine. The current global time time in PCM frames can be retrieved with
+`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with
+`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling
+a start time still requires an explicit call to `ma_sound_start()` before anything will play:
 
 
     ```c
     ```c
-    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
+    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
     ma_sound_start(&sound);
     ma_sound_start(&sound);
     ```
     ```
 
 
@@ -462,6 +462,11 @@ is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled wit
 miniaudio should work cleanly out of the box without the need to download or install any
 miniaudio should work cleanly out of the box without the need to download or install any
 dependencies. See below for platform-specific details.
 dependencies. See below for platform-specific details.
 
 
+Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations.
+
+If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`,
+etc. you need to link with `-latomic`.
+
 
 
 2.1. Windows
 2.1. Windows
 ------------
 ------------
@@ -491,9 +496,10 @@ notarization process. To fix this there are two options. The first is to use the
     #include "miniaudio.h"
     #include "miniaudio.h"
     ```
     ```
 
 
-This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`.
-Alternatively, if you would rather keep using runtime linking you can add the following to your
-entitlements.xcent file:
+This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`.
+If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when
+using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can
+add the following to your entitlements.xcent file:
 
 
     ```
     ```
     <key>com.apple.security.cs.allow-dyld-environment-variables</key>
     <key>com.apple.security.cs.allow-dyld-environment-variables</key>
@@ -534,6 +540,20 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi
 The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
 The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
 You cannot use `-std=c*` compiler flags, nor `-ansi`.
 You cannot use `-std=c*` compiler flags, nor `-ansi`.
 
 
+You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling
+with the following options:
+
+    -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
+
+An example for compiling with AudioWorklet support might look like this:
+
+    emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
+
+To run locally, you'll need to use emrun:
+
+    emrun bin/program.html
+
+
 
 
 2.7. Build Options
 2.7. Build Options
 ------------------
 ------------------
@@ -629,10 +649,29 @@ You cannot use `-std=c*` compiler flags, nor `-ansi`.
     |                                  | and `ma_device` APIs. This is useful if you only want to use       |
     |                                  | and `ma_device` APIs. This is useful if you only want to use       |
     |                                  | miniaudio's data conversion and/or decoding APIs.                  |
     |                                  | miniaudio's data conversion and/or decoding APIs.                  |
     +----------------------------------+--------------------------------------------------------------------+
     +----------------------------------+--------------------------------------------------------------------+
+    | MA_NO_RESOURCE_MANAGER           | Disables the resource manager. When using the engine this will     |
+    |                                  | also disable the following functions:                              |
+    |                                  |                                                                    |
+    |                                  | ```                                                                |
+    |                                  | ma_sound_init_from_file()                                          |
+    |                                  | ma_sound_init_from_file_w()                                        |
+    |                                  | ma_sound_init_copy()                                               |
+    |                                  | ma_engine_play_sound_ex()                                          |
+    |                                  | ma_engine_play_sound()                                             |
+    |                                  | ```                                                                |
+    |                                  |                                                                    |
+    |                                  | The only way to initialize a `ma_sound` object is to initialize it |
+    |                                  | from a data source.                                                |
+    +----------------------------------+--------------------------------------------------------------------+
+    | MA_NO_NODE_GRAPH                 | Disables the node graph API. This will also disable the engine API |
+    |                                  | because it depends on the node graph.                              |
+    +----------------------------------+--------------------------------------------------------------------+
+    | MA_NO_ENGINE                     | Disables the engine API.                                           |
+    +----------------------------------+--------------------------------------------------------------------+
     | MA_NO_THREADING                  | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and           |
     | MA_NO_THREADING                  | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and           |
     |                                  | `ma_event` APIs. This option is useful if you only need to use     |
     |                                  | `ma_event` APIs. This option is useful if you only need to use     |
     |                                  | miniaudio for data conversion, decoding and/or encoding. Some      |
     |                                  | miniaudio for data conversion, decoding and/or encoding. Some      |
-    |                                  | families of APIsrequire threading which means the following        |
+    |                                  | families of APIs require threading which means the following       |
     |                                  | options must also be set:                                          |
     |                                  | options must also be set:                                          |
     |                                  |                                                                    |
     |                                  |                                                                    |
     |                                  |     ```                                                            |
     |                                  |     ```                                                            |
@@ -731,7 +770,7 @@ To read data from a data source:
     ma_result result;
     ma_result result;
     ma_uint64 framesRead;
     ma_uint64 framesRead;
 
 
-    result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop);
+    result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead);
     if (result != MA_SUCCESS) {
     if (result != MA_SUCCESS) {
         return result;  // Failed to read data from the data source.
         return result;  // Failed to read data from the data source.
     }
     }
@@ -751,7 +790,7 @@ you could plug in a decoder like so:
     ma_uint64 framesRead;
     ma_uint64 framesRead;
     ma_decoder decoder;   // <-- This would be initialized with `ma_decoder_init_*()`.
     ma_decoder decoder;   // <-- This would be initialized with `ma_decoder_init_*()`.
 
 
-    result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop);
+    result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead);
     if (result != MA_SUCCESS) {
     if (result != MA_SUCCESS) {
         return result;  // Failed to read data from the decoder.
         return result;  // Failed to read data from the decoder.
     }
     }
@@ -805,7 +844,7 @@ retrieved like so:
     ma_uint32 channels;
     ma_uint32 channels;
     ma_uint32 sampleRate;
     ma_uint32 sampleRate;
     ma_channel channelMap[MA_MAX_CHANNELS];
     ma_channel channelMap[MA_MAX_CHANNELS];
-    
+
     result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
     result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
     if (result != MA_SUCCESS) {
     if (result != MA_SUCCESS) {
         return result;  // Failed to retrieve data format.
         return result;  // Failed to retrieve data format.
@@ -825,7 +864,9 @@ read data within a certain range of the underlying data. To do this you can use
     ```
     ```
 
 
 This is useful if you have a sound bank where many sounds are stored in the same file and you want
 This is useful if you have a sound bank where many sounds are stored in the same file and you want
-the data source to only play one of those sub-sounds.
+the data source to only play one of those sub-sounds. Note that once the range is set, everything
+that takes a position, such as cursors and loop points, should always be relatvie to the start of
+the range. When the range is set, any previously defined loop point will be reset.
 
 
 Custom loop points can also be used with data sources. By default, data sources will loop after
 Custom loop points can also be used with data sources. By default, data sources will loop after
 they reach the end of the data source, but if you need to loop at a specific location, you can do
 they reach the end of the data source, but if you need to loop at a specific location, you can do
@@ -854,19 +895,19 @@ To do this, you can use chaining:
         return result;  // Failed to set the next data source.
         return result;  // Failed to set the next data source.
     }
     }
 
 
-    result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE);
+    result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead);
     if (result != MA_SUCCESS) {
     if (result != MA_SUCCESS) {
         return result;  // Failed to read from the decoder.
         return result;  // Failed to read from the decoder.
     }
     }
     ```
     ```
 
 
 In the example above we're using decoders. When reading from a chain, you always want to read from
 In the example above we're using decoders. When reading from a chain, you always want to read from
-the top level data source in the chain. In the example above, `decoder1` is the top level data 
+the top level data source in the chain. In the example above, `decoder1` is the top level data
 source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
 source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
 gaps.
 gaps.
 
 
-Note that the `loop` parameter is set to false in the example above. When this is set to true, only
-the current data source will be looped. You can loop the entire chain by linking in a loop like so:
+Note that when looping is enabled, only the current data source will be looped. You can loop the
+entire chain by linking in a loop like so:
 
 
     ```c
     ```c
     ma_data_source_set_next(&decoder1, &decoder2);  // decoder1 -> decoder2
     ma_data_source_set_next(&decoder1, &decoder2);  // decoder1 -> decoder2
@@ -877,9 +918,9 @@ Note that setting up chaining is not thread safe, so care needs to be taken if y
 changing links while the audio thread is in the middle of reading.
 changing links while the audio thread is in the middle of reading.
 
 
 Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
 Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
-instances of the same sound simultaneously. Instead, initialize multiple data sources for each
-instance. This can be extremely inefficient depending on the data source and can result in
-glitching due to subtle changes to the state of internal filters.
+instances of the same sound simultaneously. This can be extremely inefficient depending on the type
+of data source and can result in glitching due to subtle changes to the state of internal filters.
+Instead, initialize multiple data sources for each instance.
 
 
 
 
 4.1. Custom Data Sources
 4.1. Custom Data Sources
@@ -924,7 +965,7 @@ base object (`ma_data_source_base`):
         // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.
         // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown.
     }
     }
 
 
-    static g_my_data_source_vtable =
+    static ma_data_source_vtable g_my_data_source_vtable =
     {
     {
         my_data_source_read,
         my_data_source_read,
         my_data_source_seek,
         my_data_source_seek,
@@ -954,7 +995,7 @@ base object (`ma_data_source_base`):
     void my_data_source_uninit(my_data_source* pMyDataSource)
     void my_data_source_uninit(my_data_source* pMyDataSource)
     {
     {
         // ... do the uninitialization of your custom data source here ...
         // ... do the uninitialization of your custom data source here ...
-        
+
         // You must uninitialize the base data source.
         // You must uninitialize the base data source.
         ma_data_source_uninit(&pMyDataSource->base);
         ma_data_source_uninit(&pMyDataSource->base);
     }
     }
@@ -1003,7 +1044,7 @@ configure the engine with an engine config:
     ma_engine_config engineConfig;
     ma_engine_config engineConfig;
 
 
     engineConfig = ma_engine_config_init();
     engineConfig = ma_engine_config_init();
-    engineConfig.pPlaybackDevice = &myDevice;
+    engineConfig.pDevice = &myDevice;
 
 
     result = ma_engine_init(&engineConfig, &engine);
     result = ma_engine_init(&engineConfig, &engine);
     if (result != MA_SUCCESS) {
     if (result != MA_SUCCESS) {
@@ -1044,7 +1085,7 @@ Note that when you're not using a device, you must set the channel count and sam
 config or else miniaudio won't know what to use (miniaudio will use the device to determine this
 config or else miniaudio won't know what to use (miniaudio will use the device to determine this
 normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
 normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
 data from the engine. This kind of setup is useful if you want to do something like offline
 data from the engine. This kind of setup is useful if you want to do something like offline
-processing.
+processing or want to use a different audio system for playback such as SDL.
 
 
 When a sound is loaded it goes through a resource manager. By default the engine will initialize a
 When a sound is loaded it goes through a resource manager. By default the engine will initialize a
 resource manager internally, but you can also specify a pre-initialized resource manager:
 resource manager internally, but you can also specify a pre-initialized resource manager:
@@ -1209,7 +1250,7 @@ might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_
 
 
 By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
 By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
 the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
 the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
-by specificying the `MA_SOUND_FLAG_ASYNC` flag:
+by specifying the `MA_SOUND_FLAG_ASYNC` flag:
 
 
     ```c
     ```c
     ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
     ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
@@ -1230,7 +1271,7 @@ counter hit's zero. You can specify a fence like so:
     ma_sound sounds[4];
     ma_sound sounds[4];
 
 
     result = ma_fence_init(&fence);
     result = ma_fence_init(&fence);
-    if (result != MA_SUCCES) {
+    if (result != MA_SUCCESS) {
         return result;
         return result;
     }
     }
 
 
@@ -1256,6 +1297,18 @@ When streaming sounds, 2 seconds worth of audio data is stored in memory. Althou
 fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
 fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
 tracks in games.
 tracks in games.
 
 
+When loading a sound from a file path, the engine will reference count the file to prevent it from
+being loaded if it's already in memory. When you uninitialize a sound, the reference count will be
+decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting
+system is not used for streams. The engine will use a 64-bit hash of the file name when comparing
+file paths which means there's a small chance you might encounter a name collision. If this is an
+issue, you'll need to use a different name for one of the colliding file paths, or just not load
+from files and instead load from a data source.
+
+You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this
+only works for sounds that were initialized with `ma_sound_init_from_file()` and without the
+`MA_SOUND_FLAG_STREAM` flag.
+
 When you initialize a sound, if you specify a sound group the sound will be attached to that group
 When you initialize a sound, if you specify a sound group the sound will be attached to that group
 automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
 automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
 If you would instead rather leave the sound unattached by default, you can can specify the
 If you would instead rather leave the sound unattached by default, you can can specify the
@@ -1395,19 +1448,19 @@ can be useful to schedule a sound to start or stop:
 
 
     ```c
     ```c
     // Start the sound in 1 second from now.
     // Start the sound in 1 second from now.
-    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
+    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
 
 
     // Stop the sound in 2 seconds from now.
     // Stop the sound in 2 seconds from now.
-    ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
+    ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
     ```
     ```
 
 
 Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
 Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
 anything will play.
 anything will play.
 
 
 The time is specified in global time which is controlled by the engine. You can get the engine's
 The time is specified in global time which is controlled by the engine. You can get the engine's
-current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as
-audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be
-resynchronized for some reason.
+current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented
+automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()`
+in case it needs to be resynchronized for some reason.
 
 
 To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
 To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
 take the scheduled start and stop times into account.
 take the scheduled start and stop times into account.
@@ -1416,7 +1469,25 @@ Whether or not a sound should loop can be controlled with `ma_sound_set_looping(
 be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
 be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
 
 
 Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
 Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
-sound this should never return true.
+sound this should never return true. Alternatively, you can configure a callback that will be fired
+when the sound reaches the end. Note that the callback is fired from the audio thread which means
+you cannot be uninitializing sound from the callback. To set the callback you can use
+`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it
+into the config like so:
+
+    ```c
+    soundConfig.endCallback = my_end_callback;
+    soundConfig.pEndCallbackUserData = pMyEndCallbackUserData;
+    ```
+
+The end callback is declared like so:
+
+    ```c
+    void my_end_callback(void* pUserData, ma_sound* pSound)
+    {
+        ...
+    }
+    ```
 
 
 Internally a sound wraps around a data source. Some APIs exist to control the underlying data
 Internally a sound wraps around a data source. Some APIs exist to control the underlying data
 source, mainly for convenience:
 source, mainly for convenience:
@@ -1431,7 +1502,7 @@ source, mainly for convenience:
 Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
 Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
 not have any notion of a data source, anything relating to a data source is unavailable.
 not have any notion of a data source, anything relating to a data source is unavailable.
 
 
-Internally, sound data is loaded via the `ma_decoder` API which means by default in only supports
+Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports
 file formats that have built-in support in miniaudio. You can extend this to support any kind of
 file formats that have built-in support in miniaudio. You can extend this to support any kind of
 file format through the use of custom decoders. To do this you'll need to use a self-managed
 file format through the use of custom decoders. To do this you'll need to use a self-managed
 resource manager and configure it appropriately. See the "Resource Management" section below for
 resource manager and configure it appropriately. See the "Resource Management" section below for
@@ -1446,7 +1517,7 @@ streaming. This is supported by miniaudio via the `ma_resource_manager` API.
 The resource manager is mainly responsible for the following:
 The resource manager is mainly responsible for the following:
 
 
   * Loading of sound files into memory with reference counting.
   * Loading of sound files into memory with reference counting.
-  * Streaming of sound data
+  * Streaming of sound data.
 
 
 When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
 When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
 object called `ma_resource_manager_data_source`. This object can be passed into any
 object called `ma_resource_manager_data_source`. This object can be passed into any
@@ -1541,7 +1612,7 @@ need to retrieve a job using `ma_resource_manager_next_job()` and then process i
             ma_job job;
             ma_job job;
             ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
             ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
             if (result != MA_SUCCESS) {
             if (result != MA_SUCCESS) {
-                if (result == MA_NOT_DATA_AVAILABLE) {
+                if (result == MA_NO_DATA_AVAILABLE) {
                     // No jobs are available. Keep going. Will only get this if the resource manager was initialized
                     // No jobs are available. Keep going. Will only get this if the resource manager was initialized
                     // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
                     // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
                     continue;
                     continue;
@@ -1580,7 +1651,7 @@ default. This can be done by setting `pVFS` member of the resource manager's con
 
 
 This is particularly useful in programs like games where you want to read straight from an archive
 This is particularly useful in programs like games where you want to read straight from an archive
 rather than the normal file system. If you do not specify a custom VFS, the resource manager will
 rather than the normal file system. If you do not specify a custom VFS, the resource manager will
-use the operating system's normal file operations. This is default.
+use the operating system's normal file operations.
 
 
 To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
 To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
 loading a sound you need to specify the file path and options for how the sounds should be loaded.
 loading a sound you need to specify the file path and options for how the sounds should be loaded.
@@ -1606,7 +1677,7 @@ an example for initializing a data source:
 
 
     // ...
     // ...
 
 
-    ma_resource_manager_data_source_uninit(pResourceManager, &dataSource);
+    ma_resource_manager_data_source_uninit(&dataSource);
     ```
     ```
 
 
 The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
 The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
@@ -1843,19 +1914,21 @@ once after the other:
 
 
     ```c
     ```c
     ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
     ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
-    ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0);               // Refcount = 0. Unloaded.
+    ma_resource_manager_data_source_uninit(&myDataBuffer0);                                 // Refcount = 0. Unloaded.
 
 
     ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
     ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
-    ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1);               // Refcount = 0. Unloaded.
+    ma_resource_manager_data_source_uninit(&myDataBuffer1);                                 // Refcount = 0. Unloaded.
     ```
     ```
 
 
 A binary search tree (BST) is used for storing data buffers as it has good balance between
 A binary search tree (BST) is used for storing data buffers as it has good balance between
 efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
 efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
 into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
 into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
 memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
 memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
-due to the random nature of the hash. The disadvantage is that file names are case-sensitive. If
-this is an issue, you should normalize your file names to upper- or lower-case before initializing
-your data sources.
+due to the random nature of the hash. The disadvantages are that file names are case-sensitive and
+there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize
+your file names to upper- or lower-case before initializing your data sources. If name collisions
+become an issue, you'll need to change the name of one of the colliding names or just not use the
+resource manager.
 
 
 When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
 When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
 flag is excluded, the file will be decoded synchronously by the calling thread. There are two
 flag is excluded, the file will be decoded synchronously by the calling thread. There are two
@@ -1935,7 +2008,7 @@ miniaudio's routing infrastructure follows a node graph paradigm. The idea is th
 node whose outputs are attached to inputs of another node, thereby creating a graph. There are
 node whose outputs are attached to inputs of another node, thereby creating a graph. There are
 different types of nodes, with each node in the graph processing input data to produce output,
 different types of nodes, with each node in the graph processing input data to produce output,
 which is then fed through the chain. Each node in the graph can apply their own custom effects. At
 which is then fed through the chain. Each node in the graph can apply their own custom effects. At
-the start of the graph will usually be one or more data source nodes which have no inputs, but
+the start of the graph will usually be one or more data source nodes which have no inputs and
 instead pull their data from a data source. At the end of the graph is an endpoint which represents
 instead pull their data from a data source. At the end of the graph is an endpoint which represents
 the end of the chain and is where the final output is ultimately extracted from.
 the end of the chain and is where the final output is ultimately extracted from.
 
 
@@ -1961,7 +2034,7 @@ splitter node. It's at this point that the two data sources are mixed. After mix
 performs it's processing routine and produces two outputs which is simply a duplication of the
 performs it's processing routine and produces two outputs which is simply a duplication of the
 input stream. One output is attached to a low pass filter, whereas the other output is attached to
 input stream. One output is attached to a low pass filter, whereas the other output is attached to
 a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
 a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
-since they're both connected to the same input but, they'll be mixed.
+since they're both connected to the same input bus, they'll be mixed.
 
 
 Each input bus must be configured to accept the same number of channels, but the number of channels
 Each input bus must be configured to accept the same number of channels, but the number of channels
 used by input buses can be different to the number of channels for output buses in which case
 used by input buses can be different to the number of channels for output buses in which case
@@ -2001,14 +2074,14 @@ data from the graph:
     ```
     ```
 
 
 When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
 When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
-data from it's input attachments, which in turn recusively pull in data from their inputs, and so
+data from it's input attachments, which in turn recursively pull in data from their inputs, and so
 on. At the start of the graph there will be some kind of data source node which will have zero
 on. At the start of the graph there will be some kind of data source node which will have zero
 inputs and will instead read directly from a data source. The base nodes don't literally need to
 inputs and will instead read directly from a data source. The base nodes don't literally need to
 read from a `ma_data_source` object, but they will always have some kind of underlying object that
 read from a `ma_data_source` object, but they will always have some kind of underlying object that
 sources some kind of audio. The `ma_data_source_node` node can be used to read from a
 sources some kind of audio. The `ma_data_source_node` node can be used to read from a
 `ma_data_source`. Data is always in floating-point format and in the number of channels you
 `ma_data_source`. Data is always in floating-point format and in the number of channels you
 specified when the graph was initialized. The sample rate is defined by the underlying data sources.
 specified when the graph was initialized. The sample rate is defined by the underlying data sources.
-It's up to you to ensure they use a consistent and appropraite sample rate.
+It's up to you to ensure they use a consistent and appropriate sample rate.
 
 
 The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
 The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
 miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
 miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
@@ -2049,7 +2122,7 @@ another, you do not need to detach first. You can just call `ma_node_attach_outp
 deal with it for you.
 deal with it for you.
 
 
 Less frequently you may want to create a specialized node. This will be a node where you implement
 Less frequently you may want to create a specialized node. This will be a node where you implement
-your own processing callback to apply a custom effect of some kind. This is similar to initalizing
+your own processing callback to apply a custom effect of some kind. This is similar to initializing
 one of the stock node types, only this time you need to specify a pointer to a vtable containing a
 one of the stock node types, only this time you need to specify a pointer to a vtable containing a
 pointer to the processing function and the number of input and output buses. Example:
 pointer to the processing function and the number of input and output buses. Example:
 
 
@@ -2076,7 +2149,7 @@ pointer to the processing function and the number of input and output buses. Exa
 
 
     static ma_node_vtable my_custom_node_vtable =
     static ma_node_vtable my_custom_node_vtable =
     {
     {
-        my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing.
+        my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing.
         NULL,   // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
         NULL,   // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
         2,      // 2 input buses.
         2,      // 2 input buses.
         1,      // 1 output bus.
         1,      // 1 output bus.
@@ -2088,7 +2161,7 @@ pointer to the processing function and the number of input and output buses. Exa
     // Each bus needs to have a channel count specified. To do this you need to specify the channel
     // Each bus needs to have a channel count specified. To do this you need to specify the channel
     // counts in an array and then pass that into the node config.
     // counts in an array and then pass that into the node config.
     ma_uint32 inputChannels[2];     // Equal in size to the number of input channels specified in the vtable.
     ma_uint32 inputChannels[2];     // Equal in size to the number of input channels specified in the vtable.
-    ma_uint32 outputChannels[1];    // Equal in size to the number of output channels specicied in the vtable.
+    ma_uint32 outputChannels[1];    // Equal in size to the number of output channels specified in the vtable.
 
 
     inputChannels[0]  = channelsIn;
     inputChannels[0]  = channelsIn;
     inputChannels[1]  = channelsIn;
     inputChannels[1]  = channelsIn;
@@ -2172,10 +2245,19 @@ and include the following:
     +-----------------------------------------+---------------------------------------------------+
     +-----------------------------------------+---------------------------------------------------+
     | MA_NODE_FLAG_CONTINUOUS_PROCESSING      | Causes the processing callback to be called even  |
     | MA_NODE_FLAG_CONTINUOUS_PROCESSING      | Causes the processing callback to be called even  |
     |                                         | when no data is available to be read from input   |
     |                                         | when no data is available to be read from input   |
-    |                                         | attachments. This is useful for effects like      |
+    |                                         | attachments. When a node has at least one input   |
+    |                                         | bus, but there are no inputs attached or the      |
+    |                                         | inputs do not deliver any data, the node's        |
+    |                                         | processing callback will not get fired. This flag |
+    |                                         | will make it so the callback is always fired      |
+    |                                         | regardless of whether or not any input data is    |
+    |                                         | received. This is useful for effects like         |
     |                                         | echos where there will be a tail of audio data    |
     |                                         | echos where there will be a tail of audio data    |
     |                                         | that still needs to be processed even when the    |
     |                                         | that still needs to be processed even when the    |
-    |                                         | original data sources have reached their ends.    |
+    |                                         | original data sources have reached their ends. It |
+    |                                         | may also be useful for nodes that must always     |
+    |                                         | have their processing callback fired when there   |
+    |                                         | are no inputs attached.                           |
     +-----------------------------------------+---------------------------------------------------+
     +-----------------------------------------+---------------------------------------------------+
     | MA_NODE_FLAG_ALLOW_NULL_INPUT           | Used in conjunction with                          |
     | MA_NODE_FLAG_ALLOW_NULL_INPUT           | Used in conjunction with                          |
     |                                         | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this   |
     |                                         | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this   |
@@ -2206,7 +2288,7 @@ called `ma_splitter_node`. This takes has 1 input bus and splits the stream into
 You can use it like this:
 You can use it like this:
 
 
     ```c
     ```c
-    ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channelsIn, channelsOut);
+    ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels);
 
 
     ma_splitter_node splitterNode;
     ma_splitter_node splitterNode;
     result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
     result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
@@ -2366,7 +2448,7 @@ bus and input bus is locked. This locking is specifically for attaching and deta
 different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
 different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
 unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
 unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
 considering that iterating over attachments must not break as a result of attaching or detaching a
 considering that iterating over attachments must not break as a result of attaching or detaching a
-node while iteration is occuring.
+node while iteration is occurring.
 
 
 Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
 Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
 bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
 bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
@@ -2394,37 +2476,18 @@ used. The same general process applies to detachment. See `ma_node_attach_output
 8. Decoding
 8. Decoding
 ===========
 ===========
 The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
 The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
-devices and can be used independently. The following formats are supported:
+devices and can be used independently. Built-in support is included for the following formats:
 
 
-    +---------+------------------+----------+
-    | Format  | Decoding Backend | Built-In |
-    +---------+------------------+----------+
-    | WAV     | dr_wav           | Yes      |
-    | MP3     | dr_mp3           | Yes      |
-    | FLAC    | dr_flac          | Yes      |
-    | Vorbis  | stb_vorbis       | No       |
-    +---------+------------------+----------+
+    +---------+
+    | Format  |
+    +---------+
+    | WAV     |
+    | MP3     |
+    | FLAC    |
+    +---------+
 
 
-Vorbis is supported via stb_vorbis which can be enabled by including the header section before the
-implementation of miniaudio, like the following:
-
-    ```c
-    #define STB_VORBIS_HEADER_ONLY
-    #include "extras/stb_vorbis.c"    // Enables Vorbis decoding.
-
-    #define MINIAUDIO_IMPLEMENTATION
-    #include "miniaudio.h"
-
-    // The stb_vorbis implementation must come after the implementation of miniaudio.
-    #undef STB_VORBIS_HEADER_ONLY
-    #include "extras/stb_vorbis.c"
-    ```
-
-A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio).
-
-Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the
-built-in decoders by specifying one or more of the following options before the miniaudio
-implementation:
+You can disable the built-in decoders by specifying one or more of the following options before the
+miniaudio implementation:
 
 
     ```c
     ```c
     #define MA_NO_WAV
     #define MA_NO_WAV
@@ -2432,8 +2495,8 @@ implementation:
     #define MA_NO_FLAC
     #define MA_NO_FLAC
     ```
     ```
 
 
-Disabling built-in decoding libraries is useful if you use these libraries independantly of the
-`ma_decoder` API.
+miniaudio supports the ability to plug in custom decoders. See the section below for details on how
+to use custom decoders.
 
 
 A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
 A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
 `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
 `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
@@ -2534,7 +2597,7 @@ The `ma_decoding_backend_vtable` vtable has the following functions:
 
 
     ```
     ```
     onInit
     onInit
-    onInitFile 
+    onInitFile
     onInitFileW
     onInitFileW
     onInitMemory
     onInitMemory
     onUninit
     onUninit
@@ -2546,11 +2609,11 @@ these are not specified, miniaudio will deal with it for you via a generic imple
 
 
 When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
 When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
 will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
 will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
-section about data sources for details on how to implemen this. Alternatively, see the
+section about data sources for details on how to implement this. Alternatively, see the
 "custom_decoders" example in the miniaudio repository.
 "custom_decoders" example in the miniaudio repository.
 
 
 The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
 The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
-from some abitrary source. You'll use these functions to read from the raw data and perform the
+from some arbitrary source. You'll use these functions to read from the raw data and perform the
 decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
 decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
 parameter.
 parameter.
 
 
@@ -2574,8 +2637,7 @@ opportunity to clean up and internal data.
 
 
 9. Encoding
 9. Encoding
 ===========
 ===========
-The `ma_encoding` API is used for writing audio files. The only supported output format is WAV
-which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio.
+The `ma_encoding` API is used for writing audio files. The only supported output format is WAV.
 This can be disabled by specifying the following option before the implementation of miniaudio:
 This can be disabled by specifying the following option before the implementation of miniaudio:
 
 
     ```c
     ```c
@@ -2615,9 +2677,16 @@ outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frame
 example below:
 example below:
 
 
     ```c
     ```c
-    framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite);
+    ma_uint64 framesWritten;
+    result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten);
+    if (result != MA_SUCCESS) {
+        ... handle error ...
+    }
     ```
     ```
 
 
+The `framesWritten` variable will contain the number of PCM frames that were actually written. This
+is optionally and you can pass in `NULL` if you need this.
+
 Encoders must be uninitialized with `ma_encoder_uninit()`.
 Encoders must be uninitialized with `ma_encoder_uninit()`.
 
 
 
 
@@ -2701,7 +2770,7 @@ To perform the conversion simply call `ma_channel_converter_process_pcm_frames()
     }
     }
     ```
     ```
 
 
-It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM
+It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM
 frames.
 frames.
 
 
 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
 Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
@@ -3147,7 +3216,7 @@ you can chain first and second order filters together.
 
 
 If you need to change the configuration of the filter, but need to maintain the state of internal
 If you need to change the configuration of the filter, but need to maintain the state of internal
 registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
 registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
-rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the
+rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the
 format or channel count after initialization is invalid and will result in an error.
 format or channel count after initialization is invalid and will result in an error.
 
 
 The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
 The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
@@ -3320,8 +3389,8 @@ The noise API uses simple LCG random number generation. It supports a custom see
 for things like automated testing requiring reproducibility. Setting the seed to zero will default
 for things like automated testing requiring reproducibility. Setting the seed to zero will default
 to `MA_DEFAULT_LCG_SEED`.
 to `MA_DEFAULT_LCG_SEED`.
 
 
-The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`,
-`ma_noise_set_seed()`, and `ma_noise_set_type()` respectively.
+The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and
+`ma_noise_set_seed()` respectively.
 
 
 By default, the noise API will use different values for different channels. So, for example, the
 By default, the noise API will use different values for different channels. So, for example, the
 left side in a stereo stream will be different to the right side. To instead have each channel use
 left side in a stereo stream will be different to the right side. To instead have each channel use
@@ -3349,7 +3418,7 @@ miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buf
 read from memory that's managed by the application, but can also handle the memory management for
 read from memory that's managed by the application, but can also handle the memory management for
 you internally. Memory management is flexible and should support most use cases.
 you internally. Memory management is flexible and should support most use cases.
 
 
-Audio buffers are initialised using the standard configuration system used everywhere in miniaudio:
+Audio buffers are initialized using the standard configuration system used everywhere in miniaudio:
 
 
     ```c
     ```c
     ma_audio_buffer_config config = ma_audio_buffer_config_init(
     ma_audio_buffer_config config = ma_audio_buffer_config_init(
@@ -3469,7 +3538,7 @@ you will want to use. To initialize a ring buffer, do something like the followi
     ```
     ```
 
 
 The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
 The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
-it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you
+it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you
 would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
 would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
 instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
 instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
 is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
 is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
@@ -3516,21 +3585,26 @@ producer thread.
 
 
 15. Backends
 15. Backends
 ============
 ============
-The following backends are supported by miniaudio.
+The following backends are supported by miniaudio. These are listed in order of default priority.
+When no backend is specified when initializing a context or device, miniaudio will attempt to use
+each of these backends in the order listed in the table below.
+
+Note that backends that are not usable by the build target will not be included in the build. For
+example, ALSA, which is specific to Linux, will not be included in the Windows build.
 
 
     +-------------+-----------------------+--------------------------------------------------------+
     +-------------+-----------------------+--------------------------------------------------------+
     | Name        | Enum Name             | Supported Operating Systems                            |
     | Name        | Enum Name             | Supported Operating Systems                            |
     +-------------+-----------------------+--------------------------------------------------------+
     +-------------+-----------------------+--------------------------------------------------------+
     | WASAPI      | ma_backend_wasapi     | Windows Vista+                                         |
     | WASAPI      | ma_backend_wasapi     | Windows Vista+                                         |
     | DirectSound | ma_backend_dsound     | Windows XP+                                            |
     | DirectSound | ma_backend_dsound     | Windows XP+                                            |
-    | WinMM       | ma_backend_winmm      | Windows XP+ (may work on older versions, but untested) |
+    | WinMM       | ma_backend_winmm      | Windows 95+                                            |
     | Core Audio  | ma_backend_coreaudio  | macOS, iOS                                             |
     | Core Audio  | ma_backend_coreaudio  | macOS, iOS                                             |
-    | ALSA        | ma_backend_alsa       | Linux                                                  |
-    | PulseAudio  | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android)  |
-    | JACK        | ma_backend_jack       | Cross Platform (disabled on BSD and Android)           |
     | sndio       | ma_backend_sndio      | OpenBSD                                                |
     | sndio       | ma_backend_sndio      | OpenBSD                                                |
     | audio(4)    | ma_backend_audio4     | NetBSD, OpenBSD                                        |
     | audio(4)    | ma_backend_audio4     | NetBSD, OpenBSD                                        |
     | OSS         | ma_backend_oss        | FreeBSD                                                |
     | OSS         | ma_backend_oss        | FreeBSD                                                |
+    | PulseAudio  | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android)  |
+    | ALSA        | ma_backend_alsa       | Linux                                                  |
+    | JACK        | ma_backend_jack       | Cross Platform (disabled on BSD and Android)           |
     | AAudio      | ma_backend_aaudio     | Android 8+                                             |
     | AAudio      | ma_backend_aaudio     | Android 8+                                             |
     | OpenSL ES   | ma_backend_opensl     | Android (API level 16+)                                |
     | OpenSL ES   | ma_backend_opensl     | Android (API level 16+)                                |
     | Web Audio   | ma_backend_webaudio   | Web (via Emscripten)                                   |
     | Web Audio   | ma_backend_webaudio   | Web (via Emscripten)                                   |
@@ -3569,6 +3643,12 @@ Some backends have some nuance details you may want to be aware of.
   miniaudio's built-in resampler is to take advantage of any potential device-specific
   miniaudio's built-in resampler is to take advantage of any potential device-specific
   optimizations the driver may implement.
   optimizations the driver may implement.
 
 
+BSD
+---
+- The sndio backend is currently only enabled on OpenBSD builds.
+- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
+  use it.
+
 15.4. UWP
 15.4. UWP
 ---------
 ---------
 - UWP only supports default playback and capture devices.
 - UWP only supports default playback and capture devices.
@@ -3599,14 +3679,28 @@ Some backends have some nuance details you may want to be aware of.
 
 
 16. Optimization Tips
 16. Optimization Tips
 =====================
 =====================
+See below for some tips on improving performance.
 
 
-16.1. High Level API
+16.1. Low Level API
+-------------------
+- In the data callback, if your data is already clipped prior to copying it into the output buffer,
+  set the `noClip` config option in the device config to true. This will disable miniaudio's built
+  in clipping function.
+- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you
+  will always write valid data to the output buffer you can disable pre-silencing by setting the
+  `noPreSilence` config option in the device config to true.
+
+16.2. High Level API
 --------------------
 --------------------
 - If a sound does not require doppler or pitch shifting, consider disabling pitching by
 - If a sound does not require doppler or pitch shifting, consider disabling pitching by
   initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
   initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
-- If a sound does not require spatialization, disable it by initialzing the sound with the
-  `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with
+- If a sound does not require spatialization, disable it by initializing the sound with the
+  `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with
   `ma_sound_set_spatialization_enabled()`.
   `ma_sound_set_spatialization_enabled()`.
+- If you know all of your sounds will always be the same sample rate, set the engine's sample
+  rate to match that of the sounds. Likewise, if you're using a self-managed resource manager,
+  consider setting the decoded sample rate to match your sounds. By configuring everything to
+  use a consistent sample rate, sample rate conversion can be avoided.
 
 
 
 
 
 
@@ -3615,17 +3709,6 @@ Some backends have some nuance details you may want to be aware of.
 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
   WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
   WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
   not all have been tested.
   not all have been tested.
-- The contents of the output buffer passed into the data callback will always be pre-initialized to
-  silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to
-  true, in which case it'll be undefined which will require you to write something to the entire
-  buffer.
-- By default miniaudio will automatically clip samples. This only applies when the playback sample
-  format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this
-  overhead by setting `noClip` to true in the device config.
-- Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations.
-- The sndio backend is currently only enabled on OpenBSD builds.
-- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
-  use it.
 - When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
 - When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
   is due to 64-bit file APIs not being available.
   is due to 64-bit file APIs not being available.
 */
 */

+ 26 - 13
vendor/miniaudio/effects.odin

@@ -1,6 +1,6 @@
 package miniaudio
 package miniaudio
 
 
-import c "core:c/libc"
+import "core:c"
 
 
 when ODIN_OS == .Windows {
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 	foreign import lib "lib/miniaudio.lib"
@@ -24,7 +24,7 @@ delay_config :: struct {
 delay :: struct {
 delay :: struct {
 	config: delay_config,
 	config: delay_config,
 	cursor: u32,               /* Feedback is written to this cursor. Always equal or in front of the read cursor. */
 	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. */
+	bufferSizeInFrames: u32,
 	pBuffer: [^]f32,
 	pBuffer: [^]f32,
 }
 }
 
 
@@ -51,10 +51,11 @@ gainer_config :: struct {
 }
 }
 
 
 gainer :: struct {
 gainer :: struct {
-	config:    gainer_config,
-	t:         u32,
-	pOldGains: [^]f32,
-	pNewGains: [^]f32,
+	config:       gainer_config,
+	t:            u32,
+	masterVolume: f32,
+	pOldGains:    [^]f32,
+	pNewGains:    [^]f32,
 
 
 	/* Memory management. */
 	/* Memory management. */
 	_pHeap:    rawptr,
 	_pHeap:    rawptr,
@@ -72,6 +73,8 @@ foreign lib {
 	gainer_process_pcm_frames :: proc(pGainer: ^gainer, pFramesOut: rawptr, pFramesIn: rawptr, frameCount: u64) -> result ---
 	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_gain           :: proc(pGainer: ^gainer, newGain: f32) -> result ---
 	gainer_set_gains          :: proc(pGainer: ^gainer, pNewGains: [^]f32) -> result ---
 	gainer_set_gains          :: proc(pGainer: ^gainer, pNewGains: [^]f32) -> result ---
+	gainer_set_master_volume  :: proc(pGainer: ^gainer, volume: f32) -> result ---
+	gainer_get_master_volume  :: proc(pGainer: ^gainer, volume: ^f32) -> result --- 
 }
 }
 
 
 
 
@@ -120,7 +123,7 @@ fader :: struct {
 	volumeBeg:      f32,    /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */
 	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,
 	volumeEnd:      f32,
 	lengthInFrames: u64,    /* The total length of the fade. */
 	lengthInFrames: u64,    /* The total length of the fade. */
-	cursorInFrames: u64,    /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */
+	cursorInFrames: i64,    /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */
 }
 }
 
 
 @(default_calling_convention="c", link_prefix="ma_")
 @(default_calling_convention="c", link_prefix="ma_")
@@ -131,6 +134,7 @@ foreign lib {
 	fader_process_pcm_frames :: proc(pFader: ^fader, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> 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_get_data_format    :: proc(pFader: ^fader, pFormat: ^format, pChannels, pSampleRate: ^u32) ---
 	fader_set_fade           :: proc(pFader: ^fader, volumeBeg, volumeEnd: f32, lengthInFrames: u64) ---
 	fader_set_fade           :: proc(pFader: ^fader, volumeBeg, volumeEnd: f32, lengthInFrames: u64) ---
+	fader_set_fade_ex        :: proc(pFader: ^fader, volumeBeg, volumeEnd: f32, lengthInFrames: u64, startOffsetInFrames: i64) ---
 	fader_get_current_volume :: proc(pFader: ^fader) -> f32 ---
 	fader_get_current_volume :: proc(pFader: ^fader) -> f32 ---
 }
 }
 
 
@@ -142,6 +146,11 @@ vec3f :: struct {
 	z: f32,
 	z: f32,
 }
 }
 
 
+atomic_vec3f :: struct {
+	v:    vec3f,
+	lock: spinlock,
+}
+
 attenuation_model :: enum c.int {
 attenuation_model :: enum c.int {
 	none,          /* No distance attenuation and no spatialization. */
 	none,          /* No distance attenuation and no spatialization. */
 	inverse,       /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
 	inverse,       /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
@@ -172,9 +181,9 @@ spatializer_listener_config :: struct {
 
 
 spatializer_listener :: struct {
 spatializer_listener :: struct {
 		config:    spatializer_listener_config,
 		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,
+		position:  atomic_vec3f,  /* The absolute position of the listener. */
+		direction: atomic_vec3f,  /* The direction the listener is facing. The world up vector is config.worldUp. */
+		velocity:  atomic_vec3f,
 		isEnabled: b32,
 		isEnabled: b32,
 
 
 		/* Memory management. */
 		/* Memory management. */
@@ -224,6 +233,7 @@ spatializer_config :: struct {
 	coneOuterGain:                f32,
 	coneOuterGain:                f32,
 	dopplerFactor:                f32,    /* Set to 0 to disable doppler effect. */
 	dopplerFactor:                f32,    /* Set to 0 to disable doppler effect. */
 	directionalAttenuationFactor: f32,    /* Set to 0 to disable directional attenuation. */
 	directionalAttenuationFactor: f32,    /* Set to 0 to disable directional attenuation. */
+	minSpatializationChannelGain: f32,    /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */
 	gainSmoothTimeInFrames:       u32,    /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
 	gainSmoothTimeInFrames:       u32,    /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
 }
 }
 
 
@@ -245,10 +255,11 @@ spatializer :: struct {
 		dopplerFactor:                f32,      /* Set to 0 to disable doppler effect. */
 		dopplerFactor:                f32,      /* Set to 0 to disable doppler effect. */
 		directionalAttenuationFactor: f32,      /* Set to 0 to disable directional attenuation. */
 		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. */
 		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. */
+		position:                     atomic_vec3f,
+		direction:                    atomic_vec3f,
+		velocity:                     atomic_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. */
 		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. */
+		minSpatializationChannelGain: f32,
 		gainer:                       gainer,   /* For smooth gain transitions. */
 		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. */
 		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. */
 
 
@@ -266,6 +277,8 @@ foreign lib {
 	spatializer_init                                :: proc(pConfig: ^spatializer_config, pAllocationCallbacks: ^allocation_callbacks, pSpatializer: ^spatializer) -> result ---
 	spatializer_init                                :: proc(pConfig: ^spatializer_config, pAllocationCallbacks: ^allocation_callbacks, pSpatializer: ^spatializer) -> result ---
 	spatializer_uninit                              :: proc(pSpatializer: ^spatializer, pAllocationCallbacks: ^allocation_callbacks) ---
 	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_process_pcm_frames                  :: proc(pSpatializer: ^spatializer, pListener: ^spatializer_listener, pFramesOut, pFramesIn: rawptr, frameCount: u64) -> result ---
+	spatializer_set_master_volume                   :: proc(pSpatializer: ^spatializer, volume: f32) -> result ---
+	spatializer_get_master_volume                   :: proc(pSpatializer: ^spatializer, pVolume: ^f32) -> result ---
 	spatializer_get_input_channels                  :: proc(pSpatializer: ^spatializer) -> u32 ---
 	spatializer_get_input_channels                  :: proc(pSpatializer: ^spatializer) -> u32 ---
 	spatializer_get_output_channels                 :: proc(pSpatializer: ^spatializer) -> u32 ---
 	spatializer_get_output_channels                 :: proc(pSpatializer: ^spatializer) -> u32 ---
 	spatializer_set_attenuation_model               :: proc(pSpatializer: ^spatializer, attenuationModel: attenuation_model) ---
 	spatializer_set_attenuation_model               :: proc(pSpatializer: ^spatializer, attenuationModel: attenuation_model) ---

+ 1 - 1
vendor/miniaudio/encoding.odin

@@ -39,7 +39,7 @@ encoder :: struct {
 	onUninit:         encoder_uninit_proc,
 	onUninit:         encoder_uninit_proc,
 	onWritePCMFrames: encoder_write_pcm_frames_proc,
 	onWritePCMFrames: encoder_write_pcm_frames_proc,
 	pUserData:        rawptr,
 	pUserData:        rawptr,
-	pInternalEncoder: rawptr, /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
+	pInternalEncoder: rawptr,
 	data: struct #raw_union {
 	data: struct #raw_union {
 		vfs: struct {
 		vfs: struct {
 			pVFS: ^vfs,
 			pVFS: ^vfs,

+ 102 - 30
vendor/miniaudio/engine.odin

@@ -16,13 +16,17 @@ Engine
 
 
 /* Sound flags. */
 /* Sound flags. */
 sound_flags :: enum c.int {
 sound_flags :: enum c.int {
+	/* Resource manager flags. */
 	STREAM                = 0x00000001,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
 	STREAM                = 0x00000001,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
 	DECODE                = 0x00000002,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
 	DECODE                = 0x00000002,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
 	ASYNC                 = 0x00000004,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
 	ASYNC                 = 0x00000004,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
 	WAIT_INIT             = 0x00000008,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
 	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. */
+	UNKNOWN_LENGTH        = 0x00000010,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */
+	
+	/* ma_sound specific flags. */
+	NO_DEFAULT_ATTACHMENT = 0x00001000,   /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
+	NO_PITCH              = 0x00002000,   /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
+	NO_SPATIALIZATION     = 0x00004000,   /* Disable spatialization. */
 }
 }
 
 
 ENGINE_MAX_LISTENERS :: 4
 ENGINE_MAX_LISTENERS :: 4
@@ -35,31 +39,44 @@ engine_node_type :: enum c.int {
 }
 }
 
 
 engine_node_config :: struct {
 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. */
+	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. */
+	volumeSmoothTimeInPCMFrames: u32,
+	monoExpansionMode:           mono_expansion_mode,
+	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. */
 /* Base node object for both ma_sound and ma_sound_group. */
 engine_node :: struct {
 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. */
+	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. */
+	volumeSmoothTimeInPCMFrames: u32,
+	monoExpansionMode:           mono_expansion_mode,
+	fader:                       fader,
+	resampler:                   linear_resampler,    /* For pitch shift. */
+	spatializer:                 spatializer,
+	panner:                      panner,
+	volumeGainer:                gainer,              /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */
+	volume:                      f32, /*atomic*/      /* Defaults to 1. */
+	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. */
+
+	fadeSettings: struct {
+		volumeBeg:                  f32, /*atomic*/
+		volumeEnd:                  f32, /*atomic*/
+		fadeLengthInFrames:         u64, /*atomic*/ /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */
+		absoluteGlobalTimeInFrames: u64, /*atomic*/ /* <-- The time to start the fade. */
+	},
 
 
 	/* Memory management. */
 	/* Memory management. */
 	_ownsHeap: b8,
 	_ownsHeap: b8,
@@ -79,6 +96,9 @@ foreign lib {
 
 
 SOUND_SOURCE_CHANNEL_COUNT :: 0xFFFFFFFF
 SOUND_SOURCE_CHANNEL_COUNT :: 0xFFFFFFFF
 
 
+/* Callback for when a sound reaches the end. */
+sound_end_proc :: #type proc "c" (pUserData: rawptr, pSound: ^sound)
+
 sound_config :: struct {
 sound_config :: struct {
 	pFilePath:                      cstring,          /* Set this to load from the resource manager. */
 	pFilePath:                      cstring,          /* Set this to load from the resource manager. */
 	pFilePathW:                     [^]c.wchar_t,     /* Set this to load from the resource manager. */
 	pFilePathW:                     [^]c.wchar_t,     /* Set this to load from the resource manager. */
@@ -87,14 +107,22 @@ sound_config :: struct {
 	initialAttachmentInputBusIndex: u32,              /* The index of the input bus of pInitialAttachment to attach the sound to. */
 	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. */
 	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). */
 	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). */
+	monoExpansionMode:              mono_expansion_mode, /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
 	flags:                          u32,              /* A combination of MA_SOUND_FLAG_* flags. */
 	flags:                          u32,              /* A combination of MA_SOUND_FLAG_* flags. */
+	volumeSmoothTimeInPCMFrames:    u32,              /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */
 	initialSeekPointInPCMFrames:    u64,              /* Initializes the sound such that it's seeked to this location by default. */
 	initialSeekPointInPCMFrames:    u64,              /* Initializes the sound such that it's seeked to this location by default. */
 	rangeBegInPCMFrames:            u64,
 	rangeBegInPCMFrames:            u64,
 	rangeEndInPCMFrames:            u64,
 	rangeEndInPCMFrames:            u64,
 	loopPointBegInPCMFrames:        u64,
 	loopPointBegInPCMFrames:        u64,
 	loopPointEndInPCMFrames:        u64,
 	loopPointEndInPCMFrames:        u64,
 	isLooping:                      b32,
 	isLooping:                      b32,
-	pDoneFence:                     ^fence,           /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */
+
+	endCallback:          sound_end_proc, /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */
+	pEndCallbackUserData: rawptr,
+	
+	initNotifications: resource_manager_pipeline_notifications,
+
+	pDoneFence: ^fence, /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */
 }
 }
 
 
 sound :: struct {
 sound :: struct {
@@ -102,6 +130,10 @@ sound :: struct {
 	pDataSource:    ^data_source,
 	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. */
 	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*/
 	atEnd:          b32, /*atomic*/
+
+	endCallback:          sound_end_proc,
+	pEndCallbackUserData: rawptr,
+
 	ownsDataSource: b8,
 	ownsDataSource: b8,
 
 
 	/*
 	/*
@@ -120,7 +152,9 @@ sound_inlined :: struct {
 
 
 @(default_calling_convention="c", link_prefix="ma_")
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
 foreign lib {
-	sound_config_init :: proc() -> sound_config ---
+	@(deprecated="Will be removed in 0.12. Use sound_config_init2() instead.")
+	sound_config_init  :: proc() -> sound_config ---
+	sound_config_init2 :: proc(pEngine: ^engine) -> sound_config --- /* Will be renamed to sound_config_init() in version 0.12. */
 
 
 	sound_init_from_file                     :: proc(pEngine: ^engine, pFilePath: cstring, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result ---
 	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_from_file_w                   :: proc(pEngine: ^engine, pFilePath: [^]c.wchar_t, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result ---
@@ -132,6 +166,8 @@ foreign lib {
 	sound_get_data_source                    :: proc(pSound: ^sound) -> ^data_source ---
 	sound_get_data_source                    :: proc(pSound: ^sound) -> ^data_source ---
 	sound_start                              :: proc(pSound: ^sound) -> result ---
 	sound_start                              :: proc(pSound: ^sound) -> result ---
 	sound_stop                               :: proc(pSound: ^sound) -> result ---
 	sound_stop                               :: proc(pSound: ^sound) -> result ---
+	sound_stop_with_fade_in_pcm_frames       :: proc(pSound: ^sound, fadeLengthInFrames: u64) --- /* Will overwrite any scheduled stop and fade. */
+	sound_stop_with_fade_in_milliseconds     :: proc(pSound: ^sound, fadeLengthInFrames: u64) --- /* Will overwrite any scheduled stop and fade. */
 	sound_set_volume                         :: proc(pSound: ^sound, volume: f32) ---
 	sound_set_volume                         :: proc(pSound: ^sound, volume: f32) ---
 	sound_get_volume                         :: proc(pSound: ^sound) -> f32 ---
 	sound_get_volume                         :: proc(pSound: ^sound) -> f32 ---
 	sound_set_pan                            :: proc(pSound: ^sound, pan: f32) ---
 	sound_set_pan                            :: proc(pSound: ^sound, pan: f32) ---
@@ -174,13 +210,20 @@ foreign lib {
 	sound_get_directional_attenuation_factor :: proc(pSound: ^sound) -> 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_pcm_frames             :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) ---
 	sound_set_fade_in_milliseconds           :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) ---
 	sound_set_fade_in_milliseconds           :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) ---
+	sound_set_fade_start_in_pcm_frames       :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInFrames, absoluteGlobalTimeInFrames: u64) ---
+	sound_set_fade_start_in_milliseconds     :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds, absoluteGlobalTimeInMilliseconds: u64) ---
 	sound_get_current_fade_volume            :: proc(pSound: ^sound) -> f32 ---
 	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_pcm_frames       :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) ---
 	sound_set_start_time_in_milliseconds     :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: 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_pcm_frames        :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) ---
 	sound_set_stop_time_in_milliseconds      :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) ---
 	sound_set_stop_time_in_milliseconds      :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) ---
+
+	sound_set_stop_time_with_fade_in_pcm_frames   :: proc(pSound: ^sound, stopAbsoluteGlobalTimeInFrames, fadeLengthInFrames: u64) ---
+	sound_set_stop_time_with_fade_in_milliseconds :: proc(pSound: ^sound, fadeAbsoluteGlobalTimeInMilliseconds, fadeLengthInMilliseconds: u64) ---
+
 	sound_is_playing                         :: proc(pSound: ^sound) -> b32 ---
 	sound_is_playing                         :: proc(pSound: ^sound) -> b32 ---
 	sound_get_time_in_pcm_frames             :: proc(pSound: ^sound) -> u64 ---
 	sound_get_time_in_pcm_frames             :: proc(pSound: ^sound) -> u64 ---
+	sound_get_time_in_milliseconds           :: proc(pSound: ^sound) -> u64 ---
 	sound_set_looping                        :: proc(pSound: ^sound, isLooping: b32) ---
 	sound_set_looping                        :: proc(pSound: ^sound, isLooping: b32) ---
 	sound_is_looping                         :: proc(pSound: ^sound) -> b32 ---
 	sound_is_looping                         :: proc(pSound: ^sound) -> b32 ---
 	sound_at_end                             :: proc(pSound: ^sound) -> b32 ---
 	sound_at_end                             :: proc(pSound: ^sound) -> b32 ---
@@ -190,6 +233,7 @@ foreign lib {
 	sound_get_length_in_pcm_frames           :: proc(pSound: ^sound, pLength: ^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_cursor_in_seconds              :: proc(pSound: ^sound, pCursor: ^f32) -> result ---
 	sound_get_length_in_seconds              :: proc(pSound: ^sound, pLength: ^f32) -> result ---
 	sound_get_length_in_seconds              :: proc(pSound: ^sound, pLength: ^f32) -> result ---
+	sound_set_end_callback                   :: proc(pSound: ^sound, callback: sound_end_proc, pUserData: rawptr) ---
 }
 }
 
 
 
 
@@ -199,7 +243,9 @@ sound_group        :: distinct sound
 
 
 @(default_calling_convention="c", link_prefix="ma_")
 @(default_calling_convention="c", link_prefix="ma_")
 foreign lib {
 foreign lib {
-	sound_group_config_init :: proc() -> sound_group_config ---
+	@(deprecated="Will be removed in 0.12. Use sound_config_init2() instead.")
+	sound_group_config_init  :: proc() -> sound_group_config ---
+	sound_group_config_init2 :: proc(pEngine: ^engine) -> sound_group_config ---
 
 
 	sound_group_init                               :: proc(pEngine: ^engine, flags: u32, pParentGroup, pGroup: ^sound_group) -> result ---
 	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_init_ex                            :: proc(pEngine: ^engine, pConfig: ^sound_group_config, pGroup: ^sound_group) -> result ---
@@ -258,12 +304,17 @@ foreign lib {
 	sound_group_get_time_in_pcm_frames             :: proc(pGroup: ^sound_group) -> u64 ---
 	sound_group_get_time_in_pcm_frames             :: proc(pGroup: ^sound_group) -> u64 ---
 }
 }
 
 
+engine_process_proc :: #type proc "c" (pUserData: rawptr, pFramesOut: [^]f32, frameCount: u64)
 
 
 engine_config :: struct {
 engine_config :: struct {
 	pResourceManager:             ^resource_manager,      /* Can be null in which case a resource manager will be created for you. */
 	pResourceManager:             ^resource_manager,      /* Can be null in which case a resource manager will be created for you. */
 	pContext:                     ^context_type,
 	pContext:                     ^context_type,
 	pDevice:                      ^device,                /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
 	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. */
 	pPlaybackDeviceID:            ^device_id,             /* The ID of the playback device to use with the default listener. */
+
+	dataCallback:         device_data_proc,               /* Can be null. Can be used to provide a custom device data callback. */
+	notificationCallback: device_notification_proc,
+
 	pLog:                         ^log,                   /* When set to NULL, will use the context's log. */
 	pLog:                         ^log,                   /* When set to NULL, will use the context's log. */
 	listenerCount:                u32,                    /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
 	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. */
 	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. */
@@ -272,11 +323,16 @@ engine_config :: struct {
 	periodSizeInMilliseconds:     u32,                    /* Used if periodSizeInFrames is unset. */
 	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. */
 	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. */
 	gainSmoothTimeInMilliseconds: u32,                    /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
+
+	defaultVolumeSmoothTimeInPCMFrames: u32,              /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */
+
 	allocationCallbacks:          allocation_callbacks,
 	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(). */
 	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. */
 	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. */
 	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. */
 	pResourceManagerVFS:          ^vfs,                   /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */
+	onProcess:                    engine_process_proc,    /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */
+	pProcessUserData:             rawptr,                 /* User data that's passed into onProcess. */
 }
 }
 
 
 engine :: struct {
 engine :: struct {
@@ -294,7 +350,12 @@ engine :: struct {
 	pInlinedSoundHead:      ^sound_inlined,               /* The first inlined sound. Inlined sounds are tracked in a linked 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. */
 	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. */
 	gainSmoothTimeInFrames: u32,                          /* The number of frames to interpolate the gain of spatialized sounds across. */
-	monoExpansionMode:      mono_expansion_mode,
+
+	defaultVolumeSmoothTimeInPCMFrames: u32,
+
+	monoExpansionMode: mono_expansion_mode,
+	onProcess:         engine_process_proc,
+	pProcessUserData:  rawptr,
 }
 }
 
 
 @(default_calling_convention="c", link_prefix="ma_")
 @(default_calling_convention="c", link_prefix="ma_")
@@ -309,15 +370,26 @@ foreign lib {
 	engine_get_device           :: proc(pEngine: ^engine) -> ^device ---
 	engine_get_device           :: proc(pEngine: ^engine) -> ^device ---
 	engine_get_log              :: proc(pEngine: ^engine) -> ^log ---
 	engine_get_log              :: proc(pEngine: ^engine) -> ^log ---
 	engine_get_endpoint         :: proc(pEngine: ^engine) -> ^node ---
 	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_time_in_pcm_frames   :: proc(pEngine: ^engine) -> u64 ---
+	engine_get_time_in_milliseconds :: proc(pEngine: ^engine) -> u64 ---
+	engine_set_time_in_pcm_frames   :: proc(pEngine: ^engine, globalTime: u64) -> result --- 
+	engine_set_time_in_milliseconds :: proc(pEngine: ^engine, globalTime: u64) -> result --- 
+	
+	@(deprecated="Use engine_get_time_in_pcm_frames(). Will be removed in 0.12.")
+	engine_get_time :: proc(pEngine: ^engine) -> u64 ---
+	@(deprecated="Use engine_set_time_in_pcm_frames(). Will be removed in 0.12.")
+	engine_set_time :: proc(pEngine: ^engine, globalTime: u64) -> result ---
+
 	engine_get_channels         :: proc(pEngine: ^engine) -> u32 ---
 	engine_get_channels         :: proc(pEngine: ^engine) -> u32 ---
 	engine_get_sample_rate      :: proc(pEngine: ^engine) -> u32 ---
 	engine_get_sample_rate      :: proc(pEngine: ^engine) -> u32 ---
 	
 	
 	engine_start       :: proc(pEngine: ^engine) -> result ---
 	engine_start       :: proc(pEngine: ^engine) -> result ---
 	engine_stop        :: proc(pEngine: ^engine) -> result ---
 	engine_stop        :: proc(pEngine: ^engine) -> result ---
 	engine_set_volume  :: proc(pEngine: ^engine, volume: f32) -> result ---
 	engine_set_volume  :: proc(pEngine: ^engine, volume: f32) -> result ---
+	engine_get_volume  :: proc(pEngine: ^engine) -> f32 ---
 	engine_set_gain_db :: proc(pEngine: ^engine, gainDB: f32) -> result ---
 	engine_set_gain_db :: proc(pEngine: ^engine, gainDB: f32) -> result ---
+	engine_get_gain_db :: proc(pEngine: ^engine) -> f32 ---
 	
 	
 	engine_get_listener_count     :: proc(pEngine: ^engine) -> u32 ---
 	engine_get_listener_count     :: proc(pEngine: ^engine) -> u32 ---
 	engine_find_closest_listener  :: proc(pEngine: ^engine, absolutePosX, absolutePosY, absolutePosZ: f32) -> u32 ---
 	engine_find_closest_listener  :: proc(pEngine: ^engine, absolutePosX, absolutePosY, absolutePosZ: f32) -> u32 ---

+ 1 - 1
vendor/miniaudio/filtering.odin

@@ -1,6 +1,6 @@
 package miniaudio
 package miniaudio
 
 
-import c "core:c/libc"
+import "core:c"
 
 
 when ODIN_OS == .Windows {
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 	foreign import lib "lib/miniaudio.lib"

+ 1 - 1
vendor/miniaudio/generation.odin

@@ -49,7 +49,7 @@ noise_config :: struct {
 }
 }
 
 
 noise :: struct {
 noise :: struct {
-	ds:     data_source_vtable,
+	ds:     data_source_base,
 	config: noise_config,
 	config: noise_config,
 	lcg:    lcg,
 	lcg:    lcg,
 	state: struct #raw_union {
 	state: struct #raw_union {

+ 1 - 1
vendor/miniaudio/job_queue.odin

@@ -1,6 +1,6 @@
 package miniaudio
 package miniaudio
 
 
-import c "core:c/libc"
+import "core:c"
 
 
 when ODIN_OS == .Windows {
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 	foreign import lib "lib/miniaudio.lib"

+ 2 - 7
vendor/miniaudio/logging.odin

@@ -1,6 +1,6 @@
 package miniaudio
 package miniaudio
 
 
-import c "core:c/libc"
+import "core:c/libc"
 
 
 when ODIN_OS == .Windows {
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 	foreign import lib "lib/miniaudio.lib"
@@ -34,11 +34,6 @@ logLevel (in)
 
 
 pMessage (in)
 pMessage (in)
     The log message.
     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_proc :: proc "c" (pUserData: rawptr, level: u32, pMessage: cstring)
 
 
@@ -63,6 +58,6 @@ foreign lib {
 	log_register_callback   :: proc(pLog: ^log, callback: log_callback) -> result ---
 	log_register_callback   :: proc(pLog: ^log, callback: log_callback) -> result ---
 	log_unregister_callback :: proc(pLog: ^log, callback: log_callback) -> result ---
 	log_unregister_callback :: proc(pLog: ^log, callback: log_callback) -> result ---
 	log_post                :: proc(pLog: ^log, level: u32, pMessage: cstring) -> result ---
 	log_post                :: proc(pLog: ^log, level: u32, pMessage: cstring) -> result ---
-	log_postv               :: proc(pLog: ^log, level: u32, pFormat: cstring, args: c.va_list) -> result ---
+	log_postv               :: proc(pLog: ^log, level: u32, pFormat: cstring, args: libc.va_list) -> result ---
 	log_postf               :: proc(pLog: ^log, level: u32, pFormat: cstring, #c_vararg args: ..any) -> result ---
 	log_postf               :: proc(pLog: ^log, level: u32, pFormat: cstring, #c_vararg args: ..any) -> result ---
 }
 }

+ 6 - 5
vendor/miniaudio/node_graph.odin

@@ -44,7 +44,7 @@ node_vtable :: struct {
 	/*
 	/*
 	Extended processing callback. This callback is used for effects that process input and output
 	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
 	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.
+	they take two separate 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
 	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`.
 	`pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
@@ -102,7 +102,7 @@ node_output_bus :: struct {
 	channels:       u8,                     /* The number of channels in the audio stream for this bus. */
 	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. */
 	/* 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. */
+	inputNodeInputBusIndex: u8,                             /* The index of the input bus on the input. Required for detaching. Will only be used in the spinlock so does not need to be atomic. */
 	flags:                  u32, /*atomic*/                 /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */
 	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. */
 	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. */
 	isAttached:             b32, /*atomic*/                 /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */
@@ -236,10 +236,11 @@ foreign lib {
 }
 }
 
 
 
 
-/* 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. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
 splitter_node_config :: struct {
 splitter_node_config :: struct {
-	nodeConfig: node_config,
-	channels:   u32,
+	nodeConfig:     node_config,
+	channels:       u32,
+	outputBusCount: u32,
 }
 }
 
 
 splitter_node :: struct {
 splitter_node :: struct {

+ 1 - 0
vendor/miniaudio/resource_manager.odin

@@ -190,6 +190,7 @@ resource_manager_config :: struct {
 	decodedChannels:                u32,       /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
 	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. */
 	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. */
 	jobThreadCount:                 u32,       /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
+	jobThreadStackSize:             uint,
 	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. */
 	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,
 	flags:                          u32,
 	pVFS:                           ^vfs,      /* Can be NULL in which case defaults will be used. */
 	pVFS:                           ^vfs,      /* Can be NULL in which case defaults will be used. */

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


+ 36 - 1
vendor/miniaudio/utilities.odin

@@ -1,6 +1,6 @@
 package miniaudio
 package miniaudio
 
 
-import c "core:c/libc"
+import "core:c"
 
 
 when ODIN_OS == .Windows {
 when ODIN_OS == .Windows {
 	foreign import lib "lib/miniaudio.lib"
 	foreign import lib "lib/miniaudio.lib"
@@ -104,6 +104,13 @@ foreign lib {
 	Helper for converting gain in decibels to a linear factor.
 	Helper for converting gain in decibels to a linear factor.
 	*/
 	*/
 	volume_db_to_linear :: proc(gain: f32) -> f32 ---
 	volume_db_to_linear :: proc(gain: f32) -> f32 ---
+
+	/*
+	Mixes the specified number of frames in floating point format with a volume factor.
+
+	This will run on an optimized path when the volume is equal to 1.
+	*/
+	ma_mix_pcm_frames_f32 :: proc(pDst: ^f32, pSrc: ^f32, frameCount: u64, channels: u32, volume: f32) -> result ---
 }
 }
 
 
 offset_pcm_frames_ptr_f32 :: #force_inline proc "c" (p: [^]f32, offsetInFrames: u64, channels: u32) -> [^]f32 {
 offset_pcm_frames_ptr_f32 :: #force_inline proc "c" (p: [^]f32, offsetInFrames: u64, channels: u32) -> [^]f32 {
@@ -297,3 +304,31 @@ foreign lib {
 	paged_audio_buffer_get_cursor_in_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pCursor: ^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 ---
 	paged_audio_buffer_get_length_in_pcm_frames :: proc(pPagedAudioBuffer: ^paged_audio_buffer, pLength: ^u64) -> result ---
 }
 }
+
+pulsewave_config :: struct {
+	format:     format,
+	channels:   u32,
+	sampleRate: u32,
+	dutyCycle:  f64,
+	amplitude:  f64,
+	frequency:  f64,
+}
+
+pulsewave :: struct {
+	waveform: waveform,
+	config:   pulsewave_config,
+}
+
+@(default_calling_convention="c", link_prefix="ma_")
+foreign lib {
+	pulsewave_config_init :: proc(format: format, channels: u32, sampleRate: u32, dutyCycle: f64, amplitude: f64, frequency: f64) -> pulsewave_config ---
+
+	pulsewave_init              :: proc(pConfig: ^pulsewave_config, pWaveForm: ^pulsewave) -> result ---
+	pulsewave_uninit            :: proc(pWaveForm: ^pulsewave) ---
+	pulsewave_read_pcm_frames   :: proc(pWaveForm: ^pulsewave, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result ---
+	pulsewave_seek_to_pcm_frame :: proc(pWaveForm: ^pulsewave, frameIndex: u64) -> result ---
+	pulsewave_set_amplitude     :: proc(pWaveForm: ^pulsewave, amplitude: f64) -> result ---
+	pulsewave_set_frequency     :: proc(pWaveForm: ^pulsewave, frequency: f64) -> result ---
+	pulsewave_set_sample_rate   :: proc(pWaveForm: ^pulsewave, sampleRate: u32) -> result ---
+	pulsewave_set_duty_cycle    :: proc(pWaveForm: ^pulsewave, dutyCycle: f64) -> result ---
+}

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