mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-09 03:16:26 +00:00
Refactored the audio queueing code to a generic SDL_DataQueue interface.
This is not a public API (at the moment), but we will be needing this for other internal things soon.
This commit is contained in:
@@ -418,136 +418,23 @@ SDL_RemoveAudioDevice(const int iscapture, void *handle)
|
||||
|
||||
/* buffer queueing support... */
|
||||
|
||||
/* this expects that you managed thread safety elsewhere. */
|
||||
static void
|
||||
free_audio_queue(SDL_AudioBufferQueue *packet)
|
||||
{
|
||||
while (packet) {
|
||||
SDL_AudioBufferQueue *next = packet->next;
|
||||
SDL_free(packet);
|
||||
packet = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_AudioBufferQueue *orighead;
|
||||
SDL_AudioBufferQueue *origtail;
|
||||
Uint32 origlen;
|
||||
Uint32 datalen;
|
||||
|
||||
orighead = device->buffer_queue_head;
|
||||
origtail = device->buffer_queue_tail;
|
||||
origlen = origtail ? origtail->datalen : 0;
|
||||
|
||||
while (len > 0) {
|
||||
SDL_AudioBufferQueue *packet = device->buffer_queue_tail;
|
||||
SDL_assert(!packet || (packet->datalen <= SDL_AUDIOBUFFERQUEUE_PACKETLEN));
|
||||
if (!packet || (packet->datalen >= SDL_AUDIOBUFFERQUEUE_PACKETLEN)) {
|
||||
/* tail packet missing or completely full; we need a new packet. */
|
||||
packet = device->buffer_queue_pool;
|
||||
if (packet != NULL) {
|
||||
/* we have one available in the pool. */
|
||||
device->buffer_queue_pool = packet->next;
|
||||
} else {
|
||||
/* Have to allocate a new one! */
|
||||
packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
|
||||
if (packet == NULL) {
|
||||
/* uhoh, reset so we've queued nothing new, free what we can. */
|
||||
if (!origtail) {
|
||||
packet = device->buffer_queue_head; /* whole queue. */
|
||||
} else {
|
||||
packet = origtail->next; /* what we added to existing queue. */
|
||||
origtail->next = NULL;
|
||||
origtail->datalen = origlen;
|
||||
}
|
||||
device->buffer_queue_head = orighead;
|
||||
device->buffer_queue_tail = origtail;
|
||||
device->buffer_queue_pool = NULL;
|
||||
|
||||
free_audio_queue(packet); /* give back what we can. */
|
||||
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = NULL;
|
||||
|
||||
SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
|
||||
if (device->buffer_queue_tail == NULL) {
|
||||
device->buffer_queue_head = packet;
|
||||
} else {
|
||||
device->buffer_queue_tail->next = packet;
|
||||
}
|
||||
device->buffer_queue_tail = packet;
|
||||
}
|
||||
|
||||
datalen = SDL_min(len, SDL_AUDIOBUFFERQUEUE_PACKETLEN - packet->datalen);
|
||||
SDL_memcpy(packet->data + packet->datalen, data, datalen);
|
||||
data += datalen;
|
||||
len -= datalen;
|
||||
packet->datalen += datalen;
|
||||
device->queued_bytes += datalen;
|
||||
}
|
||||
|
||||
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;
|
||||
size_t dequeued;
|
||||
|
||||
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;
|
||||
dequeued = SDL_ReadFromDataQueue(device->buffer_queue, stream, len);
|
||||
stream += dequeued;
|
||||
len -= (int) dequeued;
|
||||
|
||||
if (len > 0) { /* fill any remaining space in the stream with silence. */
|
||||
SDL_assert(device->buffer_queue_head == NULL);
|
||||
SDL_assert(SDL_CountDataQueue(device->buffer_queue) == 0);
|
||||
SDL_memset(stream, device->spec.silence, len);
|
||||
}
|
||||
}
|
||||
@@ -565,7 +452,7 @@ SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len)
|
||||
/* 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);
|
||||
SDL_WriteToDataQueue(device->buffer_queue, stream, len);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -584,7 +471,7 @@ SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len)
|
||||
|
||||
if (len > 0) {
|
||||
current_audio.impl.LockDevice(device);
|
||||
rc = queue_audio_to_device(device, data, len);
|
||||
rc = SDL_WriteToDataQueue(device->buffer_queue, data, len);
|
||||
current_audio.impl.UnlockDevice(device);
|
||||
}
|
||||
|
||||
@@ -605,7 +492,7 @@ SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len)
|
||||
}
|
||||
|
||||
current_audio.impl.LockDevice(device);
|
||||
rc = dequeue_audio_from_device(device, data, len);
|
||||
rc = (Uint32) SDL_ReadFromDataQueue(device->buffer_queue, data, len);
|
||||
current_audio.impl.UnlockDevice(device);
|
||||
return rc;
|
||||
}
|
||||
@@ -623,11 +510,11 @@ SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
|
||||
/* Nothing to do unless we're set up for queueing. */
|
||||
if (device->spec.callback == SDL_BufferQueueDrainCallback) {
|
||||
current_audio.impl.LockDevice(device);
|
||||
retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device);
|
||||
retval = SDL_CountDataQueue(device->buffer_queue) + 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;
|
||||
retval = SDL_CountDataQueue(device->buffer_queue);
|
||||
current_audio.impl.UnlockDevice(device);
|
||||
}
|
||||
|
||||
@@ -638,7 +525,6 @@ void
|
||||
SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
|
||||
{
|
||||
SDL_AudioDevice *device = get_audio_device(devid);
|
||||
SDL_AudioBufferQueue *packet;
|
||||
|
||||
if (!device) {
|
||||
return; /* nothing to do. */
|
||||
@@ -647,35 +533,10 @@ SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
|
||||
/* Blank out the device and release the mutex. Free it afterwards. */
|
||||
current_audio.impl.LockDevice(device);
|
||||
|
||||
/* merge the available pool and the current queue into one list. */
|
||||
packet = device->buffer_queue_head;
|
||||
if (packet) {
|
||||
device->buffer_queue_tail->next = device->buffer_queue_pool;
|
||||
} else {
|
||||
packet = device->buffer_queue_pool;
|
||||
}
|
||||
|
||||
/* Remove the queued packets from the device. */
|
||||
device->buffer_queue_tail = NULL;
|
||||
device->buffer_queue_head = NULL;
|
||||
device->queued_bytes = 0;
|
||||
device->buffer_queue_pool = packet;
|
||||
|
||||
/* Keep up to two packets in the pool to reduce future malloc pressure. */
|
||||
if (packet) {
|
||||
if (!packet->next) {
|
||||
packet = NULL; /* one packet (the only one) for the pool. */
|
||||
} else {
|
||||
SDL_AudioBufferQueue *next = packet->next->next;
|
||||
packet->next->next = NULL; /* two packets for the pool. */
|
||||
packet = next; /* rest will be freed. */
|
||||
}
|
||||
}
|
||||
SDL_ClearDataQueue(device->buffer_queue, SDL_AUDIOBUFFERQUEUE_PACKETLEN * 2);
|
||||
|
||||
current_audio.impl.UnlockDevice(device);
|
||||
|
||||
/* free any extra packets we didn't keep in the pool. */
|
||||
free_audio_queue(packet);
|
||||
}
|
||||
|
||||
|
||||
@@ -1076,9 +937,7 @@ close_audio_device(SDL_AudioDevice * device)
|
||||
current_audio.impl.CloseDevice(device);
|
||||
}
|
||||
|
||||
free_audio_queue(device->buffer_queue_head);
|
||||
free_audio_queue(device->buffer_queue_pool);
|
||||
|
||||
SDL_FreeDataQueue(device->buffer_queue);
|
||||
SDL_free(device);
|
||||
}
|
||||
|
||||
@@ -1348,19 +1207,13 @@ open_audio_device(const char *devname, int iscapture,
|
||||
|
||||
if (device->spec.callback == NULL) { /* use buffer queueing? */
|
||||
/* pool a few packets to start. Enough for two callbacks. */
|
||||
const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
|
||||
const int wantbytes = ((device->convert.needed) ? device->convert.len : device->spec.size) * 2;
|
||||
const int wantpackets = (wantbytes / packetlen) + ((wantbytes % packetlen) ? packetlen : 0);
|
||||
for (i = 0; i < wantpackets; i++) {
|
||||
SDL_AudioBufferQueue *packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
|
||||
if (packet) { /* don't care if this fails, we'll deal later. */
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = device->buffer_queue_pool;
|
||||
device->buffer_queue_pool = packet;
|
||||
}
|
||||
const size_t slack = ((device->convert.needed) ? device->convert.len : device->spec.size) * 2;
|
||||
device->buffer_queue = SDL_NewDataQueue(SDL_AUDIOBUFFERQUEUE_PACKETLEN, slack);
|
||||
if (!device->buffer_queue) {
|
||||
close_audio_device(device);
|
||||
SDL_SetError("Couldn't create audio buffer queue");
|
||||
return 0;
|
||||
}
|
||||
|
||||
device->spec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback;
|
||||
device->spec.userdata = device;
|
||||
}
|
||||
|
Reference in New Issue
Block a user