Merge branch 'dev' into dev-0.12

This commit is contained in:
David Reid
2023-11-30 09:39:38 +10:00
4 changed files with 174 additions and 77 deletions
+14
View File
@@ -1,3 +1,17 @@
v0.11.22 - TBD
=====================
* ALSA: Fix some warnings relating to unhandled return value of `read()`.
* DirectSound: Add support for specifying an explicit window handle for SetCooperativeLevel().
* Web: Fix ScriptProcessorNode path when compiling with `--closure=1`. Note that the Audio Worklets path is not currently working due to the callback specified in `emscripten_create_wasm_audio_worklet_processor_async` never getting fired.
v0.11.21 - 2023-11-15
=====================
* Add new ma_device_notification_type_unlocked notification. This is used on Web and will be fired after the user has performed a gesture and thus unlocked the ability to play audio.
* Web: Fix an error where the buffer size is incorrectly calculated.
* Core Audio: Fix a -Wshadow warning.
v0.11.20 - 2023-11-10 v0.11.20 - 2023-11-10
===================== =====================
* Fix a compilation error with iOS. * Fix a compilation error with iOS.
+69 -36
View File
@@ -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. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.20 - 2023-11-10 miniaudio - v0.11.22 - TBD
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
@@ -7245,6 +7245,14 @@ static void ma_device__on_notification_rerouted(ma_device* pDevice)
} }
#endif #endif
#if defined(MA_EMSCRIPTEN)
EMSCRIPTEN_KEEPALIVE
void ma_device__on_notification_unlocked(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked));
}
#endif
static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{ {
@@ -12592,9 +12600,12 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma
} }
/* The cooperative level must be set before doing anything else. */ /* The cooperative level must be set before doing anything else. */
hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); hWnd = (HWND)pContext->dsound.hWnd;
if (hWnd == 0) { if (hWnd == 0) {
hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
if (hWnd == 0) {
hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
}
} }
hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
@@ -13911,6 +13922,8 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_
return MA_API_NOT_FOUND; return MA_API_NOT_FOUND;
} }
pContext->dsound.hWnd = pConfig->dsound.hWnd;
pCallbacks->onContextInit = ma_context_init__dsound; pCallbacks->onContextInit = ma_context_init__dsound;
pCallbacks->onContextUninit = ma_context_uninit__dsound; pCallbacks->onContextUninit = ma_context_uninit__dsound;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
@@ -16631,6 +16644,7 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable.
*/ */
int resultPoll; int resultPoll;
int resultRead;
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
@@ -16645,12 +16659,15 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
} }
/* Clear the wakeupfd. */ /* Clear the wakeupfd. */
resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
if (resultPoll > 0) { if (resultPoll > 0) {
ma_uint64 t; ma_uint64 t;
read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
} if (resultRead != sizeof(t)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead);
}
}
} }
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
@@ -16667,11 +16684,14 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
} }
/* Clear the wakeupfd. */ /* Clear the wakeupfd. */
resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
if (resultPoll > 0) { if (resultPoll > 0) {
ma_uint64 t; ma_uint64 t;
read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
} if (resultRead != sizeof(t)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead);
}
}
} }
@@ -21374,9 +21394,9 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec
hasSupportedFormat = MA_FALSE; hasSupportedFormat = MA_FALSE;
for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
ma_format format; ma_format formatFromDescription;
ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format); ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription);
if (formatResult == MA_SUCCESS && format != ma_format_unknown) { if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) {
hasSupportedFormat = MA_TRUE; hasSupportedFormat = MA_TRUE;
bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
break; break;
@@ -28342,7 +28362,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
#if defined(MA_USE_AUDIO_WORKLETS) #if defined(MA_USE_AUDIO_WORKLETS)
{ {
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
if (device.streamNode !== undefined) { if (device.streamNode !== undefined) {
device.streamNode.disconnect(); device.streamNode.disconnect();
@@ -28357,7 +28377,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
#else #else
{ {
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
/* Make sure all nodes are disconnected and marked for collection. */ /* Make sure all nodes are disconnected and marked for collection. */
if (device.scriptNode !== undefined) { if (device.scriptNode !== undefined) {
@@ -28377,13 +28397,14 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
*/ */
device.webaudio.close(); device.webaudio.close();
device.webaudio = undefined; device.webaudio = undefined;
device.pDevice = undefined;
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
} }
#endif #endif
/* Clean up the device on the JS side. */ /* Clean up the device on the JS side. */
EM_ASM({ EM_ASM({
miniaudio.untrack_device_by_index($0); window.miniaudio.untrack_device_by_index($0);
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
@@ -28400,6 +28421,10 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co
*/ */
ma_uint32 periodSizeInFrames; ma_uint32 periodSizeInFrames;
if (nativeSampleRate == 0) {
nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
}
if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInFrames == 0) {
if (pDescriptor->periodSizeInMilliseconds == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) {
if (performanceProfile == ma_performance_profile_low_latency) { if (performanceProfile == ma_performance_profile_low_latency) {
@@ -28544,7 +28569,6 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a
return; return;
} }
pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
/* With the audio worklet initialized we can now attach it to the graph. */ /* With the audio worklet initialized we can now attach it to the graph. */
@@ -28684,7 +28708,6 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
/* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */
pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);
/* /*
With the context created we can now create the worklet. We can only have a single worklet per audio With the context created we can now create the worklet. We can only have a single worklet per audio
context which means we'll need to craft this appropriately to handle duplex devices correctly. context which means we'll need to craft this appropriately to handle duplex devices correctly.
@@ -28733,7 +28756,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
/* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */
pDevice->webaudio.deviceIndex = EM_ASM_INT({ pDevice->webaudio.deviceIndex = EM_ASM_INT({
return miniaudio.track_device({ return window.miniaudio.track_device({
webaudio: emscriptenGetAudioObject($0), webaudio: emscriptenGetAudioObject($0),
state: 1 /* 1 = ma_device_state_stopped */ state: 1 /* 1 = ma_device_state_stopped */
}); });
@@ -28818,11 +28841,11 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
/* The node processing callback. */ /* The node processing callback. */
device.scriptNode.onaudioprocess = function(e) { device.scriptNode.onaudioprocess = function(e) {
if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {
device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
} }
/* Do the capture side first. */ /* Do the capture side first. */
if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
/* The data must be interleaved before being processed miniaudio. */ /* The data must be interleaved before being processed miniaudio. */
for (var iChannel = 0; iChannel < channels; iChannel += 1) { for (var iChannel = 0; iChannel < channels; iChannel += 1) {
var inputBuffer = e.inputBuffer.getChannelData(iChannel); var inputBuffer = e.inputBuffer.getChannelData(iChannel);
@@ -28836,7 +28859,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
_ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
} }
if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) {
_ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
@@ -28856,7 +28879,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
}; };
/* Now we need to connect our node to the graph. */ /* Now we need to connect our node to the graph. */
if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
navigator.mediaDevices.getUserMedia({audio:true, video:false}) navigator.mediaDevices.getUserMedia({audio:true, video:false})
.then(function(stream) { .then(function(stream) {
device.streamNode = device.webaudio.createMediaStreamSource(stream); device.streamNode = device.webaudio.createMediaStreamSource(stream);
@@ -28868,11 +28891,13 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
}); });
} }
if (deviceType == miniaudio.device_type.playback) { if (deviceType == window.miniaudio.device_type.playback) {
device.scriptNode.connect(device.webaudio.destination); device.scriptNode.connect(device.webaudio.destination);
} }
return miniaudio.track_device(device); device.pDevice = pDevice;
return window.miniaudio.track_device(device);
}, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);
if (deviceIndex < 0) { if (deviceIndex < 0) {
@@ -28882,7 +28907,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
pDevice->webaudio.deviceIndex = deviceIndex; pDevice->webaudio.deviceIndex = deviceIndex;
/* Grab the sample rate from the audio context directly. */ /* Grab the sample rate from the audio context directly. */
sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
if (pDescriptorCapture != NULL) { if (pDescriptorCapture != NULL) {
pDescriptorCapture->format = ma_format_f32; pDescriptorCapture->format = ma_format_f32;
@@ -28912,9 +28937,9 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice)
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
device.webaudio.resume(); device.webaudio.resume();
device.state = miniaudio.device_state.started; device.state = window.miniaudio.device_state.started;
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
return MA_SUCCESS; return MA_SUCCESS;
@@ -28934,9 +28959,9 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice)
do any kind of explicit draining. do any kind of explicit draining.
*/ */
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
device.webaudio.suspend(); device.webaudio.suspend();
device.state = miniaudio.device_state.stopped; device.state = window.miniaudio.device_state.stopped;
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
ma_device__on_notification_stopped(pDevice); ma_device__on_notification_stopped(pDevice);
@@ -28995,6 +29020,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
window.miniaudio.device_state.started = $4; window.miniaudio.device_state.started = $4;
/* Device cache for mapping devices to indexes for JavaScript/C interop. */ /* Device cache for mapping devices to indexes for JavaScript/C interop. */
let miniaudio = window.miniaudio;
miniaudio.devices = []; miniaudio.devices = [];
miniaudio.track_device = function(device) { miniaudio.track_device = function(device) {
@@ -29044,8 +29070,15 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
miniaudio.unlock = function() { miniaudio.unlock = function() {
for(var i = 0; i < miniaudio.devices.length; ++i) { for(var i = 0; i < miniaudio.devices.length; ++i) {
var device = miniaudio.devices[i]; var device = miniaudio.devices[i];
if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) { if (device != null &&
device.webaudio.resume(); device.webaudio != null &&
device.state === miniaudio.device_state.started) {
device.webaudio.resume().then(() => {
_ma_device__on_notification_unlocked(device.pDevice);
},
(error) => {console.error("Failed to resume audiocontext", error);
});
} }
} }
miniaudio.unlock_event_types.map(function(event_type) { miniaudio.unlock_event_types.map(function(event_type) {
+9 -3
View File
@@ -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. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.20 - 2023-11-10 miniaudio - v0.11.22 - TBD
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
@@ -20,7 +20,7 @@ extern "C" {
#define MA_VERSION_MAJOR 0 #define MA_VERSION_MAJOR 0
#define MA_VERSION_MINOR 11 #define MA_VERSION_MINOR 11
#define MA_VERSION_REVISION 20 #define MA_VERSION_REVISION 22
#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #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__) #if defined(_MSC_VER) && !defined(__clang__)
@@ -3013,7 +3013,8 @@ typedef enum
ma_device_notification_type_stopped, ma_device_notification_type_stopped,
ma_device_notification_type_rerouted, ma_device_notification_type_rerouted,
ma_device_notification_type_interruption_began, ma_device_notification_type_interruption_began,
ma_device_notification_type_interruption_ended ma_device_notification_type_interruption_ended,
ma_device_notification_type_unlocked
} ma_device_notification_type; } ma_device_notification_type;
typedef struct typedef struct
@@ -3544,6 +3545,10 @@ struct ma_context_config
void* pUserData; void* pUserData;
ma_allocation_callbacks allocationCallbacks; ma_allocation_callbacks allocationCallbacks;
struct struct
{
ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */
} dsound;
struct
{ {
ma_bool32 useVerboseDeviceEnumeration; ma_bool32 useVerboseDeviceEnumeration;
} alsa; } alsa;
@@ -3632,6 +3637,7 @@ struct ma_context
#ifdef MA_SUPPORT_DSOUND #ifdef MA_SUPPORT_DSOUND
struct struct
{ {
ma_handle hWnd; /* Can be null. */
ma_handle hDSoundDLL; ma_handle hDSoundDLL;
ma_proc DirectSoundCreate; ma_proc DirectSoundCreate;
ma_proc DirectSoundEnumerateA; ma_proc DirectSoundEnumerateA;
+82 -38
View File
@@ -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. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.11.20 - 2023-11-10 miniaudio - v0.11.22 - TBD
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
@@ -3723,7 +3723,7 @@ extern "C" {
#define MA_VERSION_MAJOR 0 #define MA_VERSION_MAJOR 0
#define MA_VERSION_MINOR 11 #define MA_VERSION_MINOR 11
#define MA_VERSION_REVISION 20 #define MA_VERSION_REVISION 22
#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #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__) #if defined(_MSC_VER) && !defined(__clang__)
@@ -6785,7 +6785,8 @@ typedef enum
ma_device_notification_type_stopped, ma_device_notification_type_stopped,
ma_device_notification_type_rerouted, ma_device_notification_type_rerouted,
ma_device_notification_type_interruption_began, ma_device_notification_type_interruption_began,
ma_device_notification_type_interruption_ended ma_device_notification_type_interruption_ended,
ma_device_notification_type_unlocked
} ma_device_notification_type; } ma_device_notification_type;
typedef struct typedef struct
@@ -7334,6 +7335,10 @@ struct ma_context_config
void* pUserData; void* pUserData;
ma_allocation_callbacks allocationCallbacks; ma_allocation_callbacks allocationCallbacks;
struct struct
{
ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */
} dsound;
struct
{ {
ma_bool32 useVerboseDeviceEnumeration; ma_bool32 useVerboseDeviceEnumeration;
} alsa; } alsa;
@@ -7429,6 +7434,7 @@ struct ma_context
#ifdef MA_SUPPORT_DSOUND #ifdef MA_SUPPORT_DSOUND
struct struct
{ {
ma_handle hWnd; /* Can be null. */
ma_handle hDSoundDLL; ma_handle hDSoundDLL;
ma_proc DirectSoundCreate; ma_proc DirectSoundCreate;
ma_proc DirectSoundEnumerateA; ma_proc DirectSoundEnumerateA;
@@ -18927,6 +18933,19 @@ static void ma_device__on_notification_rerouted(ma_device* pDevice)
} }
#endif #endif
#if defined(MA_EMSCRIPTEN)
#ifdef __cplusplus
extern "C" {
#endif
void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice)
{
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked));
}
#ifdef __cplusplus
}
#endif
#endif
static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{ {
@@ -24272,9 +24291,12 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma
} }
/* The cooperative level must be set before doing anything else. */ /* The cooperative level must be set before doing anything else. */
hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); hWnd = (HWND)pContext->dsound.hWnd;
if (hWnd == 0) { if (hWnd == 0) {
hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
if (hWnd == 0) {
hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
}
} }
hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
@@ -25591,6 +25613,8 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_
return MA_API_NOT_FOUND; return MA_API_NOT_FOUND;
} }
pContext->dsound.hWnd = pConfig->dsound.hWnd;
pCallbacks->onContextInit = ma_context_init__dsound; pCallbacks->onContextInit = ma_context_init__dsound;
pCallbacks->onContextUninit = ma_context_uninit__dsound; pCallbacks->onContextUninit = ma_context_uninit__dsound;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
@@ -28311,6 +28335,7 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable.
*/ */
int resultPoll; int resultPoll;
int resultRead;
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
@@ -28325,12 +28350,15 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
} }
/* Clear the wakeupfd. */ /* Clear the wakeupfd. */
resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
if (resultPoll > 0) { if (resultPoll > 0) {
ma_uint64 t; ma_uint64 t;
read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
} if (resultRead != sizeof(t)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead);
}
}
} }
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
@@ -28347,11 +28375,14 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
} }
/* Clear the wakeupfd. */ /* Clear the wakeupfd. */
resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
if (resultPoll > 0) { if (resultPoll > 0) {
ma_uint64 t; ma_uint64 t;
read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
} if (resultRead != sizeof(t)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead);
}
}
} }
@@ -33021,9 +33052,9 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec
hasSupportedFormat = MA_FALSE; hasSupportedFormat = MA_FALSE;
for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
ma_format format; ma_format formatFromDescription;
ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format); ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription);
if (formatResult == MA_SUCCESS && format != ma_format_unknown) { if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) {
hasSupportedFormat = MA_TRUE; hasSupportedFormat = MA_TRUE;
bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
break; break;
@@ -39989,7 +40020,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
#if defined(MA_USE_AUDIO_WORKLETS) #if defined(MA_USE_AUDIO_WORKLETS)
{ {
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
if (device.streamNode !== undefined) { if (device.streamNode !== undefined) {
device.streamNode.disconnect(); device.streamNode.disconnect();
@@ -40004,7 +40035,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
#else #else
{ {
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
/* Make sure all nodes are disconnected and marked for collection. */ /* Make sure all nodes are disconnected and marked for collection. */
if (device.scriptNode !== undefined) { if (device.scriptNode !== undefined) {
@@ -40024,13 +40055,14 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
*/ */
device.webaudio.close(); device.webaudio.close();
device.webaudio = undefined; device.webaudio = undefined;
device.pDevice = undefined;
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
} }
#endif #endif
/* Clean up the device on the JS side. */ /* Clean up the device on the JS side. */
EM_ASM({ EM_ASM({
miniaudio.untrack_device_by_index($0); window.miniaudio.untrack_device_by_index($0);
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
@@ -40047,6 +40079,10 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co
*/ */
ma_uint32 periodSizeInFrames; ma_uint32 periodSizeInFrames;
if (nativeSampleRate == 0) {
nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
}
if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInFrames == 0) {
if (pDescriptor->periodSizeInMilliseconds == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) {
if (performanceProfile == ma_performance_profile_low_latency) { if (performanceProfile == ma_performance_profile_low_latency) {
@@ -40191,7 +40227,6 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a
return; return;
} }
pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
/* With the audio worklet initialized we can now attach it to the graph. */ /* With the audio worklet initialized we can now attach it to the graph. */
@@ -40331,7 +40366,6 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
/* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */
pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);
/* /*
With the context created we can now create the worklet. We can only have a single worklet per audio With the context created we can now create the worklet. We can only have a single worklet per audio
context which means we'll need to craft this appropriately to handle duplex devices correctly. context which means we'll need to craft this appropriately to handle duplex devices correctly.
@@ -40380,7 +40414,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
/* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */
pDevice->webaudio.deviceIndex = EM_ASM_INT({ pDevice->webaudio.deviceIndex = EM_ASM_INT({
return miniaudio.track_device({ return window.miniaudio.track_device({
webaudio: emscriptenGetAudioObject($0), webaudio: emscriptenGetAudioObject($0),
state: 1 /* 1 = ma_device_state_stopped */ state: 1 /* 1 = ma_device_state_stopped */
}); });
@@ -40465,11 +40499,11 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
/* The node processing callback. */ /* The node processing callback. */
device.scriptNode.onaudioprocess = function(e) { device.scriptNode.onaudioprocess = function(e) {
if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {
device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
} }
/* Do the capture side first. */ /* Do the capture side first. */
if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
/* The data must be interleaved before being processed miniaudio. */ /* The data must be interleaved before being processed miniaudio. */
for (var iChannel = 0; iChannel < channels; iChannel += 1) { for (var iChannel = 0; iChannel < channels; iChannel += 1) {
var inputBuffer = e.inputBuffer.getChannelData(iChannel); var inputBuffer = e.inputBuffer.getChannelData(iChannel);
@@ -40483,7 +40517,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
_ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
} }
if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) {
_ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
@@ -40503,7 +40537,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
}; };
/* Now we need to connect our node to the graph. */ /* Now we need to connect our node to the graph. */
if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
navigator.mediaDevices.getUserMedia({audio:true, video:false}) navigator.mediaDevices.getUserMedia({audio:true, video:false})
.then(function(stream) { .then(function(stream) {
device.streamNode = device.webaudio.createMediaStreamSource(stream); device.streamNode = device.webaudio.createMediaStreamSource(stream);
@@ -40515,11 +40549,13 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
}); });
} }
if (deviceType == miniaudio.device_type.playback) { if (deviceType == window.miniaudio.device_type.playback) {
device.scriptNode.connect(device.webaudio.destination); device.scriptNode.connect(device.webaudio.destination);
} }
return miniaudio.track_device(device); device.pDevice = pDevice;
return window.miniaudio.track_device(device);
}, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);
if (deviceIndex < 0) { if (deviceIndex < 0) {
@@ -40529,7 +40565,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
pDevice->webaudio.deviceIndex = deviceIndex; pDevice->webaudio.deviceIndex = deviceIndex;
/* Grab the sample rate from the audio context directly. */ /* Grab the sample rate from the audio context directly. */
sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
if (pDescriptorCapture != NULL) { if (pDescriptorCapture != NULL) {
pDescriptorCapture->format = ma_format_f32; pDescriptorCapture->format = ma_format_f32;
@@ -40559,9 +40595,9 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice)
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
device.webaudio.resume(); device.webaudio.resume();
device.state = miniaudio.device_state.started; device.state = window.miniaudio.device_state.started;
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
return MA_SUCCESS; return MA_SUCCESS;
@@ -40581,9 +40617,9 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice)
do any kind of explicit draining. do any kind of explicit draining.
*/ */
EM_ASM({ EM_ASM({
var device = miniaudio.get_device_by_index($0); var device = window.miniaudio.get_device_by_index($0);
device.webaudio.suspend(); device.webaudio.suspend();
device.state = miniaudio.device_state.stopped; device.state = window.miniaudio.device_state.stopped;
}, pDevice->webaudio.deviceIndex); }, pDevice->webaudio.deviceIndex);
ma_device__on_notification_stopped(pDevice); ma_device__on_notification_stopped(pDevice);
@@ -40642,6 +40678,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
window.miniaudio.device_state.started = $4; window.miniaudio.device_state.started = $4;
/* Device cache for mapping devices to indexes for JavaScript/C interop. */ /* Device cache for mapping devices to indexes for JavaScript/C interop. */
let miniaudio = window.miniaudio;
miniaudio.devices = []; miniaudio.devices = [];
miniaudio.track_device = function(device) { miniaudio.track_device = function(device) {
@@ -40691,8 +40728,15 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
miniaudio.unlock = function() { miniaudio.unlock = function() {
for(var i = 0; i < miniaudio.devices.length; ++i) { for(var i = 0; i < miniaudio.devices.length; ++i) {
var device = miniaudio.devices[i]; var device = miniaudio.devices[i];
if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) { if (device != null &&
device.webaudio.resume(); device.webaudio != null &&
device.state === miniaudio.device_state.started) {
device.webaudio.resume().then(() => {
_ma_device__on_notification_unlocked(device.pDevice);
},
(error) => {console.error("Failed to resume audiocontext", error);
});
} }
} }
miniaudio.unlock_event_types.map(function(event_type) { miniaudio.unlock_event_types.map(function(event_type) {