From dac25fe9eb8176e061d86559332360d36ffd3df2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 24 Jun 2023 14:51:39 -0400 Subject: [PATCH] audio: Seperate audio capture into Wait/Read operations. Before it would just block in read operations, but separating this out matches what output devices already do, and also lets us separate out the unlocked waiting part from the fast part that holds the device lock. --- src/audio/SDL_audio.c | 9 ++- src/audio/SDL_sysaudio.h | 1 + src/audio/disk/SDL_diskaudio.c | 1 + src/audio/pulseaudio/SDL_pulseaudio.c | 88 ++++++++++++++------------- 4 files changed, 56 insertions(+), 43 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 27b2633cfb..992cfde99e 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -414,6 +414,7 @@ static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device) { /* no-op. */ static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, int buffer_size) { /* no-op. */ } +static void SDL_AudioWaitCaptureDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ } @@ -458,6 +459,7 @@ static void CompleteAudioEntryPoints(void) FILL_STUB(WaitDevice); FILL_STUB(PlayDevice); FILL_STUB(GetDeviceBuf); + FILL_STUB(WaitCaptureDevice); FILL_STUB(CaptureFromDevice); FILL_STUB(FlushCapture); FILL_STUB(CloseDevice); @@ -769,6 +771,7 @@ SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device) } else if (device->logical_devices == NULL) { current_audio.impl.FlushCapture(device); // nothing wants data, dump anything pending. } else { + // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitCaptureDevice! const int rc = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size); if (rc < 0) { // uhoh, device failed for some reason! retval = SDL_FALSE; @@ -818,7 +821,11 @@ static int SDLCALL CaptureAudioThread(void *devicep) // thread entry point SDL_assert(device != NULL); SDL_assert(device->iscapture); SDL_CaptureAudioThreadSetup(device); - while (SDL_CaptureAudioThreadIterate(device)) { /* spin, CaptureAudioThreadIterate will block if necessary. !!! FIXME: maybe this is bad. */ } + + do { + current_audio.impl.WaitCaptureDevice(device); + } while (SDL_CaptureAudioThreadIterate(device)); + SDL_CaptureAudioThreadShutdown(device); return 0; } diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 38bfa2a3f0..b9c7da6cc4 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -109,6 +109,7 @@ typedef struct SDL_AudioDriverImpl void (*WaitDevice)(SDL_AudioDevice *device); void (*PlayDevice)(SDL_AudioDevice *device, int buffer_size); Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); + void (*WaitCaptureDevice)(SDL_AudioDevice *device); int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen); void (*FlushCapture)(SDL_AudioDevice *device); void (*CloseDevice)(SDL_AudioDevice *device); diff --git a/src/audio/disk/SDL_diskaudio.c b/src/audio/disk/SDL_diskaudio.c index 517a4859ee..e96014ce95 100644 --- a/src/audio/disk/SDL_diskaudio.c +++ b/src/audio/disk/SDL_diskaudio.c @@ -162,6 +162,7 @@ static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl) /* Set the function pointers */ impl->OpenDevice = DISKAUDIO_OpenDevice; impl->WaitDevice = DISKAUDIO_WaitDevice; + impl->WaitCaptureDevice = DISKAUDIO_WaitDevice; impl->PlayDevice = DISKAUDIO_PlayDevice; impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf; impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice; diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c index c5189b7652..fcd9d15ecf 100644 --- a/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/src/audio/pulseaudio/SDL_pulseaudio.c @@ -429,62 +429,65 @@ static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* the capture code queries what it needs, we just need to signal to end any wait */ } -static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) +static void PULSEAUDIO_WaitCaptureDevice(SDL_AudioDevice *device) { struct SDL_PrivateAudioData *h = device->hidden; - const void *data = NULL; - size_t nbytes = 0; - int retval = 0; + + if (h->capturebuf != NULL) { + return; // there's still data available to read. + } PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); while (!SDL_AtomicGet(&device->shutdown)) { - if (h->capturebuf != NULL) { - const int cpy = SDL_min(buflen, h->capturelen); - SDL_memcpy(buffer, h->capturebuf, cpy); - /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/ - h->capturebuf += cpy; - h->capturelen -= cpy; - if (h->capturelen == 0) { - h->capturebuf = NULL; - PULSEAUDIO_pa_stream_drop(h->stream); /* done with this fragment. */ - } - retval = cpy; /* new data, return it. */ + PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); + if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { + //printf("PULSEAUDIO DEVICE FAILURE IN WAITCAPTUREDEVICE!\n"); + SDL_AudioDeviceDisconnected(device); break; - } - - while (!SDL_AtomicGet(&device->shutdown) && (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0)) { - PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); - if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { - /*printf("PULSEAUDIO DEVICE FAILURE IN CAPTUREFROMDEVICE!\n");*/ - SDL_AudioDeviceDisconnected(device); - retval = -1; + } else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) { + // a new fragment is available! + const void *data = NULL; + size_t nbytes = 0; + PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); + SDL_assert(nbytes > 0); + if (data == NULL) { // If NULL, then the buffer had a hole, ignore that + PULSEAUDIO_pa_stream_drop(h->stream); // drop this fragment. + } else { + // store this fragment's data for use with CaptureFromDevice + //printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes); + h->capturebuf = (const Uint8 *)data; + h->capturelen = nbytes; break; } } - - if ((retval == -1) || SDL_AtomicGet(&device->shutdown)) { /* in case this happened while we were blocking. */ - retval = -1; - break; - } - - /* a new fragment is available! */ - PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); - SDL_assert(nbytes > 0); - /* If data == NULL, then the buffer had a hole, ignore that */ - if (data == NULL) { - PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */ - } else { - /* store this fragment's data, start feeding it to SDL. */ - /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/ - h->capturebuf = (const Uint8 *)data; - h->capturelen = nbytes; - } } PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); +} - return retval; +static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) +{ + struct SDL_PrivateAudioData *h = device->hidden; + + if (h->capturebuf != NULL) { + const int cpy = SDL_min(buflen, h->capturelen); + if (cpy > 0) { + //printf("PULSEAUDIO: fed %d captured bytes\n", cpy); + SDL_memcpy(buffer, h->capturebuf, cpy); + h->capturebuf += cpy; + h->capturelen -= cpy; + } + if (h->capturelen == 0) { + h->capturebuf = NULL; + PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); // don't know if you _have_ to lock for this, but just in case. + PULSEAUDIO_pa_stream_drop(h->stream); // done with this fragment. + PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); + } + return cpy; /* new data, return it. */ + } + + return 0; } static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *device) @@ -991,6 +994,7 @@ static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl) impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf; impl->CloseDevice = PULSEAUDIO_CloseDevice; impl->Deinitialize = PULSEAUDIO_Deinitialize; + impl->WaitCaptureDevice = PULSEAUDIO_WaitCaptureDevice; impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice; impl->FlushCapture = PULSEAUDIO_FlushCapture; #if 0