From d3d4d425f12a7b367765d8b21ac5e540c5f41bfb Mon Sep 17 00:00:00 2001 From: David Reid Date: Mon, 27 Dec 2021 21:37:46 +1000 Subject: [PATCH] Version 0.11.1 --- extras/miniaudio_split/miniaudio.c | 557 +++++++++++++++++++-------- extras/miniaudio_split/miniaudio.h | 589 ++++++++++++++++++----------- miniaudio.h | 13 +- 3 files changed, 780 insertions(+), 379 deletions(-) diff --git a/extras/miniaudio_split/miniaudio.c b/extras/miniaudio_split/miniaudio.c index c0391159..f48012e6 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.0 - 2021-12-18 +miniaudio - v0.11.1 - 2021-12-27 David Reid - mackron@gmail.com @@ -34,6 +34,7 @@ GitHub: https://github.com/mackron/miniaudio #include /* For memset() */ #include #include /* select() (used for ma_sleep()). */ +#include #endif #include /* For fstat(), etc. */ @@ -4536,7 +4537,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority (void)stackSize; #endif - result = pthread_create(pThread, pAttr, entryProc, pData); + result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); /* The thread attributes object is no longer required. */ if (pAttr != NULL) { @@ -4552,8 +4553,8 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority static void ma_thread_wait__posix(ma_thread* pThread) { - pthread_join(*pThread, NULL); - pthread_detach(*pThread); + pthread_join((pthread_t)*pThread, NULL); + pthread_detach((pthread_t)*pThread); } @@ -4587,14 +4588,14 @@ static ma_result ma_event_init__posix(ma_event* pEvent) { int result; - result = pthread_mutex_init(&pEvent->lock, NULL); + result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); if (result != 0) { return ma_result_from_errno(result); } - result = pthread_cond_init(&pEvent->cond, NULL); + result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); if (result != 0) { - pthread_mutex_destroy(&pEvent->lock); + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); return ma_result_from_errno(result); } @@ -4604,32 +4605,32 @@ static ma_result ma_event_init__posix(ma_event* pEvent) static void ma_event_uninit__posix(ma_event* pEvent) { - pthread_cond_destroy(&pEvent->cond); - pthread_mutex_destroy(&pEvent->lock); + pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); } static ma_result ma_event_wait__posix(ma_event* pEvent) { - pthread_mutex_lock(&pEvent->lock); + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); { while (pEvent->value == 0) { - pthread_cond_wait(&pEvent->cond, &pEvent->lock); + pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); } pEvent->value = 0; /* Auto-reset. */ } - pthread_mutex_unlock(&pEvent->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); return MA_SUCCESS; } static ma_result ma_event_signal__posix(ma_event* pEvent) { - pthread_mutex_lock(&pEvent->lock); + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); { pEvent->value = 1; - pthread_cond_signal(&pEvent->cond); + pthread_cond_signal((pthread_cond_t*)&pEvent->cond); } - pthread_mutex_unlock(&pEvent->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); return MA_SUCCESS; } @@ -4645,14 +4646,14 @@ static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemap pSemaphore->value = initialValue; - result = pthread_mutex_init(&pSemaphore->lock, NULL); + result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); if (result != 0) { return ma_result_from_errno(result); /* Failed to create mutex. */ } - result = pthread_cond_init(&pSemaphore->cond, NULL); + result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); if (result != 0) { - pthread_mutex_destroy(&pSemaphore->lock); + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); return ma_result_from_errno(result); /* Failed to create condition variable. */ } @@ -4665,8 +4666,8 @@ static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) return; } - pthread_cond_destroy(&pSemaphore->cond); - pthread_mutex_destroy(&pSemaphore->lock); + pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); } static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) @@ -4675,16 +4676,16 @@ static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } - pthread_mutex_lock(&pSemaphore->lock); + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); { /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ while (pSemaphore->value == 0) { - pthread_cond_wait(&pSemaphore->cond, &pSemaphore->lock); + pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); } pSemaphore->value -= 1; } - pthread_mutex_unlock(&pSemaphore->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); return MA_SUCCESS; } @@ -4695,12 +4696,12 @@ static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } - pthread_mutex_lock(&pSemaphore->lock); + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); { pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); + pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); } - pthread_mutex_unlock(&pSemaphore->lock); + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); return MA_SUCCESS; } @@ -4745,7 +4746,7 @@ static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priorit ma_thread_proxy_data* pProxyData; if (pThread == NULL || entryProc == NULL) { - return MA_FALSE; + return MA_INVALID_ARGS; } pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ @@ -24370,8 +24371,9 @@ static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_conf ma_result result; ma_AAudioStreamBuilder* pBuilder; - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pConfig->deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDescriptor != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ *ppStream = NULL; @@ -24586,12 +24588,6 @@ static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_conf return MA_DEVICE_TYPE_NOT_SUPPORTED; } - /* No exclusive mode with AAudio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); if (result != MA_SUCCESS) { @@ -24731,6 +24727,36 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) return MA_SUCCESS; } +static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + ma_AAudioStream* pStream = NULL; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(type != ma_device_type_duplex); + MA_ASSERT(pDeviceInfo != NULL); + + if (type == ma_device_type_playback) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; + pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + if (type == ma_device_type_capture) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; + pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + + /* Safety. Should never happen. */ + if (pStream == NULL) { + return MA_INVALID_OPERATION; + } + + pDeviceInfo->nativeDataFormatCount = 0; + ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); + + return MA_SUCCESS; +} + static ma_result ma_context_uninit__aaudio(ma_context* pContext) { @@ -24802,6 +24828,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; (void)pConfig; return MA_SUCCESS; @@ -25132,6 +25159,7 @@ return_default_device:; if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } @@ -25140,6 +25168,7 @@ return_default_device:; if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } @@ -25238,10 +25267,12 @@ return_default_device: } } - /* Name / Description */ + /* ID and Name / Description */ if (deviceType == ma_device_type_playback) { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } @@ -25407,8 +25438,8 @@ static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 ch #endif pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate) * 1000; /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8; + ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ + pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; @@ -25535,7 +25566,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = (pDescriptorCapture->pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pDescriptorCapture->pDeviceID->opensl; + locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ locatorDevice.device = NULL; source.pLocator = &locatorDevice; @@ -25547,14 +25578,14 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf sink.pFormat = (SLDataFormat_PCM*)&pcm; resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) { + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = 1; ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ pcm.bitsPerSample = 16; pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + pcm.channelMask = 0; resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); } @@ -25670,7 +25701,7 @@ static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_conf sink.pFormat = NULL; resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) { + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = 2; @@ -27783,7 +27814,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_device_info deviceInfo; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_context_get_device_info(pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, descriptorCapture.pDeviceID, &deviceInfo); + result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); } else { @@ -27797,7 +27828,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_device_info(pContext, ma_device_type_playback, descriptorPlayback.pDeviceID, &deviceInfo); + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); } else { @@ -27845,34 +27876,44 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_device__set_state(pDevice, ma_device_state_stopped); } + /* Restricting this to debug output because it's a bit spamy and only really needed for debugging. */ + #if defined(MA_DEBUG_OUTPUT) + { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", pDevice->capture.name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", pDevice->playback.name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " %s (%s)\n", name, "Capture"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " %s (%s)\n", name, "Playback"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + } } + #endif MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); return MA_SUCCESS; @@ -28012,6 +28053,70 @@ MA_API ma_log* ma_device_get_log(ma_device* pDevice) return ma_context_get_log(ma_device_get_context(pDevice)); } +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + if (pDeviceInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDeviceInfo); + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ + if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { + return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); + } + + /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ + if (type == ma_device_type_playback) { + return ma_context_get_device_info(pDevice->pContext, type, &pDevice->playback.id, pDeviceInfo); + } else { + return ma_context_get_device_info(pDevice->pContext, type, &pDevice->capture.id, pDeviceInfo); + } +} + +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) +{ + ma_result result; + ma_device_info deviceInfo; + + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = 0; + } + + if (pName != NULL && nameCap > 0) { + pName[0] = '\0'; + } + + result = ma_device_get_info(pDevice, type, &deviceInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pName != NULL) { + ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); + + /* + For safety, make sure the length is based on the truncated output string rather than the + source. Otherwise the caller might assume the output buffer contains more content than it + actually does. + */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(pName); + } + } else { + /* Name not specified. Just report the length of the source string. */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); + } + } + + return MA_SUCCESS; +} + MA_API ma_result ma_device_start(ma_device* pDevice) { ma_result result; @@ -35900,22 +36005,23 @@ MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma ma_spatializer_config config; MA_ZERO_OBJECT(&config); - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = NULL; - config.attenuationModel = ma_attenuation_model_inverse; - config.positioning = ma_positioning_absolute; - config.handedness = ma_handedness_right; - config.minGain = 0; - config.maxGain = 1; - config.minDistance = 1; - config.maxDistance = MA_FLT_MAX; - config.rolloff = 1; - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ - config.coneOuterGain = 0.0f; - config.dopplerFactor = 1; - config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = NULL; + config.attenuationModel = ma_attenuation_model_inverse; + config.positioning = ma_positioning_absolute; + config.handedness = ma_handedness_right; + config.minGain = 0; + config.maxGain = 1; + config.minDistance = 1; + config.maxDistance = MA_FLT_MAX; + config.rolloff = 1; + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ + config.coneOuterGain = 0.0f; + config.dopplerFactor = 1; + config.directionalAttenuationFactor = 1; + config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ return config; } @@ -36227,64 +36333,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, sound's position and direction so that it's relative to listener. Later on we'll use this for determining the factors to apply to each channel to apply the panning effect. */ - ma_vec3f v; - ma_vec3f axisX; - ma_vec3f axisY; - ma_vec3f axisZ; - float m[4][4]; - - /* - We need to calcualte the right vector from our forward and up vectors. This is done with - a cross product. - */ - axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */ - axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ - - /* - The calculation of axisX above can result in a zero-length vector if the listener is - looking straight up on the Y axis. We'll need to fall back to a +X in this case so that - the calculations below don't fall apart. This is where a quaternion based listener and - sound orientation would come in handy. - */ - if (ma_vec3f_len2(axisX) == 0) { - axisX = ma_vec3f_init_3f(1, 0, 0); - } - - axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ - - /* - We need to swap the X axis if we're left handed because otherwise the cross product above - will have resulted in it pointing in the wrong direction (right handed was assumed in the - cross products above). - */ - if (pListener->config.handedness == ma_handedness_left) { - axisX = ma_vec3f_neg(axisX); - } - - /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position); - m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; - - /* - Multiply the lookat matrix by the spatializer position to transform it to listener - space. This allows calculations to work based on the sound being relative to the - origin which makes things simpler. - */ - v = pSpatializer->position; - relativePos.x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; - relativePos.y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; - relativePos.z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; - - /* - The direction of the sound needs to also be transformed so that it's relative to the - rotation of the listener. - */ - v = pSpatializer->direction; - relativeDir.x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; - relativeDir.y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; - relativeDir.z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; + ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); } distance = ma_vec3f_len(relativePos); @@ -36421,7 +36470,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); if (ma_is_spatial_channel_position(channelOut)) { - d = ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)); + d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), pSpatializer->config.directionalAttenuationFactor); } else { d = 1; /* It's not a spatial channel so there's no real notion of direction. */ } @@ -36702,6 +36751,24 @@ MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatialize return pSpatializer->config.dopplerFactor; } +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) +{ + if (pSpatializer == NULL) { + return; + } + + pSpatializer->config.directionalAttenuationFactor = directionalAttenuationFactor; +} + +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return pSpatializer->config.directionalAttenuationFactor; +} + MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) { if (pSpatializer == NULL) { @@ -36756,6 +36823,98 @@ MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) return pSpatializer->velocity; } +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) +{ + if (pRelativePos != NULL) { + pRelativePos->x = 0; + pRelativePos->y = 0; + pRelativePos->z = 0; + } + + if (pRelativeDir != NULL) { + pRelativeDir->x = 0; + pRelativeDir->y = 0; + pRelativeDir->z = -1; + } + + if (pSpatializer == NULL) { + return; + } + + if (pListener == NULL || pSpatializer->config.positioning == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + if (pRelativePos != NULL) { + *pRelativePos = pSpatializer->position; + } + if (pRelativeDir != NULL) { + *pRelativeDir = pSpatializer->direction; + } + } else { + ma_vec3f v; + ma_vec3f axisX; + ma_vec3f axisY; + ma_vec3f axisZ; + float m[4][4]; + + /* + We need to calcualte the right vector from our forward and up vectors. This is done with + a cross product. + */ + axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */ + axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ + + /* + The calculation of axisX above can result in a zero-length vector if the listener is + looking straight up on the Y axis. We'll need to fall back to a +X in this case so that + the calculations below don't fall apart. This is where a quaternion based listener and + sound orientation would come in handy. + */ + if (ma_vec3f_len2(axisX) == 0) { + axisX = ma_vec3f_init_3f(1, 0, 0); + } + + axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ + + /* + We need to swap the X axis if we're left handed because otherwise the cross product above + will have resulted in it pointing in the wrong direction (right handed was assumed in the + cross products above). + */ + if (pListener->config.handedness == ma_handedness_left) { + axisX = ma_vec3f_neg(axisX); + } + + /* Lookat. */ + m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position); + m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position); + m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position); + m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; + + /* + Multiply the lookat matrix by the spatializer position to transform it to listener + space. This allows calculations to work based on the sound being relative to the + origin which makes things simpler. + */ + if (pRelativePos != NULL) { + v = pSpatializer->position; + pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; + pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; + pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; + } + + /* + The direction of the sound needs to also be transformed so that it's relative to the + rotation of the listener. + */ + if (pRelativeDir != NULL) { + v = pSpatializer->direction; + pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; + pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; + pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; + } + } +} + @@ -45339,7 +45498,7 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 32 +#define DRFLAC_VERSION_REVISION 33 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; @@ -45365,7 +45524,7 @@ typedef unsigned int drflac_uint32; #pragma GCC diagnostic pop #endif #endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) typedef drflac_uint64 drflac_uintptr; #else typedef drflac_uint32 drflac_uintptr; @@ -52454,7 +52613,7 @@ static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_m { MA_ASSERT(pDataBufferNode != NULL); - return c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ + return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ } @@ -52510,7 +52669,7 @@ static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inl } else { while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_RESOURCE_MANAGER_JOB_QUIT) { + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { break; } } @@ -53590,7 +53749,7 @@ stage2: if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_RESOURCE_MANAGER_JOB_QUIT) { + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { result = MA_SUCCESS; break; } @@ -54147,7 +54306,7 @@ MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manage return MA_INVALID_ARGS; } - return c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ + return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ } MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) @@ -54964,7 +55123,7 @@ MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manage return MA_INVALID_ARGS; } - return c89atomic_load_i32(&pDataStream->result); + return (ma_result)c89atomic_load_i32(&pDataStream->result); } MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) @@ -56171,6 +56330,7 @@ MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 glo } +#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) { @@ -57619,6 +57779,8 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde */ if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } else { + frameCountOut = 0; /* No data was processed. */ } } @@ -59536,10 +59698,12 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng ma_spatializer_listener_config listenerConfig; ma_uint32 iListener; - if (pEngine != NULL) { - MA_ZERO_OBJECT(pEngine); + if (pEngine == NULL) { + return MA_INVALID_ARGS; } + MA_ZERO_OBJECT(pEngine); + /* The config is allowed to be NULL in which case we use defaults for everything. */ if (pConfig != NULL) { engineConfig = *pConfig; @@ -59668,7 +59832,13 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng #if !defined(MA_NO_DEVICE_IO) { if (pEngine->pDevice != NULL) { - listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap; + /* + Temporarily disabled. There is a subtle bug here where front-left and front-right + will be used by the device's channel map, but this is not what we want to use for + spatialization. Instead we want to use side-left and side-right. I need to figure + out a better solution for this. For now, disabling the user of device channel maps. + */ + /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ } } #endif @@ -60765,6 +60935,42 @@ MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); } +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) +{ + ma_uint32 listenerIndex; + + if (pSound == NULL) { + return 0; + } + + listenerIndex = ma_sound_get_pinned_listener_index(pSound); + if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { + ma_vec3f position = ma_sound_get_position(pSound); + return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); + } + + return listenerIndex; +} + +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) +{ + ma_vec3f relativePos; + ma_engine* pEngine; + + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + pEngine = ma_sound_get_engine(pSound); + if (pEngine == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); + + return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); +} + MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) { if (pSound == NULL) { @@ -60989,6 +61195,24 @@ MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); } +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); +} + +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 1; + } + + return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); +} + MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) { @@ -61328,6 +61552,16 @@ MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* return ma_sound_get_pinned_listener_index(pGroup); } +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_listener_index(pGroup); +} + +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction_to_listener(pGroup); +} + MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) { ma_sound_set_position(pGroup, x, y, z); @@ -61448,6 +61682,16 @@ MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) return ma_sound_get_doppler_factor(pGroup); } +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) +{ + ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); +} + +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_directional_attenuation_factor(pGroup); +} + MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) { ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); @@ -70158,6 +70402,9 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return DRFLAC_FALSE; + } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { break; diff --git a/extras/miniaudio_split/miniaudio.h b/extras/miniaudio_split/miniaudio.h index 8e087203..82905849 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.0 - 2021-12-18 +miniaudio - v0.11.1 - 2021-12-27 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 0 +#define MA_VERSION_REVISION 1 #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__) @@ -36,39 +36,9 @@ extern "C" { #endif #endif -/* Platform/backend detection. */ -#ifdef _WIN32 - #define MA_WIN32 - #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - #define MA_WIN32_UWP - #else - #define MA_WIN32_DESKTOP - #endif -#else - #define MA_POSIX - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - #ifdef __unix__ - #define MA_UNIX - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #endif - #ifdef __linux__ - #define MA_LINUX - #endif - #ifdef __APPLE__ - #define MA_APPLE - #endif - #ifdef __ANDROID__ - #define MA_ANDROID - #endif - #ifdef __EMSCRIPTEN__ - #define MA_EMSCRIPTEN - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) #define MA_SIZEOF_PTR 8 #else #define MA_SIZEOF_PTR 4 @@ -131,6 +101,56 @@ typedef ma_uint16 wchar_t; #endif +/* Platform/backend detection. */ +#ifdef _WIN32 + #define MA_WIN32 + #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + #define MA_WIN32_UWP + #else + #define MA_WIN32_DESKTOP + #endif +#else + #define MA_POSIX + + /* + Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. + You can use this to avoid including pthread.h in the header section. The downside is that it + results in some fixed sized structures being declared for the various types that are used in + miniaudio. The risk here is that these types might be too small for a given platform. This + risk is yours to take and no support will be offered if you enable this option. + */ + #ifndef MA_NO_PTHREAD_IN_HEADER + #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ + typedef pthread_t ma_pthread_t; + typedef pthread_mutex_t ma_pthread_mutex_t; + typedef pthread_cond_t ma_pthread_cond_t; + #else + typedef ma_uintptr ma_pthread_t; + typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; + typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; + #endif + + #ifdef __unix__ + #define MA_UNIX + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #endif + #ifdef __linux__ + #define MA_LINUX + #endif + #ifdef __APPLE__ + #define MA_APPLE + #endif + #ifdef __ANDROID__ + #define MA_ANDROID + #endif + #ifdef __EMSCRIPTEN__ + #define MA_EMSCRIPTEN + #endif +#endif + + #ifdef _MSC_VER #define MA_INLINE __forceinline #elif defined(__GNUC__) @@ -247,148 +267,152 @@ typedef struct ma_context ma_context; typedef struct ma_device ma_device; typedef ma_uint8 ma_channel; -#define MA_CHANNEL_NONE 0 -#define MA_CHANNEL_MONO 1 -#define MA_CHANNEL_FRONT_LEFT 2 -#define MA_CHANNEL_FRONT_RIGHT 3 -#define MA_CHANNEL_FRONT_CENTER 4 -#define MA_CHANNEL_LFE 5 -#define MA_CHANNEL_BACK_LEFT 6 -#define MA_CHANNEL_BACK_RIGHT 7 -#define MA_CHANNEL_FRONT_LEFT_CENTER 8 -#define MA_CHANNEL_FRONT_RIGHT_CENTER 9 -#define MA_CHANNEL_BACK_CENTER 10 -#define MA_CHANNEL_SIDE_LEFT 11 -#define MA_CHANNEL_SIDE_RIGHT 12 -#define MA_CHANNEL_TOP_CENTER 13 -#define MA_CHANNEL_TOP_FRONT_LEFT 14 -#define MA_CHANNEL_TOP_FRONT_CENTER 15 -#define MA_CHANNEL_TOP_FRONT_RIGHT 16 -#define MA_CHANNEL_TOP_BACK_LEFT 17 -#define MA_CHANNEL_TOP_BACK_CENTER 18 -#define MA_CHANNEL_TOP_BACK_RIGHT 19 -#define MA_CHANNEL_AUX_0 20 -#define MA_CHANNEL_AUX_1 21 -#define MA_CHANNEL_AUX_2 22 -#define MA_CHANNEL_AUX_3 23 -#define MA_CHANNEL_AUX_4 24 -#define MA_CHANNEL_AUX_5 25 -#define MA_CHANNEL_AUX_6 26 -#define MA_CHANNEL_AUX_7 27 -#define MA_CHANNEL_AUX_8 28 -#define MA_CHANNEL_AUX_9 29 -#define MA_CHANNEL_AUX_10 30 -#define MA_CHANNEL_AUX_11 31 -#define MA_CHANNEL_AUX_12 32 -#define MA_CHANNEL_AUX_13 33 -#define MA_CHANNEL_AUX_14 34 -#define MA_CHANNEL_AUX_15 35 -#define MA_CHANNEL_AUX_16 36 -#define MA_CHANNEL_AUX_17 37 -#define MA_CHANNEL_AUX_18 38 -#define MA_CHANNEL_AUX_19 39 -#define MA_CHANNEL_AUX_20 40 -#define MA_CHANNEL_AUX_21 41 -#define MA_CHANNEL_AUX_22 42 -#define MA_CHANNEL_AUX_23 43 -#define MA_CHANNEL_AUX_24 44 -#define MA_CHANNEL_AUX_25 45 -#define MA_CHANNEL_AUX_26 46 -#define MA_CHANNEL_AUX_27 47 -#define MA_CHANNEL_AUX_28 48 -#define MA_CHANNEL_AUX_29 49 -#define MA_CHANNEL_AUX_30 50 -#define MA_CHANNEL_AUX_31 51 -#define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT -#define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT -#define MA_CHANNEL_POSITION_COUNT (MA_CHANNEL_AUX_31 + 1) +typedef enum +{ + MA_CHANNEL_NONE = 0, + MA_CHANNEL_MONO = 1, + MA_CHANNEL_FRONT_LEFT = 2, + MA_CHANNEL_FRONT_RIGHT = 3, + MA_CHANNEL_FRONT_CENTER = 4, + MA_CHANNEL_LFE = 5, + MA_CHANNEL_BACK_LEFT = 6, + MA_CHANNEL_BACK_RIGHT = 7, + MA_CHANNEL_FRONT_LEFT_CENTER = 8, + MA_CHANNEL_FRONT_RIGHT_CENTER = 9, + MA_CHANNEL_BACK_CENTER = 10, + MA_CHANNEL_SIDE_LEFT = 11, + MA_CHANNEL_SIDE_RIGHT = 12, + MA_CHANNEL_TOP_CENTER = 13, + MA_CHANNEL_TOP_FRONT_LEFT = 14, + MA_CHANNEL_TOP_FRONT_CENTER = 15, + MA_CHANNEL_TOP_FRONT_RIGHT = 16, + MA_CHANNEL_TOP_BACK_LEFT = 17, + MA_CHANNEL_TOP_BACK_CENTER = 18, + MA_CHANNEL_TOP_BACK_RIGHT = 19, + MA_CHANNEL_AUX_0 = 20, + MA_CHANNEL_AUX_1 = 21, + MA_CHANNEL_AUX_2 = 22, + MA_CHANNEL_AUX_3 = 23, + MA_CHANNEL_AUX_4 = 24, + MA_CHANNEL_AUX_5 = 25, + MA_CHANNEL_AUX_6 = 26, + MA_CHANNEL_AUX_7 = 27, + MA_CHANNEL_AUX_8 = 28, + MA_CHANNEL_AUX_9 = 29, + MA_CHANNEL_AUX_10 = 30, + MA_CHANNEL_AUX_11 = 31, + MA_CHANNEL_AUX_12 = 32, + MA_CHANNEL_AUX_13 = 33, + MA_CHANNEL_AUX_14 = 34, + MA_CHANNEL_AUX_15 = 35, + MA_CHANNEL_AUX_16 = 36, + MA_CHANNEL_AUX_17 = 37, + MA_CHANNEL_AUX_18 = 38, + MA_CHANNEL_AUX_19 = 39, + MA_CHANNEL_AUX_20 = 40, + MA_CHANNEL_AUX_21 = 41, + MA_CHANNEL_AUX_22 = 42, + MA_CHANNEL_AUX_23 = 43, + MA_CHANNEL_AUX_24 = 44, + MA_CHANNEL_AUX_25 = 45, + MA_CHANNEL_AUX_26 = 46, + MA_CHANNEL_AUX_27 = 47, + MA_CHANNEL_AUX_28 = 48, + MA_CHANNEL_AUX_29 = 49, + MA_CHANNEL_AUX_30 = 50, + MA_CHANNEL_AUX_31 = 51, + MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, + MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, + MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) +} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ + +typedef enum +{ + MA_SUCCESS = 0, + MA_ERROR = -1, /* A generic error. */ + MA_INVALID_ARGS = -2, + MA_INVALID_OPERATION = -3, + MA_OUT_OF_MEMORY = -4, + MA_OUT_OF_RANGE = -5, + MA_ACCESS_DENIED = -6, + MA_DOES_NOT_EXIST = -7, + MA_ALREADY_EXISTS = -8, + MA_TOO_MANY_OPEN_FILES = -9, + MA_INVALID_FILE = -10, + MA_TOO_BIG = -11, + MA_PATH_TOO_LONG = -12, + MA_NAME_TOO_LONG = -13, + MA_NOT_DIRECTORY = -14, + MA_IS_DIRECTORY = -15, + MA_DIRECTORY_NOT_EMPTY = -16, + MA_AT_END = -17, + MA_NO_SPACE = -18, + MA_BUSY = -19, + MA_IO_ERROR = -20, + MA_INTERRUPT = -21, + MA_UNAVAILABLE = -22, + MA_ALREADY_IN_USE = -23, + MA_BAD_ADDRESS = -24, + MA_BAD_SEEK = -25, + MA_BAD_PIPE = -26, + MA_DEADLOCK = -27, + MA_TOO_MANY_LINKS = -28, + MA_NOT_IMPLEMENTED = -29, + MA_NO_MESSAGE = -30, + MA_BAD_MESSAGE = -31, + MA_NO_DATA_AVAILABLE = -32, + MA_INVALID_DATA = -33, + MA_TIMEOUT = -34, + MA_NO_NETWORK = -35, + MA_NOT_UNIQUE = -36, + MA_NOT_SOCKET = -37, + MA_NO_ADDRESS = -38, + MA_BAD_PROTOCOL = -39, + MA_PROTOCOL_UNAVAILABLE = -40, + MA_PROTOCOL_NOT_SUPPORTED = -41, + MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, + MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, + MA_SOCKET_NOT_SUPPORTED = -44, + MA_CONNECTION_RESET = -45, + MA_ALREADY_CONNECTED = -46, + MA_NOT_CONNECTED = -47, + MA_CONNECTION_REFUSED = -48, + MA_NO_HOST = -49, + MA_IN_PROGRESS = -50, + MA_CANCELLED = -51, + MA_MEMORY_ALREADY_MAPPED = -52, + + /* General miniaudio-specific errors. */ + MA_FORMAT_NOT_SUPPORTED = -100, + MA_DEVICE_TYPE_NOT_SUPPORTED = -101, + MA_SHARE_MODE_NOT_SUPPORTED = -102, + MA_NO_BACKEND = -103, + MA_NO_DEVICE = -104, + MA_API_NOT_FOUND = -105, + MA_INVALID_DEVICE_CONFIG = -106, + MA_LOOP = -107, + + /* State errors. */ + MA_DEVICE_NOT_INITIALIZED = -200, + MA_DEVICE_ALREADY_INITIALIZED = -201, + MA_DEVICE_NOT_STARTED = -202, + MA_DEVICE_NOT_STOPPED = -203, + + /* Operation errors. */ + MA_FAILED_TO_INIT_BACKEND = -300, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, + MA_FAILED_TO_START_BACKEND_DEVICE = -302, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 +} ma_result; -typedef int ma_result; -#define MA_SUCCESS 0 -#define MA_ERROR -1 /* A generic error. */ -#define MA_INVALID_ARGS -2 -#define MA_INVALID_OPERATION -3 -#define MA_OUT_OF_MEMORY -4 -#define MA_OUT_OF_RANGE -5 -#define MA_ACCESS_DENIED -6 -#define MA_DOES_NOT_EXIST -7 -#define MA_ALREADY_EXISTS -8 -#define MA_TOO_MANY_OPEN_FILES -9 -#define MA_INVALID_FILE -10 -#define MA_TOO_BIG -11 -#define MA_PATH_TOO_LONG -12 -#define MA_NAME_TOO_LONG -13 -#define MA_NOT_DIRECTORY -14 -#define MA_IS_DIRECTORY -15 -#define MA_DIRECTORY_NOT_EMPTY -16 -#define MA_AT_END -17 -#define MA_NO_SPACE -18 -#define MA_BUSY -19 -#define MA_IO_ERROR -20 -#define MA_INTERRUPT -21 -#define MA_UNAVAILABLE -22 -#define MA_ALREADY_IN_USE -23 -#define MA_BAD_ADDRESS -24 -#define MA_BAD_SEEK -25 -#define MA_BAD_PIPE -26 -#define MA_DEADLOCK -27 -#define MA_TOO_MANY_LINKS -28 -#define MA_NOT_IMPLEMENTED -29 -#define MA_NO_MESSAGE -30 -#define MA_BAD_MESSAGE -31 -#define MA_NO_DATA_AVAILABLE -32 -#define MA_INVALID_DATA -33 -#define MA_TIMEOUT -34 -#define MA_NO_NETWORK -35 -#define MA_NOT_UNIQUE -36 -#define MA_NOT_SOCKET -37 -#define MA_NO_ADDRESS -38 -#define MA_BAD_PROTOCOL -39 -#define MA_PROTOCOL_UNAVAILABLE -40 -#define MA_PROTOCOL_NOT_SUPPORTED -41 -#define MA_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define MA_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define MA_SOCKET_NOT_SUPPORTED -44 -#define MA_CONNECTION_RESET -45 -#define MA_ALREADY_CONNECTED -46 -#define MA_NOT_CONNECTED -47 -#define MA_CONNECTION_REFUSED -48 -#define MA_NO_HOST -49 -#define MA_IN_PROGRESS -50 -#define MA_CANCELLED -51 -#define MA_MEMORY_ALREADY_MAPPED -52 - -/* General miniaudio-specific errors. */ -#define MA_FORMAT_NOT_SUPPORTED -100 -#define MA_DEVICE_TYPE_NOT_SUPPORTED -101 -#define MA_SHARE_MODE_NOT_SUPPORTED -102 -#define MA_NO_BACKEND -103 -#define MA_NO_DEVICE -104 -#define MA_API_NOT_FOUND -105 -#define MA_INVALID_DEVICE_CONFIG -106 -#define MA_LOOP -107 - -/* State errors. */ -#define MA_DEVICE_NOT_INITIALIZED -200 -#define MA_DEVICE_ALREADY_INITIALIZED -201 -#define MA_DEVICE_NOT_STARTED -202 -#define MA_DEVICE_NOT_STOPPED -203 - -/* Operation errors. */ -#define MA_FAILED_TO_INIT_BACKEND -300 -#define MA_FAILED_TO_OPEN_BACKEND_DEVICE -301 -#define MA_FAILED_TO_START_BACKEND_DEVICE -302 -#define MA_FAILED_TO_STOP_BACKEND_DEVICE -303 - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 254 +#define MA_MIN_CHANNELS 1 +#ifndef MA_MAX_CHANNELS +#define MA_MAX_CHANNELS 254 #endif #ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 +#define MA_MAX_FILTER_ORDER 8 #endif typedef enum @@ -515,14 +539,14 @@ typedef enum typedef ma_handle ma_thread; #endif #if defined(MA_POSIX) -typedef pthread_t ma_thread; +typedef ma_pthread_t ma_thread; #endif #if defined(MA_WIN32) typedef ma_handle ma_mutex; #endif #if defined(MA_POSIX) -typedef pthread_mutex_t ma_mutex; +typedef ma_pthread_mutex_t ma_mutex; #endif #if defined(MA_WIN32) @@ -532,8 +556,8 @@ typedef ma_handle ma_event; typedef struct { ma_uint32 value; - pthread_mutex_t lock; - pthread_cond_t cond; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; } ma_event; #endif /* MA_POSIX */ @@ -544,8 +568,8 @@ typedef ma_handle ma_semaphore; typedef struct { int value; - pthread_mutex_t lock; - pthread_cond_t cond; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; } ma_semaphore; #endif /* MA_POSIX */ #else @@ -1280,7 +1304,8 @@ typedef struct float coneInnerAngleInRadians; float coneOuterAngleInRadians; float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. This will run on a fast path. */ + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ } ma_spatializer_config; @@ -1327,12 +1352,15 @@ MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAng MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); @@ -2320,11 +2348,15 @@ typedef struct ma_backend_callbacks ma_backend_callbacks; #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ +#ifndef MA_MAX_DEVICE_NAME_LENGTH +#define MA_MAX_DEVICE_NAME_LENGTH 255 +#endif + typedef struct { /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ ma_device_id id; - char name[256]; + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ ma_bool32 isDefault; ma_uint32 nativeDataFormatCount; @@ -2469,7 +2501,7 @@ callbacks defined in this structure. Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function return with a success code. +needs to stop and the `onContextEnumerateDevices()` function returns with a success code. Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the @@ -2511,6 +2543,9 @@ callback. When the device is stopped, the `ma_device_get_state() == ma_device_st which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to wake up the audio thread. + +If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the +`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. */ struct ma_backend_callbacks { @@ -2526,6 +2561,7 @@ struct ma_backend_callbacks ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); ma_result (* onDeviceDataLoop)(ma_device* pDevice); ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); + ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); }; struct ma_context_config @@ -3029,7 +3065,7 @@ struct ma_device 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. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* 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_format format; ma_uint32 channels; @@ -3050,7 +3086,7 @@ struct ma_device 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. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* 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_format format; ma_uint32 channels; @@ -4216,6 +4252,95 @@ Helper function for retrieving the log object associated with the context that o MA_API ma_log* ma_device_get_log(ma_device* pDevice); +/* +Retrieves information about the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pDeviceInfo (out) + A pointer to the `ma_device_info` that will receive the device information. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. +*/ +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); + + +/* +Retrieves the name of the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pName (out) + A pointer to the buffer that will receive the name. + +nameCap (in) + The capacity of the output buffer, including space for the null terminator. + +pLengthNotIncludingNullTerminator (out, optional) + A pointer to the variable that will receive the length of the name, not including the null + terminator. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. + + +Remarks +------- +If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to +`pName` if you want to first get the length of the name for the purpose of memory allocation of the +output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for +most cases and will avoid the need for the inefficiency of calling this function twice. + +This is implemented in terms of `ma_device_get_info()`. +*/ +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); + + /* Starts the device. For playback devices this begins playback. For capture devices it begins recording. @@ -5269,8 +5394,11 @@ appropriate for a given situation. typedef void ma_vfs; typedef ma_handle ma_vfs_file; -#define MA_OPEN_MODE_READ 0x00000001 -#define MA_OPEN_MODE_WRITE 0x00000002 +typedef enum +{ + MA_OPEN_MODE_READ = 0x00000001, + MA_OPEN_MODE_WRITE = 0x00000002 +} ma_open_mode_flags; typedef enum { @@ -5666,22 +5794,28 @@ typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buf typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; -#define MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM 0x00000001 /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ -#define MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE 0x00000002 /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ -#define MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC 0x00000004 /* When set, the resource manager will load the data source asynchronously. */ -#define MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT 0x00000008 /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ +typedef enum +{ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008 /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ +} ma_resource_manager_data_source_flags; -#define MA_RESOURCE_MANAGER_JOB_QUIT 0x00000000 -#define MA_RESOURCE_MANAGER_JOB_LOAD_DATA_BUFFER_NODE 0x00000001 -#define MA_RESOURCE_MANAGER_JOB_FREE_DATA_BUFFER_NODE 0x00000002 -#define MA_RESOURCE_MANAGER_JOB_PAGE_DATA_BUFFER_NODE 0x00000003 -#define MA_RESOURCE_MANAGER_JOB_LOAD_DATA_BUFFER 0x00000004 -#define MA_RESOURCE_MANAGER_JOB_FREE_DATA_BUFFER 0x00000005 -#define MA_RESOURCE_MANAGER_JOB_LOAD_DATA_STREAM 0x00000006 -#define MA_RESOURCE_MANAGER_JOB_FREE_DATA_STREAM 0x00000007 -#define MA_RESOURCE_MANAGER_JOB_PAGE_DATA_STREAM 0x00000008 -#define MA_RESOURCE_MANAGER_JOB_SEEK_DATA_STREAM 0x00000009 -#define MA_RESOURCE_MANAGER_JOB_CUSTOM 0x00000100 /* Number your custom job codes as (MA_RESOURCE_MANAGER_JOB_CUSTOM + 0), (MA_RESOURCE_MANAGER_JOB_CUSTOM + 1), etc. */ +typedef enum +{ + MA_RESOURCE_MANAGER_JOB_QUIT = 0x00000000, + MA_RESOURCE_MANAGER_JOB_LOAD_DATA_BUFFER_NODE = 0x00000001, + MA_RESOURCE_MANAGER_JOB_FREE_DATA_BUFFER_NODE = 0x00000002, + MA_RESOURCE_MANAGER_JOB_PAGE_DATA_BUFFER_NODE = 0x00000003, + MA_RESOURCE_MANAGER_JOB_LOAD_DATA_BUFFER = 0x00000004, + MA_RESOURCE_MANAGER_JOB_FREE_DATA_BUFFER = 0x00000005, + MA_RESOURCE_MANAGER_JOB_LOAD_DATA_STREAM = 0x00000006, + MA_RESOURCE_MANAGER_JOB_FREE_DATA_STREAM = 0x00000007, + MA_RESOURCE_MANAGER_JOB_PAGE_DATA_STREAM = 0x00000008, + MA_RESOURCE_MANAGER_JOB_SEEK_DATA_STREAM = 0x00000009, + MA_RESOURCE_MANAGER_JOB_CUSTOM = 0x00000100 /* Number your custom job codes as (MA_RESOURCE_MANAGER_JOB_CUSTOM + 0), (MA_RESOURCE_MANAGER_JOB_CUSTOM + 1), etc. */ +} ma_resource_manager_job_type; /* @@ -5804,7 +5938,10 @@ ma_resource_manager_job_queue_post(). ma_resource_manager_job_queue_next() will This flag should always be used for platforms that do not support multithreading. */ -#define MA_RESOURCE_MANAGER_JOB_QUEUE_FLAG_NON_BLOCKING 0x00000001 +typedef enum +{ + MA_RESOURCE_MANAGER_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 +} ma_resource_manager_job_queue_flags; typedef struct { @@ -5848,12 +5985,14 @@ MA_API ma_result ma_resource_manager_job_queue_next(ma_resource_manager_job_queu #define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 #endif -/* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ -#define MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING 0x00000001 - -/* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ -#define MA_RESOURCE_MANAGER_FLAG_NO_THREADING 0x00000002 +typedef enum +{ + /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ + MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, + /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ + MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 +} ma_resource_manager_flags; typedef struct { @@ -6113,10 +6252,13 @@ typedef void ma_node; /* Node flags. */ -#define MA_NODE_FLAG_PASSTHROUGH 0x00000001 -#define MA_NODE_FLAG_CONTINUOUS_PROCESSING 0x00000002 -#define MA_NODE_FLAG_ALLOW_NULL_INPUT 0x00000004 -#define MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES 0x00000008 +typedef enum +{ + MA_NODE_FLAG_PASSTHROUGH = 0x00000001, + MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, + MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008 +} ma_node_flags; /* The playback state of a node. Either started or stopped. */ @@ -6132,7 +6274,7 @@ typedef struct /* Extended processing callback. This callback is used for effects that process input and output at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two sepate frame counts: one for input, and one for output. + they take two seperate frame counts: one for input, and one for output. On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. @@ -6187,8 +6329,6 @@ MA_API ma_node_config ma_node_config_init(void); A node has multiple output buses. An output bus is attached to an input bus as an item in a linked list. Think of the input bus as a linked list, with the output bus being an item in that list. */ -#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ - typedef struct ma_node_output_bus ma_node_output_bus; struct ma_node_output_bus { @@ -6199,7 +6339,7 @@ struct ma_node_output_bus /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */ - MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. */ + MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ @@ -6569,13 +6709,16 @@ typedef struct ma_sound ma_sound; /* Sound flags. */ -#define MA_SOUND_FLAG_STREAM MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM /* 0x00000001 */ -#define MA_SOUND_FLAG_DECODE MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE /* 0x00000002 */ -#define MA_SOUND_FLAG_ASYNC MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC /* 0x00000004 */ -#define MA_SOUND_FLAG_WAIT_INIT MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT /* 0x00000008 */ -#define 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. */ -#define MA_SOUND_FLAG_NO_PITCH 0x00000020 /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ -#define MA_SOUND_FLAG_NO_SPATIALIZATION 0x00000040 /* Disable spatialization. */ +typedef enum +{ + 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_flags; #ifndef MA_ENGINE_MAX_LISTENERS #define MA_ENGINE_MAX_LISTENERS 4 @@ -6802,6 +6945,8 @@ MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enab MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); @@ -6826,6 +6971,8 @@ MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound); @@ -6861,6 +7008,8 @@ MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); @@ -6885,6 +7034,8 @@ MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRa MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); diff --git a/miniaudio.h b/miniaudio.h index 44c0c85b..69923c22 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.1 - TBD +miniaudio - v0.11.1 - 2021-12-27 David Reid - mackron@gmail.com @@ -3627,7 +3627,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 0 +#define MA_VERSION_REVISION 1 #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__) @@ -56157,7 +56157,7 @@ extern "C" { #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 32 +#define DRFLAC_VERSION_REVISION 33 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; @@ -56183,7 +56183,7 @@ typedef unsigned int drflac_uint32; #pragma GCC diagnostic pop #endif #endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) typedef drflac_uint64 drflac_uintptr; #else typedef drflac_uint32 drflac_uintptr; @@ -81061,6 +81061,9 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return DRFLAC_FALSE; + } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { break; @@ -89123,7 +89126,7 @@ There have also been some other smaller changes added to this release. /* REVISION HISTORY ================ -v0.11.1 - TBD +v0.11.1 - 2021-12-27 - Result codes are now declared as an enum rather than #defines. - Channel positions (MA_CHANNEL_*) are now declared as an enum rather than #defines. - Add ma_device_get_info() for retrieving device information from an initialized device.