audio: implemented higher level infrastructure for running capture devices.
authorRyan C. Gordon
Tue, 02 Aug 2016 13:50:21 -0400
changeset 1023904cda108b406
parent 10238 6fa358b97f4b
child 10240 942b45f2fae3
audio: implemented higher level infrastructure for running capture devices.
src/audio/SDL_audio.c
src/audio/SDL_audio_c.h
src/audio/SDL_sysaudio.h
     1.1 --- a/src/audio/SDL_audio.c	Tue Aug 02 13:48:52 2016 -0400
     1.2 +++ b/src/audio/SDL_audio.c	Tue Aug 02 13:50:21 2016 -0400
     1.3 @@ -206,6 +206,17 @@
     1.4  {                               /* no-op. */
     1.5  }
     1.6  
     1.7 +static int
     1.8 +SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen)
     1.9 +{
    1.10 +    return -1;  /* just fail immediately. */
    1.11 +}
    1.12 +
    1.13 +static void
    1.14 +SDL_AudioFlushCapture_Default(_THIS)
    1.15 +{                               /* no-op. */
    1.16 +}
    1.17 +
    1.18  static void
    1.19  SDL_AudioCloseDevice_Default(_THIS)
    1.20  {                               /* no-op. */
    1.21 @@ -279,6 +290,8 @@
    1.22      FILL_STUB(GetPendingBytes);
    1.23      FILL_STUB(GetDeviceBuf);
    1.24      FILL_STUB(WaitDone);
    1.25 +    FILL_STUB(CaptureFromDevice);
    1.26 +    FILL_STUB(FlushCapture);
    1.27      FILL_STUB(CloseDevice);
    1.28      FILL_STUB(LockDevice);
    1.29      FILL_STUB(UnlockDevice);
    1.30 @@ -592,7 +605,7 @@
    1.31  
    1.32  
    1.33  /* The general mixing thread function */
    1.34 -int SDLCALL
    1.35 +static int SDLCALL
    1.36  SDL_RunAudio(void *devicep)
    1.37  {
    1.38      SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
    1.39 @@ -601,7 +614,9 @@
    1.40      const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
    1.41      Uint8 *stream;
    1.42      void *udata = device->spec.userdata;
    1.43 -    void (SDLCALL *fill) (void *, Uint8 *, int) = device->spec.callback;
    1.44 +    void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
    1.45 +
    1.46 +    SDL_assert(!device->iscapture);
    1.47  
    1.48      /* The audio mixing is always a high priority thread */
    1.49      SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
    1.50 @@ -635,7 +650,7 @@
    1.51          if (SDL_AtomicGet(&device->paused)) {
    1.52              SDL_memset(stream, silence, stream_len);
    1.53          } else {
    1.54 -            (*fill) (udata, stream, stream_len);
    1.55 +            (*callback) (udata, stream, stream_len);
    1.56          }
    1.57          SDL_UnlockMutex(device->mixer_lock);
    1.58  
    1.59 @@ -661,11 +676,92 @@
    1.60      }
    1.61  
    1.62      /* Wait for the audio to drain. */
    1.63 +    /* !!! FIXME: can we rename this WaitDrain? */
    1.64      current_audio.impl.WaitDone(device);
    1.65  
    1.66      return 0;
    1.67  }
    1.68  
    1.69 +/* The general capture thread function */
    1.70 +static int SDLCALL
    1.71 +SDL_CaptureAudio(void *devicep)
    1.72 +{
    1.73 +    SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
    1.74 +    const int silence = (int) device->spec.silence;
    1.75 +    const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
    1.76 +    const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
    1.77 +    Uint8 *stream;
    1.78 +    void *udata = device->spec.userdata;
    1.79 +    void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
    1.80 +
    1.81 +    SDL_assert(device->iscapture);
    1.82 +
    1.83 +    /* The audio mixing is always a high priority thread */
    1.84 +    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
    1.85 +
    1.86 +    /* Perform any thread setup */
    1.87 +    device->threadid = SDL_ThreadID();
    1.88 +    current_audio.impl.ThreadInit(device);
    1.89 +
    1.90 +    /* Loop, filling the audio buffers */
    1.91 +    while (!SDL_AtomicGet(&device->shutdown)) {
    1.92 +        int still_need;
    1.93 +        Uint8 *ptr;
    1.94 +
    1.95 +        if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) {
    1.96 +            SDL_Delay(delay);  /* just so we don't cook the CPU. */
    1.97 +            current_audio.impl.FlushCapture(device);  /* dump anything pending. */
    1.98 +            continue;
    1.99 +        }
   1.100 +
   1.101 +        /* Fill the current buffer with sound */
   1.102 +        still_need = stream_len;
   1.103 +        if (device->convert.needed) {
   1.104 +            ptr = stream = device->convert.buf;
   1.105 +        } else {
   1.106 +            /* just use the "fake" stream to hold data read from the device. */
   1.107 +            ptr = stream = device->fake_stream;
   1.108 +        }
   1.109 +
   1.110 +        /* We still read from the device when "paused" to keep the state sane,
   1.111 +           and block when there isn't data so this thread isn't eating CPU.
   1.112 +           But we don't process it further or call the app's callback. */
   1.113 +
   1.114 +        while (still_need > 0) {
   1.115 +            const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need);
   1.116 +            SDL_assert(rc != 0);  /* device should have blocked, failed, or returned data. */
   1.117 +            SDL_assert(rc <= still_need);  /* device should not overflow buffer. :) */
   1.118 +            if (rc > 0) {
   1.119 +                still_need -= rc;
   1.120 +                ptr += rc;
   1.121 +            } else {  /* uhoh, device failed for some reason! */
   1.122 +                SDL_OpenedAudioDeviceDisconnected(device);
   1.123 +                break;
   1.124 +            }
   1.125 +        }
   1.126 +
   1.127 +        if (still_need > 0) {
   1.128 +            /* Keep any data we already read, silence the rest. */
   1.129 +            SDL_memset(ptr, silence, still_need);
   1.130 +        }
   1.131 +
   1.132 +        if (device->convert.needed) {
   1.133 +            SDL_ConvertAudio(&device->convert);
   1.134 +        }
   1.135 +
   1.136 +        /* !!! FIXME: this should be LockDevice. */
   1.137 +        SDL_LockMutex(device->mixer_lock);
   1.138 +        if (!SDL_AtomicGet(&device->paused)) {
   1.139 +            (*callback)(udata, stream, stream_len);
   1.140 +        }
   1.141 +        SDL_UnlockMutex(device->mixer_lock);
   1.142 +    }
   1.143 +
   1.144 +    current_audio.impl.FlushCapture(device);
   1.145 +
   1.146 +    return 0;
   1.147 +}
   1.148 +
   1.149  
   1.150  static SDL_AudioFormat
   1.151  SDL_ParseAudioFormat(const char *string)
   1.152 @@ -1198,10 +1294,11 @@
   1.153          /* !!! FIXME: we don't force the audio thread stack size here because it calls into user code, but maybe we should? */
   1.154          /* buffer queueing callback only needs a few bytes, so make the stack tiny. */
   1.155          char name[64];
   1.156 -        const size_t stacksize = (device->spec.callback == SDL_BufferQueueDrainCallback) ? 64 * 1024 : 0;
   1.157 +        const SDL_bool is_internal_thread = (device->spec.callback == SDL_BufferQueueDrainCallback);
   1.158 +        const size_t stacksize = is_internal_thread ? 64 * 1024 : 0;
   1.159  
   1.160          SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
   1.161 -        device->thread = SDL_CreateThreadInternal(SDL_RunAudio, name, stacksize, device);
   1.162 +        device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, name, stacksize, device);
   1.163  
   1.164          if (device->thread == NULL) {
   1.165              SDL_CloseAudioDevice(device->id);
     2.1 --- a/src/audio/SDL_audio_c.h	Tue Aug 02 13:48:52 2016 -0400
     2.2 +++ b/src/audio/SDL_audio_c.h	Tue Aug 02 13:50:21 2016 -0400
     2.3 @@ -29,9 +29,6 @@
     2.4  /* Function to calculate the size and silence for a SDL_AudioSpec */
     2.5  extern void SDL_CalculateAudioSpec(SDL_AudioSpec * spec);
     2.6  
     2.7 -/* The actual mixing thread function */
     2.8 -extern int SDLCALL SDL_RunAudio(void *audiop);
     2.9 -
    2.10  /* this is used internally to access some autogenerated code. */
    2.11  typedef struct
    2.12  {
     3.1 --- a/src/audio/SDL_sysaudio.h	Tue Aug 02 13:48:52 2016 -0400
     3.2 +++ b/src/audio/SDL_sysaudio.h	Tue Aug 02 13:50:21 2016 -0400
     3.3 @@ -76,6 +76,8 @@
     3.4      int (*GetPendingBytes) (_THIS);
     3.5      Uint8 *(*GetDeviceBuf) (_THIS);
     3.6      void (*WaitDone) (_THIS);
     3.7 +    int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
     3.8 +    void (*FlushCapture) (_THIS);
     3.9      void (*CloseDevice) (_THIS);
    3.10      void (*LockDevice) (_THIS);
    3.11      void (*UnlockDevice) (_THIS);