src/audio/directsound/SDL_directsound.c
author Ryan C. Gordon
Tue, 24 Jan 2017 16:18:25 -0500
changeset 10850 c9dc0068b0e7
parent 10780 4ea5472ed455
child 11811 5d94cb6b24d3
permissions -rw-r--r--
configure: report libsamplerate support status.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_AUDIO_DRIVER_DSOUND
    24 
    25 /* Allow access to a raw mixing buffer */
    26 
    27 #include "SDL_assert.h"
    28 #include "SDL_timer.h"
    29 #include "SDL_loadso.h"
    30 #include "SDL_audio.h"
    31 #include "../SDL_audio_c.h"
    32 #include "SDL_directsound.h"
    33 
    34 #ifndef WAVE_FORMAT_IEEE_FLOAT
    35 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
    36 #endif
    37 
    38 /* DirectX function pointers for audio */
    39 static void* DSoundDLL = NULL;
    40 typedef HRESULT (WINAPI *fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
    41 typedef HRESULT (WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
    42 typedef HRESULT (WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID,LPDIRECTSOUNDCAPTURE8 *,LPUNKNOWN);
    43 typedef HRESULT (WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
    44 static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
    45 static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
    46 static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
    47 static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
    48 
    49 static void
    50 DSOUND_Unload(void)
    51 {
    52     pDirectSoundCreate8 = NULL;
    53     pDirectSoundEnumerateW = NULL;
    54     pDirectSoundCaptureCreate8 = NULL;
    55     pDirectSoundCaptureEnumerateW = NULL;
    56 
    57     if (DSoundDLL != NULL) {
    58         SDL_UnloadObject(DSoundDLL);
    59         DSoundDLL = NULL;
    60     }
    61 }
    62 
    63 
    64 static int
    65 DSOUND_Load(void)
    66 {
    67     int loaded = 0;
    68 
    69     DSOUND_Unload();
    70 
    71     DSoundDLL = SDL_LoadObject("DSOUND.DLL");
    72     if (DSoundDLL == NULL) {
    73         SDL_SetError("DirectSound: failed to load DSOUND.DLL");
    74     } else {
    75         /* Now make sure we have DirectX 8 or better... */
    76         #define DSOUNDLOAD(f) { \
    77             p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \
    78             if (!p##f) loaded = 0; \
    79         }
    80         loaded = 1;  /* will reset if necessary. */
    81         DSOUNDLOAD(DirectSoundCreate8);
    82         DSOUNDLOAD(DirectSoundEnumerateW);
    83         DSOUNDLOAD(DirectSoundCaptureCreate8);
    84         DSOUNDLOAD(DirectSoundCaptureEnumerateW);
    85         #undef DSOUNDLOAD
    86 
    87         if (!loaded) {
    88             SDL_SetError("DirectSound: System doesn't appear to have DX8.");
    89         }
    90     }
    91 
    92     if (!loaded) {
    93         DSOUND_Unload();
    94     }
    95 
    96     return loaded;
    97 }
    98 
    99 static int
   100 SetDSerror(const char *function, int code)
   101 {
   102     static const char *error;
   103     static char errbuf[1024];
   104 
   105     errbuf[0] = 0;
   106     switch (code) {
   107     case E_NOINTERFACE:
   108         error = "Unsupported interface -- Is DirectX 8.0 or later installed?";
   109         break;
   110     case DSERR_ALLOCATED:
   111         error = "Audio device in use";
   112         break;
   113     case DSERR_BADFORMAT:
   114         error = "Unsupported audio format";
   115         break;
   116     case DSERR_BUFFERLOST:
   117         error = "Mixing buffer was lost";
   118         break;
   119     case DSERR_CONTROLUNAVAIL:
   120         error = "Control requested is not available";
   121         break;
   122     case DSERR_INVALIDCALL:
   123         error = "Invalid call for the current state";
   124         break;
   125     case DSERR_INVALIDPARAM:
   126         error = "Invalid parameter";
   127         break;
   128     case DSERR_NODRIVER:
   129         error = "No audio device found";
   130         break;
   131     case DSERR_OUTOFMEMORY:
   132         error = "Out of memory";
   133         break;
   134     case DSERR_PRIOLEVELNEEDED:
   135         error = "Caller doesn't have priority";
   136         break;
   137     case DSERR_UNSUPPORTED:
   138         error = "Function not supported";
   139         break;
   140     default:
   141         SDL_snprintf(errbuf, SDL_arraysize(errbuf),
   142                      "%s: Unknown DirectSound error: 0x%x", function, code);
   143         break;
   144     }
   145     if (!errbuf[0]) {
   146         SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function,
   147                      error);
   148     }
   149     return SDL_SetError("%s", errbuf);
   150 }
   151 
   152 static void
   153 DSOUND_FreeDeviceHandle(void *handle)
   154 {
   155     SDL_free(handle);
   156 }
   157 
   158 static BOOL CALLBACK
   159 FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
   160 {
   161     const int iscapture = (int) ((size_t) data);
   162     if (guid != NULL) {  /* skip default device */
   163         char *str = WIN_LookupAudioDeviceName(desc, guid);
   164         if (str != NULL) {
   165             LPGUID cpyguid = (LPGUID) SDL_malloc(sizeof (GUID));
   166             SDL_memcpy(cpyguid, guid, sizeof (GUID));
   167             SDL_AddAudioDevice(iscapture, str, cpyguid);
   168             SDL_free(str);  /* addfn() makes a copy of this string. */
   169         }
   170     }
   171     return TRUE;  /* keep enumerating. */
   172 }
   173 
   174 static void
   175 DSOUND_DetectDevices(void)
   176 {
   177     pDirectSoundCaptureEnumerateW(FindAllDevs, (void *) ((size_t) 1));
   178     pDirectSoundEnumerateW(FindAllDevs, (void *) ((size_t) 0));
   179 }
   180 
   181 
   182 static void
   183 DSOUND_WaitDevice(_THIS)
   184 {
   185     DWORD status = 0;
   186     DWORD cursor = 0;
   187     DWORD junk = 0;
   188     HRESULT result = DS_OK;
   189 
   190     /* Semi-busy wait, since we have no way of getting play notification
   191        on a primary mixing buffer located in hardware (DirectX 5.0)
   192      */
   193     result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
   194                                                    &junk, &cursor);
   195     if (result != DS_OK) {
   196         if (result == DSERR_BUFFERLOST) {
   197             IDirectSoundBuffer_Restore(this->hidden->mixbuf);
   198         }
   199 #ifdef DEBUG_SOUND
   200         SetDSerror("DirectSound GetCurrentPosition", result);
   201 #endif
   202         return;
   203     }
   204 
   205     while ((cursor / this->spec.size) == this->hidden->lastchunk) {
   206         /* FIXME: find out how much time is left and sleep that long */
   207         SDL_Delay(1);
   208 
   209         /* Try to restore a lost sound buffer */
   210         IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status);
   211         if ((status & DSBSTATUS_BUFFERLOST)) {
   212             IDirectSoundBuffer_Restore(this->hidden->mixbuf);
   213             IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status);
   214             if ((status & DSBSTATUS_BUFFERLOST)) {
   215                 break;
   216             }
   217         }
   218         if (!(status & DSBSTATUS_PLAYING)) {
   219             result = IDirectSoundBuffer_Play(this->hidden->mixbuf, 0, 0,
   220                                              DSBPLAY_LOOPING);
   221             if (result == DS_OK) {
   222                 continue;
   223             }
   224 #ifdef DEBUG_SOUND
   225             SetDSerror("DirectSound Play", result);
   226 #endif
   227             return;
   228         }
   229 
   230         /* Find out where we are playing */
   231         result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
   232                                                        &junk, &cursor);
   233         if (result != DS_OK) {
   234             SetDSerror("DirectSound GetCurrentPosition", result);
   235             return;
   236         }
   237     }
   238 }
   239 
   240 static void
   241 DSOUND_PlayDevice(_THIS)
   242 {
   243     /* Unlock the buffer, allowing it to play */
   244     if (this->hidden->locked_buf) {
   245         IDirectSoundBuffer_Unlock(this->hidden->mixbuf,
   246                                   this->hidden->locked_buf,
   247                                   this->spec.size, NULL, 0);
   248     }
   249 }
   250 
   251 static Uint8 *
   252 DSOUND_GetDeviceBuf(_THIS)
   253 {
   254     DWORD cursor = 0;
   255     DWORD junk = 0;
   256     HRESULT result = DS_OK;
   257     DWORD rawlen = 0;
   258 
   259     /* Figure out which blocks to fill next */
   260     this->hidden->locked_buf = NULL;
   261     result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
   262                                                    &junk, &cursor);
   263     if (result == DSERR_BUFFERLOST) {
   264         IDirectSoundBuffer_Restore(this->hidden->mixbuf);
   265         result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf,
   266                                                        &junk, &cursor);
   267     }
   268     if (result != DS_OK) {
   269         SetDSerror("DirectSound GetCurrentPosition", result);
   270         return (NULL);
   271     }
   272     cursor /= this->spec.size;
   273 #ifdef DEBUG_SOUND
   274     /* Detect audio dropouts */
   275     {
   276         DWORD spot = cursor;
   277         if (spot < this->hidden->lastchunk) {
   278             spot += this->hidden->num_buffers;
   279         }
   280         if (spot > this->hidden->lastchunk + 1) {
   281             fprintf(stderr, "Audio dropout, missed %d fragments\n",
   282                     (spot - (this->hidden->lastchunk + 1)));
   283         }
   284     }
   285 #endif
   286     this->hidden->lastchunk = cursor;
   287     cursor = (cursor + 1) % this->hidden->num_buffers;
   288     cursor *= this->spec.size;
   289 
   290     /* Lock the audio buffer */
   291     result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
   292                                      this->spec.size,
   293                                      (LPVOID *) & this->hidden->locked_buf,
   294                                      &rawlen, NULL, &junk, 0);
   295     if (result == DSERR_BUFFERLOST) {
   296         IDirectSoundBuffer_Restore(this->hidden->mixbuf);
   297         result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
   298                                          this->spec.size,
   299                                          (LPVOID *) & this->
   300                                          hidden->locked_buf, &rawlen, NULL,
   301                                          &junk, 0);
   302     }
   303     if (result != DS_OK) {
   304         SetDSerror("DirectSound Lock", result);
   305         return (NULL);
   306     }
   307     return (this->hidden->locked_buf);
   308 }
   309 
   310 static int
   311 DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
   312 {
   313     struct SDL_PrivateAudioData *h = this->hidden;
   314     DWORD junk, cursor, ptr1len, ptr2len;
   315     VOID *ptr1, *ptr2;
   316 
   317     SDL_assert(buflen == this->spec.size);
   318 
   319     while (SDL_TRUE) {
   320         if (SDL_AtomicGet(&this->shutdown)) {  /* in case the buffer froze... */
   321             SDL_memset(buffer, this->spec.silence, buflen);
   322             return buflen;
   323         }
   324 
   325         if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
   326             return -1;
   327         }
   328         if ((cursor / this->spec.size) == h->lastchunk) {
   329             SDL_Delay(1);  /* FIXME: find out how much time is left and sleep that long */
   330         } else {
   331             break;
   332         }
   333     }
   334 
   335     if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * this->spec.size, this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
   336         return -1;
   337     }
   338 
   339     SDL_assert(ptr1len == this->spec.size);
   340     SDL_assert(ptr2 == NULL);
   341     SDL_assert(ptr2len == 0);
   342 
   343     SDL_memcpy(buffer, ptr1, ptr1len);
   344 
   345     if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
   346         return -1;
   347     }
   348 
   349     h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
   350 
   351     return ptr1len;
   352 }
   353 
   354 static void
   355 DSOUND_FlushCapture(_THIS)
   356 {
   357     struct SDL_PrivateAudioData *h = this->hidden;
   358     DWORD junk, cursor;
   359     if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
   360         h->lastchunk = cursor / this->spec.size;
   361     }
   362 }
   363 
   364 static void
   365 DSOUND_CloseDevice(_THIS)
   366 {
   367     if (this->hidden->mixbuf != NULL) {
   368         IDirectSoundBuffer_Stop(this->hidden->mixbuf);
   369         IDirectSoundBuffer_Release(this->hidden->mixbuf);
   370     }
   371     if (this->hidden->sound != NULL) {
   372         IDirectSound_Release(this->hidden->sound);
   373     }
   374     if (this->hidden->capturebuf != NULL) {
   375         IDirectSoundCaptureBuffer_Stop(this->hidden->capturebuf);
   376         IDirectSoundCaptureBuffer_Release(this->hidden->capturebuf);
   377     }
   378     if (this->hidden->capture != NULL) {
   379         IDirectSoundCapture_Release(this->hidden->capture);
   380     }
   381     SDL_free(this->hidden);
   382 }
   383 
   384 /* This function tries to create a secondary audio buffer, and returns the
   385    number of audio chunks available in the created buffer. This is for
   386    playback devices, not capture.
   387 */
   388 static int
   389 CreateSecondary(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
   390 {
   391     LPDIRECTSOUND sndObj = this->hidden->sound;
   392     LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
   393     HRESULT result = DS_OK;
   394     DSBUFFERDESC format;
   395     LPVOID pvAudioPtr1, pvAudioPtr2;
   396     DWORD dwAudioBytes1, dwAudioBytes2;
   397 
   398     /* Try to create the secondary buffer */
   399     SDL_zero(format);
   400     format.dwSize = sizeof(format);
   401     format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
   402     format.dwFlags |= DSBCAPS_GLOBALFOCUS;
   403     format.dwBufferBytes = bufsize;
   404     format.lpwfxFormat = wfmt;
   405     result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
   406     if (result != DS_OK) {
   407         return SetDSerror("DirectSound CreateSoundBuffer", result);
   408     }
   409     IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
   410 
   411     /* Silence the initial audio buffer */
   412     result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
   413                                      (LPVOID *) & pvAudioPtr1, &dwAudioBytes1,
   414                                      (LPVOID *) & pvAudioPtr2, &dwAudioBytes2,
   415                                      DSBLOCK_ENTIREBUFFER);
   416     if (result == DS_OK) {
   417         SDL_memset(pvAudioPtr1, this->spec.silence, dwAudioBytes1);
   418         IDirectSoundBuffer_Unlock(*sndbuf,
   419                                   (LPVOID) pvAudioPtr1, dwAudioBytes1,
   420                                   (LPVOID) pvAudioPtr2, dwAudioBytes2);
   421     }
   422 
   423     /* We're ready to go */
   424     return 0;
   425 }
   426 
   427 /* This function tries to create a capture buffer, and returns the
   428    number of audio chunks available in the created buffer. This is for
   429    capture devices, not playback.
   430 */
   431 static int
   432 CreateCaptureBuffer(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
   433 {
   434     LPDIRECTSOUNDCAPTURE capture = this->hidden->capture;
   435     LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &this->hidden->capturebuf;
   436     DSCBUFFERDESC format;
   437     HRESULT result;
   438 
   439     SDL_zero(format);
   440     format.dwSize = sizeof (format);
   441     format.dwFlags = DSCBCAPS_WAVEMAPPED;
   442     format.dwBufferBytes = bufsize;
   443     format.lpwfxFormat = wfmt;
   444 
   445     result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
   446     if (result != DS_OK) {
   447         return SetDSerror("DirectSound CreateCaptureBuffer", result);
   448     }
   449 
   450     result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
   451     if (result != DS_OK) {
   452         IDirectSoundCaptureBuffer_Release(*capturebuf);
   453         return SetDSerror("DirectSound Start", result);
   454     }
   455 
   456 #if 0
   457     /* presumably this starts at zero, but just in case... */
   458     result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
   459     if (result != DS_OK) {
   460         IDirectSoundCaptureBuffer_Stop(*capturebuf);
   461         IDirectSoundCaptureBuffer_Release(*capturebuf);
   462         return SetDSerror("DirectSound GetCurrentPosition", result);
   463     }
   464 
   465     this->hidden->lastchunk = cursor / this->spec.size;
   466 #endif
   467 
   468     return 0;
   469 }
   470 
   471 static int
   472 DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   473 {
   474     const DWORD numchunks = 8;
   475     HRESULT result;
   476     SDL_bool valid_format = SDL_FALSE;
   477     SDL_bool tried_format = SDL_FALSE;
   478     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   479     LPGUID guid = (LPGUID) handle;
   480 	DWORD bufsize;
   481 	
   482     /* Initialize all variables that we clean on shutdown */
   483     this->hidden = (struct SDL_PrivateAudioData *)
   484         SDL_malloc((sizeof *this->hidden));
   485     if (this->hidden == NULL) {
   486         return SDL_OutOfMemory();
   487     }
   488     SDL_zerop(this->hidden);
   489 
   490     /* Open the audio device */
   491     if (iscapture) {
   492         result = pDirectSoundCaptureCreate8(guid, &this->hidden->capture, NULL);
   493         if (result != DS_OK) {
   494             return SetDSerror("DirectSoundCaptureCreate8", result);
   495         }
   496     } else {
   497         result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
   498         if (result != DS_OK) {
   499             return SetDSerror("DirectSoundCreate8", result);
   500         }
   501         result = IDirectSound_SetCooperativeLevel(this->hidden->sound,
   502                                                   GetDesktopWindow(),
   503                                                   DSSCL_NORMAL);
   504         if (result != DS_OK) {
   505             return SetDSerror("DirectSound SetCooperativeLevel", result);
   506         }
   507     }
   508 
   509     while ((!valid_format) && (test_format)) {
   510         switch (test_format) {
   511         case AUDIO_U8:
   512         case AUDIO_S16:
   513         case AUDIO_S32:
   514         case AUDIO_F32:
   515             tried_format = SDL_TRUE;
   516 
   517             this->spec.format = test_format;
   518 
   519             /* Update the fragment size as size in bytes */
   520             SDL_CalculateAudioSpec(&this->spec);
   521 
   522             bufsize = numchunks * this->spec.size;
   523             if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
   524                 SDL_SetError("Sound buffer size must be between %d and %d",
   525                              (int) ((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks),
   526                              (int) (DSBSIZE_MAX / numchunks));
   527             } else {
   528                 int rc;
   529 				WAVEFORMATEX wfmt;
   530                 SDL_zero(wfmt);
   531                 if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
   532                     wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   533                 } else {
   534                     wfmt.wFormatTag = WAVE_FORMAT_PCM;
   535                 }
   536 
   537                 wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   538                 wfmt.nChannels = this->spec.channels;
   539                 wfmt.nSamplesPerSec = this->spec.freq;
   540                 wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
   541                 wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
   542 
   543                 rc = iscapture ? CreateCaptureBuffer(this, bufsize, &wfmt) : CreateSecondary(this, bufsize, &wfmt);
   544                 if (rc == 0) {
   545                     this->hidden->num_buffers = numchunks;
   546                     valid_format = SDL_TRUE;
   547                 }
   548             }
   549             break;
   550         }
   551         test_format = SDL_NextAudioFormat();
   552     }
   553 
   554     if (!valid_format) {
   555         if (tried_format) {
   556             return -1;  /* CreateSecondary() should have called SDL_SetError(). */
   557         }
   558         return SDL_SetError("DirectSound: Unsupported audio format");
   559     }
   560 
   561     /* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
   562 
   563     return 0;                   /* good to go. */
   564 }
   565 
   566 
   567 static void
   568 DSOUND_Deinitialize(void)
   569 {
   570     DSOUND_Unload();
   571 }
   572 
   573 
   574 static int
   575 DSOUND_Init(SDL_AudioDriverImpl * impl)
   576 {
   577     if (!DSOUND_Load()) {
   578         return 0;
   579     }
   580 
   581     /* Set the function pointers */
   582     impl->DetectDevices = DSOUND_DetectDevices;
   583     impl->OpenDevice = DSOUND_OpenDevice;
   584     impl->PlayDevice = DSOUND_PlayDevice;
   585     impl->WaitDevice = DSOUND_WaitDevice;
   586     impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
   587     impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
   588     impl->FlushCapture = DSOUND_FlushCapture;
   589     impl->CloseDevice = DSOUND_CloseDevice;
   590     impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
   591     impl->Deinitialize = DSOUND_Deinitialize;
   592 
   593     impl->HasCaptureSupport = SDL_TRUE;
   594 
   595     return 1;   /* this audio target is available. */
   596 }
   597 
   598 AudioBootStrap DSOUND_bootstrap = {
   599     "directsound", "DirectSound", DSOUND_Init, 0
   600 };
   601 
   602 #endif /* SDL_AUDIO_DRIVER_DSOUND */
   603 
   604 /* vi: set ts=4 sw=4 expandtab: */