From a0e003eebb90649a3ea7a02c0fae92b56650ef07 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 6 Dec 2016 02:23:54 -0500 Subject: [PATCH] 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. --- VisualC/SDL/SDL.vcxproj | 1 + VisualC/SDL/SDL.vcxproj.filters | 2 + VisualC/SDL/SDL_VS2008.vcproj | 8 + src/SDL_dataqueue.c | 260 ++++++++++++++++++++++++++++++++ src/SDL_dataqueue.h | 39 +++++ src/audio/SDL_audio.c | 183 +++------------------- src/audio/SDL_sysaudio.h | 16 +- 7 files changed, 330 insertions(+), 179 deletions(-) create mode 100644 src/SDL_dataqueue.c create mode 100644 src/SDL_dataqueue.h diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index fd9732b7d61d9..1615d6c3b69e1 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -387,6 +387,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 8ae4b26fd0418..ef0f35b55ea9e 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -253,6 +253,7 @@ + @@ -357,6 +358,7 @@ + diff --git a/VisualC/SDL/SDL_VS2008.vcproj b/VisualC/SDL/SDL_VS2008.vcproj index 2ae464db50780..2675dbcebbec4 100644 --- a/VisualC/SDL/SDL_VS2008.vcproj +++ b/VisualC/SDL/SDL_VS2008.vcproj @@ -961,6 +961,14 @@ RelativePath="..\..\src\video\SDL_egl.c" > + + + + diff --git a/src/SDL_dataqueue.c b/src/SDL_dataqueue.c new file mode 100644 index 0000000000000..8aa617c1525c7 --- /dev/null +++ b/src/SDL_dataqueue.c @@ -0,0 +1,260 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2016 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "./SDL_internal.h" +#include "SDL.h" +#include "./SDL_dataqueue.h" +#include "SDL_assert.h" + +typedef struct SDL_DataQueuePacket +{ + size_t datalen; /* bytes currently in use in this packet. */ + size_t startpos; /* bytes currently consumed in this packet. */ + struct SDL_DataQueuePacket *next; /* next item in linked list. */ + Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; /* packet data */ +} SDL_DataQueuePacket; + +struct SDL_DataQueue +{ + SDL_DataQueuePacket *head; /* device fed from here. */ + SDL_DataQueuePacket *tail; /* queue fills to here. */ + SDL_DataQueuePacket *pool; /* these are unused packets. */ + size_t packet_size; /* size of new packets */ + size_t queued_bytes; /* number of bytes of data in the queue. */ +}; + +static void +SDL_FreeDataQueueList(SDL_DataQueuePacket *packet) +{ + while (packet) { + SDL_DataQueuePacket *next = packet->next; + SDL_free(packet); + packet = next; + } +} + + +/* this all expects that you managed thread safety elsewhere. */ + +SDL_DataQueue * +SDL_NewDataQueue(const size_t _packetlen, const size_t initialslack) +{ + SDL_DataQueue *queue = (SDL_DataQueue *) SDL_malloc(sizeof (SDL_DataQueue)); + + if (!queue) { + SDL_OutOfMemory(); + return NULL; + } else { + const size_t packetlen = _packetlen ? _packetlen : 1024; + const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen; + size_t i; + + SDL_zerop(queue); + queue->packet_size = packetlen; + + for (i = 0; i < wantpackets; i++) { + SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packetlen); + if (packet) { /* don't care if this fails, we'll deal later. */ + packet->datalen = 0; + packet->startpos = 0; + packet->next = queue->pool; + queue->pool = packet; + } + } + } + + return queue; +} + +void +SDL_FreeDataQueue(SDL_DataQueue *queue) +{ + if (queue) { + SDL_FreeDataQueueList(queue->head); + SDL_FreeDataQueueList(queue->pool); + SDL_free(queue); + } +} + +void +SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack) +{ + const size_t packet_size = queue ? queue->packet_size : 1; + const size_t slackpackets = (slack + (packet_size-1)) / packet_size; + SDL_DataQueuePacket *packet; + SDL_DataQueuePacket *prev = NULL; + size_t i; + + if (!queue) { + return; + } + + packet = queue->head; + + /* merge the available pool and the current queue into one list. */ + if (packet) { + queue->tail->next = queue->pool; + } else { + packet = queue->pool; + } + + /* Remove the queued packets from the device. */ + queue->tail = NULL; + queue->head = NULL; + queue->queued_bytes = 0; + queue->pool = packet; + + /* Optionally keep some slack in the pool to reduce malloc pressure. */ + for (i = 0; packet && (i < slackpackets); i++) { + prev = packet; + packet = packet->next; + } + + if (prev) { + prev->next = NULL; + } else { + queue->pool = NULL; + } + + SDL_FreeDataQueueList(packet); /* free extra packets */ +} + +int +SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len) +{ + size_t len = _len; + const Uint8 *data = (const Uint8 *) _data; + const size_t packet_size = queue ? queue->packet_size : 0; + SDL_DataQueuePacket *orighead; + SDL_DataQueuePacket *origtail; + size_t origlen; + size_t datalen; + + if (!queue) { + return SDL_InvalidParamError("queue"); + } + + orighead = queue->head; + origtail = queue->tail; + origlen = origtail ? origtail->datalen : 0; + + while (len > 0) { + SDL_DataQueuePacket *packet = queue->tail; + SDL_assert(!packet || (packet->datalen <= packet_size)); + if (!packet || (packet->datalen >= packet_size)) { + /* tail packet missing or completely full; we need a new packet. */ + packet = queue->pool; + if (packet != NULL) { + /* we have one available in the pool. */ + queue->pool = packet->next; + } else { + /* Have to allocate a new one! */ + packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packet_size); + if (packet == NULL) { + /* uhoh, reset so we've queued nothing new, free what we can. */ + if (!origtail) { + packet = queue->head; /* whole queue. */ + } else { + packet = origtail->next; /* what we added to existing queue. */ + origtail->next = NULL; + origtail->datalen = origlen; + } + queue->head = orighead; + queue->tail = origtail; + queue->pool = NULL; + + SDL_FreeDataQueueList(packet); /* give back what we can. */ + + return SDL_OutOfMemory(); + } + } + packet->datalen = 0; + packet->startpos = 0; + packet->next = NULL; + + SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0)); + if (queue->tail == NULL) { + queue->head = packet; + } else { + queue->tail->next = packet; + } + queue->tail = packet; + } + + datalen = SDL_min(len, packet_size - packet->datalen); + SDL_memcpy(packet->data + packet->datalen, data, datalen); + data += datalen; + len -= datalen; + packet->datalen += datalen; + queue->queued_bytes += datalen; + } + + return 0; +} + +size_t +SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len) +{ + size_t len = _len; + Uint8 *buf = (Uint8 *) _buf; + Uint8 *ptr = buf; + SDL_DataQueuePacket *packet; + + if (!queue) { + return 0; + } + + while ((len > 0) && ((packet = queue->head) != NULL)) { + const size_t avail = packet->datalen - packet->startpos; + const size_t cpy = SDL_min(len, avail); + SDL_assert(queue->queued_bytes >= avail); + + SDL_memcpy(ptr, packet->data + packet->startpos, cpy); + packet->startpos += cpy; + ptr += cpy; + queue->queued_bytes -= cpy; + len -= cpy; + + if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */ + queue->head = packet->next; + SDL_assert((packet->next != NULL) || (packet == queue->tail)); + packet->next = queue->pool; + queue->pool = packet; + } + } + + SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0)); + + if (queue->head == NULL) { + queue->tail = NULL; /* in case we drained the queue entirely. */ + } + + return (size_t) (ptr - buf); +} + +size_t +SDL_CountDataQueue(SDL_DataQueue *queue) +{ + return queue ? queue->queued_bytes : 0; +} + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/SDL_dataqueue.h b/src/SDL_dataqueue.h new file mode 100644 index 0000000000000..ee701dd75d12d --- /dev/null +++ b/src/SDL_dataqueue.h @@ -0,0 +1,39 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2016 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#ifndef SDL_dataqueue_h_ +#define SDL_dataqueue_h_ + +/* this is not (currently) a public API. But maybe it should be! */ + +struct SDL_DataQueue; +typedef struct SDL_DataQueue SDL_DataQueue; + +SDL_DataQueue *SDL_NewDataQueue(const size_t packetlen, const size_t initialslack); +void SDL_FreeDataQueue(SDL_DataQueue *queue); +void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack); +int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *data, const size_t len); +size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len); +size_t SDL_CountDataQueue(SDL_DataQueue *queue); + +#endif /* SDL_dataqueue_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index c7af699b5044b..4ff5400436ebb 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -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; } diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index a5574c503f74d..dff2208562947 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -25,6 +25,7 @@ #include "SDL_mutex.h" #include "SDL_thread.h" +#include "../SDL_dataqueue.h" /* !!! FIXME: These are wordy and unlocalized... */ #define DEFAULT_OUTPUT_DEVNAME "System audio output device" @@ -49,7 +50,6 @@ extern void SDL_RemoveAudioDevice(const int iscapture, void *handle); as appropriate so SDL's list of devices is accurate. */ extern void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device); - /* This is the size of a packet when using SDL_QueueAudio(). We allocate these as necessary and pool them, under the assumption that we'll eventually end up with a handful that keep recycling, meeting whatever @@ -61,15 +61,6 @@ extern void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device); The system preallocates enough packets for 2 callbacks' worth of data. */ #define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024) -/* Used by apps that queue audio instead of using the callback. */ -typedef struct SDL_AudioBufferQueue -{ - Uint8 data[SDL_AUDIOBUFFERQUEUE_PACKETLEN]; /* packet data. */ - Uint32 datalen; /* bytes currently in use in this packet. */ - Uint32 startpos; /* bytes currently consumed in this packet. */ - struct SDL_AudioBufferQueue *next; /* next item in linked list. */ -} SDL_AudioBufferQueue; - typedef struct SDL_AudioDriverImpl { void (*DetectDevices) (void); @@ -175,10 +166,7 @@ struct SDL_AudioDevice SDL_threadID threadid; /* Queued buffers (if app not using callback). */ - SDL_AudioBufferQueue *buffer_queue_head; /* device fed from here. */ - SDL_AudioBufferQueue *buffer_queue_tail; /* queue fills to here. */ - SDL_AudioBufferQueue *buffer_queue_pool; /* these are unused packets. */ - Uint32 queued_bytes; /* number of bytes of audio data in the queue. */ + SDL_DataQueue *buffer_queue; /* * * */ /* Data private to this driver */