audio: Implemented buffer queueing for capture devices (SDL_DequeueAudio()).
authorRyan C. Gordon <icculus@icculus.org>
Sat, 06 Aug 2016 02:47:27 -0400
changeset 10262aaed7b1f783a
parent 10261 e8facf18d5bd
child 10263 ec1844380edb
audio: Implemented buffer queueing for capture devices (SDL_DequeueAudio()).
include/SDL_audio.h
src/audio/SDL_audio.c
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
     1.1 --- a/include/SDL_audio.h	Sat Aug 06 02:45:51 2016 -0400
     1.2 +++ b/include/SDL_audio.h	Sat Aug 06 02:47:27 2016 -0400
     1.3 @@ -278,7 +278,8 @@
     1.4   *      protect data structures that it accesses by calling SDL_LockAudio()
     1.5   *      and SDL_UnlockAudio() in your code. Alternately, you may pass a NULL
     1.6   *      pointer here, and call SDL_QueueAudio() with some frequency, to queue
     1.7 - *      more audio samples to be played.
     1.8 + *      more audio samples to be played (or for capture devices, call
     1.9 + *      SDL_DequeueAudio() with some frequency, to obtain audio samples).
    1.10   *    - \c desired->userdata is passed as the first parameter to your callback
    1.11   *      function. If you passed a NULL callback, this value is ignored.
    1.12   *
    1.13 @@ -482,6 +483,10 @@
    1.14  /**
    1.15   *  Queue more audio on non-callback devices.
    1.16   *
    1.17 + *  (If you are looking to retrieve queued audio from a non-callback capture
    1.18 + *  device, you want SDL_DequeueAudio() instead. This will return -1 to
    1.19 + *  signify an error if you use it with capture devices.)
    1.20 + *
    1.21   *  SDL offers two ways to feed audio to the device: you can either supply a
    1.22   *  callback that SDL triggers with some frequency to obtain more audio
    1.23   *  (pull method), or you can supply no callback, and then SDL will expect
    1.24 @@ -517,20 +522,75 @@
    1.25  extern DECLSPEC int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID dev, const void *data, Uint32 len);
    1.26  
    1.27  /**
    1.28 + *  Dequeue more audio on non-callback devices.
    1.29 + *
    1.30 + *  (If you are looking to queue audio for output on a non-callback playback
    1.31 + *  device, you want SDL_QueueAudio() instead. This will always return 0
    1.32 + *  if you use it with playback devices.)
    1.33 + *
    1.34 + *  SDL offers two ways to retrieve audio from a capture device: you can
    1.35 + *  either supply a callback that SDL triggers with some frequency as the
    1.36 + *  device records more audio data, (push method), or you can supply no
    1.37 + *  callback, and then SDL will expect you to retrieve data at regular
    1.38 + *  intervals (pull method) with this function.
    1.39 + *
    1.40 + *  There are no limits on the amount of data you can queue, short of
    1.41 + *  exhaustion of address space. Data from the device will keep queuing as
    1.42 + *  necessary without further intervention from you. This means you will
    1.43 + *  eventually run out of memory if you aren't routinely dequeueing data.
    1.44 + *
    1.45 + *  Capture devices will not queue data when paused; if you are expecting
    1.46 + *  to not need captured audio for some length of time, use
    1.47 + *  SDL_PauseAudioDevice() to stop the capture device from queueing more
    1.48 + *  data. This can be useful during, say, level loading times. When
    1.49 + *  unpaused, capture devices will start queueing data from that point,
    1.50 + *  having flushed any capturable data available while paused.
    1.51 + *
    1.52 + *  This function is thread-safe, but dequeueing from the same device from
    1.53 + *  two threads at once does not promise which thread will dequeued data
    1.54 + *  first.
    1.55 + *
    1.56 + *  You may not dequeue audio from a device that is using an
    1.57 + *  application-supplied callback; doing so returns an error. You have to use
    1.58 + *  the audio callback, or dequeue audio with this function, but not both.
    1.59 + *
    1.60 + *  You should not call SDL_LockAudio() on the device before queueing; SDL
    1.61 + *  handles locking internally for this function.
    1.62 + *
    1.63 + *  \param dev The device ID from which we will dequeue audio.
    1.64 + *  \param data A pointer into where audio data should be copied.
    1.65 + *  \param len The number of bytes (not samples!) to which (data) points.
    1.66 + *  \return number of bytes dequeued, which could be less than requested.
    1.67 + *
    1.68 + *  \sa SDL_GetQueuedAudioSize
    1.69 + *  \sa SDL_ClearQueuedAudio
    1.70 + */
    1.71 +extern DECLSPEC Uint32 SDLCALL SDL_DequeueAudio(SDL_AudioDeviceID dev, void *data, Uint32 len);
    1.72 +
    1.73 +/**
    1.74   *  Get the number of bytes of still-queued audio.
    1.75   *
    1.76 - *  This is the number of bytes that have been queued for playback with
    1.77 - *  SDL_QueueAudio(), but have not yet been sent to the hardware.
    1.78 + *  For playback device:
    1.79   *
    1.80 - *  Once we've sent it to the hardware, this function can not decide the exact
    1.81 - *  byte boundary of what has been played. It's possible that we just gave the
    1.82 - *  hardware several kilobytes right before you called this function, but it
    1.83 - *  hasn't played any of it yet, or maybe half of it, etc.
    1.84 + *    This is the number of bytes that have been queued for playback with
    1.85 + *    SDL_QueueAudio(), but have not yet been sent to the hardware. This
    1.86 + *    number may shrink at any time, so this only informs of pending data.
    1.87 + *
    1.88 + *    Once we've sent it to the hardware, this function can not decide the
    1.89 + *    exact byte boundary of what has been played. It's possible that we just
    1.90 + *    gave the hardware several kilobytes right before you called this
    1.91 + *    function, but it hasn't played any of it yet, or maybe half of it, etc.
    1.92 + *
    1.93 + *  For capture devices:
    1.94 + *
    1.95 + *    This is the number of bytes that have been captured by the device and
    1.96 + *    are waiting for you to dequeue. This number may grow at any time, so
    1.97 + *    this only informs of the lower-bound of available data.
    1.98   *
    1.99   *  You may not queue audio on a device that is using an application-supplied
   1.100   *  callback; calling this function on such a device always returns 0.
   1.101 - *  You have to use the audio callback or queue audio with SDL_QueueAudio(),
   1.102 - *  but not both.
   1.103 + *  You have to queue audio with SDL_QueueAudio()/SDL_DequeueAudio(), or use
   1.104 + *  the audio callback, but not both.
   1.105   *
   1.106   *  You should not call SDL_LockAudio() on the device before querying; SDL
   1.107   *  handles locking internally for this function.
   1.108 @@ -544,10 +604,17 @@
   1.109  extern DECLSPEC Uint32 SDLCALL SDL_GetQueuedAudioSize(SDL_AudioDeviceID dev);
   1.110  
   1.111  /**
   1.112 - *  Drop any queued audio data waiting to be sent to the hardware.
   1.113 + *  Drop any queued audio data. For playback devices, this is any queued data
   1.114 + *  still waiting to be submitted to the hardware. For capture devices, this
   1.115 + *  is any data that was queued by the device that hasn't yet been dequeued by
   1.116 + *  the application.
   1.117   *
   1.118 - *  Immediately after this call, SDL_GetQueuedAudioSize() will return 0 and
   1.119 - *  the hardware will start playing silence if more audio isn't queued.
   1.120 + *  Immediately after this call, SDL_GetQueuedAudioSize() will return 0. For
   1.121 + *  playback devices, the hardware will start playing silence if more audio
   1.122 + *  isn't queued. Unpaused capture devices will start filling the queue again
   1.123 + *  as soon as they have more data available (which, depending on the state
   1.124 + *  of the hardware and the thread, could be before this function call
   1.125 + *  returns!).
   1.126   *
   1.127   *  This will not prevent playback of queued audio that's already been sent
   1.128   *  to the hardware, as we can not undo that, so expect there to be some
   1.129 @@ -557,8 +624,8 @@
   1.130   *
   1.131   *  You may not queue audio on a device that is using an application-supplied
   1.132   *  callback; calling this function on such a device is always a no-op.
   1.133 - *  You have to use the audio callback or queue audio with SDL_QueueAudio(),
   1.134 - *  but not both.
   1.135 + *  You have to queue audio with SDL_QueueAudio()/SDL_DequeueAudio(), or use
   1.136 + *  the audio callback, but not both.
   1.137   *
   1.138   *  You should not call SDL_LockAudio() on the device before clearing the
   1.139   *  queue; SDL handles locking internally for this function.
     2.1 --- a/src/audio/SDL_audio.c	Sat Aug 06 02:45:51 2016 -0400
     2.2 +++ b/src/audio/SDL_audio.c	Sat Aug 06 02:47:27 2016 -0400
     2.3 @@ -433,77 +433,24 @@
     2.4  
     2.5  /* this expects that you managed thread safety elsewhere. */
     2.6  static void
     2.7 -free_audio_queue(SDL_AudioBufferQueue *buffer)
     2.8 +free_audio_queue(SDL_AudioBufferQueue *packet)
     2.9  {
    2.10 -    while (buffer) {
    2.11 -        SDL_AudioBufferQueue *next = buffer->next;
    2.12 -        SDL_free(buffer);
    2.13 -        buffer = next;
    2.14 +    while (packet) {
    2.15 +        SDL_AudioBufferQueue *next = packet->next;
    2.16 +        SDL_free(packet);
    2.17 +        packet = next;
    2.18      }
    2.19  }
    2.20  
    2.21 -static void SDLCALL
    2.22 -SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int _len)
    2.23 +/* NOTE: This assumes you'll hold the mixer lock before calling! */
    2.24 +static int
    2.25 +queue_audio_to_device(SDL_AudioDevice *device, const Uint8 *data, Uint32 len)
    2.26  {
    2.27 -    /* this function always holds the mixer lock before being called. */
    2.28 -    Uint32 len = (Uint32) _len;
    2.29 -    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
    2.30 -    SDL_AudioBufferQueue *buffer;
    2.31 -
    2.32 -    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
    2.33 -    SDL_assert(_len >= 0);  /* this shouldn't ever happen, right?! */
    2.34 -
    2.35 -    while ((len > 0) && ((buffer = device->buffer_queue_head) != NULL)) {
    2.36 -        const Uint32 avail = buffer->datalen - buffer->startpos;
    2.37 -        const Uint32 cpy = SDL_min(len, avail);
    2.38 -        SDL_assert(device->queued_bytes >= avail);
    2.39 -
    2.40 -        SDL_memcpy(stream, buffer->data + buffer->startpos, cpy);
    2.41 -        buffer->startpos += cpy;
    2.42 -        stream += cpy;
    2.43 -        device->queued_bytes -= cpy;
    2.44 -        len -= cpy;
    2.45 -
    2.46 -        if (buffer->startpos == buffer->datalen) {  /* packet is done, put it in the pool. */
    2.47 -            device->buffer_queue_head = buffer->next;
    2.48 -            SDL_assert((buffer->next != NULL) || (buffer == device->buffer_queue_tail));
    2.49 -            buffer->next = device->buffer_queue_pool;
    2.50 -            device->buffer_queue_pool = buffer;
    2.51 -        }
    2.52 -    }
    2.53 -
    2.54 -    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
    2.55 -
    2.56 -    if (len > 0) {  /* fill any remaining space in the stream with silence. */
    2.57 -        SDL_assert(device->buffer_queue_head == NULL);
    2.58 -        SDL_memset(stream, device->spec.silence, len);
    2.59 -    }
    2.60 -
    2.61 -    if (device->buffer_queue_head == NULL) {
    2.62 -        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
    2.63 -    }
    2.64 -}
    2.65 -
    2.66 -int
    2.67 -SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
    2.68 -{
    2.69 -    SDL_AudioDevice *device = get_audio_device(devid);
    2.70 -    const Uint8 *data = (const Uint8 *) _data;
    2.71      SDL_AudioBufferQueue *orighead;
    2.72      SDL_AudioBufferQueue *origtail;
    2.73      Uint32 origlen;
    2.74      Uint32 datalen;
    2.75  
    2.76 -    if (!device) {
    2.77 -        return -1;  /* get_audio_device() will have set the error state */
    2.78 -    }
    2.79 -
    2.80 -    if (device->spec.callback != SDL_BufferQueueDrainCallback) {
    2.81 -        return SDL_SetError("Audio device has a callback, queueing not allowed");
    2.82 -    }
    2.83 -
    2.84 -    current_audio.impl.LockDevice(device);
    2.85 -
    2.86      orighead = device->buffer_queue_head;
    2.87      origtail = device->buffer_queue_tail;
    2.88      origlen = origtail ? origtail->datalen : 0;
    2.89 @@ -533,8 +480,6 @@
    2.90                      device->buffer_queue_tail = origtail;
    2.91                      device->buffer_queue_pool = NULL;
    2.92  
    2.93 -                    current_audio.impl.UnlockDevice(device);
    2.94 -
    2.95                      free_audio_queue(packet);  /* give back what we can. */
    2.96  
    2.97                      return SDL_OutOfMemory();
    2.98 @@ -561,9 +506,121 @@
    2.99          device->queued_bytes += datalen;
   2.100      }
   2.101  
   2.102 +    return 0;
   2.103 +}
   2.104 +
   2.105 +/* NOTE: This assumes you'll hold the mixer lock before calling! */
   2.106 +static Uint32
   2.107 +dequeue_audio_from_device(SDL_AudioDevice *device, Uint8 *stream, Uint32 len)
   2.108 +{
   2.109 +    SDL_AudioBufferQueue *packet;
   2.110 +    Uint8 *ptr = stream;
   2.111 +
   2.112 +    while ((len > 0) && ((packet = device->buffer_queue_head) != NULL)) {
   2.113 +        const Uint32 avail = packet->datalen - packet->startpos;
   2.114 +        const Uint32 cpy = SDL_min(len, avail);
   2.115 +        SDL_assert(device->queued_bytes >= avail);
   2.116 +
   2.117 +        SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
   2.118 +        packet->startpos += cpy;
   2.119 +        ptr += cpy;
   2.120 +        device->queued_bytes -= cpy;
   2.121 +        len -= cpy;
   2.122 +
   2.123 +        if (packet->startpos == packet->datalen) {  /* packet is done, put it in the pool. */
   2.124 +            device->buffer_queue_head = packet->next;
   2.125 +            SDL_assert((packet->next != NULL) || (packet == device->buffer_queue_tail));
   2.126 +            packet->next = device->buffer_queue_pool;
   2.127 +            device->buffer_queue_pool = packet;
   2.128 +        }
   2.129 +    }
   2.130 +
   2.131 +    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
   2.132 +
   2.133 +    if (device->buffer_queue_head == NULL) {
   2.134 +        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
   2.135 +    }
   2.136 +
   2.137 +    return (Uint32) (ptr - stream);
   2.138 +}
   2.139 +
   2.140 +static void SDLCALL
   2.141 +SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int len)
   2.142 +{
   2.143 +    /* this function always holds the mixer lock before being called. */
   2.144 +    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
   2.145 +    Uint32 written;
   2.146 +
   2.147 +    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
   2.148 +    SDL_assert(!device->iscapture);  /* this shouldn't ever happen, right?! */
   2.149 +    SDL_assert(len >= 0);  /* this shouldn't ever happen, right?! */
   2.150 +
   2.151 +    written = dequeue_audio_from_device(device, stream, (Uint32) len);
   2.152 +    stream += written;
   2.153 +    len -= (int) written;
   2.154 +
   2.155 +    if (len > 0) {  /* fill any remaining space in the stream with silence. */
   2.156 +        SDL_assert(device->buffer_queue_head == NULL);
   2.157 +        SDL_memset(stream, device->spec.silence, len);
   2.158 +    }
   2.159 +}
   2.160 +
   2.161 +static void SDLCALL
   2.162 +SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len)
   2.163 +{
   2.164 +    /* this function always holds the mixer lock before being called. */
   2.165 +    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
   2.166 +
   2.167 +    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
   2.168 +    SDL_assert(device->iscapture);  /* this shouldn't ever happen, right?! */
   2.169 +    SDL_assert(len >= 0);  /* this shouldn't ever happen, right?! */
   2.170 +
   2.171 +    /* note that if this needs to allocate more space and run out of memory,
   2.172 +       we have no choice but to quietly drop the data and hope it works out
   2.173 +       later, but you probably have bigger problems in this case anyhow. */
   2.174 +    queue_audio_to_device(device, stream, (Uint32) len);
   2.175 +}
   2.176 +
   2.177 +int
   2.178 +SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len)
   2.179 +{
   2.180 +    SDL_AudioDevice *device = get_audio_device(devid);
   2.181 +    int rc = 0;
   2.182 +
   2.183 +    if (!device) {
   2.184 +        return -1;  /* get_audio_device() will have set the error state */
   2.185 +    } else if (device->iscapture) {
   2.186 +        return SDL_SetError("This is a capture device, queueing not allowed");
   2.187 +    } else if (device->spec.callback != SDL_BufferQueueDrainCallback) {
   2.188 +        return SDL_SetError("Audio device has a callback, queueing not allowed");
   2.189 +    }
   2.190 +
   2.191 +    if (len > 0) {
   2.192 +        current_audio.impl.LockDevice(device);
   2.193 +        rc = queue_audio_to_device(device, data, len);
   2.194 +        current_audio.impl.UnlockDevice(device);
   2.195 +    }
   2.196 +
   2.197 +    return rc;
   2.198 +}
   2.199 +
   2.200 +Uint32
   2.201 +SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len)
   2.202 +{
   2.203 +    SDL_AudioDevice *device = get_audio_device(devid);
   2.204 +    Uint32 rc;
   2.205 +
   2.206 +    if ( (len == 0) ||  /* nothing to do? */
   2.207 +         (!device) ||  /* called with bogus device id */
   2.208 +         (!device->iscapture) ||  /* playback devices can't dequeue */
   2.209 +         (device->spec.callback != SDL_BufferQueueFillCallback) ) { /* not set for queueing */
   2.210 +        return 0;  /* just report zero bytes dequeued. */
   2.211 +    }
   2.212 +
   2.213 +    current_audio.impl.LockDevice(device);
   2.214 +    rc = dequeue_audio_from_device(device, data, len);
   2.215      current_audio.impl.UnlockDevice(device);
   2.216 -
   2.217 -    return 0;
   2.218 +    return rc;
   2.219  }
   2.220  
   2.221  Uint32
   2.222 @@ -572,11 +629,19 @@
   2.223      Uint32 retval = 0;
   2.224      SDL_AudioDevice *device = get_audio_device(devid);
   2.225  
   2.226 +    if (!device) {
   2.227 +        return 0;
   2.228 +    }
   2.229 +
   2.230      /* Nothing to do unless we're set up for queueing. */
   2.231 -    if (device && (device->spec.callback == SDL_BufferQueueDrainCallback)) {
   2.232 +    if (device->spec.callback == SDL_BufferQueueDrainCallback) {
   2.233          current_audio.impl.LockDevice(device);
   2.234          retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device);
   2.235          current_audio.impl.UnlockDevice(device);
   2.236 +    } else if (device->spec.callback == SDL_BufferQueueFillCallback) {
   2.237 +        current_audio.impl.LockDevice(device);
   2.238 +        retval = device->queued_bytes;
   2.239 +        current_audio.impl.UnlockDevice(device);
   2.240      }
   2.241  
   2.242      return retval;
   2.243 @@ -1305,7 +1370,7 @@
   2.244              }
   2.245          }
   2.246  
   2.247 -        device->spec.callback = SDL_BufferQueueDrainCallback;
   2.248 +        device->spec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback;
   2.249          device->spec.userdata = device;
   2.250      }
   2.251  
   2.252 @@ -1319,7 +1384,9 @@
   2.253          /* !!! FIXME: we don't force the audio thread stack size here because it calls into user code, but maybe we should? */
   2.254          /* buffer queueing callback only needs a few bytes, so make the stack tiny. */
   2.255          char name[64];
   2.256 -        const SDL_bool is_internal_thread = (device->spec.callback == SDL_BufferQueueDrainCallback);
   2.257 +        const SDL_bool is_internal_thread =
   2.258 +            (device->spec.callback == SDL_BufferQueueDrainCallback) ||
   2.259 +            (device->spec.callback == SDL_BufferQueueFillCallback);
   2.260          const size_t stacksize = is_internal_thread ? 64 * 1024 : 0;
   2.261  
   2.262          SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
     3.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Sat Aug 06 02:45:51 2016 -0400
     3.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Sat Aug 06 02:47:27 2016 -0400
     3.3 @@ -605,3 +605,4 @@
     3.4  #define SDL_SetWindowModalFor SDL_SetWindowModalFor_REAL
     3.5  #define SDL_RenderSetIntegerScale SDL_RenderSetIntegerScale_REAL
     3.6  #define SDL_RenderGetIntegerScale SDL_RenderGetIntegerScale_REAL
     3.7 +#define SDL_DequeueAudio SDL_DequeueAudio_REAL
     4.1 --- a/src/dynapi/SDL_dynapi_procs.h	Sat Aug 06 02:45:51 2016 -0400
     4.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Sat Aug 06 02:47:27 2016 -0400
     4.3 @@ -639,3 +639,4 @@
     4.4  SDL_DYNAPI_PROC(int,SDL_SetWindowModalFor,(SDL_Window *a, SDL_Window *b),(a,b),return)
     4.5  SDL_DYNAPI_PROC(int,SDL_RenderSetIntegerScale,(SDL_Renderer *a, SDL_bool b),(a,b),return)
     4.6  SDL_DYNAPI_PROC(SDL_bool,SDL_RenderGetIntegerScale,(SDL_Renderer *a),(a),return)
     4.7 +SDL_DYNAPI_PROC(Uint32,SDL_DequeueAudio,(SDL_AudioDeviceID a, void *b, Uint32 c),(a,b,c),return)