cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_directsound.c (17011B)


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