mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 08:44:04 +02:00
PulseAudio: Attempt to fix a deadlock.
Public issues https://github.com/mackron/miniaudio/issues/877 https://github.com/mackron/miniaudio/issues/881
This commit is contained in:
@@ -12,6 +12,7 @@ v0.11.22 - TBD
|
|||||||
* AAudio: The default minimum SDK version has been increased from 26 to 27 when enabling AAudio. If you need to support version 26, you can use `#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 26`.
|
* AAudio: The default minimum SDK version has been increased from 26 to 27 when enabling AAudio. If you need to support version 26, you can use `#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 26`.
|
||||||
* AAudio: Fix ma_device_get_info() implementation
|
* AAudio: Fix ma_device_get_info() implementation
|
||||||
* PulseAudio: Allow setting the channel map requested from PulseAudio in device configs
|
* PulseAudio: Allow setting the channel map requested from PulseAudio in device configs
|
||||||
|
* PulseAudio: Attempt to fix a deadlock. This involves converting the PulseAudio main loop from blocking to non-blocking. To restore the old blocking behaviour, you can do so via the device config: `deviceConfig.pulse.blockingMainLoop = MA_TRUE`.
|
||||||
|
|
||||||
|
|
||||||
v0.11.21 - 2023-11-15
|
v0.11.21 - 2023-11-15
|
||||||
|
|||||||
+72
-29
@@ -7108,6 +7108,7 @@ struct ma_device_config
|
|||||||
const char* pStreamNamePlayback;
|
const char* pStreamNamePlayback;
|
||||||
const char* pStreamNameCapture;
|
const char* pStreamNameCapture;
|
||||||
int channelMap;
|
int channelMap;
|
||||||
|
ma_bool32 blockingMainLoop;
|
||||||
} pulse;
|
} pulse;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@@ -7809,7 +7810,7 @@ struct ma_device
|
|||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
#ifdef MA_SUPPORT_WASAPI
|
#ifdef MA_SUPPORT_WASAPI
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*IAudioClient**/ ma_ptr pAudioClientPlayback;
|
/*IAudioClient**/ ma_ptr pAudioClientPlayback;
|
||||||
@@ -7849,8 +7850,8 @@ struct ma_device
|
|||||||
void* hAvrtHandle;
|
void* hAvrtHandle;
|
||||||
ma_mutex rerouteLock;
|
ma_mutex rerouteLock;
|
||||||
} wasapi;
|
} wasapi;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_DSOUND
|
#ifdef MA_SUPPORT_DSOUND
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*LPDIRECTSOUND*/ ma_ptr pPlayback;
|
/*LPDIRECTSOUND*/ ma_ptr pPlayback;
|
||||||
@@ -7859,8 +7860,8 @@ struct ma_device
|
|||||||
/*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
|
/*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
|
||||||
/*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
|
/*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
|
||||||
} dsound;
|
} dsound;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_WINMM
|
#ifdef MA_SUPPORT_WINMM
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*HWAVEOUT*/ ma_handle hDevicePlayback;
|
/*HWAVEOUT*/ ma_handle hDevicePlayback;
|
||||||
@@ -7878,8 +7879,8 @@ struct ma_device
|
|||||||
ma_uint8* pIntermediaryBufferCapture;
|
ma_uint8* pIntermediaryBufferCapture;
|
||||||
ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
|
ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
|
||||||
} winmm;
|
} winmm;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_ALSA
|
#ifdef MA_SUPPORT_ALSA
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*snd_pcm_t**/ ma_ptr pPCMPlayback;
|
/*snd_pcm_t**/ ma_ptr pPCMPlayback;
|
||||||
@@ -7893,17 +7894,18 @@ struct ma_device
|
|||||||
ma_bool8 isUsingMMapPlayback;
|
ma_bool8 isUsingMMapPlayback;
|
||||||
ma_bool8 isUsingMMapCapture;
|
ma_bool8 isUsingMMapCapture;
|
||||||
} alsa;
|
} alsa;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_PULSEAUDIO
|
#ifdef MA_SUPPORT_PULSEAUDIO
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*pa_mainloop**/ ma_ptr pMainLoop;
|
/*pa_mainloop**/ ma_ptr pMainLoop;
|
||||||
/*pa_context**/ ma_ptr pPulseContext;
|
/*pa_context**/ ma_ptr pPulseContext;
|
||||||
/*pa_stream**/ ma_ptr pStreamPlayback;
|
/*pa_stream**/ ma_ptr pStreamPlayback;
|
||||||
/*pa_stream**/ ma_ptr pStreamCapture;
|
/*pa_stream**/ ma_ptr pStreamCapture;
|
||||||
|
ma_bool32 blockingMainLoop;
|
||||||
} pulse;
|
} pulse;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_JACK
|
#ifdef MA_SUPPORT_JACK
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*jack_client_t**/ ma_ptr pClient;
|
/*jack_client_t**/ ma_ptr pClient;
|
||||||
@@ -7912,8 +7914,8 @@ struct ma_device
|
|||||||
float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
|
float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
|
||||||
float* pIntermediaryBufferCapture;
|
float* pIntermediaryBufferCapture;
|
||||||
} jack;
|
} jack;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_COREAUDIO
|
#ifdef MA_SUPPORT_COREAUDIO
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ma_uint32 deviceObjectIDPlayback;
|
ma_uint32 deviceObjectIDPlayback;
|
||||||
@@ -7933,8 +7935,8 @@ struct ma_device
|
|||||||
ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
|
ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
|
||||||
void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
|
void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
|
||||||
} coreaudio;
|
} coreaudio;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_SNDIO
|
#ifdef MA_SUPPORT_SNDIO
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ma_ptr handlePlayback;
|
ma_ptr handlePlayback;
|
||||||
@@ -7942,22 +7944,22 @@ struct ma_device
|
|||||||
ma_bool32 isStartedPlayback;
|
ma_bool32 isStartedPlayback;
|
||||||
ma_bool32 isStartedCapture;
|
ma_bool32 isStartedCapture;
|
||||||
} sndio;
|
} sndio;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_AUDIO4
|
#ifdef MA_SUPPORT_AUDIO4
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int fdPlayback;
|
int fdPlayback;
|
||||||
int fdCapture;
|
int fdCapture;
|
||||||
} audio4;
|
} audio4;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_OSS
|
#ifdef MA_SUPPORT_OSS
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
int fdPlayback;
|
int fdPlayback;
|
||||||
int fdCapture;
|
int fdCapture;
|
||||||
} oss;
|
} oss;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_AAUDIO
|
#ifdef MA_SUPPORT_AAUDIO
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*AAudioStream**/ ma_ptr pStreamPlayback;
|
/*AAudioStream**/ ma_ptr pStreamPlayback;
|
||||||
@@ -7968,8 +7970,8 @@ struct ma_device
|
|||||||
ma_aaudio_allowed_capture_policy allowedCapturePolicy;
|
ma_aaudio_allowed_capture_policy allowedCapturePolicy;
|
||||||
ma_bool32 noAutoStartAfterReroute;
|
ma_bool32 noAutoStartAfterReroute;
|
||||||
} aaudio;
|
} aaudio;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_OPENSL
|
#ifdef MA_SUPPORT_OPENSL
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/*SLObjectItf*/ ma_ptr pOutputMixObj;
|
/*SLObjectItf*/ ma_ptr pOutputMixObj;
|
||||||
@@ -7987,8 +7989,8 @@ struct ma_device
|
|||||||
ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
|
ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
|
||||||
ma_uint8* pBufferCapture;
|
ma_uint8* pBufferCapture;
|
||||||
} opensl;
|
} opensl;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_WEBAUDIO
|
#ifdef MA_SUPPORT_WEBAUDIO
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/* AudioWorklets path. */
|
/* AudioWorklets path. */
|
||||||
@@ -7999,8 +8001,8 @@ struct ma_device
|
|||||||
ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */
|
ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */
|
||||||
int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */
|
int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */
|
||||||
} webaudio;
|
} webaudio;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_NULL
|
#ifdef MA_SUPPORT_NULL
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ma_thread deviceThread;
|
ma_thread deviceThread;
|
||||||
@@ -8017,7 +8019,7 @@ struct ma_device
|
|||||||
ma_uint64 lastProcessedFrameCapture;
|
ma_uint64 lastProcessedFrameCapture;
|
||||||
ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
|
ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
|
||||||
} null_device;
|
} null_device;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
#if defined(_MSC_VER) && !defined(__clang__)
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
@@ -30471,6 +30473,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
|
|||||||
sampleRate = pDescriptorCapture->sampleRate;
|
sampleRate = pDescriptorCapture->sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pDevice->pulse.blockingMainLoop = pConfig->pulse.blockingMainLoop;
|
||||||
|
|
||||||
|
|
||||||
result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext);
|
result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext);
|
||||||
@@ -30956,10 +30959,50 @@ static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
|
|||||||
the callbacks deal with it.
|
the callbacks deal with it.
|
||||||
*/
|
*/
|
||||||
while (ma_device_get_state(pDevice) == ma_device_state_started) {
|
while (ma_device_get_state(pDevice) == ma_device_state_started) {
|
||||||
resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
|
int block = (pDevice->pulse.blockingMainLoop) ? 1 : 0;
|
||||||
|
|
||||||
|
resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, block, NULL);
|
||||||
if (resultPA < 0) {
|
if (resultPA < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we're not blocking we need to sleep for a bit to prevent the CPU core being pinned at 100% usage. */
|
||||||
|
if (!block) {
|
||||||
|
ma_uint32 sleepTimeInMilliseconds;
|
||||||
|
|
||||||
|
/*
|
||||||
|
My original idea was to sleep for an amount of time proportionate to the configured period size, but I
|
||||||
|
wasn't able to figure out how to make this work without glitching. Instead I'm just going to hardcode
|
||||||
|
it to 1ms and move on.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
if (((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) == 0) {
|
||||||
|
/* The amount of time we spend sleeping should be proportionate to the size of a period. */
|
||||||
|
if (pDevice->type == ma_device_type_playback) {
|
||||||
|
sleepTimeInMilliseconds = (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalSampleRate) / 1000;
|
||||||
|
} else {
|
||||||
|
sleepTimeInMilliseconds = (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalSampleRate) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
At this point the sleep time is equal to the period size in milliseconds. I'm going to divide this by 2
|
||||||
|
in an attempt to reduce latency as a result of sleeping.
|
||||||
|
*/
|
||||||
|
sleepTimeInMilliseconds /= 2;
|
||||||
|
|
||||||
|
/* Clamp the sleep time to within reasonable values just in case. */
|
||||||
|
sleepTimeInMilliseconds = ma_clamp(sleepTimeInMilliseconds, 1, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
sleepTimeInMilliseconds = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ma_sleep(sleepTimeInMilliseconds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE: Don't stop the device here. It'll be done at a higher level. */
|
/* NOTE: Don't stop the device here. It'll be done at a higher level. */
|
||||||
|
|||||||
Reference in New Issue
Block a user