src/SDL_dataqueue.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 06 Dec 2016 02:23:54 -0500
changeset 10681 34b25c97b17f
child 10737 3406a0f8b041
permissions -rw-r--r--
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.
icculus@10681
     1
/*
icculus@10681
     2
  Simple DirectMedia Layer
icculus@10681
     3
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
icculus@10681
     4
icculus@10681
     5
  This software is provided 'as-is', without any express or implied
icculus@10681
     6
  warranty.  In no event will the authors be held liable for any damages
icculus@10681
     7
  arising from the use of this software.
icculus@10681
     8
icculus@10681
     9
  Permission is granted to anyone to use this software for any purpose,
icculus@10681
    10
  including commercial applications, and to alter it and redistribute it
icculus@10681
    11
  freely, subject to the following restrictions:
icculus@10681
    12
icculus@10681
    13
  1. The origin of this software must not be misrepresented; you must not
icculus@10681
    14
     claim that you wrote the original software. If you use this software
icculus@10681
    15
     in a product, an acknowledgment in the product documentation would be
icculus@10681
    16
     appreciated but is not required.
icculus@10681
    17
  2. Altered source versions must be plainly marked as such, and must not be
icculus@10681
    18
     misrepresented as being the original software.
icculus@10681
    19
  3. This notice may not be removed or altered from any source distribution.
icculus@10681
    20
*/
icculus@10681
    21
icculus@10681
    22
#include "./SDL_internal.h"
icculus@10681
    23
#include "SDL.h"
icculus@10681
    24
#include "./SDL_dataqueue.h"
icculus@10681
    25
#include "SDL_assert.h"
icculus@10681
    26
icculus@10681
    27
typedef struct SDL_DataQueuePacket
icculus@10681
    28
{
icculus@10681
    29
    size_t datalen;  /* bytes currently in use in this packet. */
icculus@10681
    30
    size_t startpos;  /* bytes currently consumed in this packet. */
icculus@10681
    31
    struct SDL_DataQueuePacket *next;  /* next item in linked list. */
icculus@10681
    32
    Uint8 data[SDL_VARIABLE_LENGTH_ARRAY];  /* packet data */
icculus@10681
    33
} SDL_DataQueuePacket;
icculus@10681
    34
icculus@10681
    35
struct SDL_DataQueue
icculus@10681
    36
{
icculus@10681
    37
    SDL_DataQueuePacket *head; /* device fed from here. */
icculus@10681
    38
    SDL_DataQueuePacket *tail; /* queue fills to here. */
icculus@10681
    39
    SDL_DataQueuePacket *pool; /* these are unused packets. */
icculus@10681
    40
    size_t packet_size;   /* size of new packets */
icculus@10681
    41
    size_t queued_bytes;  /* number of bytes of data in the queue. */
icculus@10681
    42
};
icculus@10681
    43
icculus@10681
    44
static void
icculus@10681
    45
SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
icculus@10681
    46
{
icculus@10681
    47
    while (packet) {
icculus@10681
    48
        SDL_DataQueuePacket *next = packet->next;
icculus@10681
    49
        SDL_free(packet);
icculus@10681
    50
        packet = next;
icculus@10681
    51
    }
icculus@10681
    52
}
icculus@10681
    53
icculus@10681
    54
icculus@10681
    55
/* this all expects that you managed thread safety elsewhere. */
icculus@10681
    56
icculus@10681
    57
SDL_DataQueue *
icculus@10681
    58
SDL_NewDataQueue(const size_t _packetlen, const size_t initialslack)
icculus@10681
    59
{
icculus@10681
    60
    SDL_DataQueue *queue = (SDL_DataQueue *) SDL_malloc(sizeof (SDL_DataQueue));
icculus@10681
    61
icculus@10681
    62
    if (!queue) {
icculus@10681
    63
        SDL_OutOfMemory();
icculus@10681
    64
        return NULL;
icculus@10681
    65
    } else {
icculus@10681
    66
        const size_t packetlen = _packetlen ? _packetlen : 1024;
icculus@10681
    67
        const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
icculus@10681
    68
        size_t i;
icculus@10681
    69
icculus@10681
    70
        SDL_zerop(queue);
icculus@10681
    71
        queue->packet_size = packetlen;
icculus@10681
    72
icculus@10681
    73
        for (i = 0; i < wantpackets; i++) {
icculus@10681
    74
            SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packetlen);
icculus@10681
    75
            if (packet) { /* don't care if this fails, we'll deal later. */
icculus@10681
    76
                packet->datalen = 0;
icculus@10681
    77
                packet->startpos = 0;
icculus@10681
    78
                packet->next = queue->pool;
icculus@10681
    79
                queue->pool = packet;
icculus@10681
    80
            }
icculus@10681
    81
        }
icculus@10681
    82
    }
icculus@10681
    83
icculus@10681
    84
    return queue;
icculus@10681
    85
}
icculus@10681
    86
icculus@10681
    87
void
icculus@10681
    88
SDL_FreeDataQueue(SDL_DataQueue *queue)
icculus@10681
    89
{
icculus@10681
    90
    if (queue) {
icculus@10681
    91
        SDL_FreeDataQueueList(queue->head);
icculus@10681
    92
        SDL_FreeDataQueueList(queue->pool);
icculus@10681
    93
        SDL_free(queue);
icculus@10681
    94
    }
icculus@10681
    95
}
icculus@10681
    96
icculus@10681
    97
void
icculus@10681
    98
SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
icculus@10681
    99
{
icculus@10681
   100
    const size_t packet_size = queue ? queue->packet_size : 1;
icculus@10681
   101
    const size_t slackpackets = (slack + (packet_size-1)) / packet_size;
icculus@10681
   102
    SDL_DataQueuePacket *packet;
icculus@10681
   103
    SDL_DataQueuePacket *prev = NULL;
icculus@10681
   104
    size_t i;
icculus@10681
   105
icculus@10681
   106
    if (!queue) {
icculus@10681
   107
        return;
icculus@10681
   108
    }
icculus@10681
   109
icculus@10681
   110
    packet = queue->head;
icculus@10681
   111
icculus@10681
   112
    /* merge the available pool and the current queue into one list. */
icculus@10681
   113
    if (packet) {
icculus@10681
   114
        queue->tail->next = queue->pool;
icculus@10681
   115
    } else {
icculus@10681
   116
        packet = queue->pool;
icculus@10681
   117
    }
icculus@10681
   118
icculus@10681
   119
    /* Remove the queued packets from the device. */
icculus@10681
   120
    queue->tail = NULL;
icculus@10681
   121
    queue->head = NULL;
icculus@10681
   122
    queue->queued_bytes = 0;
icculus@10681
   123
    queue->pool = packet;
icculus@10681
   124
icculus@10681
   125
    /* Optionally keep some slack in the pool to reduce malloc pressure. */
icculus@10681
   126
    for (i = 0; packet && (i < slackpackets); i++) {
icculus@10681
   127
        prev = packet;
icculus@10681
   128
        packet = packet->next;
icculus@10681
   129
    }
icculus@10681
   130
icculus@10681
   131
    if (prev) {
icculus@10681
   132
        prev->next = NULL;
icculus@10681
   133
    } else {
icculus@10681
   134
        queue->pool = NULL;
icculus@10681
   135
    }
icculus@10681
   136
icculus@10681
   137
    SDL_FreeDataQueueList(packet);  /* free extra packets */
icculus@10681
   138
}
icculus@10681
   139
icculus@10681
   140
int
icculus@10681
   141
SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
icculus@10681
   142
{
icculus@10681
   143
    size_t len = _len;
icculus@10681
   144
    const Uint8 *data = (const Uint8 *) _data;
icculus@10681
   145
    const size_t packet_size = queue ? queue->packet_size : 0;
icculus@10681
   146
    SDL_DataQueuePacket *orighead;
icculus@10681
   147
    SDL_DataQueuePacket *origtail;
icculus@10681
   148
    size_t origlen;
icculus@10681
   149
    size_t datalen;
icculus@10681
   150
icculus@10681
   151
    if (!queue) {
icculus@10681
   152
        return SDL_InvalidParamError("queue");
icculus@10681
   153
    }
icculus@10681
   154
icculus@10681
   155
    orighead = queue->head;
icculus@10681
   156
    origtail = queue->tail;
icculus@10681
   157
    origlen = origtail ? origtail->datalen : 0;
icculus@10681
   158
icculus@10681
   159
    while (len > 0) {
icculus@10681
   160
        SDL_DataQueuePacket *packet = queue->tail;
icculus@10681
   161
        SDL_assert(!packet || (packet->datalen <= packet_size));
icculus@10681
   162
        if (!packet || (packet->datalen >= packet_size)) {
icculus@10681
   163
            /* tail packet missing or completely full; we need a new packet. */
icculus@10681
   164
            packet = queue->pool;
icculus@10681
   165
            if (packet != NULL) {
icculus@10681
   166
                /* we have one available in the pool. */
icculus@10681
   167
                queue->pool = packet->next;
icculus@10681
   168
            } else {
icculus@10681
   169
                /* Have to allocate a new one! */
icculus@10681
   170
                packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packet_size);
icculus@10681
   171
                if (packet == NULL) {
icculus@10681
   172
                    /* uhoh, reset so we've queued nothing new, free what we can. */
icculus@10681
   173
                    if (!origtail) {
icculus@10681
   174
                        packet = queue->head;  /* whole queue. */
icculus@10681
   175
                    } else {
icculus@10681
   176
                        packet = origtail->next;  /* what we added to existing queue. */
icculus@10681
   177
                        origtail->next = NULL;
icculus@10681
   178
                        origtail->datalen = origlen;
icculus@10681
   179
                    }
icculus@10681
   180
                    queue->head = orighead;
icculus@10681
   181
                    queue->tail = origtail;
icculus@10681
   182
                    queue->pool = NULL;
icculus@10681
   183
icculus@10681
   184
                    SDL_FreeDataQueueList(packet);  /* give back what we can. */
icculus@10681
   185
icculus@10681
   186
                    return SDL_OutOfMemory();
icculus@10681
   187
                }
icculus@10681
   188
            }
icculus@10681
   189
            packet->datalen = 0;
icculus@10681
   190
            packet->startpos = 0;
icculus@10681
   191
            packet->next = NULL;
icculus@10681
   192
                
icculus@10681
   193
            SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
icculus@10681
   194
            if (queue->tail == NULL) {
icculus@10681
   195
                queue->head = packet;
icculus@10681
   196
            } else {
icculus@10681
   197
                queue->tail->next = packet;
icculus@10681
   198
            }
icculus@10681
   199
            queue->tail = packet;
icculus@10681
   200
        }
icculus@10681
   201
icculus@10681
   202
        datalen = SDL_min(len, packet_size - packet->datalen);
icculus@10681
   203
        SDL_memcpy(packet->data + packet->datalen, data, datalen);
icculus@10681
   204
        data += datalen;
icculus@10681
   205
        len -= datalen;
icculus@10681
   206
        packet->datalen += datalen;
icculus@10681
   207
        queue->queued_bytes += datalen;
icculus@10681
   208
    }
icculus@10681
   209
icculus@10681
   210
    return 0;
icculus@10681
   211
}
icculus@10681
   212
icculus@10681
   213
size_t
icculus@10681
   214
SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
icculus@10681
   215
{
icculus@10681
   216
    size_t len = _len;
icculus@10681
   217
    Uint8 *buf = (Uint8 *) _buf;
icculus@10681
   218
    Uint8 *ptr = buf;
icculus@10681
   219
    SDL_DataQueuePacket *packet;
icculus@10681
   220
icculus@10681
   221
    if (!queue) {
icculus@10681
   222
        return 0;
icculus@10681
   223
    }
icculus@10681
   224
icculus@10681
   225
    while ((len > 0) && ((packet = queue->head) != NULL)) {
icculus@10681
   226
        const size_t avail = packet->datalen - packet->startpos;
icculus@10681
   227
        const size_t cpy = SDL_min(len, avail);
icculus@10681
   228
        SDL_assert(queue->queued_bytes >= avail);
icculus@10681
   229
icculus@10681
   230
        SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
icculus@10681
   231
        packet->startpos += cpy;
icculus@10681
   232
        ptr += cpy;
icculus@10681
   233
        queue->queued_bytes -= cpy;
icculus@10681
   234
        len -= cpy;
icculus@10681
   235
icculus@10681
   236
        if (packet->startpos == packet->datalen) {  /* packet is done, put it in the pool. */
icculus@10681
   237
            queue->head = packet->next;
icculus@10681
   238
            SDL_assert((packet->next != NULL) || (packet == queue->tail));
icculus@10681
   239
            packet->next = queue->pool;
icculus@10681
   240
            queue->pool = packet;
icculus@10681
   241
        }
icculus@10681
   242
    }
icculus@10681
   243
icculus@10681
   244
    SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
icculus@10681
   245
icculus@10681
   246
    if (queue->head == NULL) {
icculus@10681
   247
        queue->tail = NULL;  /* in case we drained the queue entirely. */
icculus@10681
   248
    }
icculus@10681
   249
icculus@10681
   250
    return (size_t) (ptr - buf);
icculus@10681
   251
}
icculus@10681
   252
icculus@10681
   253
size_t
icculus@10681
   254
SDL_CountDataQueue(SDL_DataQueue *queue)
icculus@10681
   255
{
icculus@10681
   256
    return queue ? queue->queued_bytes : 0;
icculus@10681
   257
}
icculus@10681
   258
icculus@10681
   259
/* vi: set ts=4 sw=4 expandtab: */
icculus@10681
   260