Added audio device buffer queueing API.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 22 Jul 2014 21:41:49 -0400
changeset 9012aa058c87737b
parent 9011 c3591f14ab7e
child 9014 795fb603eda8
Added audio device buffer queueing API.
.hgignore
include/SDL_audio.h
src/audio/SDL_audio.c
src/audio/SDL_sysaudio.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
test/Makefile.in
test/README
test/loopwavequeue.c
     1.1 --- a/.hgignore	Sat Jul 26 16:52:26 2014 -0700
     1.2 +++ b/.hgignore	Tue Jul 22 21:41:49 2014 -0400
     1.3 @@ -73,6 +73,7 @@
     1.4  test/SDL2.dll
     1.5  test/checkkeys
     1.6  test/loopwave
     1.7 +test/loopwavequeue
     1.8  test/testatomic
     1.9  test/testaudioinfo
    1.10  test/testautomation
     2.1 --- a/include/SDL_audio.h	Sat Jul 26 16:52:26 2014 -0700
     2.2 +++ b/include/SDL_audio.h	Tue Jul 22 21:41:49 2014 -0400
     2.3 @@ -155,6 +155,9 @@
     2.4   *
     2.5   *  Once the callback returns, the buffer will no longer be valid.
     2.6   *  Stereo samples are stored in a LRLRLR ordering.
     2.7 + *
     2.8 + *  You can choose to avoid callbacks and use SDL_QueueAudio() instead, if
     2.9 + *  you like. Just open your audio device with a NULL callback.
    2.10   */
    2.11  typedef void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 * stream,
    2.12                                              int len);
    2.13 @@ -171,8 +174,8 @@
    2.14      Uint16 samples;             /**< Audio buffer size in samples (power of 2) */
    2.15      Uint16 padding;             /**< Necessary for some compile environments */
    2.16      Uint32 size;                /**< Audio buffer size in bytes (calculated) */
    2.17 -    SDL_AudioCallback callback;
    2.18 -    void *userdata;
    2.19 +    SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
    2.20 +    void *userdata;             /**< Userdata passed to callback (ignored for NULL callbacks). */
    2.21  } SDL_AudioSpec;
    2.22  
    2.23  
    2.24 @@ -273,9 +276,11 @@
    2.25   *      to the audio buffer, and the length in bytes of the audio buffer.
    2.26   *      This function usually runs in a separate thread, and so you should
    2.27   *      protect data structures that it accesses by calling SDL_LockAudio()
    2.28 - *      and SDL_UnlockAudio() in your code.
    2.29 + *      and SDL_UnlockAudio() in your code. Alternately, you may pass a NULL
    2.30 + *      pointer here, and call SDL_QueueAudio() with some frequency, to queue
    2.31 + *      more audio samples to be played.
    2.32   *    - \c desired->userdata is passed as the first parameter to your callback
    2.33 - *      function.
    2.34 + *      function. If you passed a NULL callback, this value is ignored.
    2.35   *
    2.36   *  The audio device starts out playing silence when it's opened, and should
    2.37   *  be enabled for playing by calling \c SDL_PauseAudio(0) when you are ready
    2.38 @@ -475,6 +480,100 @@
    2.39                                                  Uint32 len, int volume);
    2.40  
    2.41  /**
    2.42 + *  Queue more audio on non-callback devices.
    2.43 + *
    2.44 + *  SDL offers two ways to feed audio to the device: you can either supply a
    2.45 + *  callback that SDL triggers with some frequency to obtain more audio
    2.46 + *  (pull method), or you can supply no callback, and then SDL will expect
    2.47 + *  you to supply data at regular intervals (push method) with this function.
    2.48 + *
    2.49 + *  There are no limits on the amount of data you can queue, short of
    2.50 + *  exhaustion of address space. Queued data will drain to the device as
    2.51 + *  necessary without further intervention from you. If the device needs
    2.52 + *  audio but there is not enough queued, it will play silence to make up
    2.53 + *  the difference. This means you will have skips in your audio playback
    2.54 + *  if you aren't routinely queueing sufficient data.
    2.55 + *
    2.56 + *  This function copies the supplied data, so you are safe to free it when
    2.57 + *  the function returns. This function is thread-safe, but queueing to the
    2.58 + *  same device from two threads at once does not promise which buffer will
    2.59 + *  be queued first.
    2.60 + *
    2.61 + *  You may not queue audio on a device that is using an application-supplied
    2.62 + *  callback; doing so returns an error. You have to use the audio callback
    2.63 + *  or queue audio with this function, but not both.
    2.64 + *
    2.65 + *  You should not call SDL_LockAudio() on the device before queueing; SDL
    2.66 + *  handles locking internally for this function.
    2.67 + *
    2.68 + *  \param dev The device ID to which we will queue audio.
    2.69 + *  \param data The data to queue to the device for later playback.
    2.70 + *  \param len The number of bytes (not samples!) to which (data) points.
    2.71 + *  \return zero on success, -1 on error.
    2.72 + *
    2.73 + *  \sa SDL_GetQueuedAudioSize
    2.74 + *  \sa SDL_ClearQueuedAudio
    2.75 + */
    2.76 +extern DECLSPEC int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID dev, const void *data, Uint32 len);
    2.77 +
    2.78 +/**
    2.79 + *  Get the number of bytes of still-queued audio.
    2.80 + *
    2.81 + *  This is the number of bytes that have been queued for playback with
    2.82 + *  SDL_QueueAudio(), but have not yet been sent to the hardware.
    2.83 + *
    2.84 + *  Once we've sent it to the hardware, this function can not decide the exact
    2.85 + *  byte boundary of what has been played. It's possible that we just gave the
    2.86 + *  hardware several kilobytes right before you called this function, but it
    2.87 + *  hasn't played any of it yet, or maybe half of it, etc.
    2.88 + *
    2.89 + *  You may not queue audio on a device that is using an application-supplied
    2.90 + *  callback; calling this function on such a device always returns 0.
    2.91 + *  You have to use the audio callback or queue audio with SDL_QueueAudio(),
    2.92 + *  but not both.
    2.93 + *
    2.94 + *  You should not call SDL_LockAudio() on the device before querying; SDL
    2.95 + *  handles locking internally for this function.
    2.96 + *
    2.97 + *  \param dev The device ID of which we will query queued audio size.
    2.98 + *  \return Number of bytes (not samples!) of queued audio.
    2.99 + *
   2.100 + *  \sa SDL_QueueAudio
   2.101 + *  \sa SDL_ClearQueuedAudio
   2.102 + */
   2.103 +extern DECLSPEC Uint32 SDLCALL SDL_GetQueuedAudioSize(SDL_AudioDeviceID dev);
   2.104 +
   2.105 +/**
   2.106 + *  Drop any queued audio data waiting to be sent to the hardware.
   2.107 + *
   2.108 + *  Immediately after this call, SDL_GetQueuedAudioSize() will return 0 and
   2.109 + *  the hardware will start playing silence if more audio isn't queued.
   2.110 + *
   2.111 + *  This will not prevent playback of queued audio that's already been sent
   2.112 + *  to the hardware, as we can not undo that, so expect there to be some
   2.113 + *  fraction of a second of audio that might still be heard. This can be
   2.114 + *  useful if you want to, say, drop any pending music during a level change
   2.115 + *  in your game.
   2.116 + *
   2.117 + *  You may not queue audio on a device that is using an application-supplied
   2.118 + *  callback; calling this function on such a device is always a no-op.
   2.119 + *  You have to use the audio callback or queue audio with SDL_QueueAudio(),
   2.120 + *  but not both.
   2.121 + *
   2.122 + *  You should not call SDL_LockAudio() on the device before clearing the
   2.123 + *  queue; SDL handles locking internally for this function.
   2.124 + *
   2.125 + *  This function always succeeds and thus returns void.
   2.126 + *
   2.127 + *  \param dev The device ID of which to clear the audio queue.
   2.128 + *
   2.129 + *  \sa SDL_QueueAudio
   2.130 + *  \sa SDL_GetQueuedAudioSize
   2.131 + */
   2.132 +extern DECLSPEC void SDLCALL SDL_ClearQueuedAudio(SDL_AudioDeviceID dev);
   2.133 +
   2.134 +
   2.135 +/**
   2.136   *  \name Audio lock functions
   2.137   *
   2.138   *  The lock manipulated by these functions protects the callback function.
     3.1 --- a/src/audio/SDL_audio.c	Sat Jul 26 16:52:26 2014 -0700
     3.2 +++ b/src/audio/SDL_audio.c	Tue Jul 22 21:41:49 2014 -0400
     3.3 @@ -324,6 +324,181 @@
     3.4  }
     3.5  #endif
     3.6  
     3.7 +
     3.8 +/* buffer queueing support... */
     3.9 +
    3.10 +/* this expects that you managed thread safety elsewhere. */
    3.11 +static void
    3.12 +free_audio_queue(SDL_AudioBufferQueue *buffer)
    3.13 +{
    3.14 +    while (buffer) {
    3.15 +        SDL_AudioBufferQueue *next = buffer->next;
    3.16 +        SDL_free(buffer);
    3.17 +        buffer = next;
    3.18 +    }
    3.19 +}
    3.20 +
    3.21 +static void SDLCALL
    3.22 +SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int _len)
    3.23 +{
    3.24 +    /* this function always holds the mixer lock before being called. */
    3.25 +    Uint32 len = (Uint32) _len;
    3.26 +    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
    3.27 +    SDL_AudioBufferQueue *buffer;
    3.28 +
    3.29 +    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
    3.30 +    SDL_assert(_len >= 0);  /* this shouldn't ever happen, right?! */
    3.31 +
    3.32 +    while ((len > 0) && ((buffer = device->buffer_queue_head) != NULL)) {
    3.33 +        const Uint32 avail = buffer->datalen - buffer->startpos;
    3.34 +        const Uint32 cpy = SDL_min(len, avail);
    3.35 +        SDL_assert(device->queued_bytes >= avail);
    3.36 +
    3.37 +        SDL_memcpy(stream, buffer->data + buffer->startpos, cpy);
    3.38 +        buffer->startpos += cpy;
    3.39 +        stream += cpy;
    3.40 +        device->queued_bytes -= cpy;
    3.41 +        len -= cpy;
    3.42 +
    3.43 +        if (buffer->startpos == buffer->datalen) {  /* packet is done, put it in the pool. */
    3.44 +            device->buffer_queue_head = buffer->next;
    3.45 +            SDL_assert((buffer->next != NULL) || (buffer == device->buffer_queue_tail));
    3.46 +            buffer->next = device->buffer_queue_pool;
    3.47 +            device->buffer_queue_pool = buffer;
    3.48 +        }
    3.49 +    }
    3.50 +
    3.51 +    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
    3.52 +
    3.53 +    if (len > 0) {  /* fill any remaining space in the stream with silence. */
    3.54 +        SDL_assert(device->buffer_queue_head == NULL);
    3.55 +        SDL_memset(stream, device->spec.silence, len);
    3.56 +    }
    3.57 +
    3.58 +    if (device->buffer_queue_head == NULL) {
    3.59 +        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
    3.60 +    }
    3.61 +}
    3.62 +
    3.63 +int
    3.64 +SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
    3.65 +{
    3.66 +    SDL_AudioDevice *device = get_audio_device(devid);
    3.67 +    const Uint8 *data = (const Uint8 *) _data;
    3.68 +    SDL_AudioBufferQueue *orighead;
    3.69 +    SDL_AudioBufferQueue *origtail;
    3.70 +    Uint32 origlen;
    3.71 +    Uint32 datalen;
    3.72 +
    3.73 +    if (!device) {
    3.74 +        return -1;  /* get_audio_device() will have set the error state */
    3.75 +    }
    3.76 +
    3.77 +    if (device->spec.callback != SDL_BufferQueueDrainCallback) {
    3.78 +        return SDL_SetError("Audio device has a callback, queueing not allowed");
    3.79 +    }
    3.80 +
    3.81 +    current_audio.impl.LockDevice(device);
    3.82 +
    3.83 +    orighead = device->buffer_queue_head;
    3.84 +    origtail = device->buffer_queue_tail;
    3.85 +    origlen = origtail ? origtail->datalen : 0;
    3.86 +
    3.87 +    while (len > 0) {
    3.88 +        SDL_AudioBufferQueue *packet = device->buffer_queue_tail;
    3.89 +        SDL_assert(!packet || (packet->datalen <= SDL_AUDIOBUFFERQUEUE_PACKETLEN));
    3.90 +        if (!packet || (packet->datalen >= SDL_AUDIOBUFFERQUEUE_PACKETLEN)) {
    3.91 +            /* tail packet missing or completely full; we need a new packet. */
    3.92 +            packet = device->buffer_queue_pool;
    3.93 +            if (packet != NULL) {
    3.94 +                /* we have one available in the pool. */
    3.95 +                device->buffer_queue_pool = packet->next;
    3.96 +            } else {
    3.97 +                /* Have to allocate a new one! */
    3.98 +                packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
    3.99 +                if (packet == NULL) {
   3.100 +                    /* uhoh, reset so we've queued nothing new, free what we can. */
   3.101 +                    if (!origtail) {
   3.102 +                        packet = device->buffer_queue_head;  /* whole queue. */
   3.103 +                    } else {
   3.104 +                        packet = origtail->next;  /* what we added to existing queue. */
   3.105 +                        origtail->next = NULL;
   3.106 +                        origtail->datalen = origlen;
   3.107 +                    }
   3.108 +                    device->buffer_queue_head = orighead;
   3.109 +                    device->buffer_queue_tail = origtail;
   3.110 +                    device->buffer_queue_pool = NULL;
   3.111 +
   3.112 +                    current_audio.impl.UnlockDevice(device);
   3.113 +
   3.114 +                    free_audio_queue(packet);  /* give back what we can. */
   3.115 +
   3.116 +                    return SDL_OutOfMemory();
   3.117 +                }
   3.118 +            }
   3.119 +            packet->datalen = 0;
   3.120 +            packet->startpos = 0;
   3.121 +            packet->next = NULL;
   3.122 +
   3.123 +            SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
   3.124 +            if (device->buffer_queue_tail == NULL) {
   3.125 +                device->buffer_queue_head = packet;
   3.126 +            } else {
   3.127 +                device->buffer_queue_tail->next = packet;
   3.128 +            }
   3.129 +            device->buffer_queue_tail = packet;
   3.130 +        }
   3.131 +
   3.132 +        datalen = SDL_min(len, SDL_AUDIOBUFFERQUEUE_PACKETLEN - packet->datalen);
   3.133 +        SDL_memcpy(packet->data + packet->datalen, data, datalen);
   3.134 +        data += datalen;
   3.135 +        len -= datalen;
   3.136 +        packet->datalen += datalen;
   3.137 +        device->queued_bytes += datalen;
   3.138 +    }
   3.139 +
   3.140 +    current_audio.impl.UnlockDevice(device);
   3.141 +
   3.142 +    return 0;
   3.143 +}
   3.144 +
   3.145 +Uint32
   3.146 +SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
   3.147 +{
   3.148 +    /* this happens to work for non-queueing devices, since we memset()
   3.149 +       the device to zero at init time, and these devices should return 0. */
   3.150 +    Uint32 retval = 0;
   3.151 +    SDL_AudioDevice *device = get_audio_device(devid);
   3.152 +    if (device) {
   3.153 +        current_audio.impl.LockDevice(device);
   3.154 +        retval = device->queued_bytes;
   3.155 +        current_audio.impl.UnlockDevice(device);
   3.156 +    }
   3.157 +
   3.158 +    return retval;
   3.159 +}
   3.160 +
   3.161 +void
   3.162 +SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
   3.163 +{
   3.164 +    SDL_AudioDevice *device = get_audio_device(devid);
   3.165 +    SDL_AudioBufferQueue *buffer = NULL;
   3.166 +    if (!device) {
   3.167 +        return;  /* nothing to do. */
   3.168 +    }
   3.169 +
   3.170 +    /* Blank out the device and release the mutex. Free it afterwards. */
   3.171 +    current_audio.impl.LockDevice(device);
   3.172 +    buffer = device->buffer_queue_head;
   3.173 +    device->buffer_queue_tail = NULL;
   3.174 +    device->buffer_queue_head = NULL;
   3.175 +    device->queued_bytes = 0;
   3.176 +    current_audio.impl.UnlockDevice(device);
   3.177 +
   3.178 +    free_audio_queue(buffer);
   3.179 +}
   3.180 +
   3.181 +
   3.182  #if defined(__ANDROID__)
   3.183  #include <android/log.h>
   3.184  #endif
   3.185 @@ -800,6 +975,10 @@
   3.186          current_audio.impl.CloseDevice(device);
   3.187          device->opened = 0;
   3.188      }
   3.189 +
   3.190 +    free_audio_queue(device->buffer_queue_head);
   3.191 +    free_audio_queue(device->buffer_queue_pool);
   3.192 +
   3.193      SDL_FreeAudioMem(device);
   3.194  }
   3.195  
   3.196 @@ -814,11 +993,6 @@
   3.197  {
   3.198      SDL_memcpy(prepared, orig, sizeof(SDL_AudioSpec));
   3.199  
   3.200 -    if (orig->callback == NULL) {
   3.201 -        SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   3.202 -        return 0;
   3.203 -    }
   3.204 -
   3.205      if (orig->freq == 0) {
   3.206          const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY");
   3.207          if ((!env) || ((prepared->freq = SDL_atoi(env)) == 0)) {
   3.208 @@ -871,7 +1045,6 @@
   3.209      return 1;
   3.210  }
   3.211  
   3.212 -
   3.213  static SDL_AudioDeviceID
   3.214  open_audio_device(const char *devname, int iscapture,
   3.215                    const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
   3.216 @@ -950,7 +1123,7 @@
   3.217          SDL_OutOfMemory();
   3.218          return 0;
   3.219      }
   3.220 -    SDL_memset(device, '\0', sizeof(SDL_AudioDevice));
   3.221 +    SDL_zerop(device);
   3.222      device->spec = *obtained;
   3.223      device->enabled = 1;
   3.224      device->paused = 1;
   3.225 @@ -968,8 +1141,9 @@
   3.226  
   3.227      /* force a device detection if we haven't done one yet. */
   3.228      if ( ((iscapture) && (current_audio.inputDevices == NULL)) ||
   3.229 -         ((!iscapture) && (current_audio.outputDevices == NULL)) )
   3.230 +         ((!iscapture) && (current_audio.outputDevices == NULL)) ) {
   3.231          SDL_GetNumAudioDevices(iscapture);
   3.232 +    }
   3.233  
   3.234      if (current_audio.impl.OpenDevice(device, devname, iscapture) < 0) {
   3.235          close_audio_device(device);
   3.236 @@ -1043,6 +1217,25 @@
   3.237          }
   3.238      }
   3.239  
   3.240 +    if (device->spec.callback == NULL) {  /* use buffer queueing? */
   3.241 +        /* pool a few packets to start. Enough for two callbacks. */
   3.242 +        const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
   3.243 +        const int wantbytes = ((device->convert.needed) ? device->convert.len : device->spec.size) * 2;
   3.244 +        const int wantpackets = (wantbytes / packetlen) + ((wantbytes % packetlen) ? packetlen : 0);
   3.245 +        for (i = 0; i < wantpackets; i++) {
   3.246 +            SDL_AudioBufferQueue *packet = (SDL_AudioBufferQueue *) SDL_malloc(sizeof (SDL_AudioBufferQueue));
   3.247 +            if (packet) { /* don't care if this fails, we'll deal later. */
   3.248 +                packet->datalen = 0;
   3.249 +                packet->startpos = 0;
   3.250 +                packet->next = device->buffer_queue_pool;
   3.251 +                device->buffer_queue_pool = packet;
   3.252 +            }
   3.253 +        }
   3.254 +
   3.255 +        device->spec.callback = SDL_BufferQueueDrainCallback;
   3.256 +        device->spec.userdata = device;
   3.257 +    }
   3.258 +
   3.259      /* Find an available device ID and store the structure... */
   3.260      for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
   3.261          if (open_devices[id] == NULL) {
     4.1 --- a/src/audio/SDL_sysaudio.h	Sat Jul 26 16:52:26 2014 -0700
     4.2 +++ b/src/audio/SDL_sysaudio.h	Tue Jul 22 21:41:49 2014 -0400
     4.3 @@ -33,6 +33,26 @@
     4.4  /* Used by audio targets during DetectDevices() */
     4.5  typedef void (*SDL_AddAudioDevice)(const char *name);
     4.6  
     4.7 +/* This is the size of a packet when using SDL_QueueAudio(). We allocate
     4.8 +   these as necessary and pool them, under the assumption that we'll
     4.9 +   eventually end up with a handful that keep recycling, meeting whatever
    4.10 +   the app needs. We keep packing data tightly as more arrives to avoid
    4.11 +   wasting space, and if we get a giant block of data, we'll split them
    4.12 +   into multiple packets behind the scenes. My expectation is that most
    4.13 +   apps will have 2-3 of these in the pool. 8k should cover most needs, but
    4.14 +   if this is crippling for some embedded system, we can #ifdef this.
    4.15 +   The system preallocates enough packets for 2 callbacks' worth of data. */
    4.16 +#define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024)
    4.17 +
    4.18 +/* Used by apps that queue audio instead of using the callback. */
    4.19 +typedef struct SDL_AudioBufferQueue
    4.20 +{
    4.21 +    Uint8 data[SDL_AUDIOBUFFERQUEUE_PACKETLEN];  /* packet data. */
    4.22 +    Uint32 datalen;  /* bytes currently in use in this packet. */
    4.23 +    Uint32 startpos;  /* bytes currently consumed in this packet. */
    4.24 +    struct SDL_AudioBufferQueue *next;  /* next item in linked list. */
    4.25 +} SDL_AudioBufferQueue;
    4.26 +
    4.27  typedef struct SDL_AudioDriverImpl
    4.28  {
    4.29      void (*DetectDevices) (int iscapture, SDL_AddAudioDevice addfn);
    4.30 @@ -119,6 +139,12 @@
    4.31      SDL_Thread *thread;
    4.32      SDL_threadID threadid;
    4.33  
    4.34 +    /* Queued buffers (if app not using callback). */
    4.35 +    SDL_AudioBufferQueue *buffer_queue_head; /* device fed from here. */
    4.36 +    SDL_AudioBufferQueue *buffer_queue_tail; /* queue fills to here. */
    4.37 +    SDL_AudioBufferQueue *buffer_queue_pool; /* these are unused packets. */
    4.38 +    Uint32 queued_bytes;  /* number of bytes of audio data in the queue. */
    4.39 +
    4.40      /* * * */
    4.41      /* Data private to this driver */
    4.42      struct SDL_PrivateAudioData *hidden;
     5.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Sat Jul 26 16:52:26 2014 -0700
     5.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Tue Jul 22 21:41:49 2014 -0400
     5.3 @@ -588,3 +588,6 @@
     5.4  #define SDL_SetWindowHitTest SDL_SetWindowHitTest_REAL
     5.5  #define SDL_GetGlobalMouseState SDL_GetGlobalMouseState_REAL
     5.6  #define SDL_HasAVX2 SDL_HasAVX2_REAL
     5.7 +#define SDL_QueueAudio SDL_QueueAudio_REAL
     5.8 +#define SDL_GetQueuedAudioSize SDL_GetQueuedAudioSize_REAL
     5.9 +#define SDL_ClearQueuedAudio SDL_ClearQueuedAudio_REAL
     6.1 --- a/src/dynapi/SDL_dynapi_procs.h	Sat Jul 26 16:52:26 2014 -0700
     6.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Tue Jul 22 21:41:49 2014 -0400
     6.3 @@ -620,3 +620,6 @@
     6.4  SDL_DYNAPI_PROC(int,SDL_SetWindowHitTest,(SDL_Window *a, SDL_HitTest b, void *c),(a,b,c),return)
     6.5  SDL_DYNAPI_PROC(Uint32,SDL_GetGlobalMouseState,(int *a, int *b),(a,b),return)
     6.6  SDL_DYNAPI_PROC(SDL_bool,SDL_HasAVX2,(void),(),return)
     6.7 +SDL_DYNAPI_PROC(int,SDL_QueueAudio,(SDL_AudioDeviceID a, const void *b, Uint32 c),(a,b,c),return)
     6.8 +SDL_DYNAPI_PROC(Uint32,SDL_GetQueuedAudioSize,(SDL_AudioDeviceID a),(a),return)
     6.9 +SDL_DYNAPI_PROC(void,SDL_ClearQueuedAudio,(SDL_AudioDeviceID a),(a),)
     7.1 --- a/test/Makefile.in	Sat Jul 26 16:52:26 2014 -0700
     7.2 +++ b/test/Makefile.in	Tue Jul 22 21:41:49 2014 -0400
     7.3 @@ -10,6 +10,7 @@
     7.4  TARGETS = \
     7.5  	checkkeys$(EXE) \
     7.6  	loopwave$(EXE) \
     7.7 +	loopwavequeue$(EXE) \
     7.8  	testatomic$(EXE) \
     7.9  	testaudioinfo$(EXE) \
    7.10  	testautomation$(EXE) \
    7.11 @@ -71,6 +72,9 @@
    7.12  loopwave$(EXE): $(srcdir)/loopwave.c
    7.13  	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
    7.14  
    7.15 +loopwavequeue$(EXE): $(srcdir)/loopwavequeue.c
    7.16 +	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
    7.17 +
    7.18  testresample$(EXE): $(srcdir)/testresample.c
    7.19  	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
    7.20  
     8.1 --- a/test/README	Sat Jul 26 16:52:26 2014 -0700
     8.2 +++ b/test/README	Tue Jul 22 21:41:49 2014 -0400
     8.3 @@ -3,6 +3,7 @@
     8.4  
     8.5  	checkkeys	Watch the key events to check the keyboard
     8.6  	loopwave	Audio test -- loop playing a WAV file
     8.7 +	loopwavequeue	Audio test -- loop playing a WAV file with SDL_QueueAudio
     8.8  	testaudioinfo	Lists audio device capabilities
     8.9  	testcdrom	Sample audio CD control program
    8.10  	testerror	Tests multi-threaded error handling
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/test/loopwavequeue.c	Tue Jul 22 21:41:49 2014 -0400
     9.3 @@ -0,0 +1,127 @@
     9.4 +/*
     9.5 +  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     9.6 +
     9.7 +  This software is provided 'as-is', without any express or implied
     9.8 +  warranty.  In no event will the authors be held liable for any damages
     9.9 +  arising from the use of this software.
    9.10 +
    9.11 +  Permission is granted to anyone to use this software for any purpose,
    9.12 +  including commercial applications, and to alter it and redistribute it
    9.13 +  freely.
    9.14 +*/
    9.15 +
    9.16 +/* Program to load a wave file and loop playing it using SDL sound queueing */
    9.17 +
    9.18 +#include <stdio.h>
    9.19 +#include <stdlib.h>
    9.20 +
    9.21 +#if HAVE_SIGNAL_H
    9.22 +#include <signal.h>
    9.23 +#endif
    9.24 +
    9.25 +#include "SDL.h"
    9.26 +
    9.27 +struct
    9.28 +{
    9.29 +    SDL_AudioSpec spec;
    9.30 +    Uint8 *sound;               /* Pointer to wave data */
    9.31 +    Uint32 soundlen;            /* Length of wave data */
    9.32 +    int soundpos;               /* Current play position */
    9.33 +} wave;
    9.34 +
    9.35 +
    9.36 +/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
    9.37 +static void
    9.38 +quit(int rc)
    9.39 +{
    9.40 +    SDL_Quit();
    9.41 +    exit(rc);
    9.42 +}
    9.43 +
    9.44 +static int done = 0;
    9.45 +void
    9.46 +poked(int sig)
    9.47 +{
    9.48 +    done = 1;
    9.49 +}
    9.50 +
    9.51 +int
    9.52 +main(int argc, char *argv[])
    9.53 +{
    9.54 +    int i;
    9.55 +    char filename[4096];
    9.56 +
    9.57 +	/* Enable standard application logging */
    9.58 +	SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
    9.59 +
    9.60 +    /* Load the SDL library */
    9.61 +    if (SDL_Init(SDL_INIT_AUDIO) < 0) {
    9.62 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
    9.63 +        return (1);
    9.64 +    }
    9.65 +
    9.66 +    if (argc > 1) {
    9.67 +        SDL_strlcpy(filename, argv[1], sizeof(filename));
    9.68 +    } else {
    9.69 +        SDL_strlcpy(filename, "sample.wav", sizeof(filename));
    9.70 +    }
    9.71 +    /* Load the wave file into memory */
    9.72 +    if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) {
    9.73 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
    9.74 +        quit(1);
    9.75 +    }
    9.76 +
    9.77 +    wave.spec.callback = NULL;  /* we'll push audio. */
    9.78 +
    9.79 +#if HAVE_SIGNAL_H
    9.80 +    /* Set the signals */
    9.81 +#ifdef SIGHUP
    9.82 +    signal(SIGHUP, poked);
    9.83 +#endif
    9.84 +    signal(SIGINT, poked);
    9.85 +#ifdef SIGQUIT
    9.86 +    signal(SIGQUIT, poked);
    9.87 +#endif
    9.88 +    signal(SIGTERM, poked);
    9.89 +#endif /* HAVE_SIGNAL_H */
    9.90 +
    9.91 +    /* Initialize fillerup() variables */
    9.92 +    if (SDL_OpenAudio(&wave.spec, NULL) < 0) {
    9.93 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError());
    9.94 +        SDL_FreeWAV(wave.sound);
    9.95 +        quit(2);
    9.96 +    }
    9.97 +
    9.98 +    /*static x[99999]; SDL_QueueAudio(1, x, sizeof (x));*/
    9.99 +
   9.100 +    /* Let the audio run */
   9.101 +    SDL_PauseAudio(0);
   9.102 +
   9.103 +    /* Note that we stuff the entire audio buffer into the queue in one
   9.104 +       shot. Most apps would want to feed it a little at a time, as it
   9.105 +       plays, but we're going for simplicity here. */
   9.106 +    
   9.107 +    while (!done && (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING))
   9.108 +    {
   9.109 +        /* The device from SDL_OpenAudio() is always device #1. */
   9.110 +        const Uint32 queued = SDL_GetQueuedAudioSize(1);
   9.111 +        SDL_Log("Device has %u bytes queued.\n", (unsigned int) queued);
   9.112 +        if (queued <= 8192) {  /* time to requeue the whole thing? */
   9.113 +            if (SDL_QueueAudio(1, wave.sound, wave.soundlen) == 0) {
   9.114 +                SDL_Log("Device queued %u more bytes.\n", (unsigned int) wave.soundlen);
   9.115 +            } else {
   9.116 +                SDL_Log("Device FAILED to queue %u more bytes: %s\n", (unsigned int) wave.soundlen, SDL_GetError());
   9.117 +            }
   9.118 +        }
   9.119 +
   9.120 +        SDL_Delay(100);  /* let it play for awhile. */
   9.121 +    }
   9.122 +
   9.123 +    /* Clean up on signal */
   9.124 +    SDL_CloseAudio();
   9.125 +    SDL_FreeWAV(wave.sound);
   9.126 +    SDL_Quit();
   9.127 +    return 0;
   9.128 +}
   9.129 +
   9.130 +/* vi: set ts=4 sw=4 expandtab: */