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_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;
}

View File

@ -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);

View File

@ -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;

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 */
}
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