From ffe2364334b0646a30a05d451d8111dddb9ef181 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 30 Aug 2020 20:01:38 +0200 Subject: [PATCH] Update miniaudio to version 0.10.18 --- src/external/miniaudio.h | 1602 ++++++++++++++++++++++++++------------ 1 file changed, 1100 insertions(+), 502 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 89e98f432..1545c233d 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,11 +1,12 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.10.14 - 2020-07-14 +miniaudio - v0.10.18 - 2020-08-30 -David Reid - davidreidsoftware@gmail.com +David Reid - mackron@gmail.com -Website: https://miniaud.io -GitHub: https://github.com/dr-soft/miniaudio +Website: https://miniaud.io +Documentation: https://miniaud.io/docs +GitHub: https://github.com/mackron/miniaudio */ /* @@ -18,7 +19,7 @@ miniaudio is a single file library for audio playback and capture. To use it, do #include "miniaudio.h" ``` -You can do `#include miniaudio.h` in other parts of the program just like any other header. +You can do `#include "miniaudio.h"` in other parts of the program just like any other header. miniaudio uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from, and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when @@ -39,25 +40,27 @@ but you could allocate it on the heap if that suits your situation better. // frameCount frames. } - ... + int main() + { + ma_device_config config = ma_device_config_init(ma_device_type_playback); + config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. + config.playback.channels = 2; // Set to 0 to use the device's native channel count. + config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. + config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. + config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.format = MY_FORMAT; - config.playback.channels = MY_CHANNEL_COUNT; - config.sampleRate = MY_SAMPLE_RATE; - config.dataCallback = data_callback; - config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). + ma_device device; + if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { + return -1; // Failed to initialize the device. + } - ma_device device; - if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { - ... An error occurred ... + ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. + + // 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. + return 0; } - - ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. - - ... - - ma_device_uninit(&device); // This will stop the device so no need to do that manually. ``` In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted @@ -161,22 +164,22 @@ backends and enumerating devices. The example below shows how to enumerate devic // Error. } - ma_device_info* pPlaybackDeviceInfos; - ma_uint32 playbackDeviceCount; - ma_device_info* pCaptureDeviceInfos; - ma_uint32 captureDeviceCount; - if (ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, &pCaptureDeviceInfos, &captureDeviceCount) != MA_SUCCESS) { + ma_device_info* pPlaybackInfos; + ma_uint32 playbackCount; + ma_device_info* pCaptureInfos; + ma_uint32 captureCount; + if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { // Error. } - // Loop over each device info and do something with it. Here we just print the name with their index. You may want to give the user the - // opportunity to choose which device they'd prefer. - for (ma_uint32 iDevice = 0; iDevice < playbackDeviceCount; iDevice += 1) { - printf("%d - %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name); + // Loop over each device info and do something with it. Here we just print the name with their index. You may want + // to give the user the opportunity to choose which device they'd prefer. + for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { + printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); } ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.pDeviceID = &pPlaybackDeviceInfos[chosenPlaybackDeviceIndex].id; + config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; config.playback.format = MY_FORMAT; config.playback.channels = MY_CHANNEL_COUNT; config.sampleRate = MY_SAMPLE_RATE; @@ -381,8 +384,18 @@ All formats are native-endian. 4. Decoding =========== -The `ma_decoder` API is used for reading audio files. Built in support is included for WAV, FLAC and MP3. Support for Vorbis is enabled via stb_vorbis which -can be enabled by including the header section before the implementation of miniaudio, like the following: +The `ma_decoder` API is used for reading audio files. The following formats are supported: + + +---------+------------------+----------+ + | Format | Decoding Backend | Built-In | + +---------+------------------+----------+ + | WAV | dr_wav | Yes | + | MP3 | dr_mp3 | Yes | + | FLAC | dr_flac | Yes | + | Vorbis | stb_vorbis | No | + +---------+------------------+----------+ + +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 @@ -396,18 +409,18 @@ can be enabled by including the header section before the implementation of mini #include "extras/stb_vorbis.c" ``` -A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/dr-soft/miniaudio). +A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). -Built-in decoders are implemented via dr_wav, dr_flac and dr_mp3. These 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: +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: ```c #define MA_NO_WAV - #define MA_NO_FLAC #define MA_NO_MP3 + #define MA_NO_FLAC ``` -Disabling built-in versions of dr_wav, dr_flac and dr_mp3 is useful if you use these libraries independantly of the `ma_decoder` API. +Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API. 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 an example for loading a decoder from a file: @@ -569,7 +582,14 @@ Channel conversion is used for channel rearrangement and conversion from one cha conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo. ```c - ma_channel_converter_config config = ma_channel_converter_config_init(ma_format, 1, NULL, 2, NULL, ma_channel_mix_mode_default, NULL); + ma_channel_converter_config config = ma_channel_converter_config_init( + ma_format, // Sample format + 1, // Input channels + NULL, // Input channel map + 2, // Output channels + NULL, // Output channel map + ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. + result = ma_channel_converter_init(&config, &converter); if (result != MA_SUCCESS) { // Error. @@ -587,15 +607,12 @@ 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 frames. -The only formats supported are `ma_format_s16` and `ma_format_f32`. If you need another format you need to convert your data manually which you can do with -`ma_pcm_convert()`, etc. - Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. 6.2.1. Channel Mapping ---------------------- -In addition to converting from one channel count to another, like the example above, The channel converter can also be used to rearrange channels. When +In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If, however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is @@ -627,7 +644,7 @@ be one of the following: | ma_standard_channel_map_flac | FLAC channel map. | | ma_standard_channel_map_vorbis | Vorbis channel map. | | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | - | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html | + | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | +-----------------------------------+-----------------------------------------------------------+ @@ -692,7 +709,13 @@ Below are the channel maps used by default in miniaudio (ma_standard_channel_map Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following: ```c - ma_resampler_config config = ma_resampler_config_init(ma_format_s16, channels, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear); + ma_resampler_config config = ma_resampler_config_init( + ma_format_s16, + channels, + sampleRateIn, + sampleRateOut, + ma_resample_algorithm_linear); + ma_resampler resampler; ma_result result = ma_resampler_init(&config, &resampler); if (result != MA_SUCCESS) { @@ -1077,7 +1100,13 @@ adjust the volume of low frequencies, the high shelf filter does the same thing miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example: ```c - ma_waveform_config config = ma_waveform_config_init(FORMAT, CHANNELS, SAMPLE_RATE, ma_waveform_type_sine, amplitude, frequency); + ma_waveform_config config = ma_waveform_config_init( + FORMAT, + CHANNELS, + SAMPLE_RATE, + ma_waveform_type_sine, + amplitude, + frequency); ma_waveform waveform; ma_result result = ma_waveform_init(&config, &waveform); @@ -1093,7 +1122,7 @@ miniaudio supports generation of sine, square, triangle and sawtooth waveforms. The amplitude, frequency and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()` and `ma_waveform_set_sample_rate()` respectively. -You can reverse the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative +You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative ramp, for example. Below are the supported waveform types: @@ -1114,7 +1143,12 @@ Below are the supported waveform types: miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: ```c - ma_noise_config config = ma_noise_config_init(FORMAT, CHANNELS, ma_noise_type_white, SEED, amplitude); + ma_noise_config config = ma_noise_config_init( + FORMAT, + CHANNELS, + ma_noise_type_white, + SEED, + amplitude); ma_noise noise; ma_result result = ma_noise_init(&config, &noise); @@ -1151,13 +1185,19 @@ Below are the supported noise types. 9. Audio Buffers ================ -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from both memory that's managed by the application, but -can also handle the memory management for you internally. The way memory is managed is flexible and should support most use cases. +miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can 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. Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init(format, channels, sizeInFrames, pExistingData, &allocationCallbacks); + ma_audio_buffer_config config = ma_audio_buffer_config_init( + format, + channels, + sizeInFrames, + pExistingData, + &allocationCallbacks); + ma_audio_buffer buffer; result = ma_audio_buffer_init(&config, &buffer); if (result != MA_SUCCESS) { @@ -1169,14 +1209,20 @@ Audio buffers are initialised using the standard configuration system used every ma_audio_buffer_uninit(&buffer); ``` -In the example above, the memory pointed to by `pExistingData` will _not_ be copied which is how an application can handle memory allocations themselves. If -you would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. +In the example above, the memory pointed to by `pExistingData` will _not_ be copied and is how an application can do self-managed memory allocation. If you +would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure _and_ the raw audio data in a contiguous block of memory. That is, the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`: ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init(format, channels, sizeInFrames, pExistingData, &allocationCallbacks); + ma_audio_buffer_config config = ma_audio_buffer_config_init( + format, + channels, + sizeInFrames, + pExistingData, + &allocationCallbacks); + ma_audio_buffer* pBuffer result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); if (result != MA_SUCCESS) { @@ -1251,8 +1297,8 @@ ring buffer that operates on bytes you would call `ma_rb_init()` which leaves th 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. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your sub- -buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`. +Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your +sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`. Use 'ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require @@ -1269,7 +1315,7 @@ the consumer thread, and the write pointer forward by the producer thread. If th there is too little space between the pointers, move the write pointer forward. You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the same, only you will use the `ma_rb` -functions instead of `ma_pcm_rb` and instead of frame counts pass around byte counts. +functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts. The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most significant bit being used to encode a loop flag and the internally managed buffers always being aligned to MA_SIMD_ALIGNMENT. @@ -1360,6 +1406,7 @@ Some backends have some nuance details you may want to be aware of. - 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. - Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations. +- 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. */ #ifndef miniaudio_h @@ -1374,7 +1421,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 10 -#define MA_VERSION_REVISION 14 +#define MA_VERSION_REVISION 18 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -1424,56 +1471,34 @@ extern "C" { #include /* For size_t. */ -/* Sized types. Prefer built-in types. Fall back to stdint. */ -#ifdef _MSC_VER - #if defined(__clang__) +/* Sized types. */ +typedef signed char ma_int8; +typedef unsigned char ma_uint8; +typedef signed short ma_int16; +typedef unsigned short ma_uint16; +typedef signed int ma_int32; +typedef unsigned int ma_uint32; +#if defined(_MSC_VER) + typedef signed __int64 ma_int64; + typedef unsigned __int64 ma_uint64; +#else + #if defined(__GNUC__) #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlanguage-extension-token" #pragma GCC diagnostic ignored "-Wlong-long" - #pragma GCC diagnostic ignored "-Wc++11-long-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif #endif - typedef signed __int8 ma_int8; - typedef unsigned __int8 ma_uint8; - typedef signed __int16 ma_int16; - typedef unsigned __int16 ma_uint16; - typedef signed __int32 ma_int32; - typedef unsigned __int32 ma_uint32; - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; - #if defined(__clang__) + typedef signed long long ma_int64; + typedef unsigned long long ma_uint64; + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif -#else - #define MA_HAS_STDINT - #include - typedef int8_t ma_int8; - typedef uint8_t ma_uint8; - typedef int16_t ma_int16; - typedef uint16_t ma_uint16; - typedef int32_t ma_int32; - typedef uint32_t ma_uint32; - typedef int64_t ma_int64; - typedef uint64_t ma_uint64; #endif - -#ifdef MA_HAS_STDINT - typedef uintptr_t ma_uintptr; +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + typedef ma_uint64 ma_uintptr; #else - #if defined(_WIN32) - #if defined(_WIN64) - typedef ma_uint64 ma_uintptr; - #else - typedef ma_uint32 ma_uintptr; - #endif - #elif defined(__GNUC__) - #if defined(__LP64__) - typedef ma_uint64 ma_uintptr; - #else - typedef ma_uint32 ma_uintptr; - #endif - #else - typedef ma_uint64 ma_uintptr; /* Fallback. */ - #endif + typedef ma_uint32 ma_uintptr; #endif typedef ma_uint8 ma_bool8; @@ -1898,7 +1923,7 @@ MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevisio /* Retrieves the version of miniaudio as a string which can be useful for logging purposes. */ -MA_API const char* ma_version_string(); +MA_API const char* ma_version_string(void); /************************************************************************************************************************************************************** @@ -2579,6 +2604,13 @@ Both input and output channel map buffers must have a capacity of at at least `c */ MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); +/* +Copies a channel map if one is specified, otherwise copies the default channel map. + +The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. +*/ +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); + /* Determines whether or not a channel map is valid. @@ -3162,6 +3194,8 @@ typedef struct { ma_ios_session_category sessionCategory; ma_uint32 sessionCategoryOptions; + ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ + ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ } coreaudio; struct { @@ -3430,6 +3464,8 @@ struct ma_context ma_proc AudioUnitRender; /*AudioComponent*/ ma_ptr component; + + ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ } coreaudio; #endif #ifdef MA_SUPPORT_SNDIO @@ -3502,7 +3538,14 @@ struct ma_context #ifdef MA_SUPPORT_OPENSL struct { - int _unused; + ma_handle libOpenSLES; + ma_handle SL_IID_ENGINE; + ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; + ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + ma_handle SL_IID_RECORD; + ma_handle SL_IID_PLAY; + ma_handle SL_IID_OUTPUTMIX; + ma_proc slCreateEngine; } opensl; #endif #ifdef MA_SUPPORT_WEBAUDIO @@ -3603,6 +3646,7 @@ struct ma_device } resampling; struct { + ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ ma_bool32 usingDefaultFormat : 1; @@ -3621,6 +3665,7 @@ struct ma_device } playback; struct { + ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ ma_bool32 usingDefaultFormat : 1; @@ -5157,31 +5202,31 @@ Helper for applying a volume factor to samples. Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. */ -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor); -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor); +MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); /* @@ -5203,15 +5248,19 @@ typedef struct ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); ma_result (* onMap)(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ ma_result (* onUnmap)(ma_data_source* pDataSource, ma_uint64 frameCount); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); } ma_data_source_callbacks; MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_bool32 loop); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked, ma_bool32 loop); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount); */ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); MA_API ma_result ma_data_source_map(ma_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels); +MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ typedef struct @@ -5248,7 +5297,7 @@ MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer); - +MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); @@ -5367,7 +5416,8 @@ struct ma_decoder ma_decoder_read_proc onRead; ma_decoder_seek_proc onSeek; void* pUserData; - ma_uint64 readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */ + ma_uint64 readPointerInBytes; /* In internal encoded data. */ + ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; @@ -5441,6 +5491,11 @@ MA_API ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const m MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); +/* +Retrieves the current position of the read cursor in PCM frames. +*/ +MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); + /* Retrieves the length of the decoder in PCM frames. @@ -5471,6 +5526,17 @@ This is not thread safe without your own synchronization. */ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); +/* +Retrieves the number of frames that can be read before reaching the end. + +This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in +particular ensuring you do not call it on streams of an undefined length, such as internet radio. + +If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be +returned. +*/ +MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); + /* Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, pConfig should be set to what you want. On output it will be set to what you got. @@ -6276,7 +6342,7 @@ MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevisio } } -MA_API const char* ma_version_string() +MA_API const char* ma_version_string(void) { return MA_VERSION_STRING; } @@ -7547,32 +7613,32 @@ Atomics #if defined(__cplusplus) extern "C" { #endif -#if defined(__GNUC__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" +typedef signed char c89atomic_int8; +typedef unsigned char c89atomic_uint8; +typedef signed short c89atomic_int16; +typedef unsigned short c89atomic_uint16; +typedef signed int c89atomic_int32; +typedef unsigned int c89atomic_uint32; +#if defined(_MSC_VER) + typedef signed __int64 c89atomic_int64; + typedef unsigned __int64 c89atomic_uint64; +#else + #if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long c89atomic_int64; + typedef unsigned long long c89atomic_uint64; + #if defined(__GNUC__) + #pragma GCC diagnostic pop #endif #endif -typedef signed char c89atomic_int8; -typedef unsigned char c89atomic_uint8; -typedef signed short c89atomic_int16; -typedef unsigned short c89atomic_uint16; -typedef signed int c89atomic_int32; -typedef unsigned int c89atomic_uint32; -#if defined(_MSC_VER) -typedef signed __int64 c89atomic_int64; -typedef unsigned __int64 c89atomic_uint64; -#else -typedef unsigned long long c89atomic_int64; -typedef unsigned long long c89atomic_uint64; -#endif -#if defined(__GNUC__) - #pragma GCC diagnostic pop -#endif -typedef int c89atomic_memory_order; -typedef unsigned char c89atomic_bool; -typedef unsigned char c89atomic_flag; +typedef int c89atomic_memory_order; +typedef unsigned char c89atomic_bool; +typedef unsigned char c89atomic_flag; #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -7657,6 +7723,7 @@ typedef unsigned char c89atomic_flag; static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(int order) { volatile c89atomic_uint32 barrier; + (void)order; __asm { xchg barrier, eax } @@ -7795,7 +7862,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = oldValue - src; + newValue = (c89atomic_uint8)(oldValue - src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7806,7 +7873,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = oldValue - src; + newValue = (c89atomic_uint16)(oldValue - src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7839,7 +7906,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = oldValue & src; + newValue = (c89atomic_uint8)(oldValue & src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7850,7 +7917,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = oldValue & src; + newValue = (c89atomic_uint16)(oldValue & src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7883,7 +7950,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = oldValue ^ src; + newValue = (c89atomic_uint8)(oldValue ^ src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7894,7 +7961,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = oldValue ^ src; + newValue = (c89atomic_uint16)(oldValue ^ src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7927,7 +7994,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = oldValue | src; + newValue = (c89atomic_uint8)(oldValue | src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -7938,7 +8005,7 @@ typedef unsigned char c89atomic_flag; volatile c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = oldValue | src; + newValue = (c89atomic_uint16)(oldValue | src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; @@ -10121,7 +10188,7 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m break; } - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInDeviceFormat, pFramesInClientFormat); /* Safe cast. */ + result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat, pFramesInClientFormat); /* Safe cast. */ if (result != MA_SUCCESS) { ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result); break; @@ -10350,7 +10417,7 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) } /* Getting here means a suspend or kill operation has been requested. */ - c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS); + c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS); ma_event_signal(&pDevice->null_device.operationCompletionEvent); continue; } @@ -10364,7 +10431,7 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) ma_timer_init(&pDevice->null_device.timer); /* We're done. */ - c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS); + c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS); ma_event_signal(&pDevice->null_device.operationCompletionEvent); continue; } @@ -10372,7 +10439,7 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) /* Killing the device means we need to get out of this loop so that this thread can terminate. */ if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) { c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL); - c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS); + c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS); ma_event_signal(&pDevice->null_device.operationCompletionEvent); break; } @@ -10380,7 +10447,7 @@ static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) { MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - c89atomic_exchange_32(&pDevice->null_device.operationResult, MA_INVALID_OPERATION); + c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, (c89atomic_uint32)MA_INVALID_OPERATION); ma_event_signal(&pDevice->null_device.operationCompletionEvent); continue; /* Continue the loop. Don't terminate. */ } @@ -11019,8 +11086,6 @@ typedef struct #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #endif -static GUID MA_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) { @@ -11105,7 +11170,7 @@ static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 ma_uint32 iChannel = 0; ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iBit < channels; ++iBit) { + for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { DWORD bitValue = (dwChannelMask & (1UL << iBit)); if (bitValue != 0) { /* The bit is set. */ @@ -11126,6 +11191,12 @@ static ma_bool32 ma_is_guid_equal(const void* a, const void* b) #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) #endif +static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) +{ + static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + return ma_is_guid_equal(guid, &nullguid); +} + static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) { MA_ASSERT(pWF != NULL); @@ -11839,16 +11910,38 @@ static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificati return (ULONG)newRefCount; } - static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) { + ma_bool32 isThisDevice = MA_FALSE; + #ifdef MA_DEBUG_OUTPUT /*printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ #endif - (void)pThis; - (void)pDeviceID; - (void)dwNewState; + if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { + return S_OK; + } + + /* + There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect + that the device is disabled or has been unplugged. + */ + if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { + if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { + isThisDevice = MA_TRUE; + } + } + + if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { + if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { + isThisDevice = MA_TRUE; + } + } + + if (isThisDevice) { + ma_device_stop(pThis->pDevice); + } + return S_OK; } @@ -12128,6 +12221,8 @@ static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pCont MA_ASSERT(pContext != NULL); MA_ASSERT(ppDeviceEnumerator != NULL); + *ppDeviceEnumerator = NULL; /* Safety. */ + hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr)); @@ -13142,6 +13237,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev static ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) { ma_result result = MA_SUCCESS; + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; (void)pContext; @@ -13310,8 +13407,9 @@ static ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_co } /* - We need to get notifications of when the default device changes. We do this through a device enumerator by - registering a IMMNotificationClient with it. We only care about this if it's the default device. + We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When + we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just + stop the device outright and let the application handle it. */ #ifdef MA_WIN32_DESKTOP if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { @@ -13321,27 +13419,24 @@ static ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_co if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; } + } - if (pDevice->wasapi.allowCaptureAutoStreamRouting || pDevice->wasapi.allowPlaybackAutoStreamRouting) { - ma_IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_device_uninit__wasapi(pDevice); - return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr)); - } + hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_device_uninit__wasapi(pDevice); + return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", ma_result_from_HRESULT(hr)); + } - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; + pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; + pDevice->wasapi.notificationClient.counter = 1; + pDevice->wasapi.notificationClient.pDevice = pDevice; - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); - if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; - } else { - /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - } - } + hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); + if (SUCCEEDED(hr)) { + pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; + } else { + /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); } #endif @@ -13437,13 +13532,19 @@ static ma_result ma_device_stop__wasapi(ma_device* pDevice) MA_ASSERT(pDevice != NULL); /* - We need to explicitly signal the capture event in loopback mode to ensure we return from WaitForSingleObject() when nothing is being played. When nothing + It's possible for the main loop to get stuck if the device is disconnected. + + In loopback mode it's possible for WaitForSingleObject() to get stuck in a deadlock when nothing is being played. When nothing is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device. */ - if (pDevice->type == ma_device_type_loopback) { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_duplex) { SetEvent((HANDLE)pDevice->wasapi.hEventCapture); } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); + } + return MA_SUCCESS; } @@ -13514,7 +13615,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) if (pMappedDeviceBufferPlayback == NULL) { /* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) { + if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { return MA_ERROR; /* Wait failed. */ } } @@ -13538,7 +13639,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) if (framesAvailablePlayback == 0) { /* In exclusive mode we waited at the top. */ if (pDevice->playback.shareMode != ma_share_mode_exclusive) { - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) { + if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { return MA_ERROR; /* Wait failed. */ } } @@ -13563,7 +13664,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) /* Try grabbing some captured data if we haven't already got a mapped buffer. */ if (pMappedDeviceBufferCapture == NULL) { if (pDevice->capture.shareMode == ma_share_mode_shared) { - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) { + if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) { return MA_ERROR; /* Wait failed. */ } } @@ -13580,7 +13681,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) if (framesAvailableCapture == 0) { /* In exclusive mode we waited at the top. */ if (pDevice->capture.shareMode != ma_share_mode_shared) { - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) { + if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) { return MA_ERROR; /* Wait failed. */ } } @@ -13823,7 +13924,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */ /* Wait for data to become available first. */ - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) { + if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) { exitLoop = MA_TRUE; break; /* Wait failed. */ } @@ -13921,7 +14022,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) ma_uint32 framesAvailablePlayback; /* Wait for space to become available first. */ - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) { + if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { exitLoop = MA_TRUE; break; /* Wait failed. */ } @@ -14009,8 +14110,11 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ if (pDevice->wasapi.isStartedPlayback) { + /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ + DWORD waitTime = pDevice->wasapi.actualPeriodSizeInFramesPlayback / pDevice->playback.internalSampleRate; + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE); + WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); } else { ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; ma_uint32 framesAvailablePlayback; @@ -14033,7 +14137,7 @@ static ma_result ma_device_main_loop__wasapi(ma_device* pDevice) } prevFramesAvaialablePlayback = framesAvailablePlayback; - WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE); + WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ } } @@ -14781,7 +14885,7 @@ static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; MA_ASSERT(pData != NULL); - if ((pData->pDeviceID == NULL || ma_is_guid_equal(pData->pDeviceID->dsound, &MA_GUID_NULL)) && (lpGuid == NULL || ma_is_guid_equal(lpGuid, &MA_GUID_NULL))) { + if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { /* Default device. */ ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); pData->found = MA_TRUE; @@ -16140,7 +16244,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component name, and then concatenate the name from the registry. */ - if (!ma_is_guid_equal(&pCaps->NameGuid, &MA_GUID_NULL)) { + if (!ma_is_guid_null(&pCaps->NameGuid)) { wchar_t guidStrW[256]; if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { char guidStr[256]; @@ -22690,7 +22794,7 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* UInt32 iChannel = 0; UInt32 iBit; AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (iBit = 0; iBit < 32 && iBit < channelMapCap; ++iBit) { + for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { AudioChannelBitmap bit = bitmap & (1 << iBit); if (bit != 0) { pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); @@ -23565,6 +23669,15 @@ static ma_bool32 ma_context_is_device_id_equal__coreaudio(ma_context* pContext, return strcmp(pID0->coreaudio, pID1->coreaudio) == 0; } +#if !defined(MA_APPLE_DESKTOP) +static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) +{ + MA_ZERO_OBJECT(pInfo); + ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); + ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); +} +#endif + static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { #if defined(MA_APPLE_DESKTOP) @@ -23604,19 +23717,22 @@ static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, m ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); #else - /* Only supporting default devices on non-Desktop platforms. */ ma_device_info info; + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - MA_ZERO_OBJECT(&info); - ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - return MA_SUCCESS; + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); + if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { + return MA_SUCCESS; + } } - MA_ZERO_OBJECT(&info); - ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - return MA_SUCCESS; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); + if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { + return MA_SUCCESS; + } } #endif @@ -23732,12 +23848,41 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ AudioUnitElement formatElement; AudioStreamBasicDescription bestFormat; UInt32 propSize; - - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + + /* We want to ensure we use a consistent device name to device enumeration. */ + if (pDeviceID != NULL) { + ma_bool32 found = MA_FALSE; + if (deviceType == ma_device_type_playback) { + NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); + found = MA_TRUE; + break; + } + } + } else { + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); + found = MA_TRUE; + break; + } + } + } + + if (!found) { + return MA_DOES_NOT_EXIST; + } } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } } + /* Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is @@ -24539,6 +24684,28 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(result); } +#else + /* + For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change + the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. + */ + if (pDeviceID != NULL) { + if (deviceType == ma_device_type_capture) { + ma_bool32 found = MA_FALSE; + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; + found = MA_TRUE; + break; + } + } + + if (found == MA_FALSE) { + return MA_DOES_NOT_EXIST; + } + } + } #endif /* @@ -25129,6 +25296,14 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_coreaudio); +#if defined(MA_APPLE_MOBILE) + if (!pContext->coreaudio.noAudioSessionDeactivate) { + if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { + return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.", MA_FAILED_TO_INIT_BACKEND); + } + } +#endif + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); @@ -25166,7 +25341,9 @@ static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_catego static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_context* pContext) { +#if !defined(MA_APPLE_MOBILE) ma_result result; +#endif MA_ASSERT(pConfig != NULL); MA_ASSERT(pContext != NULL); @@ -25203,6 +25380,12 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma } } } + + if (!pConfig->coreaudio.noAudioSessionActivate) { + if (![pAudioSession setActive:true error:nil]) { + return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND); + } + } } #endif @@ -25335,6 +25518,8 @@ static ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma } #endif + pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; + return MA_SUCCESS; } #endif /* Core Audio */ @@ -28740,10 +28925,13 @@ OpenSL|ES Backend #include #endif +typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); + /* OpenSL|ES has one-per-application objects :( */ -SLObjectItf g_maEngineObjectSL = NULL; -SLEngineItf g_maEngineSL = NULL; -ma_uint32 g_maOpenSLInitCounter = 0; +static SLObjectItf g_maEngineObjectSL = NULL; +static SLEngineItf g_maEngineSL = NULL; +static ma_uint32 g_maOpenSLInitCounter = 0; +static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) @@ -28863,7 +29051,7 @@ static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint /* Just iterate over each bit. */ ma_uint32 iChannel = 0; ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iBit < channels; ++iBit) { + for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { SLuint32 bitValue = (channelMask & (1UL << iBit)); if (bitValue != 0) { /* The bit is set. */ @@ -28959,7 +29147,7 @@ static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_e SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { /* The interface may not be supported so just report a default device. */ goto return_default_device; @@ -29065,7 +29253,7 @@ static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_dev */ #if 0 && !defined(MA_ANDROID) SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { /* The interface may not be supported so just report a default device. */ goto return_default_device; @@ -29354,7 +29542,7 @@ static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_co SLresult resultSL; ma_uint32 periodSizeInFrames; size_t bufferSizeInBytes; - const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + SLInterfaceID itfIDs1[1]; const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE}; #endif @@ -29375,6 +29563,8 @@ static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_co queues). */ #ifdef MA_ANDROID + itfIDs1[0] = (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + /* No exclusive mode with OpenSL|ES. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { @@ -29431,13 +29621,13 @@ static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_co return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL)); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL)); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL)); @@ -29490,7 +29680,7 @@ static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_co return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL)); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL)); @@ -29534,13 +29724,13 @@ static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_co return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL)); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL)); } - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL)); @@ -29739,39 +29929,136 @@ static ma_result ma_context_uninit__opensl(ma_context* pContext) (void)pContext; /* Uninit global data. */ - if (g_maOpenSLInitCounter > 0) { - if (c89atomic_fetch_sub_32(&g_maOpenSLInitCounter, 1) == 1) { + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ + + g_maOpenSLInitCounter -= 1; + if (g_maOpenSLInitCounter == 0) { (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); } } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + return MA_SUCCESS; +} + +static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) +{ + /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ + ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); + if (p == NULL) { + ma_post_log_messagef(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol %s", pName); + return MA_NO_BACKEND; + } + + *pHandle = *p; + return MA_SUCCESS; +} + +static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) +{ + g_maOpenSLInitCounter += 1; + if (g_maOpenSLInitCounter == 1) { + SLresult resultSL; + + resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + + (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); + + resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); + if (resultSL != SL_RESULT_SUCCESS) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + } return MA_SUCCESS; } static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext) { + ma_result result; + size_t i; + const char* libOpenSLESNames[] = { + "libOpenSLES.so" + }; + MA_ASSERT(pContext != NULL); (void)pConfig; - /* Initialize global data first if applicable. */ - if (c89atomic_fetch_add_32(&g_maOpenSLInitCounter, 1) == 0) { - SLresult resultSL = slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - c89atomic_fetch_sub_32(&g_maOpenSLInitCounter, 1); - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - c89atomic_fetch_sub_32(&g_maOpenSLInitCounter, 1); - return ma_result_from_OpenSL(resultSL); + /* + Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One + report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime + and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any + references to the symbols and will hopefully skip the checks. + */ + for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { + pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]); + if (pContext->opensl.libOpenSLES != NULL) { + break; } } + if (pContext->opensl.libOpenSLES == NULL) { + return MA_NO_BACKEND; /* Couldn't find libOpenSLES.so */ + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); + if (result != MA_SUCCESS) { + return result; + } + + pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); + if (pContext->opensl.slCreateEngine == NULL) { + ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine."); + return MA_NO_BACKEND; + } + + + /* Initialize global data first if applicable. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + result = ma_context_init_engine_nolock__opensl(pContext); + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the OpenSL engine. */ + } + + pContext->isBackendAsynchronous = MA_TRUE; pContext->onUninit = ma_context_uninit__opensl; @@ -31220,6 +31507,13 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } } + if (config.playback.pDeviceID != NULL) { + MA_COPY_MEMORY(&pDevice->playback.id, config.playback.pDeviceID, sizeof(pDevice->playback.id)); + } + if (config.capture.pDeviceID != NULL) { + MA_COPY_MEMORY(&pDevice->capture.id, config.capture.pDeviceID, sizeof(pDevice->capture.id)); + } + pDevice->noPreZeroedOutputBuffer = config.noPreZeroedOutputBuffer; pDevice->noClip = config.noClip; pDevice->masterVolumeFactor = 1; @@ -31591,7 +31885,6 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_device__set_state(pDevice, MA_STATE_STOPPING); /* There's no need to wake up the thread like we do when starting. */ - if (pDevice->pContext->onDeviceStop) { result = pDevice->pContext->onDeviceStop(pDevice); } else { @@ -31747,9 +32040,9 @@ MA_API void ma_clip_samples_f32(float* p, ma_uint64 sampleCount) } -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor) +MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) { - ma_uint32 iSample; + ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; @@ -31760,9 +32053,9 @@ MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_u } } -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor) +MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) { - ma_uint32 iSample; + ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; @@ -31773,9 +32066,9 @@ MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_ } } -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor) +MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) { - ma_uint32 iSample; + ma_uint64 iSample; ma_uint8* pSamplesOut8; ma_uint8* pSamplesIn8; @@ -31798,9 +32091,9 @@ MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* p } } -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor) +MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) { - ma_uint32 iSample; + ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; @@ -31811,9 +32104,9 @@ MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_ } } -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor) +MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) { - ma_uint32 iSample; + ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; @@ -31824,57 +32117,57 @@ MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* } } -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor) +MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); } -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor) +MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); } -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor) +MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); } -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor) +MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); } -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor) +MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor); } -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor) +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) { switch (format) { @@ -31887,32 +32180,32 @@ MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, cons } } -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor); } -MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor) +MA_API void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor); } @@ -37651,8 +37944,8 @@ MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format fo config.format = format; config.channelsIn = channelsIn; config.channelsOut = channelsOut; - ma_channel_map_copy(config.channelMapIn, pChannelMapIn, channelsIn); - ma_channel_map_copy(config.channelMapOut, pChannelMapOut, channelsOut); + ma_channel_map_copy_or_default(config.channelMapIn, pChannelMapIn, channelsIn); + ma_channel_map_copy_or_default(config.channelMapOut, pChannelMapOut, channelsOut); config.mixingMode = mixingMode; return config; @@ -38334,7 +38627,7 @@ static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_con for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; - s += (pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; + s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); } @@ -39944,6 +40237,19 @@ MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint } } +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) +{ + if (pOut == NULL || channels == 0) { + return; + } + + if (pIn != NULL) { + ma_channel_map_copy(pOut, pIn, channels); + } else { + ma_get_standard_channel_map(ma_standard_channel_map_default, channels, pOut); + } +} + MA_API ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel* pChannelMap) { if (pChannelMap == NULL) { @@ -40923,7 +41229,8 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi } else { ma_format format; ma_uint32 channels; - if (ma_data_source_get_data_format(pDataSource, &format, &channels) != MA_SUCCESS) { + ma_uint32 sampleRate; + if (ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate) != MA_SUCCESS) { return pCallbacks->onRead(pDataSource, pFramesOut, frameCount, pFramesRead); /* We don't have a way to retrieve the data format which means we don't know how to offset the output buffer. Just read as much as we can. */ } else { ma_result result = MA_SUCCESS; @@ -40942,7 +41249,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is not necessarily considered an error. */ - if (result != MA_SUCCESS) { + if (result != MA_SUCCESS && result != MA_AT_END) { break; } @@ -40950,7 +41257,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi We can determine if we've reached the end by checking the return value of the onRead() callback. If it's less than what we requested it means we've reached the end. To loop back to the start, all we need to do is seek back to the first frame. */ - if (framesProcessed < framesRemaining) { + if (framesProcessed < framesRemaining || result == MA_AT_END) { if (ma_data_source_seek_to_pcm_frame(pDataSource, 0) != MA_SUCCESS) { break; } @@ -40961,7 +41268,10 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi } } - *pFramesRead = totalFramesProcessed; + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + return result; } } @@ -41002,18 +41312,19 @@ MA_API ma_result ma_data_source_unmap(ma_data_source* pDataSource, ma_uint64 fra return pCallbacks->onUnmap(pDataSource, frameCount); } -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels) +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) { ma_result result; ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; if (pCallbacks == NULL || pCallbacks->onGetDataFormat == NULL) { return MA_INVALID_ARGS; } - result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels); + result = pCallbacks->onGetDataFormat(pDataSource, &format, &channels, &sampleRate); if (result != MA_SUCCESS) { return result; } @@ -41024,10 +41335,55 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_ if (pChannels != NULL) { *pChannels = channels; } + if (pSampleRate != NULL) { + *pSampleRate = sampleRate; + } return MA_SUCCESS; } +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pCallbacks == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onGetCursor == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onGetCursor(pDataSource, pCursor); +} + +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_data_source_callbacks* pCallbacks = (ma_data_source_callbacks*)pDataSource; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pCallbacks == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onGetLength == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onGetLength(pDataSource, pLength); +} + MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) @@ -41075,12 +41431,31 @@ static ma_result ma_audio_buffer__data_source_on_unmap(ma_data_source* pDataSour return ma_audio_buffer_unmap((ma_audio_buffer*)pDataSource, frameCount); } -static ma_result ma_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels) +static ma_result ma_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) { ma_audio_buffer* pAudioBuffer = (ma_audio_buffer*)pDataSource; - *pFormat = pAudioBuffer->format; - *pChannels = pAudioBuffer->channels; + *pFormat = pAudioBuffer->format; + *pChannels = pAudioBuffer->channels; + *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_audio_buffer* pAudioBuffer = (ma_audio_buffer*)pDataSource; + + *pCursor = pAudioBuffer->cursor; + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_audio_buffer* pAudioBuffer = (ma_audio_buffer*)pDataSource; + + *pLength = pAudioBuffer->sizeInFrames; return MA_SUCCESS; } @@ -41106,6 +41481,8 @@ static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, pAudioBuffer->ds.onMap = ma_audio_buffer__data_source_on_map; pAudioBuffer->ds.onUnmap = ma_audio_buffer__data_source_on_unmap; pAudioBuffer->ds.onGetDataFormat = ma_audio_buffer__data_source_on_get_data_format; + pAudioBuffer->ds.onGetCursor = ma_audio_buffer__data_source_on_get_cursor; + pAudioBuffer->ds.onGetLength = ma_audio_buffer__data_source_on_get_length; pAudioBuffer->format = pConfig->format; pAudioBuffer->channels = pConfig->channels; pAudioBuffer->cursor = 0; @@ -41268,7 +41645,7 @@ MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, MA_ASSERT(pAudioBuffer->cursor < pAudioBuffer->sizeInFrames); } - return frameCount; + return totalFramesRead; } MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) @@ -41346,6 +41723,27 @@ MA_API ma_result ma_audio_buffer_at_end(ma_audio_buffer* pAudioBuffer) return pAudioBuffer->cursor == pAudioBuffer->sizeInFrames; } +MA_API ma_result ma_audio_buffer_get_available_frames(ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (pAudioBuffer->sizeInFrames <= pAudioBuffer->cursor) { + *pAvailableFrames = 0; + } else { + *pAvailableFrames = pAudioBuffer->sizeInFrames - pAudioBuffer->cursor; + } + + return MA_SUCCESS; +} + /************************************************************************************************************************************************************** @@ -41726,9 +42124,6 @@ static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, con return result; } -#if !defined(WINVER) || WINVER <= 0x0502 -WINBASEAPI BOOL WINAPI SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* pNewFilePointer, DWORD dwMoveMethod); -#endif static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { @@ -41748,7 +42143,16 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i dwMoveMethod = FILE_BEGIN; } +#if defined(_MSC_VER) && _MSC_VER <= 1200 + /* No SetFilePointerEx() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); +#else result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); +#endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -41761,12 +42165,20 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i LARGE_INTEGER liZero; LARGE_INTEGER liTell; BOOL result; +#if defined(_MSC_VER) && _MSC_VER <= 1200 + LONG tell; +#endif (void)pVFS; liZero.QuadPart = 0; +#if defined(_MSC_VER) && _MSC_VER <= 1200 + result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); + liTell.QuadPart = tell; +#else result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); +#endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -42156,7 +42568,7 @@ MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_c Decoding and Encoding Headers. These are auto-generated from a tool. **************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && !defined(MA_NO_DECODING) && !defined(MA_NO_ENCODING) +#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) /* dr_wav_h begin */ #ifndef dr_wav_h #define dr_wav_h @@ -42167,42 +42579,41 @@ extern "C" { #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 12 -#define DRWAV_VERSION_REVISION 6 +#define DRWAV_VERSION_REVISION 10 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include -#ifdef _MSC_VER - #if defined(__clang__) +typedef signed char drwav_int8; +typedef unsigned char drwav_uint8; +typedef signed short drwav_int16; +typedef unsigned short drwav_uint16; +typedef signed int drwav_int32; +typedef unsigned int drwav_uint32; +#if defined(_MSC_VER) + typedef signed __int64 drwav_int64; + typedef unsigned __int64 drwav_uint64; +#else + #if defined(__GNUC__) #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlanguage-extension-token" #pragma GCC diagnostic ignored "-Wlong-long" - #pragma GCC diagnostic ignored "-Wc++11-long-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif #endif - typedef signed __int8 drwav_int8; - typedef unsigned __int8 drwav_uint8; - typedef signed __int16 drwav_int16; - typedef unsigned __int16 drwav_uint16; - typedef signed __int32 drwav_int32; - typedef unsigned __int32 drwav_uint32; - typedef signed __int64 drwav_int64; - typedef unsigned __int64 drwav_uint64; - #if defined(__clang__) + typedef signed long long drwav_int64; + typedef unsigned long long drwav_uint64; + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif -#else - #include - typedef int8_t drwav_int8; - typedef uint8_t drwav_uint8; - typedef int16_t drwav_int16; - typedef uint16_t drwav_uint16; - typedef int32_t drwav_int32; - typedef uint32_t drwav_uint32; - typedef int64_t drwav_int64; - typedef uint64_t drwav_uint64; #endif -typedef drwav_uint8 drwav_bool8; -typedef drwav_uint32 drwav_bool32; -#define DRWAV_TRUE 1 -#define DRWAV_FALSE 0 +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + typedef drwav_uint64 drwav_uintptr; +#else + typedef drwav_uint32 drwav_uintptr; +#endif +typedef drwav_uint8 drwav_bool8; +typedef drwav_uint32 drwav_bool32; +#define DRWAV_TRUE 1 +#define DRWAV_FALSE 0 #if !defined(DRWAV_API) #if defined(DRWAV_DLL) #if defined(_WIN32) @@ -42298,7 +42709,7 @@ typedef drwav_int32 drwav_result; #endif #define DRWAV_SEQUENTIAL 0x00000001 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); -DRWAV_API const char* drwav_version_string(); +DRWAV_API const char* drwav_version_string(void); typedef enum { drwav_seek_origin_start, @@ -42540,42 +42951,41 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 15 +#define DRFLAC_VERSION_REVISION 19 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include -#ifdef _MSC_VER - #if defined(__clang__) +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +#if defined(_MSC_VER) + typedef signed __int64 drflac_int64; + typedef unsigned __int64 drflac_uint64; +#else + #if defined(__GNUC__) #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlanguage-extension-token" #pragma GCC diagnostic ignored "-Wlong-long" - #pragma GCC diagnostic ignored "-Wc++11-long-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif #endif - typedef signed __int8 drflac_int8; - typedef unsigned __int8 drflac_uint8; - typedef signed __int16 drflac_int16; - typedef unsigned __int16 drflac_uint16; - typedef signed __int32 drflac_int32; - typedef unsigned __int32 drflac_uint32; - typedef signed __int64 drflac_int64; - typedef unsigned __int64 drflac_uint64; - #if defined(__clang__) + typedef signed long long drflac_int64; + typedef unsigned long long drflac_uint64; + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif -#else - #include - typedef int8_t drflac_int8; - typedef uint8_t drflac_uint8; - typedef int16_t drflac_int16; - typedef uint16_t drflac_uint16; - typedef int32_t drflac_int32; - typedef uint32_t drflac_uint32; - typedef int64_t drflac_int64; - typedef uint64_t drflac_uint64; #endif -typedef drflac_uint8 drflac_bool8; -typedef drflac_uint32 drflac_bool32; -#define DRFLAC_TRUE 1 -#define DRFLAC_FALSE 0 +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + typedef drflac_uint64 drflac_uintptr; +#else + typedef drflac_uint32 drflac_uintptr; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 #if !defined(DRFLAC_API) #if defined(DRFLAC_DLL) #if defined(_WIN32) @@ -42618,7 +43028,7 @@ typedef drflac_uint32 drflac_bool32; #define DRFLAC_DEPRECATED #endif DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); -DRFLAC_API const char* drflac_version_string(); +DRFLAC_API const char* drflac_version_string(void); #ifndef DR_FLAC_BUFFER_SIZE #define DR_FLAC_BUFFER_SIZE 4096 #endif @@ -42902,42 +43312,41 @@ extern "C" { #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 13 +#define DRMP3_VERSION_REVISION 16 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include -#ifdef _MSC_VER - #if defined(__clang__) +typedef signed char drmp3_int8; +typedef unsigned char drmp3_uint8; +typedef signed short drmp3_int16; +typedef unsigned short drmp3_uint16; +typedef signed int drmp3_int32; +typedef unsigned int drmp3_uint32; +#if defined(_MSC_VER) + typedef signed __int64 drmp3_int64; + typedef unsigned __int64 drmp3_uint64; +#else + #if defined(__GNUC__) #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlanguage-extension-token" #pragma GCC diagnostic ignored "-Wlong-long" - #pragma GCC diagnostic ignored "-Wc++11-long-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif #endif - typedef signed __int8 drmp3_int8; - typedef unsigned __int8 drmp3_uint8; - typedef signed __int16 drmp3_int16; - typedef unsigned __int16 drmp3_uint16; - typedef signed __int32 drmp3_int32; - typedef unsigned __int32 drmp3_uint32; - typedef signed __int64 drmp3_int64; - typedef unsigned __int64 drmp3_uint64; - #if defined(__clang__) + typedef signed long long drmp3_int64; + typedef unsigned long long drmp3_uint64; + #if defined(__GNUC__) #pragma GCC diagnostic pop #endif -#else - #include - typedef int8_t drmp3_int8; - typedef uint8_t drmp3_uint8; - typedef int16_t drmp3_int16; - typedef uint16_t drmp3_uint16; - typedef int32_t drmp3_int32; - typedef uint32_t drmp3_uint32; - typedef int64_t drmp3_int64; - typedef uint64_t drmp3_uint64; #endif -typedef drmp3_uint8 drmp3_bool8; -typedef drmp3_uint32 drmp3_bool32; -#define DRMP3_TRUE 1 -#define DRMP3_FALSE 0 +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + typedef drmp3_uint64 drmp3_uintptr; +#else + typedef drmp3_uint32 drmp3_uintptr; +#endif +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 #if !defined(DRMP3_API) #if defined(DRMP3_DLL) #if defined(_WIN32) @@ -43035,7 +43444,7 @@ typedef drmp3_int32 drmp3_result; #define DRMP3_INLINE #endif DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); -DRMP3_API const char* drmp3_version_string(); +DRMP3_API const char* drmp3_version_string(void); typedef struct { int frame_bytes, channels, hz, layer, bitrate_kbps; @@ -43160,7 +43569,7 @@ static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size MA_ASSERT(pBufferOut != NULL); bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead); - pDecoder->readPointer += bytesRead; + pDecoder->readPointerInBytes += bytesRead; return bytesRead; } @@ -43174,9 +43583,9 @@ static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_ wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin); if (wasSuccessful) { if (origin == ma_seek_origin_start) { - pDecoder->readPointer = (ma_uint64)byteOffset; + pDecoder->readPointerInBytes = (ma_uint64)byteOffset; } else { - pDecoder->readPointer += byteOffset; + pDecoder->readPointerInBytes += byteOffset; } } @@ -43513,13 +43922,20 @@ static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig /* dr_flac supports reading as s32, s16 and f32. Try to do a one-to-one mapping if possible, but fall back to s32 if not. s32 is the "native" FLAC format - since it's the only one that's truly lossless. + since it's the only one that's truly lossless. If the internal bits per sample is <= 16 we will decode to ma_format_s16 to keep it more efficient. */ - pDecoder->internalFormat = ma_format_s32; - if (pConfig->format == ma_format_s16) { - pDecoder->internalFormat = ma_format_s16; - } else if (pConfig->format == ma_format_f32) { - pDecoder->internalFormat = ma_format_f32; + if (pConfig->format == ma_format_unknown) { + if (pFlac->bitsPerSample <= 16) { + pDecoder->internalFormat = ma_format_s16; + } else { + pDecoder->internalFormat = ma_format_s32; + } + } else { + if (pConfig->format == ma_format_s16 || pConfig->format == ma_format_f32) { + pDecoder->internalFormat = pConfig->format; + } else { + pDecoder->internalFormat = ma_format_s32; /* s32 as the baseline to ensure no loss of precision for 24-bit encoded files. */ + } } pDecoder->internalChannels = pFlac->channels; @@ -44116,12 +44532,32 @@ static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); } -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels) +static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) { ma_decoder* pDecoder = (ma_decoder*)pDataSource; - *pFormat = pDecoder->outputFormat; - *pChannels = pDecoder->outputChannels; + *pFormat = pDecoder->outputFormat; + *pChannels = pDecoder->outputChannels; + *pSampleRate = pDecoder->outputSampleRate; + + return MA_SUCCESS; +} + +static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_decoder* pDecoder = (ma_decoder*)pDataSource; + + return ma_decoder_get_cursor_in_pcm_frames(pDecoder, pLength); +} + +static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_decoder* pDecoder = (ma_decoder*)pDataSource; + + *pLength = ma_decoder_get_length_in_pcm_frames(pDecoder); + if (*pLength == 0) { + return MA_NOT_IMPLEMENTED; + } return MA_SUCCESS; } @@ -44145,6 +44581,8 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see pDecoder->ds.onRead = ma_decoder__data_source_on_read; pDecoder->ds.onSeek = ma_decoder__data_source_on_seek; pDecoder->ds.onGetDataFormat = ma_decoder__data_source_on_get_data_format; + pDecoder->ds.onGetCursor = ma_decoder__data_source_on_get_cursor; + pDecoder->ds.onGetLength = ma_decoder__data_source_on_get_length; pDecoder->onRead = onRead; pDecoder->onSeek = onSeek; @@ -44599,6 +45037,16 @@ MA_API ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, return ma_decoder__postinit(&config, pDecoder); } + +#if defined(MA_HAS_WAV) || \ + defined(MA_HAS_MP3) || \ + defined(MA_HAS_FLAC) || \ + defined(MA_HAS_VORBIS) || \ + defined(MA_HAS_OPUS) +#define MA_HAS_PATH_API +#endif + +#if defined(MA_HAS_PATH_API) static const char* ma_path_file_name(const char* path) { const char* fileName; @@ -44765,6 +45213,7 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e } #endif } +#endif /* MA_HAS_PATH_API */ @@ -45296,6 +45745,23 @@ MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) return MA_SUCCESS; } +MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pDecoder->readPointerInPCMFrames; + + return MA_SUCCESS; +} + MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder) { if (pDecoder == NULL) { @@ -45331,68 +45797,70 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO /* Fast path. */ if (pDecoder->converter.isPassthrough) { - return pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount); - } - - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure it's internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - return pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */ - } - - /* Slow path. Need to run everything through the data converter. */ - totalFramesReadOut = 0; - totalFramesReadIn = 0; - pRunningFramesOut = pFramesOut; - - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn); - totalFramesReadIn += framesReadThisIterationIn; - } - + totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount); + } else { /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. + Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we + need to run through each sample because we need to ensure it's internal cache is updated. */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } + if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { + totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */ + } else { + /* Slow path. Need to run everything through the data converter. */ + totalFramesReadOut = 0; + totalFramesReadIn = 0; + pRunningFramesOut = pFramesOut; + + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; - totalFramesReadOut += framesReadThisIterationOut; + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } + requiredInputFrameCount = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ + if (requiredInputFrameCount > 0) { + framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn); + totalFramesReadIn += framesReadThisIterationIn; + } + + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + totalFramesReadOut += framesReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } + } } } + pDecoder->readPointerInPCMFrames += totalFramesReadOut; + return totalFramesReadOut; } @@ -45403,6 +45871,7 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr } if (pDecoder->onSeekToPCMFrame) { + ma_result result; ma_uint64 internalFrameIndex; if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) { internalFrameIndex = frameIndex; @@ -45410,13 +45879,44 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex); } - return pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex); + result = pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex); + if (result == MA_SUCCESS) { + pDecoder->readPointerInPCMFrames = frameIndex; + } } /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ return MA_INVALID_ARGS; } +MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) +{ + ma_uint64 totalFrameCount; + + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + totalFrameCount = ma_decoder_get_length_in_pcm_frames(pDecoder); + if (totalFrameCount == 0) { + return MA_NOT_IMPLEMENTED; + } + + if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { + *pAvailableFrames = 0; + } else { + *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; + } + + return MA_SUCCESS; /* No frames available. */ +} + static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) { @@ -45857,12 +46357,22 @@ static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, m return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); } -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels) +static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) { ma_waveform* pWaveform = (ma_waveform*)pDataSource; - *pFormat = pWaveform->config.format; - *pChannels = pWaveform->config.channels; + *pFormat = pWaveform->config.format; + *pChannels = pWaveform->config.channels; + *pSampleRate = pWaveform->config.sampleRate; + + return MA_SUCCESS; +} + +static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_waveform* pWaveform = (ma_waveform*)pDataSource; + + *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); return MA_SUCCESS; } @@ -45877,9 +46387,11 @@ MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform pWaveform->ds.onRead = ma_waveform__data_source_on_read; pWaveform->ds.onSeek = ma_waveform__data_source_on_seek; pWaveform->ds.onGetDataFormat = ma_waveform__data_source_on_get_data_format; - pWaveform->config = *pConfig; - pWaveform->advance = 1.0 / pWaveform->config.sampleRate; - pWaveform->time = 0; + pWaveform->ds.onGetCursor = ma_waveform__data_source_on_get_cursor; + pWaveform->ds.onGetLength = NULL; /* Intentionally set to NULL since there's no notion of a length in waveforms. */ + pWaveform->config = *pConfig; + pWaveform->advance = 1.0 / pWaveform->config.sampleRate; + pWaveform->time = 0; return MA_SUCCESS; } @@ -46236,12 +46748,13 @@ static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_u return MA_SUCCESS; } -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels) +static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) { ma_noise* pNoise = (ma_noise*)pDataSource; - *pFormat = pNoise->config.format; - *pChannels = pNoise->config.channels; + *pFormat = pNoise->config.format; + *pChannels = pNoise->config.channels; + *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ return MA_SUCCESS; } @@ -46265,7 +46778,9 @@ MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, ma_noise* pNoise) pNoise->ds.onRead = ma_noise__data_source_on_read; pNoise->ds.onSeek = ma_noise__data_source_on_seek; /* <-- No-op for noise. */ pNoise->ds.onGetDataFormat = ma_noise__data_source_on_get_data_format; - pNoise->config = *pConfig; + pNoise->ds.onGetCursor = NULL; /* No notion of a cursor for noise. */ + pNoise->ds.onGetLength = NULL; /* No notion of a length for noise. */ + pNoise->config = *pConfig; ma_lcg_seed(&pNoise->lcg, pConfig->seed); if (pNoise->config.type == ma_noise_type_pink) { @@ -46595,7 +47110,7 @@ code below please report the bug to the respective repository for the relevant p *************************************************************************************************************************************************************** **************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && !defined(MA_NO_DECODING) && !defined(MA_NO_ENCODING) +#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) #if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_wav_c begin */ #ifndef dr_wav_c @@ -46699,7 +47214,7 @@ DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_u *pRevision = DRWAV_VERSION_REVISION; } } -DRWAV_API const char* drwav_version_string() +DRWAV_API const char* drwav_version_string(void) { return DRWAV_VERSION_STRING; } @@ -47570,6 +48085,39 @@ static drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) { return 24 + dataChunkSize; } +static size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + return pWav->onWrite(pWav->pUserData, pData, dataSize); +} +static size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + if (!drwav__is_little_endian()) { + value = drwav__bswap16(value); + } + return drwav__write(pWav, &value, 2); +} +static size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + if (!drwav__is_little_endian()) { + value = drwav__bswap32(value); + } + return drwav__write(pWav, &value, 4); +} +static size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) +{ + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + if (!drwav__is_little_endian()) { + value = drwav__bswap64(value); + } + return drwav__write(pWav, &value, 8); +} static drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onWrite == NULL) { @@ -47618,39 +48166,39 @@ static drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_for pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; if (pFormat->container == drwav_container_riff) { drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; - runningPos += pWav->onWrite(pWav->pUserData, "RIFF", 4); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 4); - runningPos += pWav->onWrite(pWav->pUserData, "WAVE", 4); + runningPos += drwav__write(pWav, "RIFF", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, "WAVE", 4); } else { drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_RIFF, 16); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 8); - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_WAVE, 16); + runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); } if (pFormat->container == drwav_container_riff) { chunkSizeFMT = 16; - runningPos += pWav->onWrite(pWav->pUserData, "fmt ", 4); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 4); + runningPos += drwav__write(pWav, "fmt ", 4); + runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); } else { chunkSizeFMT = 40; - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_FMT, 16); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 8); + runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); } - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.formatTag, 2); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.channels, 2); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.sampleRate, 4); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.avgBytesPerSec, 4); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.blockAlign, 2); - runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.bitsPerSample, 2); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); pWav->dataChunkDataPos = runningPos; if (pFormat->container == drwav_container_riff) { drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; - runningPos += pWav->onWrite(pWav->pUserData, "data", 4); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 4); + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); } else { drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_DATA, 16); - runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 8); + runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); } if (pFormat->container == drwav_container_riff) { if (runningPos != 20 + chunkSizeFMT + 8) { @@ -48478,26 +49026,26 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) } if (paddingSize > 0) { drwav_uint64 paddingData = 0; - pWav->onWrite(pWav->pUserData, &paddingData, paddingSize); + drwav__write(pWav, &paddingData, paddingSize); } if (pWav->onSeek && !pWav->isSequentialWrite) { if (pWav->container == drwav_container_riff) { if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &riffChunkSize, 4); + drwav__write_u32ne_to_le(pWav, riffChunkSize); } if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) { drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &dataChunkSize, 4); + drwav__write_u32ne_to_le(pWav, dataChunkSize); } } else { if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &riffChunkSize, 8); + drwav__write_u64ne_to_le(pWav, riffChunkSize); } if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) { drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); - pWav->onWrite(pWav->pUserData, &dataChunkSize, 8); + drwav__write_u64ne_to_le(pWav, dataChunkSize); } } } @@ -48598,6 +49146,13 @@ DRWAV_API drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) } if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { pWav->compressed.iCurrentPCMFrame = 0; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->ima); + } else { + DRWAV_ASSERT(DRWAV_FALSE); + } } pWav->bytesRemaining = pWav->dataChunkDataSize; return DRWAV_TRUE; @@ -49578,7 +50133,11 @@ DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t samp return; } for (i = 0; i < sampleCount; ++i) { - double x = (double)(((drwav_int32)(((drwav_uint32)(pIn[i*3+0]) << 8) | ((drwav_uint32)(pIn[i*3+1]) << 16) | ((drwav_uint32)(pIn[i*3+2])) << 24)) >> 8); + double x; + drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); + drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); + drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); + x = (double)((drwav_int32)(a | b | c) >> 8); *pOut++ = (float)(x * 0.00000011920928955078125); } } @@ -50436,7 +50995,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) return DRFLAC_FALSE; #endif } -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) #define DRFLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define DRFLAC_HAS_LZCNT_INTRINSIC @@ -50447,7 +51006,7 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) #endif #endif #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC @@ -50575,7 +51134,7 @@ DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drf *pRevision = DRFLAC_VERSION_REVISION; } } -DRFLAC_API const char* drflac_version_string() +DRFLAC_API const char* drflac_version_string(void) { return DRFLAC_VERSION_STRING; } @@ -50648,7 +51207,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) { #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) + #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap16(n); @@ -50663,7 +51222,7 @@ static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) { #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) + #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) @@ -50692,7 +51251,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) { #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) + #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(n); @@ -51132,7 +51691,6 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) { drflac_uint32 result; - drflac_uint32 signbit; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); @@ -51140,8 +51698,11 @@ static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, dr if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; + if (bitCount < 32) { + drflac_uint32 signbit; + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + } *pResult = (drflac_int32)result; return DRFLAC_TRUE; } @@ -51308,7 +51869,7 @@ static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) #define DRFLAC_IMPLEMENT_CLZ_LZCNT #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) #define DRFLAC_IMPLEMENT_CLZ_MSVC #endif static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) @@ -51355,7 +51916,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) } static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { -#if defined(_MSC_VER) && !defined(__clang__) +#if defined(_MSC_VER) #ifdef DRFLAC_64BIT return (drflac_uint32)__lzcnt64(x); #else @@ -51367,7 +51928,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { drflac_uint64 r; __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) + "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return (drflac_uint32)r; } @@ -51375,7 +51936,7 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { drflac_uint32 r; __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) + "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return r; } @@ -58437,7 +58998,7 @@ DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_u *pRevision = DRMP3_VERSION_REVISION; } } -DRMP3_API const char* drmp3_version_string() +DRMP3_API const char* drmp3_version_string(void) { return DRMP3_VERSION_STRING; } @@ -60315,7 +60876,9 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa drmp3dec_frame_info info; if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { size_t bytesRead; - memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + if (pMP3->pData != NULL) { + memmove(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + } pMP3->dataConsumed = 0; if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { drmp3_uint8* pNewData; @@ -61966,6 +62529,41 @@ The following miscellaneous changes have also been made. /* REVISION HISTORY ================ +v0.10.18 - 2020-08-30 + - Fix build errors with VC6. + - Fix a bug in channel converter for s32 format. + - Change channel converter configs to use the default channel map instead of a blank channel map when no channel map is specified when initializing the + config. This fixes an issue where the optimized mono expansion path would never get used. + - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit. + - Update FLAC decoder. + - Update links to point to the new repository location (https://github.com/mackron/miniaudio). + +v0.10.17 - 2020-08-28 + - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set. + - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed. + - Fix compilation error on Android. + - Core Audio: Fix a bug with full-duplex mode. + - Add ma_decoder_get_cursor_in_pcm_frames(). + - Update WAV codec. + +v0.10.16 - 2020-08-14 + - WASAPI: Fix a potential crash due to using an uninitialized variable. + - OpenSL: Enable runtime linking. + - OpenSL: Fix a multithreading bug when initializing and uninitializing multiple contexts at the same time. + - iOS: Improvements to device enumeration. + - Fix a crash in ma_data_source_read_pcm_frames() when the output frame count parameter is NULL. + - Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work. + - Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled. + - Fix some compilation warnings when no decoders are enabled. + - Add ma_audio_buffer_get_available_frames(). + - Add ma_decoder_get_available_frames(). + - Add sample rate to ma_data_source_get_data_format(). + - Change volume APIs to take 64-bit frame counts. + - Updates to documentation. + +v0.10.15 - 2020-07-15 + - Fix a bug when converting bit-masked channel maps to miniaudio channel maps. This affects the WASAPI and OpenSL backends. + v0.10.14 - 2020-07-14 - Fix compilation errors on Android. - Fix compilation errors with -march=armv6.