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.
main
Ryan C. Gordon 2023-06-24 14:51:39 -04:00
parent 3e10c0005d
commit dac25fe9eb
No known key found for this signature in database
GPG Key ID: FA148B892AB48044
4 changed files with 56 additions and 43 deletions

View File

@ -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_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioWaitDevice_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_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_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ } static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ }
@ -458,6 +459,7 @@ static void CompleteAudioEntryPoints(void)
FILL_STUB(WaitDevice); FILL_STUB(WaitDevice);
FILL_STUB(PlayDevice); FILL_STUB(PlayDevice);
FILL_STUB(GetDeviceBuf); FILL_STUB(GetDeviceBuf);
FILL_STUB(WaitCaptureDevice);
FILL_STUB(CaptureFromDevice); FILL_STUB(CaptureFromDevice);
FILL_STUB(FlushCapture); FILL_STUB(FlushCapture);
FILL_STUB(CloseDevice); FILL_STUB(CloseDevice);
@ -769,6 +771,7 @@ SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device)
} else if (device->logical_devices == NULL) { } else if (device->logical_devices == NULL) {
current_audio.impl.FlushCapture(device); // nothing wants data, dump anything pending. current_audio.impl.FlushCapture(device); // nothing wants data, dump anything pending.
} else { } 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); const int rc = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size);
if (rc < 0) { // uhoh, device failed for some reason! if (rc < 0) { // uhoh, device failed for some reason!
retval = SDL_FALSE; retval = SDL_FALSE;
@ -818,7 +821,11 @@ static int SDLCALL CaptureAudioThread(void *devicep) // thread entry point
SDL_assert(device != NULL); SDL_assert(device != NULL);
SDL_assert(device->iscapture); SDL_assert(device->iscapture);
SDL_CaptureAudioThreadSetup(device); 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); SDL_CaptureAudioThreadShutdown(device);
return 0; return 0;
} }

View File

@ -109,6 +109,7 @@ typedef struct SDL_AudioDriverImpl
void (*WaitDevice)(SDL_AudioDevice *device); void (*WaitDevice)(SDL_AudioDevice *device);
void (*PlayDevice)(SDL_AudioDevice *device, int buffer_size); void (*PlayDevice)(SDL_AudioDevice *device, int buffer_size);
Uint8 *(*GetDeviceBuf)(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); int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
void (*FlushCapture)(SDL_AudioDevice *device); void (*FlushCapture)(SDL_AudioDevice *device);
void (*CloseDevice)(SDL_AudioDevice *device); void (*CloseDevice)(SDL_AudioDevice *device);

View File

@ -162,6 +162,7 @@ static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl)
/* Set the function pointers */ /* Set the function pointers */
impl->OpenDevice = DISKAUDIO_OpenDevice; impl->OpenDevice = DISKAUDIO_OpenDevice;
impl->WaitDevice = DISKAUDIO_WaitDevice; impl->WaitDevice = DISKAUDIO_WaitDevice;
impl->WaitCaptureDevice = DISKAUDIO_WaitDevice;
impl->PlayDevice = DISKAUDIO_PlayDevice; impl->PlayDevice = DISKAUDIO_PlayDevice;
impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf; impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice; impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;

View File

@ -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 */ 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; struct SDL_PrivateAudioData *h = device->hidden;
const void *data = NULL;
size_t nbytes = 0; if (h->capturebuf != NULL) {
int retval = 0; return; // there's still data available to read.
}
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
while (!SDL_AtomicGet(&device->shutdown)) { while (!SDL_AtomicGet(&device->shutdown)) {
if (h->capturebuf != NULL) { PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
const int cpy = SDL_min(buflen, h->capturelen); if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
SDL_memcpy(buffer, h->capturebuf, cpy); //printf("PULSEAUDIO DEVICE FAILURE IN WAITCAPTUREDEVICE!\n");
/*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/ SDL_AudioDeviceDisconnected(device);
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. */
break; break;
} } else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) {
// a new fragment is available!
while (!SDL_AtomicGet(&device->shutdown) && (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0)) { const void *data = NULL;
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); size_t nbytes = 0;
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
/*printf("PULSEAUDIO DEVICE FAILURE IN CAPTUREFROMDEVICE!\n");*/ SDL_assert(nbytes > 0);
SDL_AudioDeviceDisconnected(device); if (data == NULL) { // If NULL, then the buffer had a hole, ignore that
retval = -1; 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; 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); 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) static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *device)
@ -991,6 +994,7 @@ static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl)
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf; impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
impl->CloseDevice = PULSEAUDIO_CloseDevice; impl->CloseDevice = PULSEAUDIO_CloseDevice;
impl->Deinitialize = PULSEAUDIO_Deinitialize; impl->Deinitialize = PULSEAUDIO_Deinitialize;
impl->WaitCaptureDevice = PULSEAUDIO_WaitCaptureDevice;
impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice; impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
impl->FlushCapture = PULSEAUDIO_FlushCapture; impl->FlushCapture = PULSEAUDIO_FlushCapture;
#if 0 #if 0