sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_directsound.c (19492B)


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