From 0d0f7080a37958e31d0697e0ce3d87e1b97c5692 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 2 Aug 2016 13:50:21 -0400 Subject: [PATCH] audio: implemented higher level infrastructure for running capture devices. --- src/audio/SDL_audio.c | 107 +++++++++++++++++++++++++++++++++++++-- src/audio/SDL_audio_c.h | 3 -- src/audio/SDL_sysaudio.h | 2 + 3 files changed, 104 insertions(+), 8 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 934652a8e5f02..b8b045a7cbee4 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -206,6 +206,17 @@ SDL_AudioWaitDone_Default(_THIS) { /* no-op. */ } +static int +SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen) +{ + return -1; /* just fail immediately. */ +} + +static void +SDL_AudioFlushCapture_Default(_THIS) +{ /* no-op. */ +} + static void SDL_AudioCloseDevice_Default(_THIS) { /* no-op. */ @@ -279,6 +290,8 @@ finalize_audio_entry_points(void) FILL_STUB(GetPendingBytes); FILL_STUB(GetDeviceBuf); FILL_STUB(WaitDone); + FILL_STUB(CaptureFromDevice); + FILL_STUB(FlushCapture); FILL_STUB(CloseDevice); FILL_STUB(LockDevice); FILL_STUB(UnlockDevice); @@ -592,7 +605,7 @@ SDL_ClearQueuedAudio(SDL_AudioDeviceID devid) /* The general mixing thread function */ -int SDLCALL +static int SDLCALL SDL_RunAudio(void *devicep) { SDL_AudioDevice *device = (SDL_AudioDevice *) devicep; @@ -601,7 +614,9 @@ SDL_RunAudio(void *devicep) const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size; Uint8 *stream; void *udata = device->spec.userdata; - void (SDLCALL *fill) (void *, Uint8 *, int) = device->spec.callback; + void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback; + + SDL_assert(!device->iscapture); /* The audio mixing is always a high priority thread */ SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); @@ -635,7 +650,7 @@ SDL_RunAudio(void *devicep) if (SDL_AtomicGet(&device->paused)) { SDL_memset(stream, silence, stream_len); } else { - (*fill) (udata, stream, stream_len); + (*callback) (udata, stream, stream_len); } SDL_UnlockMutex(device->mixer_lock); @@ -661,11 +676,92 @@ SDL_RunAudio(void *devicep) } /* Wait for the audio to drain. */ + /* !!! FIXME: can we rename this WaitDrain? */ current_audio.impl.WaitDone(device); return 0; } +/* The general capture thread function */ +static int SDLCALL +SDL_CaptureAudio(void *devicep) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *) devicep; + const int silence = (int) device->spec.silence; + const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); + const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size; + Uint8 *stream; + void *udata = device->spec.userdata; + void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback; + + SDL_assert(device->iscapture); + + /* The audio mixing is always a high priority thread */ + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); + + /* Perform any thread setup */ + device->threadid = SDL_ThreadID(); + current_audio.impl.ThreadInit(device); + + /* Loop, filling the audio buffers */ + while (!SDL_AtomicGet(&device->shutdown)) { + int still_need; + Uint8 *ptr; + + if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) { + SDL_Delay(delay); /* just so we don't cook the CPU. */ + current_audio.impl.FlushCapture(device); /* dump anything pending. */ + continue; + } + + /* Fill the current buffer with sound */ + still_need = stream_len; + if (device->convert.needed) { + ptr = stream = device->convert.buf; + } else { + /* just use the "fake" stream to hold data read from the device. */ + ptr = stream = device->fake_stream; + } + + /* We still read from the device when "paused" to keep the state sane, + and block when there isn't data so this thread isn't eating CPU. + But we don't process it further or call the app's callback. */ + + while (still_need > 0) { + const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need); + SDL_assert(rc != 0); /* device should have blocked, failed, or returned data. */ + SDL_assert(rc <= still_need); /* device should not overflow buffer. :) */ + if (rc > 0) { + still_need -= rc; + ptr += rc; + } else { /* uhoh, device failed for some reason! */ + SDL_OpenedAudioDeviceDisconnected(device); + break; + } + } + + if (still_need > 0) { + /* Keep any data we already read, silence the rest. */ + SDL_memset(ptr, silence, still_need); + } + + if (device->convert.needed) { + SDL_ConvertAudio(&device->convert); + } + + /* !!! FIXME: this should be LockDevice. */ + SDL_LockMutex(device->mixer_lock); + if (!SDL_AtomicGet(&device->paused)) { + (*callback)(udata, stream, stream_len); + } + SDL_UnlockMutex(device->mixer_lock); + } + + current_audio.impl.FlushCapture(device); + + return 0; +} + static SDL_AudioFormat SDL_ParseAudioFormat(const char *string) @@ -1198,10 +1294,11 @@ open_audio_device(const char *devname, int iscapture, /* !!! FIXME: we don't force the audio thread stack size here because it calls into user code, but maybe we should? */ /* buffer queueing callback only needs a few bytes, so make the stack tiny. */ char name[64]; - const size_t stacksize = (device->spec.callback == SDL_BufferQueueDrainCallback) ? 64 * 1024 : 0; + const SDL_bool is_internal_thread = (device->spec.callback == SDL_BufferQueueDrainCallback); + const size_t stacksize = is_internal_thread ? 64 * 1024 : 0; SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id); - device->thread = SDL_CreateThreadInternal(SDL_RunAudio, name, stacksize, device); + device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, name, stacksize, device); if (device->thread == NULL) { SDL_CloseAudioDevice(device->id); diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h index b03a9156f6013..7cde3a662531d 100644 --- a/src/audio/SDL_audio_c.h +++ b/src/audio/SDL_audio_c.h @@ -29,9 +29,6 @@ extern SDL_AudioFormat SDL_NextAudioFormat(void); /* Function to calculate the size and silence for a SDL_AudioSpec */ extern void SDL_CalculateAudioSpec(SDL_AudioSpec * spec); -/* The actual mixing thread function */ -extern int SDLCALL SDL_RunAudio(void *audiop); - /* this is used internally to access some autogenerated code. */ typedef struct { diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 0b35b27544ce9..d3a48cc3741bb 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -76,6 +76,8 @@ typedef struct SDL_AudioDriverImpl int (*GetPendingBytes) (_THIS); Uint8 *(*GetDeviceBuf) (_THIS); void (*WaitDone) (_THIS); + int (*CaptureFromDevice) (_THIS, void *buffer, int buflen); + void (*FlushCapture) (_THIS); void (*CloseDevice) (_THIS); void (*LockDevice) (_THIS); void (*UnlockDevice) (_THIS);