Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
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.
- Loading branch information
Showing
with
330 additions
and 179 deletions.
- +1 −0 VisualC/SDL/SDL.vcxproj
- +2 −0 VisualC/SDL/SDL.vcxproj.filters
- +8 −0 VisualC/SDL/SDL_VS2008.vcproj
- +260 −0 src/SDL_dataqueue.c
- +39 −0 src/SDL_dataqueue.h
- +18 −165 src/audio/SDL_audio.c
- +2 −14 src/audio/SDL_sysaudio.h
@@ -0,0 +1,260 @@ | ||
/* | ||
Simple DirectMedia Layer | ||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> | ||
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: */ | ||
|
@@ -0,0 +1,39 @@ | ||
/* | ||
Simple DirectMedia Layer | ||
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> | ||
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: */ | ||
|
Oops, something went wrong.