|
@@ -433,77 +433,24 @@ SDL_RemoveAudioDevice(const int iscapture, void *handle) |
|
|
|
|
|
/* this expects that you managed thread safety elsewhere. */ |
|
|
static void |
|
|
free_audio_queue(SDL_AudioBufferQueue *buffer) |
|
|
free_audio_queue(SDL_AudioBufferQueue *packet) |
|
|
{ |
|
|
while (buffer) { |
|
|
SDL_AudioBufferQueue *next = buffer->next; |
|
|
SDL_free(buffer); |
|
|
buffer = next; |
|
|
while (packet) { |
|
|
SDL_AudioBufferQueue *next = packet->next; |
|
|
SDL_free(packet); |
|
|
packet = next; |
|
|
} |
|
|
} |
|
|
|
|
|
static void SDLCALL |
|
|
SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int _len) |
|
|
{ |
|
|
/* this function always holds the mixer lock before being called. */ |
|
|
Uint32 len = (Uint32) _len; |
|
|
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; |
|
|
SDL_AudioBufferQueue *buffer; |
|
|
|
|
|
SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ |
|
|
SDL_assert(_len >= 0); /* this shouldn't ever happen, right?! */ |
|
|
|
|
|
while ((len > 0) && ((buffer = device->buffer_queue_head) != NULL)) { |
|
|
const Uint32 avail = buffer->datalen - buffer->startpos; |
|
|
const Uint32 cpy = SDL_min(len, avail); |
|
|
SDL_assert(device->queued_bytes >= avail); |
|
|
|
|
|
SDL_memcpy(stream, buffer->data + buffer->startpos, cpy); |
|
|
buffer->startpos += cpy; |
|
|
stream += cpy; |
|
|
device->queued_bytes -= cpy; |
|
|
len -= cpy; |
|
|
|
|
|
if (buffer->startpos == buffer->datalen) { /* packet is done, put it in the pool. */ |
|
|
device->buffer_queue_head = buffer->next; |
|
|
SDL_assert((buffer->next != NULL) || (buffer == device->buffer_queue_tail)); |
|
|
buffer->next = device->buffer_queue_pool; |
|
|
device->buffer_queue_pool = buffer; |
|
|
} |
|
|
} |
|
|
|
|
|
SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0)); |
|
|
|
|
|
if (len > 0) { /* fill any remaining space in the stream with silence. */ |
|
|
SDL_assert(device->buffer_queue_head == NULL); |
|
|
SDL_memset(stream, device->spec.silence, len); |
|
|
} |
|
|
|
|
|
if (device->buffer_queue_head == NULL) { |
|
|
device->buffer_queue_tail = NULL; /* in case we drained the queue entirely. */ |
|
|
} |
|
|
} |
|
|
|
|
|
int |
|
|
SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len) |
|
|
/* NOTE: This assumes you'll hold the mixer lock before calling! */ |
|
|
static int |
|
|
queue_audio_to_device(SDL_AudioDevice *device, const Uint8 *data, Uint32 len) |
|
|
{ |
|
|
SDL_AudioDevice *device = get_audio_device(devid); |
|
|
const Uint8 *data = (const Uint8 *) _data; |
|
|
SDL_AudioBufferQueue *orighead; |
|
|
SDL_AudioBufferQueue *origtail; |
|
|
Uint32 origlen; |
|
|
Uint32 datalen; |
|
|
|
|
|
if (!device) { |
|
|
return -1; /* get_audio_device() will have set the error state */ |
|
|
} |
|
|
|
|
|
if (device->spec.callback != SDL_BufferQueueDrainCallback) { |
|
|
return SDL_SetError("Audio device has a callback, queueing not allowed"); |
|
|
} |
|
|
|
|
|
current_audio.impl.LockDevice(device); |
|
|
|
|
|
orighead = device->buffer_queue_head; |
|
|
origtail = device->buffer_queue_tail; |
|
|
origlen = origtail ? origtail->datalen : 0; |
|
@@ -533,8 +480,6 @@ SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len) |
|
|
device->buffer_queue_tail = origtail; |
|
|
device->buffer_queue_pool = NULL; |
|
|
|
|
|
current_audio.impl.UnlockDevice(device); |
|
|
|
|
|
free_audio_queue(packet); /* give back what we can. */ |
|
|
|
|
|
return SDL_OutOfMemory(); |
|
@@ -561,22 +506,142 @@ SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len) |
|
|
device->queued_bytes += datalen; |
|
|
} |
|
|
|
|
|
current_audio.impl.UnlockDevice(device); |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* NOTE: This assumes you'll hold the mixer lock before calling! */ |
|
|
static Uint32 |
|
|
dequeue_audio_from_device(SDL_AudioDevice *device, Uint8 *stream, Uint32 len) |
|
|
{ |
|
|
SDL_AudioBufferQueue *packet; |
|
|
Uint8 *ptr = stream; |
|
|
|
|
|
while ((len > 0) && ((packet = device->buffer_queue_head) != NULL)) { |
|
|
const Uint32 avail = packet->datalen - packet->startpos; |
|
|
const Uint32 cpy = SDL_min(len, avail); |
|
|
SDL_assert(device->queued_bytes >= avail); |
|
|
|
|
|
SDL_memcpy(ptr, packet->data + packet->startpos, cpy); |
|
|
packet->startpos += cpy; |
|
|
ptr += cpy; |
|
|
device->queued_bytes -= cpy; |
|
|
len -= cpy; |
|
|
|
|
|
if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */ |
|
|
device->buffer_queue_head = packet->next; |
|
|
SDL_assert((packet->next != NULL) || (packet == device->buffer_queue_tail)); |
|
|
packet->next = device->buffer_queue_pool; |
|
|
device->buffer_queue_pool = packet; |
|
|
} |
|
|
} |
|
|
|
|
|
SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0)); |
|
|
|
|
|
if (device->buffer_queue_head == NULL) { |
|
|
device->buffer_queue_tail = NULL; /* in case we drained the queue entirely. */ |
|
|
} |
|
|
|
|
|
return (Uint32) (ptr - stream); |
|
|
} |
|
|
|
|
|
static void SDLCALL |
|
|
SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int len) |
|
|
{ |
|
|
/* this function always holds the mixer lock before being called. */ |
|
|
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; |
|
|
Uint32 written; |
|
|
|
|
|
SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ |
|
|
SDL_assert(!device->iscapture); /* this shouldn't ever happen, right?! */ |
|
|
SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */ |
|
|
|
|
|
written = dequeue_audio_from_device(device, stream, (Uint32) len); |
|
|
stream += written; |
|
|
len -= (int) written; |
|
|
|
|
|
if (len > 0) { /* fill any remaining space in the stream with silence. */ |
|
|
SDL_assert(device->buffer_queue_head == NULL); |
|
|
SDL_memset(stream, device->spec.silence, len); |
|
|
} |
|
|
} |
|
|
|
|
|
static void SDLCALL |
|
|
SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len) |
|
|
{ |
|
|
/* this function always holds the mixer lock before being called. */ |
|
|
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; |
|
|
|
|
|
SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ |
|
|
SDL_assert(device->iscapture); /* this shouldn't ever happen, right?! */ |
|
|
SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */ |
|
|
|
|
|
/* note that if this needs to allocate more space and run out of memory, |
|
|
we have no choice but to quietly drop the data and hope it works out |
|
|
later, but you probably have bigger problems in this case anyhow. */ |
|
|
queue_audio_to_device(device, stream, (Uint32) len); |
|
|
} |
|
|
|
|
|
int |
|
|
SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len) |
|
|
{ |
|
|
SDL_AudioDevice *device = get_audio_device(devid); |
|
|
int rc = 0; |
|
|
|
|
|
if (!device) { |
|
|
return -1; /* get_audio_device() will have set the error state */ |
|
|
} else if (device->iscapture) { |
|
|
return SDL_SetError("This is a capture device, queueing not allowed"); |
|
|
} else if (device->spec.callback != SDL_BufferQueueDrainCallback) { |
|
|
return SDL_SetError("Audio device has a callback, queueing not allowed"); |
|
|
} |
|
|
|
|
|
if (len > 0) { |
|
|
current_audio.impl.LockDevice(device); |
|
|
rc = queue_audio_to_device(device, data, len); |
|
|
current_audio.impl.UnlockDevice(device); |
|
|
} |
|
|
|
|
|
return rc; |
|
|
} |
|
|
|
|
|
Uint32 |
|
|
SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len) |
|
|
{ |
|
|
SDL_AudioDevice *device = get_audio_device(devid); |
|
|
Uint32 rc; |
|
|
|
|
|
if ( (len == 0) || /* nothing to do? */ |
|
|
(!device) || /* called with bogus device id */ |
|
|
(!device->iscapture) || /* playback devices can't dequeue */ |
|
|
(device->spec.callback != SDL_BufferQueueFillCallback) ) { /* not set for queueing */ |
|
|
return 0; /* just report zero bytes dequeued. */ |
|
|
} |
|
|
|
|
|
current_audio.impl.LockDevice(device); |
|
|
rc = dequeue_audio_from_device(device, data, len); |
|
|
current_audio.impl.UnlockDevice(device); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
Uint32 |
|
|
SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid) |
|
|
{ |
|
|
Uint32 retval = 0; |
|
|
SDL_AudioDevice *device = get_audio_device(devid); |
|
|
|
|
|
if (!device) { |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* Nothing to do unless we're set up for queueing. */ |
|
|
if (device && (device->spec.callback == SDL_BufferQueueDrainCallback)) { |
|
|
if (device->spec.callback == SDL_BufferQueueDrainCallback) { |
|
|
current_audio.impl.LockDevice(device); |
|
|
retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device); |
|
|
current_audio.impl.UnlockDevice(device); |
|
|
} else if (device->spec.callback == SDL_BufferQueueFillCallback) { |
|
|
current_audio.impl.LockDevice(device); |
|
|
retval = device->queued_bytes; |
|
|
current_audio.impl.UnlockDevice(device); |
|
|
} |
|
|
|
|
|
return retval; |
|
@@ -1305,7 +1370,7 @@ open_audio_device(const char *devname, int iscapture, |
|
|
} |
|
|
} |
|
|
|
|
|
device->spec.callback = SDL_BufferQueueDrainCallback; |
|
|
device->spec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback; |
|
|
device->spec.userdata = device; |
|
|
} |
|
|
|
|
@@ -1319,7 +1384,9 @@ 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 SDL_bool is_internal_thread = (device->spec.callback == SDL_BufferQueueDrainCallback); |
|
|
const SDL_bool is_internal_thread = |
|
|
(device->spec.callback == SDL_BufferQueueDrainCallback) || |
|
|
(device->spec.callback == SDL_BufferQueueFillCallback); |
|
|
const size_t stacksize = is_internal_thread ? 64 * 1024 : 0; |
|
|
|
|
|
SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id); |
|
|