directsound: Implemented audio capture support.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 10 Aug 2016 16:00:16 -0400
changeset 10279f797806699e6
parent 10278 1e7b4cfe8451
child 10280 985f92b2f8e9
directsound: Implemented audio capture support.
src/audio/directsound/SDL_directsound.c
src/audio/directsound/SDL_directsound.h
     1.1 --- a/src/audio/directsound/SDL_directsound.c	Wed Aug 10 15:34:24 2016 -0400
     1.2 +++ b/src/audio/directsound/SDL_directsound.c	Wed Aug 10 16:00:16 2016 -0400
     1.3 @@ -24,6 +24,7 @@
     1.4  
     1.5  /* Allow access to a raw mixing buffer */
     1.6  
     1.7 +#include "SDL_assert.h"
     1.8  #include "SDL_timer.h"
     1.9  #include "SDL_loadso.h"
    1.10  #include "SDL_audio.h"
    1.11 @@ -36,11 +37,13 @@
    1.12  
    1.13  /* DirectX function pointers for audio */
    1.14  static void* DSoundDLL = NULL;
    1.15 -typedef HRESULT(WINAPI*fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
    1.16 -typedef HRESULT(WINAPI*fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
    1.17 -typedef HRESULT(WINAPI*fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
    1.18 +typedef HRESULT (WINAPI *fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
    1.19 +typedef HRESULT (WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
    1.20 +typedef HRESULT (WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID,LPDIRECTSOUNDCAPTURE8 *,LPUNKNOWN);
    1.21 +typedef HRESULT (WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
    1.22  static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
    1.23  static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
    1.24 +static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
    1.25  static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
    1.26  
    1.27  static void
    1.28 @@ -48,6 +51,7 @@
    1.29  {
    1.30      pDirectSoundCreate8 = NULL;
    1.31      pDirectSoundEnumerateW = NULL;
    1.32 +    pDirectSoundCaptureCreate8 = NULL;
    1.33      pDirectSoundCaptureEnumerateW = NULL;
    1.34  
    1.35      if (DSoundDLL != NULL) {
    1.36 @@ -76,6 +80,7 @@
    1.37          loaded = 1;  /* will reset if necessary. */
    1.38          DSOUNDLOAD(DirectSoundCreate8);
    1.39          DSOUNDLOAD(DirectSoundEnumerateW);
    1.40 +        DSOUNDLOAD(DirectSoundCaptureCreate8);
    1.41          DSOUNDLOAD(DirectSoundCaptureEnumerateW);
    1.42          #undef DSOUNDLOAD
    1.43  
    1.44 @@ -197,7 +202,7 @@
    1.45          return;
    1.46      }
    1.47  
    1.48 -    while ((cursor / this->hidden->mixlen) == this->hidden->lastchunk) {
    1.49 +    while ((cursor / this->spec.size) == this->hidden->lastchunk) {
    1.50          /* FIXME: find out how much time is left and sleep that long */
    1.51          SDL_Delay(1);
    1.52  
    1.53 @@ -239,9 +244,8 @@
    1.54      if (this->hidden->locked_buf) {
    1.55          IDirectSoundBuffer_Unlock(this->hidden->mixbuf,
    1.56                                    this->hidden->locked_buf,
    1.57 -                                  this->hidden->mixlen, NULL, 0);
    1.58 +                                  this->spec.size, NULL, 0);
    1.59      }
    1.60 -
    1.61  }
    1.62  
    1.63  static Uint8 *
    1.64 @@ -265,7 +269,7 @@
    1.65          SetDSerror("DirectSound GetCurrentPosition", result);
    1.66          return (NULL);
    1.67      }
    1.68 -    cursor /= this->hidden->mixlen;
    1.69 +    cursor /= this->spec.size;
    1.70  #ifdef DEBUG_SOUND
    1.71      /* Detect audio dropouts */
    1.72      {
    1.73 @@ -281,17 +285,17 @@
    1.74  #endif
    1.75      this->hidden->lastchunk = cursor;
    1.76      cursor = (cursor + 1) % this->hidden->num_buffers;
    1.77 -    cursor *= this->hidden->mixlen;
    1.78 +    cursor *= this->spec.size;
    1.79  
    1.80      /* Lock the audio buffer */
    1.81      result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
    1.82 -                                     this->hidden->mixlen,
    1.83 +                                     this->spec.size,
    1.84                                       (LPVOID *) & this->hidden->locked_buf,
    1.85                                       &rawlen, NULL, &junk, 0);
    1.86      if (result == DSERR_BUFFERLOST) {
    1.87          IDirectSoundBuffer_Restore(this->hidden->mixbuf);
    1.88          result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
    1.89 -                                         this->hidden->mixlen,
    1.90 +                                         this->spec.size,
    1.91                                           (LPVOID *) & this->
    1.92                                           hidden->locked_buf, &rawlen, NULL,
    1.93                                           &junk, 0);
    1.94 @@ -310,7 +314,7 @@
    1.95  
    1.96      /* Wait for the playing chunk to finish */
    1.97      if (stream != NULL) {
    1.98 -        SDL_memset(stream, this->spec.silence, this->hidden->mixlen);
    1.99 +        SDL_memset(stream, this->spec.silence, this->spec.size);
   1.100          DSOUND_PlayDevice(this);
   1.101      }
   1.102      DSOUND_WaitDevice(this);
   1.103 @@ -319,83 +323,106 @@
   1.104      IDirectSoundBuffer_Stop(this->hidden->mixbuf);
   1.105  }
   1.106  
   1.107 +static int
   1.108 +DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
   1.109 +{
   1.110 +    struct SDL_PrivateAudioData *h = this->hidden;
   1.111 +    DWORD junk, cursor, ptr1len, ptr2len;
   1.112 +    VOID *ptr1, *ptr2;
   1.113 +
   1.114 +    SDL_assert(buflen == this->spec.size);
   1.115 +
   1.116 +    while (SDL_TRUE) {
   1.117 +        if (SDL_AtomicGet(&this->shutdown)) {  /* in case the buffer froze... */
   1.118 +            SDL_memset(buffer, this->spec.silence, buflen);
   1.119 +            return buflen;
   1.120 +        }
   1.121 +
   1.122 +        if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
   1.123 +            return -1;
   1.124 +        }
   1.125 +        if ((cursor / this->spec.size) == h->lastchunk) {
   1.126 +            SDL_Delay(1);  /* FIXME: find out how much time is left and sleep that long */
   1.127 +        } else {
   1.128 +            break;
   1.129 +        }
   1.130 +    }
   1.131 +
   1.132 +    if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * this->spec.size, this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
   1.133 +        return -1;
   1.134 +    }
   1.135 +
   1.136 +    SDL_assert(ptr1len == this->spec.size);
   1.137 +    SDL_assert(ptr2 == NULL);
   1.138 +    SDL_assert(ptr2len == 0);
   1.139 +
   1.140 +    SDL_memcpy(buffer, ptr1, ptr1len);
   1.141 +
   1.142 +    if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
   1.143 +        return -1;
   1.144 +    }
   1.145 +
   1.146 +    h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
   1.147 +
   1.148 +    return ptr1len;
   1.149 +}
   1.150 +
   1.151 +static void
   1.152 +DSOUND_FlushCapture(_THIS)
   1.153 +{
   1.154 +    struct SDL_PrivateAudioData *h = this->hidden;
   1.155 +    DWORD junk, cursor;
   1.156 +    if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
   1.157 +        h->lastchunk = cursor / this->spec.size;
   1.158 +    }
   1.159 +}
   1.160 +
   1.161  static void
   1.162  DSOUND_CloseDevice(_THIS)
   1.163  {
   1.164      if (this->hidden->mixbuf != NULL) {
   1.165 +        IDirectSoundBuffer_Stop(this->hidden->mixbuf);
   1.166          IDirectSoundBuffer_Release(this->hidden->mixbuf);
   1.167      }
   1.168      if (this->hidden->sound != NULL) {
   1.169          IDirectSound_Release(this->hidden->sound);
   1.170      }
   1.171 +    if (this->hidden->capturebuf != NULL) {
   1.172 +        IDirectSoundCaptureBuffer_Stop(this->hidden->capturebuf);
   1.173 +        IDirectSoundCaptureBuffer_Release(this->hidden->capturebuf);
   1.174 +    }
   1.175 +    if (this->hidden->capture != NULL) {
   1.176 +        IDirectSoundCapture_Release(this->hidden->capture);
   1.177 +    }
   1.178      SDL_free(this->hidden);
   1.179  }
   1.180  
   1.181  /* This function tries to create a secondary audio buffer, and returns the
   1.182 -   number of audio chunks available in the created buffer.
   1.183 +   number of audio chunks available in the created buffer. This is for
   1.184 +   playback devices, not capture.
   1.185  */
   1.186  static int
   1.187 -CreateSecondary(_THIS, HWND focus)
   1.188 +CreateSecondary(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
   1.189  {
   1.190      LPDIRECTSOUND sndObj = this->hidden->sound;
   1.191      LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
   1.192 -    Uint32 chunksize = this->spec.size;
   1.193 -    const int numchunks = 8;
   1.194      HRESULT result = DS_OK;
   1.195      DSBUFFERDESC format;
   1.196      LPVOID pvAudioPtr1, pvAudioPtr2;
   1.197      DWORD dwAudioBytes1, dwAudioBytes2;
   1.198 -    WAVEFORMATEX wfmt;
   1.199 -
   1.200 -    SDL_zero(wfmt);
   1.201 -
   1.202 -    if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
   1.203 -        wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   1.204 -    } else {
   1.205 -        wfmt.wFormatTag = WAVE_FORMAT_PCM;
   1.206 -    }
   1.207 -
   1.208 -    wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   1.209 -    wfmt.nChannels = this->spec.channels;
   1.210 -    wfmt.nSamplesPerSec = this->spec.freq;
   1.211 -    wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
   1.212 -    wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
   1.213 -
   1.214 -    /* Try to set primary mixing privileges */
   1.215 -    if (focus) {
   1.216 -        result = IDirectSound_SetCooperativeLevel(sndObj,
   1.217 -                                                  focus, DSSCL_PRIORITY);
   1.218 -    } else {
   1.219 -        result = IDirectSound_SetCooperativeLevel(sndObj,
   1.220 -                                                  GetDesktopWindow(),
   1.221 -                                                  DSSCL_NORMAL);
   1.222 -    }
   1.223 -    if (result != DS_OK) {
   1.224 -        return SetDSerror("DirectSound SetCooperativeLevel", result);
   1.225 -    }
   1.226  
   1.227      /* Try to create the secondary buffer */
   1.228      SDL_zero(format);
   1.229      format.dwSize = sizeof(format);
   1.230      format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
   1.231 -    if (!focus) {
   1.232 -        format.dwFlags |= DSBCAPS_GLOBALFOCUS;
   1.233 -    } else {
   1.234 -        format.dwFlags |= DSBCAPS_STICKYFOCUS;
   1.235 -    }
   1.236 -    format.dwBufferBytes = numchunks * chunksize;
   1.237 -    if ((format.dwBufferBytes < DSBSIZE_MIN) ||
   1.238 -        (format.dwBufferBytes > DSBSIZE_MAX)) {
   1.239 -        return SDL_SetError("Sound buffer size must be between %d and %d",
   1.240 -                            DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
   1.241 -    }
   1.242 -    format.dwReserved = 0;
   1.243 -    format.lpwfxFormat = &wfmt;
   1.244 +    format.dwFlags |= DSBCAPS_GLOBALFOCUS;
   1.245 +    format.dwBufferBytes = bufsize;
   1.246 +    format.lpwfxFormat = wfmt;
   1.247      result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
   1.248      if (result != DS_OK) {
   1.249          return SetDSerror("DirectSound CreateSoundBuffer", result);
   1.250      }
   1.251 -    IDirectSoundBuffer_SetFormat(*sndbuf, &wfmt);
   1.252 +    IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
   1.253  
   1.254      /* Silence the initial audio buffer */
   1.255      result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
   1.256 @@ -410,18 +437,65 @@
   1.257      }
   1.258  
   1.259      /* We're ready to go */
   1.260 -    return (numchunks);
   1.261 +    return 0;
   1.262 +}
   1.263 +
   1.264 +/* This function tries to create a capture buffer, and returns the
   1.265 +   number of audio chunks available in the created buffer. This is for
   1.266 +   capture devices, not playback.
   1.267 +*/
   1.268 +static int
   1.269 +CreateCaptureBuffer(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
   1.270 +{
   1.271 +    LPDIRECTSOUNDCAPTURE capture = this->hidden->capture;
   1.272 +    LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &this->hidden->capturebuf;
   1.273 +    DSCBUFFERDESC format;
   1.274 +//    DWORD junk, cursor;
   1.275 +    HRESULT result;
   1.276 +
   1.277 +    SDL_zero(format);
   1.278 +    format.dwSize = sizeof (format);
   1.279 +    format.dwFlags = DSCBCAPS_WAVEMAPPED;
   1.280 +    format.dwBufferBytes = bufsize;
   1.281 +    format.lpwfxFormat = wfmt;
   1.282 +
   1.283 +    result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
   1.284 +    if (result != DS_OK) {
   1.285 +        return SetDSerror("DirectSound CreateCaptureBuffer", result);
   1.286 +    }
   1.287 +
   1.288 +    result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
   1.289 +    if (result != DS_OK) {
   1.290 +        IDirectSoundCaptureBuffer_Release(*capturebuf);
   1.291 +        return SetDSerror("DirectSound Start", result);
   1.292 +    }
   1.293 +
   1.294 +#if 0
   1.295 +    /* presumably this starts at zero, but just in case... */
   1.296 +    result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
   1.297 +    if (result != DS_OK) {
   1.298 +        IDirectSoundCaptureBuffer_Stop(*capturebuf);
   1.299 +        IDirectSoundCaptureBuffer_Release(*capturebuf);
   1.300 +        return SetDSerror("DirectSound GetCurrentPosition", result);
   1.301 +    }
   1.302 +
   1.303 +    this->hidden->lastchunk = cursor / this->spec.size;
   1.304 +#endif
   1.305 +
   1.306 +    return 0;
   1.307  }
   1.308  
   1.309  static int
   1.310  DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   1.311  {
   1.312 +    const DWORD numchunks = 8;
   1.313      HRESULT result;
   1.314      SDL_bool valid_format = SDL_FALSE;
   1.315      SDL_bool tried_format = SDL_FALSE;
   1.316      SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   1.317      LPGUID guid = (LPGUID) handle;
   1.318 -
   1.319 +	DWORD bufsize;
   1.320 +	
   1.321      /* Initialize all variables that we clean on shutdown */
   1.322      this->hidden = (struct SDL_PrivateAudioData *)
   1.323          SDL_malloc((sizeof *this->hidden));
   1.324 @@ -431,9 +505,22 @@
   1.325      SDL_zerop(this->hidden);
   1.326  
   1.327      /* Open the audio device */
   1.328 -    result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
   1.329 -    if (result != DS_OK) {
   1.330 -        return SetDSerror("DirectSoundCreate", result);
   1.331 +    if (iscapture) {
   1.332 +        result = pDirectSoundCaptureCreate8(guid, &this->hidden->capture, NULL);
   1.333 +        if (result != DS_OK) {
   1.334 +            return SetDSerror("DirectSoundCaptureCreate8", result);
   1.335 +        }
   1.336 +    } else {
   1.337 +        result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
   1.338 +        if (result != DS_OK) {
   1.339 +            return SetDSerror("DirectSoundCreate8", result);
   1.340 +        }
   1.341 +        result = IDirectSound_SetCooperativeLevel(this->hidden->sound,
   1.342 +                                                  GetDesktopWindow(),
   1.343 +                                                  DSSCL_NORMAL);
   1.344 +        if (result != DS_OK) {
   1.345 +            return SetDSerror("DirectSound SetCooperativeLevel", result);
   1.346 +        }
   1.347      }
   1.348  
   1.349      while ((!valid_format) && (test_format)) {
   1.350 @@ -443,12 +530,38 @@
   1.351          case AUDIO_S32:
   1.352          case AUDIO_F32:
   1.353              tried_format = SDL_TRUE;
   1.354 +
   1.355              this->spec.format = test_format;
   1.356 +
   1.357              /* Update the fragment size as size in bytes */
   1.358              SDL_CalculateAudioSpec(&this->spec);
   1.359 -            this->hidden->num_buffers = CreateSecondary(this, NULL);
   1.360 -            if (this->hidden->num_buffers > 0) {
   1.361 -                valid_format = SDL_TRUE;
   1.362 +
   1.363 +            bufsize = numchunks * this->spec.size;
   1.364 +            if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
   1.365 +                SDL_SetError("Sound buffer size must be between %d and %d",
   1.366 +                             (DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks,
   1.367 +                             DSBSIZE_MAX / numchunks);
   1.368 +            } else {
   1.369 +                int rc;
   1.370 +				WAVEFORMATEX wfmt;
   1.371 +                SDL_zero(wfmt);
   1.372 +                if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
   1.373 +                    wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   1.374 +                } else {
   1.375 +                    wfmt.wFormatTag = WAVE_FORMAT_PCM;
   1.376 +                }
   1.377 +
   1.378 +                wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   1.379 +                wfmt.nChannels = this->spec.channels;
   1.380 +                wfmt.nSamplesPerSec = this->spec.freq;
   1.381 +                wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
   1.382 +                wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
   1.383 +
   1.384 +                rc = iscapture ? CreateCaptureBuffer(this, bufsize, &wfmt) : CreateSecondary(this, bufsize, &wfmt);
   1.385 +                if (rc == 0) {
   1.386 +                    this->hidden->num_buffers = numchunks;
   1.387 +                    valid_format = SDL_TRUE;
   1.388 +                }
   1.389              }
   1.390              break;
   1.391          }
   1.392 @@ -462,8 +575,7 @@
   1.393          return SDL_SetError("DirectSound: Unsupported audio format");
   1.394      }
   1.395  
   1.396 -    /* The buffer will auto-start playing in DSOUND_WaitDevice() */
   1.397 -    this->hidden->mixlen = this->spec.size;
   1.398 +    /* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
   1.399  
   1.400      return 0;                   /* good to go. */
   1.401  }
   1.402 @@ -490,10 +602,13 @@
   1.403      impl->WaitDevice = DSOUND_WaitDevice;
   1.404      impl->WaitDone = DSOUND_WaitDone;
   1.405      impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
   1.406 +    impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
   1.407 +    impl->FlushCapture = DSOUND_FlushCapture;
   1.408      impl->CloseDevice = DSOUND_CloseDevice;
   1.409      impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
   1.410 +    impl->Deinitialize = DSOUND_Deinitialize;
   1.411  
   1.412 -    impl->Deinitialize = DSOUND_Deinitialize;
   1.413 +    impl->HasCaptureSupport = SDL_TRUE;
   1.414  
   1.415      return 1;   /* this audio target is available. */
   1.416  }
     2.1 --- a/src/audio/directsound/SDL_directsound.h	Wed Aug 10 15:34:24 2016 -0400
     2.2 +++ b/src/audio/directsound/SDL_directsound.h	Wed Aug 10 16:00:16 2016 -0400
     2.3 @@ -35,8 +35,9 @@
     2.4  {
     2.5      LPDIRECTSOUND sound;
     2.6      LPDIRECTSOUNDBUFFER mixbuf;
     2.7 +    LPDIRECTSOUNDCAPTURE capture;
     2.8 +    LPDIRECTSOUNDCAPTUREBUFFER capturebuf;
     2.9      int num_buffers;
    2.10 -    int mixlen;
    2.11      DWORD lastchunk;
    2.12      Uint8 *locked_buf;
    2.13  };