cscg22-gearboy

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

SDL_winmm.c (12236B)


      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_WINMM
     24
     25/* Allow access to a raw mixing buffer */
     26
     27#include "../../core/windows/SDL_windows.h"
     28#include <mmsystem.h>
     29
     30#include "SDL_timer.h"
     31#include "SDL_audio.h"
     32#include "../SDL_audio_c.h"
     33#include "SDL_winmm.h"
     34
     35#ifndef WAVE_FORMAT_IEEE_FLOAT
     36#define WAVE_FORMAT_IEEE_FLOAT 0x0003
     37#endif
     38
     39#define DETECT_DEV_IMPL(typ, capstyp) \
     40static void DetectWave##typ##Devs(SDL_AddAudioDevice addfn) { \
     41    const UINT devcount = wave##typ##GetNumDevs(); \
     42    capstyp caps; \
     43    UINT i; \
     44    for (i = 0; i < devcount; i++) { \
     45        if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
     46            char *name = WIN_StringToUTF8(caps.szPname); \
     47            if (name != NULL) { \
     48                addfn(name); \
     49                SDL_free(name); \
     50            } \
     51        } \
     52    } \
     53}
     54
     55DETECT_DEV_IMPL(Out, WAVEOUTCAPS)
     56DETECT_DEV_IMPL(In, WAVEINCAPS)
     57
     58static void
     59WINMM_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
     60{
     61    if (iscapture) {
     62        DetectWaveInDevs(addfn);
     63    } else {
     64        DetectWaveOutDevs(addfn);
     65    }
     66}
     67
     68static void CALLBACK
     69CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
     70          DWORD_PTR dwParam1, DWORD_PTR dwParam2)
     71{
     72    SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
     73
     74    /* Only service "buffer is filled" messages */
     75    if (uMsg != WIM_DATA)
     76        return;
     77
     78    /* Signal that we have a new buffer of data */
     79    ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
     80}
     81
     82
     83/* The Win32 callback for filling the WAVE device */
     84static void CALLBACK
     85FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
     86          DWORD_PTR dwParam1, DWORD_PTR dwParam2)
     87{
     88    SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
     89
     90    /* Only service "buffer done playing" messages */
     91    if (uMsg != WOM_DONE)
     92        return;
     93
     94    /* Signal that we are done playing a buffer */
     95    ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
     96}
     97
     98static int
     99SetMMerror(char *function, MMRESULT code)
    100{
    101    int len;
    102    char errbuf[MAXERRORLENGTH];
    103    wchar_t werrbuf[MAXERRORLENGTH];
    104
    105    SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
    106    len = SDL_static_cast(int, SDL_strlen(errbuf));
    107
    108    waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
    109    WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
    110                        MAXERRORLENGTH - len, NULL, NULL);
    111
    112    return SDL_SetError("%s", errbuf);
    113}
    114
    115static void
    116WINMM_WaitDevice(_THIS)
    117{
    118    /* Wait for an audio chunk to finish */
    119    WaitForSingleObject(this->hidden->audio_sem, INFINITE);
    120}
    121
    122static Uint8 *
    123WINMM_GetDeviceBuf(_THIS)
    124{
    125    return (Uint8 *) (this->hidden->
    126                      wavebuf[this->hidden->next_buffer].lpData);
    127}
    128
    129static void
    130WINMM_PlayDevice(_THIS)
    131{
    132    /* Queue it up */
    133    waveOutWrite(this->hidden->hout,
    134                 &this->hidden->wavebuf[this->hidden->next_buffer],
    135                 sizeof(this->hidden->wavebuf[0]));
    136    this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
    137}
    138
    139static void
    140WINMM_WaitDone(_THIS)
    141{
    142    int i, left;
    143
    144    do {
    145        left = NUM_BUFFERS;
    146        for (i = 0; i < NUM_BUFFERS; ++i) {
    147            if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
    148                --left;
    149            }
    150        }
    151        if (left > 0) {
    152            SDL_Delay(100);
    153        }
    154    } while (left > 0);
    155}
    156
    157static void
    158WINMM_CloseDevice(_THIS)
    159{
    160    /* Close up audio */
    161    if (this->hidden != NULL) {
    162        int i;
    163
    164        if (this->hidden->audio_sem) {
    165            CloseHandle(this->hidden->audio_sem);
    166            this->hidden->audio_sem = 0;
    167        }
    168
    169        /* Clean up mixing buffers */
    170        for (i = 0; i < NUM_BUFFERS; ++i) {
    171            if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
    172                waveOutUnprepareHeader(this->hidden->hout,
    173                                       &this->hidden->wavebuf[i],
    174                                       sizeof(this->hidden->wavebuf[i]));
    175                this->hidden->wavebuf[i].dwUser = 0xFFFF;
    176            }
    177        }
    178
    179        /* Free raw mixing buffer */
    180        SDL_free(this->hidden->mixbuf);
    181        this->hidden->mixbuf = NULL;
    182
    183        if (this->hidden->hin) {
    184            waveInClose(this->hidden->hin);
    185            this->hidden->hin = 0;
    186        }
    187
    188        if (this->hidden->hout) {
    189            waveOutClose(this->hidden->hout);
    190            this->hidden->hout = 0;
    191        }
    192
    193        SDL_free(this->hidden);
    194        this->hidden = NULL;
    195    }
    196}
    197
    198static SDL_bool
    199PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture)
    200{
    201    SDL_zerop(pfmt);
    202
    203    if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
    204        pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    205    } else {
    206        pfmt->wFormatTag = WAVE_FORMAT_PCM;
    207    }
    208    pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
    209
    210    pfmt->nChannels = this->spec.channels;
    211    pfmt->nSamplesPerSec = this->spec.freq;
    212    pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
    213    pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
    214
    215    if (iscapture) {
    216        return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
    217    } else {
    218        return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
    219    }
    220}
    221
    222static int
    223WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
    224{
    225    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
    226    int valid_datatype = 0;
    227    MMRESULT result;
    228    WAVEFORMATEX waveformat;
    229    UINT devId = WAVE_MAPPER;  /* WAVE_MAPPER == choose system's default */
    230    char *utf8 = NULL;
    231    UINT i;
    232
    233    if (devname != NULL) {  /* specific device requested? */
    234        if (iscapture) {
    235            const UINT devcount = waveInGetNumDevs();
    236            WAVEINCAPS caps;
    237            for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
    238                result = waveInGetDevCaps(i, &caps, sizeof (caps));
    239                if (result != MMSYSERR_NOERROR)
    240                    continue;
    241                else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
    242                    continue;
    243                else if (SDL_strcmp(devname, utf8) == 0)
    244                    devId = i;
    245                SDL_free(utf8);
    246            }
    247        } else {
    248            const UINT devcount = waveOutGetNumDevs();
    249            WAVEOUTCAPS caps;
    250            for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
    251                result = waveOutGetDevCaps(i, &caps, sizeof (caps));
    252                if (result != MMSYSERR_NOERROR)
    253                    continue;
    254                else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
    255                    continue;
    256                else if (SDL_strcmp(devname, utf8) == 0)
    257                    devId = i;
    258                SDL_free(utf8);
    259            }
    260        }
    261
    262        if (devId == WAVE_MAPPER) {
    263            return SDL_SetError("Requested device not found");
    264        }
    265    }
    266
    267    /* Initialize all variables that we clean on shutdown */
    268    this->hidden = (struct SDL_PrivateAudioData *)
    269        SDL_malloc((sizeof *this->hidden));
    270    if (this->hidden == NULL) {
    271        return SDL_OutOfMemory();
    272    }
    273    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    274
    275    /* Initialize the wavebuf structures for closing */
    276    for (i = 0; i < NUM_BUFFERS; ++i)
    277        this->hidden->wavebuf[i].dwUser = 0xFFFF;
    278
    279    if (this->spec.channels > 2)
    280        this->spec.channels = 2;        /* !!! FIXME: is this right? */
    281
    282    /* Check the buffer size -- minimum of 1/4 second (word aligned) */
    283    if (this->spec.samples < (this->spec.freq / 4))
    284        this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
    285
    286    while ((!valid_datatype) && (test_format)) {
    287        switch (test_format) {
    288        case AUDIO_U8:
    289        case AUDIO_S16:
    290        case AUDIO_S32:
    291        case AUDIO_F32:
    292            this->spec.format = test_format;
    293            if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
    294                valid_datatype = 1;
    295            } else {
    296                test_format = SDL_NextAudioFormat();
    297            }
    298            break;
    299
    300        default:
    301            test_format = SDL_NextAudioFormat();
    302            break;
    303        }
    304    }
    305
    306    if (!valid_datatype) {
    307        WINMM_CloseDevice(this);
    308        return SDL_SetError("Unsupported audio format");
    309    }
    310
    311    /* Update the fragment size as size in bytes */
    312    SDL_CalculateAudioSpec(&this->spec);
    313
    314    /* Open the audio device */
    315    if (iscapture) {
    316        result = waveInOpen(&this->hidden->hin, devId, &waveformat,
    317                             (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
    318                             CALLBACK_FUNCTION);
    319    } else {
    320        result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
    321                             (DWORD_PTR) FillSound, (DWORD_PTR) this,
    322                             CALLBACK_FUNCTION);
    323    }
    324
    325    if (result != MMSYSERR_NOERROR) {
    326        WINMM_CloseDevice(this);
    327        return SetMMerror("waveOutOpen()", result);
    328    }
    329#ifdef SOUND_DEBUG
    330    /* Check the sound device we retrieved */
    331    {
    332        WAVEOUTCAPS caps;
    333
    334        result = waveOutGetDevCaps((UINT) this->hidden->hout,
    335                                   &caps, sizeof(caps));
    336        if (result != MMSYSERR_NOERROR) {
    337            WINMM_CloseDevice(this);
    338            return SetMMerror("waveOutGetDevCaps()", result);
    339        }
    340        printf("Audio device: %s\n", caps.szPname);
    341    }
    342#endif
    343
    344    /* Create the audio buffer semaphore */
    345    this->hidden->audio_sem =
    346        CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
    347    if (this->hidden->audio_sem == NULL) {
    348        WINMM_CloseDevice(this);
    349        return SDL_SetError("Couldn't create semaphore");
    350    }
    351
    352    /* Create the sound buffers */
    353    this->hidden->mixbuf =
    354        (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
    355    if (this->hidden->mixbuf == NULL) {
    356        WINMM_CloseDevice(this);
    357        return SDL_OutOfMemory();
    358    }
    359    for (i = 0; i < NUM_BUFFERS; ++i) {
    360        SDL_memset(&this->hidden->wavebuf[i], 0,
    361                   sizeof(this->hidden->wavebuf[i]));
    362        this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
    363        this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
    364        this->hidden->wavebuf[i].lpData =
    365            (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
    366        result = waveOutPrepareHeader(this->hidden->hout,
    367                                      &this->hidden->wavebuf[i],
    368                                      sizeof(this->hidden->wavebuf[i]));
    369        if (result != MMSYSERR_NOERROR) {
    370            WINMM_CloseDevice(this);
    371            return SetMMerror("waveOutPrepareHeader()", result);
    372        }
    373    }
    374
    375    return 0;                   /* Ready to go! */
    376}
    377
    378
    379static int
    380WINMM_Init(SDL_AudioDriverImpl * impl)
    381{
    382    /* Set the function pointers */
    383    impl->DetectDevices = WINMM_DetectDevices;
    384    impl->OpenDevice = WINMM_OpenDevice;
    385    impl->PlayDevice = WINMM_PlayDevice;
    386    impl->WaitDevice = WINMM_WaitDevice;
    387    impl->WaitDone = WINMM_WaitDone;
    388    impl->GetDeviceBuf = WINMM_GetDeviceBuf;
    389    impl->CloseDevice = WINMM_CloseDevice;
    390
    391    return 1;   /* this audio target is available. */
    392}
    393
    394AudioBootStrap WINMM_bootstrap = {
    395    "winmm", "Windows Waveform Audio", WINMM_Init, 0
    396};
    397
    398#endif /* SDL_AUDIO_DRIVER_WINMM */
    399
    400/* vi: set ts=4 sw=4 expandtab: */