mirror of https://github.com/encounter/SDL.git
audio: implemented higher level infrastructure for running capture devices.
This commit is contained in:
parent
6d5c9c1e67
commit
0d0f7080a3
|
@ -206,6 +206,17 @@ SDL_AudioWaitDone_Default(_THIS)
|
||||||
{ /* no-op. */
|
{ /* 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
|
static void
|
||||||
SDL_AudioCloseDevice_Default(_THIS)
|
SDL_AudioCloseDevice_Default(_THIS)
|
||||||
{ /* no-op. */
|
{ /* no-op. */
|
||||||
|
@ -279,6 +290,8 @@ finalize_audio_entry_points(void)
|
||||||
FILL_STUB(GetPendingBytes);
|
FILL_STUB(GetPendingBytes);
|
||||||
FILL_STUB(GetDeviceBuf);
|
FILL_STUB(GetDeviceBuf);
|
||||||
FILL_STUB(WaitDone);
|
FILL_STUB(WaitDone);
|
||||||
|
FILL_STUB(CaptureFromDevice);
|
||||||
|
FILL_STUB(FlushCapture);
|
||||||
FILL_STUB(CloseDevice);
|
FILL_STUB(CloseDevice);
|
||||||
FILL_STUB(LockDevice);
|
FILL_STUB(LockDevice);
|
||||||
FILL_STUB(UnlockDevice);
|
FILL_STUB(UnlockDevice);
|
||||||
|
@ -592,7 +605,7 @@ SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
|
||||||
|
|
||||||
|
|
||||||
/* The general mixing thread function */
|
/* The general mixing thread function */
|
||||||
int SDLCALL
|
static int SDLCALL
|
||||||
SDL_RunAudio(void *devicep)
|
SDL_RunAudio(void *devicep)
|
||||||
{
|
{
|
||||||
SDL_AudioDevice *device = (SDL_AudioDevice *) 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;
|
const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
|
||||||
Uint8 *stream;
|
Uint8 *stream;
|
||||||
void *udata = device->spec.userdata;
|
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 */
|
/* The audio mixing is always a high priority thread */
|
||||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||||
|
@ -635,7 +650,7 @@ SDL_RunAudio(void *devicep)
|
||||||
if (SDL_AtomicGet(&device->paused)) {
|
if (SDL_AtomicGet(&device->paused)) {
|
||||||
SDL_memset(stream, silence, stream_len);
|
SDL_memset(stream, silence, stream_len);
|
||||||
} else {
|
} else {
|
||||||
(*fill) (udata, stream, stream_len);
|
(*callback) (udata, stream, stream_len);
|
||||||
}
|
}
|
||||||
SDL_UnlockMutex(device->mixer_lock);
|
SDL_UnlockMutex(device->mixer_lock);
|
||||||
|
|
||||||
|
@ -661,11 +676,92 @@ SDL_RunAudio(void *devicep)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for the audio to drain. */
|
/* Wait for the audio to drain. */
|
||||||
|
/* !!! FIXME: can we rename this WaitDrain? */
|
||||||
current_audio.impl.WaitDone(device);
|
current_audio.impl.WaitDone(device);
|
||||||
|
|
||||||
return 0;
|
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
|
static SDL_AudioFormat
|
||||||
SDL_ParseAudioFormat(const char *string)
|
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? */
|
/* !!! 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. */
|
/* buffer queueing callback only needs a few bytes, so make the stack tiny. */
|
||||||
char name[64];
|
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);
|
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) {
|
if (device->thread == NULL) {
|
||||||
SDL_CloseAudioDevice(device->id);
|
SDL_CloseAudioDevice(device->id);
|
||||||
|
|
|
@ -29,9 +29,6 @@ extern SDL_AudioFormat SDL_NextAudioFormat(void);
|
||||||
/* Function to calculate the size and silence for a SDL_AudioSpec */
|
/* Function to calculate the size and silence for a SDL_AudioSpec */
|
||||||
extern void SDL_CalculateAudioSpec(SDL_AudioSpec * spec);
|
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. */
|
/* this is used internally to access some autogenerated code. */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,6 +76,8 @@ typedef struct SDL_AudioDriverImpl
|
||||||
int (*GetPendingBytes) (_THIS);
|
int (*GetPendingBytes) (_THIS);
|
||||||
Uint8 *(*GetDeviceBuf) (_THIS);
|
Uint8 *(*GetDeviceBuf) (_THIS);
|
||||||
void (*WaitDone) (_THIS);
|
void (*WaitDone) (_THIS);
|
||||||
|
int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
|
||||||
|
void (*FlushCapture) (_THIS);
|
||||||
void (*CloseDevice) (_THIS);
|
void (*CloseDevice) (_THIS);
|
||||||
void (*LockDevice) (_THIS);
|
void (*LockDevice) (_THIS);
|
||||||
void (*UnlockDevice) (_THIS);
|
void (*UnlockDevice) (_THIS);
|
||||||
|
|
Loading…
Reference in New Issue