diff --git a/CHANGES.md b/CHANGES.md index 011b85e9..1c684f14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,13 +1,14 @@ -v0.11.12 - TBD +v0.11.14 - 2023-03-29 ===================== * Fix some pedantic warnings when compiling with GCC. * Fix some crashes with the WAV decoder when loading an invalid file. +* Fix a channel mapping error with PipeWire which results in no audio being output. * Add support for using `ma_pcm_rb` as a data source. * Silence some C89 compatibility warnings with Clang. * The `pBytesRead` parameter of the VFS onRead callback is now pre-initialized to zero. -v0.11.12 - 2023-03-23 +v0.11.13 - 2023-03-23 ===================== * Fix compilation errors with the C++ build. * Fix compilation errors when WIN32_LEAN_AND_MEAN is defined. diff --git a/extras/miniaudio_split/miniaudio.c b/extras/miniaudio_split/miniaudio.c index 2775cc64..e8b190ed 100644 --- a/extras/miniaudio_split/miniaudio.c +++ b/extras/miniaudio_split/miniaudio.c @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.13 - 2023-03-23 +miniaudio - v0.11.14 - 2023-03-29 David Reid - mackron@gmail.com @@ -2596,6 +2596,13 @@ Atomics #if defined(__cplusplus) extern "C" { #endif +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #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 char c89atomic_int8; typedef unsigned char c89atomic_uint8; typedef signed short c89atomic_int16; @@ -2606,18 +2613,8 @@ typedef unsigned int c89atomic_uint32; typedef signed __int64 c89atomic_int64; typedef unsigned __int64 c89atomic_uint64; #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #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(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif #endif typedef int c89atomic_memory_order; typedef unsigned char c89atomic_bool; @@ -3982,7 +3979,7 @@ typedef unsigned char c89atomic_bool; #endif #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) #if defined(C89ATOMIC_HAS_8) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint8 expectedValue; c89atomic_uint8 result; @@ -3999,7 +3996,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_16) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint16 expectedValue; c89atomic_uint16 result; @@ -4016,7 +4013,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_32) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint32 expectedValue; c89atomic_uint32 result; @@ -4033,7 +4030,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_64) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint64 expectedValue; c89atomic_uint64 result; @@ -4511,6 +4508,9 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo { c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); } +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif #if defined(__cplusplus) } #endif @@ -4586,7 +4586,10 @@ MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) + +#if !defined(MA_NO_DEVICE_IO) MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) +#endif MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) @@ -4719,23 +4722,28 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ pAttr = &attr; - if (priority == ma_thread_priority_idle) { -#ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; + /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ + #if !defined(MA_BEOS) + { + if (priority == ma_thread_priority_idle) { + #ifdef SCHED_IDLE + if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } + #endif + } else if (priority == ma_thread_priority_realtime) { + #ifdef SCHED_FIFO + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } + #endif + #ifdef MA_LINUX + } else { + scheduler = sched_getscheduler(0); + #endif } -#endif - } else if (priority == ma_thread_priority_realtime) { -#ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } -#endif -#ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); -#endif } + #endif if (stackSize > 0) { pthread_attr_setstacksize(&attr, stackSize); @@ -5129,6 +5137,12 @@ static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priorit return MA_OUT_OF_MEMORY; } +#if defined(MA_THREAD_DEFAULT_STACK_SIZE) + if (stackSize == 0) { + stackSize = MA_THREAD_DEFAULT_STACK_SIZE; + } +#endif + pProxyData->entryProc = entryProc; pProxyData->pData = pData; ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); @@ -18819,7 +18833,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_pa_channel_map cmap; ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_channel_map* pActualCMap = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; @@ -18875,6 +18888,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorCapture->channels != 0) { + ss.channels = pDescriptorCapture->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; @@ -18969,11 +18990,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -19016,6 +19032,15 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorPlayback->channels != 0) { + ss.channels = pDescriptorPlayback->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + /* Use the requested sample rate if one was specified. */ if (pDescriptorPlayback->sampleRate != 0) { ss.rate = pDescriptorPlayback->sampleRate; @@ -19114,11 +19139,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -39001,6 +39021,26 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, /* Clamp the gain. */ gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + /* + The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel + gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions + to avoid harsh changes in gain. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the @@ -39022,19 +39062,6 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized position of the sound. */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } /* Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's @@ -45058,6 +45085,85 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB +static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + ma_result result; + ma_uint64 totalFramesRead; + + MA_ASSERT(pRB != NULL); + + /* We need to run this in a loop since the ring buffer itself may loop. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + void* pMappedBuffer; + ma_uint32 mappedFrameCount; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + mappedFrameCount = (ma_uint32)framesToRead; + result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (mappedFrameCount == 0) { + break; /* <-- End of ring buffer. */ + } + + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); + + result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + totalFramesRead += mappedFrameCount; + } + + *pFramesRead = totalFramesRead; + return MA_SUCCESS; +} + +static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + MA_ASSERT(pRB != NULL); + + if (pFormat != NULL) { + *pFormat = pRB->format; + } + + if (pChannels != NULL) { + *pChannels = pRB->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pRB->sampleRate; + } + + /* Just assume the default channel map. */ + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); + } + + return MA_SUCCESS; +} + +static ma_data_source_vtable ma_gRBDataSourceVTable = +{ + ma_pcm_rb_data_source__on_read, + NULL, /* onSeek */ + ma_pcm_rb_data_source__on_get_data_format, + NULL, /* onGetCursor */ + NULL, /* onGetLength */ + NULL, /* onSetLooping */ + 0 +}; + static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) { MA_ASSERT(pRB != NULL); @@ -45086,8 +45192,21 @@ MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint return result; } - pRB->format = format; - pRB->channels = channels; + pRB->format = format; + pRB->channels = channels; + pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ + + /* The PCM ring buffer is a data source. We need to get that set up as well. */ + { + ma_data_source_config dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_gRBDataSourceVTable; + + result = ma_data_source_init(&dataSourceConfig, &pRB->ds); + if (result != MA_SUCCESS) { + ma_rb_uninit(&pRB->rb); + return result; + } + } return MA_SUCCESS; } @@ -45103,6 +45222,7 @@ MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) return; } + ma_data_source_uninit(&pRB->ds); ma_rb_uninit(&pRB->rb); } @@ -45254,6 +45374,42 @@ MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferInde return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); } +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return ma_format_unknown; + } + + return pRB->format; +} + +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->channels; +} + +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->sampleRate; +} + +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) +{ + if (pRB == NULL) { + return; + } + + pRB->sampleRate = sampleRate; +} + MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) @@ -47201,7 +47357,7 @@ MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; ma_result result; - size_t bytesRead; + size_t bytesRead = 0; if (pBytesRead != NULL) { *pBytesRead = 0; @@ -63156,16 +63312,36 @@ MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) return ma_node_graph_get_endpoint(&pEngine->nodeGraph); } -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) { return ma_node_graph_get_time(&pEngine->nodeGraph); } -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); +} + +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) { return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); } +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); +} + MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) { return ma_node_graph_get_channels(&pEngine->nodeGraph); @@ -64386,7 +64562,7 @@ MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) return MA_FALSE; } - return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; + return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; } MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) @@ -65593,10 +65769,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars { drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); DRWAV_ASSERT(pChunkHeader != NULL); - if (bytesJustRead == sizeof(smplHeaderData)) { + if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; pMetadata->type = drwav_metadata_type_smpl; pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); @@ -65637,7 +65817,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse { drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { pMetadata->type = drwav_metadata_type_cue; @@ -65672,7 +65856,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { pMetadata->type = drwav_metadata_type_inst; @@ -65689,7 +65877,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_pars DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { pMetadata->type = drwav_metadata_type_acid; diff --git a/extras/miniaudio_split/miniaudio.h b/extras/miniaudio_split/miniaudio.h index 247f4496..da520bfa 100644 --- a/extras/miniaudio_split/miniaudio.h +++ b/extras/miniaudio_split/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.13 - 2023-03-23 +miniaudio - v0.11.14 - 2023-03-29 David Reid - mackron@gmail.com @@ -20,7 +20,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 13 +#define MA_VERSION_REVISION 14 #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__) @@ -100,7 +100,18 @@ typedef double ma_double; typedef void* ma_handle; typedef void* ma_ptr; + +/* +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get +warning C4191 about "type cast between incompatible function types". To work around this I'm going +to use a different data type depending on the compiler. +*/ +#if defined(__GNUC__) +typedef void (*ma_proc)(void); +#else typedef void* ma_proc; +#endif #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef ma_uint16 wchar_t; @@ -150,31 +161,39 @@ typedef ma_uint16 wchar_t; typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; #endif - #ifdef __unix__ + #if defined(__unix__) #define MA_UNIX - #ifdef __ORBIS__ - #define MA_ORBIS - #elif defined(__PROSPERO__) - #define MA_PROSPERO - #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif #endif - #ifdef __linux__ + #if defined(__linux__) #define MA_LINUX #endif - #ifdef __APPLE__ + #if defined(__APPLE__) #define MA_APPLE #endif - #ifdef __ANDROID__ + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #if defined(__ANDROID__) #define MA_ANDROID #endif - #ifdef __EMSCRIPTEN__ + #if defined(__EMSCRIPTEN__) #define MA_EMSCRIPTEN #endif + #if defined(__ORBIS__) + #define MA_ORBIS + #endif + #if defined(__PROSPERO__) + #define MA_PROSPERO + #endif #if defined(__NX__) #define MA_NX #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define MA_BEOS + #endif + #if defined(__HAIKU__) + #define MA_HAIKU + #endif #endif #if defined(__has_c_attribute) @@ -2022,6 +2041,197 @@ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_forma MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); +/************************************************************************************************************************************************************ + +Data Source + +************************************************************************************************************************************************************/ +typedef void ma_data_source; + +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + +typedef struct +{ + ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; + +typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); + +typedef struct +{ + const ma_data_source_vtable* vtable; +} ma_data_source_config; + +MA_API ma_data_source_config ma_data_source_config_init(void); + + +typedef struct +{ + const ma_data_source_vtable* vtable; + ma_uint64 rangeBegInFrames; + ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ + ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ + ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ + ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; +} ma_data_source_base; + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); +MA_API void ma_data_source_uninit(ma_data_source* pDataSource); +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* 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); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ +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_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +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. */ +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); + + +typedef struct +{ + ma_data_source_base ds; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; + ma_uint64 sizeInFrames; + const void* pData; +} ma_audio_buffer_ref; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); + + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 sizeInFrames; + const void* pData; /* If set to NULL, will allocate a block of memory for you. */ + ma_allocation_callbacks allocationCallbacks; +} ma_audio_buffer_config; + +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); + +typedef struct +{ + ma_audio_buffer_ref ref; + ma_allocation_callbacks allocationCallbacks; + ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ + ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ +} ma_audio_buffer; + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); +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_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); + + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + + /************************************************************************************************************************************************************ Ring Buffer @@ -2061,9 +2271,11 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB typedef struct { + ma_data_source_base ds; ma_rb rb; ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ } ma_pcm_rb; MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); @@ -2083,6 +2295,10 @@ MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); /* @@ -5843,195 +6059,6 @@ This will run on an optimized path when the volume is equal to 1. MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); -/************************************************************************************************** - -Data Source - -**************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* 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); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -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_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -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. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -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); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -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_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - /************************************************************************************************************************************************************ @@ -7243,13 +7270,17 @@ typedef struct ma_sound ma_sound; /* Sound flags. */ typedef enum { + /* Resource manager flags. */ MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ + MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + + /* ma_sound specific flags. */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ } ma_sound_flags; #ifndef MA_ENGINE_MAX_LISTENERS @@ -7439,8 +7470,12 @@ MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); diff --git a/miniaudio.h b/miniaudio.h index f7e9d98c..facf124c 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.13 - 2023-03-23 +miniaudio - v0.11.14 - 2023-03-29 David Reid - mackron@gmail.com @@ -3719,7 +3719,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 13 +#define MA_VERSION_REVISION 14 #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__) @@ -56429,7 +56429,6 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB -/* Add stubs for each of the functions in ma_data_source_vtable. Named ma_pcm_rb_data_source__on_read, etc. */ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */