cscg22-gearboy

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

SDL_xaudio2.c (17584B)


      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
     22/* WinRT NOTICE:
     23
     24   A few changes to SDL's XAudio2 backend were warranted by API
     25   changes to Windows.  Many, but not all of these are documented by Microsoft
     26   at:
     27   http://blogs.msdn.com/b/chuckw/archive/2012/04/02/xaudio2-and-windows-8-consumer-preview.aspx
     28
     29   1. Windows' thread synchronization function, CreateSemaphore, was removed
     30      from WinRT.  SDL's semaphore API was substituted instead.
     31   2. The method calls, IXAudio2::GetDeviceCount and IXAudio2::GetDeviceDetails
     32      were removed from the XAudio2 API.  Microsoft is telling developers to
     33      use APIs in Windows::Foundation instead.
     34      For SDL, the missing methods were reimplemented using the APIs Microsoft
     35      said to use.
     36   3. CoInitialize and CoUninitialize are not available in WinRT.
     37      These calls were removed, as COM will have been initialized earlier,
     38      at least by the call to the WinRT app's main function
     39      (aka 'int main(Platform::Array<Platform::String^>^)).  (DLudwig:
     40      This was my understanding of how WinRT: the 'main' function uses
     41      a tag of [MTAThread], which should initialize COM.  My understanding
     42      of COM is somewhat limited, and I may be incorrect here.)
     43   4. IXAudio2::CreateMasteringVoice changed its integer-based 'DeviceIndex'
     44      argument to a string-based one, 'szDeviceId'.  In WinRT, the
     45      string-based argument will be used.
     46*/
     47#include "../../SDL_internal.h"
     48
     49#if SDL_AUDIO_DRIVER_XAUDIO2
     50
     51#include "../../core/windows/SDL_windows.h"
     52#include "SDL_audio.h"
     53#include "../SDL_audio_c.h"
     54#include "../SDL_sysaudio.h"
     55#include "SDL_assert.h"
     56
     57#ifdef __GNUC__
     58/* The configure script already did any necessary checking */
     59#  define SDL_XAUDIO2_HAS_SDK 1
     60#elif defined(__WINRT__)
     61/* WinRT always has access to the XAudio 2 SDK */
     62#  define SDL_XAUDIO2_HAS_SDK
     63#else
     64/* XAudio2 exists as of the March 2008 DirectX SDK 
     65   The XAudio2 implementation available in the Windows 8 SDK targets Windows 8 and newer.
     66   If you want to build SDL with XAudio2 support you should install the DirectX SDK.
     67 */
     68#include <dxsdkver.h>
     69#if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284))
     70#  pragma message("Your DirectX SDK is too old. Disabling XAudio2 support.")
     71#else
     72#  define SDL_XAUDIO2_HAS_SDK 1
     73#endif
     74#endif
     75
     76#ifdef SDL_XAUDIO2_HAS_SDK
     77
     78/* Check to see if we're compiling for XAudio 2.8, or higher. */
     79#ifdef WINVER
     80#if WINVER >= 0x0602  /* Windows 8 SDK or higher? */
     81#define SDL_XAUDIO2_WIN8 1
     82#endif
     83#endif
     84
     85/* The XAudio header file, when #include'd on WinRT, will only compile in C++
     86   files, but not C.  A few preprocessor-based hacks are defined below in order
     87   to get xaudio2.h to compile in the C/non-C++ file, SDL_xaudio2.c.
     88 */
     89#ifdef __WINRT__
     90#define uuid(x)
     91#define DX_BUILD
     92#endif
     93
     94#define INITGUID 1
     95#include <xaudio2.h>
     96
     97/* Hidden "this" pointer for the audio functions */
     98#define _THIS   SDL_AudioDevice *this
     99
    100#ifdef __WINRT__
    101#include "SDL_xaudio2_winrthelpers.h"
    102#endif
    103
    104/* Fixes bug 1210 where some versions of gcc need named parameters */
    105#ifdef __GNUC__
    106#ifdef THIS
    107#undef THIS
    108#endif
    109#define THIS    INTERFACE *p
    110#ifdef THIS_
    111#undef THIS_
    112#endif
    113#define THIS_   INTERFACE *p,
    114#endif
    115
    116struct SDL_PrivateAudioData
    117{
    118    IXAudio2 *ixa2;
    119    IXAudio2SourceVoice *source;
    120    IXAudio2MasteringVoice *mastering;
    121    SDL_sem * semaphore;
    122    Uint8 *mixbuf;
    123    int mixlen;
    124    Uint8 *nextbuf;
    125};
    126
    127
    128static void
    129XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
    130{
    131    IXAudio2 *ixa2 = NULL;
    132    UINT32 devcount = 0;
    133    UINT32 i = 0;
    134
    135    if (iscapture) {
    136        SDL_SetError("XAudio2: capture devices unsupported.");
    137        return;
    138    } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
    139        SDL_SetError("XAudio2: XAudio2Create() failed at detection.");
    140        return;
    141    } else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
    142        SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed.");
    143        IXAudio2_Release(ixa2);
    144        return;
    145    }
    146
    147    for (i = 0; i < devcount; i++) {
    148        XAUDIO2_DEVICE_DETAILS details;
    149        if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
    150            char *str = WIN_StringToUTF8(details.DisplayName);
    151            if (str != NULL) {
    152                addfn(str);
    153                SDL_free(str);  /* addfn() made a copy of the string. */
    154            }
    155        }
    156    }
    157
    158    IXAudio2_Release(ixa2);
    159}
    160
    161static void STDMETHODCALLTYPE
    162VoiceCBOnBufferEnd(THIS_ void *data)
    163{
    164    /* Just signal the SDL audio thread and get out of XAudio2's way. */
    165    SDL_AudioDevice *this = (SDL_AudioDevice *) data;
    166    SDL_SemPost(this->hidden->semaphore);
    167}
    168
    169static void STDMETHODCALLTYPE
    170VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error)
    171{
    172    /* !!! FIXME: attempt to recover, or mark device disconnected. */
    173    SDL_assert(0 && "write me!");
    174}
    175
    176/* no-op callbacks... */
    177static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {}
    178static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {}
    179static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {}
    180static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {}
    181static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {}
    182
    183
    184static Uint8 *
    185XAUDIO2_GetDeviceBuf(_THIS)
    186{
    187    return this->hidden->nextbuf;
    188}
    189
    190static void
    191XAUDIO2_PlayDevice(_THIS)
    192{
    193    XAUDIO2_BUFFER buffer;
    194    Uint8 *mixbuf = this->hidden->mixbuf;
    195    Uint8 *nextbuf = this->hidden->nextbuf;
    196    const int mixlen = this->hidden->mixlen;
    197    IXAudio2SourceVoice *source = this->hidden->source;
    198    HRESULT result = S_OK;
    199
    200    if (!this->enabled) { /* shutting down? */
    201        return;
    202    }
    203
    204    /* Submit the next filled buffer */
    205    SDL_zero(buffer);
    206    buffer.AudioBytes = mixlen;
    207    buffer.pAudioData = nextbuf;
    208    buffer.pContext = this;
    209
    210    if (nextbuf == mixbuf) {
    211        nextbuf += mixlen;
    212    } else {
    213        nextbuf = mixbuf;
    214    }
    215    this->hidden->nextbuf = nextbuf;
    216
    217    result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL);
    218    if (result == XAUDIO2_E_DEVICE_INVALIDATED) {
    219        /* !!! FIXME: possibly disconnected or temporary lost. Recover? */
    220    }
    221
    222    if (result != S_OK) {  /* uhoh, panic! */
    223        IXAudio2SourceVoice_FlushSourceBuffers(source);
    224        this->enabled = 0;
    225    }
    226}
    227
    228static void
    229XAUDIO2_WaitDevice(_THIS)
    230{
    231    if (this->enabled) {
    232        SDL_SemWait(this->hidden->semaphore);
    233    }
    234}
    235
    236static void
    237XAUDIO2_WaitDone(_THIS)
    238{
    239    IXAudio2SourceVoice *source = this->hidden->source;
    240    XAUDIO2_VOICE_STATE state;
    241    SDL_assert(!this->enabled);  /* flag that stops playing. */
    242    IXAudio2SourceVoice_Discontinuity(source);
    243#if SDL_XAUDIO2_WIN8
    244    IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
    245#else
    246    IXAudio2SourceVoice_GetState(source, &state);
    247#endif
    248    while (state.BuffersQueued > 0) {
    249        SDL_SemWait(this->hidden->semaphore);
    250#if SDL_XAUDIO2_WIN8
    251        IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
    252#else
    253        IXAudio2SourceVoice_GetState(source, &state);
    254#endif
    255    }
    256}
    257
    258
    259static void
    260XAUDIO2_CloseDevice(_THIS)
    261{
    262    if (this->hidden != NULL) {
    263        IXAudio2 *ixa2 = this->hidden->ixa2;
    264        IXAudio2SourceVoice *source = this->hidden->source;
    265        IXAudio2MasteringVoice *mastering = this->hidden->mastering;
    266
    267        if (source != NULL) {
    268            IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
    269            IXAudio2SourceVoice_FlushSourceBuffers(source);
    270            IXAudio2SourceVoice_DestroyVoice(source);
    271        }
    272        if (ixa2 != NULL) {
    273            IXAudio2_StopEngine(ixa2);
    274        }
    275        if (mastering != NULL) {
    276            IXAudio2MasteringVoice_DestroyVoice(mastering);
    277        }
    278        if (ixa2 != NULL) {
    279            IXAudio2_Release(ixa2);
    280        }
    281        SDL_free(this->hidden->mixbuf);
    282        if (this->hidden->semaphore != NULL) {
    283            SDL_DestroySemaphore(this->hidden->semaphore);
    284        }
    285
    286        SDL_free(this->hidden);
    287        this->hidden = NULL;
    288    }
    289}
    290
    291static int
    292XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture)
    293{
    294    HRESULT result = S_OK;
    295    WAVEFORMATEX waveformat;
    296    int valid_format = 0;
    297    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
    298    IXAudio2 *ixa2 = NULL;
    299    IXAudio2SourceVoice *source = NULL;
    300#if defined(SDL_XAUDIO2_WIN8)
    301    LPCWSTR devId = NULL;
    302#else
    303    UINT32 devId = 0;  /* 0 == system default device. */
    304#endif
    305
    306    static IXAudio2VoiceCallbackVtbl callbacks_vtable = {
    307        VoiceCBOnVoiceProcessPassStart,
    308        VoiceCBOnVoiceProcessPassEnd,
    309        VoiceCBOnStreamEnd,
    310        VoiceCBOnBufferStart,
    311        VoiceCBOnBufferEnd,
    312        VoiceCBOnLoopEnd,
    313        VoiceCBOnVoiceError
    314    };
    315
    316    static IXAudio2VoiceCallback callbacks = { &callbacks_vtable };
    317
    318    if (iscapture) {
    319        return SDL_SetError("XAudio2: capture devices unsupported.");
    320    } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
    321        return SDL_SetError("XAudio2: XAudio2Create() failed at open.");
    322    }
    323
    324    /*
    325    XAUDIO2_DEBUG_CONFIGURATION debugConfig;
    326    debugConfig.TraceMask = XAUDIO2_LOG_ERRORS; //XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL | XAUDIO2_LOG_FUNC_CALLS | XAUDIO2_LOG_TIMING | XAUDIO2_LOG_LOCKS | XAUDIO2_LOG_MEMORY | XAUDIO2_LOG_STREAMING;
    327    debugConfig.BreakMask = XAUDIO2_LOG_ERRORS; //XAUDIO2_LOG_WARNINGS;
    328    debugConfig.LogThreadID = TRUE;
    329    debugConfig.LogFileline = TRUE;
    330    debugConfig.LogFunctionName = TRUE;
    331    debugConfig.LogTiming = TRUE;
    332    ixa2->SetDebugConfiguration(&debugConfig);
    333    */
    334
    335#if ! defined(__WINRT__)
    336    if (devname != NULL) {
    337        UINT32 devcount = 0;
    338        UINT32 i = 0;
    339
    340        if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
    341            IXAudio2_Release(ixa2);
    342            return SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed.");
    343        }
    344        for (i = 0; i < devcount; i++) {
    345            XAUDIO2_DEVICE_DETAILS details;
    346            if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
    347                char *str = WIN_StringToUTF8(details.DisplayName);
    348                if (str != NULL) {
    349                    const int match = (SDL_strcmp(str, devname) == 0);
    350                    SDL_free(str);
    351                    if (match) {
    352                        devId = i;
    353                        break;
    354                    }
    355                }
    356            }
    357        }
    358
    359        if (i == devcount) {
    360            IXAudio2_Release(ixa2);
    361            return SDL_SetError("XAudio2: Requested device not found.");
    362        }
    363    }
    364#endif
    365
    366    /* Initialize all variables that we clean on shutdown */
    367    this->hidden = (struct SDL_PrivateAudioData *)
    368        SDL_malloc((sizeof *this->hidden));
    369    if (this->hidden == NULL) {
    370        IXAudio2_Release(ixa2);
    371        return SDL_OutOfMemory();
    372    }
    373    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    374
    375    this->hidden->ixa2 = ixa2;
    376    this->hidden->semaphore = SDL_CreateSemaphore(1);
    377    if (this->hidden->semaphore == NULL) {
    378        XAUDIO2_CloseDevice(this);
    379        return SDL_SetError("XAudio2: CreateSemaphore() failed!");
    380    }
    381
    382    while ((!valid_format) && (test_format)) {
    383        switch (test_format) {
    384        case AUDIO_U8:
    385        case AUDIO_S16:
    386        case AUDIO_S32:
    387        case AUDIO_F32:
    388            this->spec.format = test_format;
    389            valid_format = 1;
    390            break;
    391        }
    392        test_format = SDL_NextAudioFormat();
    393    }
    394
    395    if (!valid_format) {
    396        XAUDIO2_CloseDevice(this);
    397        return SDL_SetError("XAudio2: Unsupported audio format");
    398    }
    399
    400    /* Update the fragment size as size in bytes */
    401    SDL_CalculateAudioSpec(&this->spec);
    402
    403    /* We feed a Source, it feeds the Mastering, which feeds the device. */
    404    this->hidden->mixlen = this->spec.size;
    405    this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen);
    406    if (this->hidden->mixbuf == NULL) {
    407        XAUDIO2_CloseDevice(this);
    408        return SDL_OutOfMemory();
    409    }
    410    this->hidden->nextbuf = this->hidden->mixbuf;
    411    SDL_memset(this->hidden->mixbuf, 0, 2 * this->hidden->mixlen);
    412
    413    /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On
    414       Xbox360, this means 5.1 output, but on Windows, it means "figure out
    415       what the system has." It might be preferable to let XAudio2 blast
    416       stereo output to appropriate surround sound configurations
    417       instead of clamping to 2 channels, even though we'll configure the
    418       Source Voice for whatever number of channels you supply. */
    419#if SDL_XAUDIO2_WIN8
    420    result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
    421                                           XAUDIO2_DEFAULT_CHANNELS,
    422                                           this->spec.freq, 0, devId, NULL, AudioCategory_GameEffects);
    423#else
    424    result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
    425                                           XAUDIO2_DEFAULT_CHANNELS,
    426                                           this->spec.freq, 0, devId, NULL);
    427#endif
    428    if (result != S_OK) {
    429        XAUDIO2_CloseDevice(this);
    430        return SDL_SetError("XAudio2: Couldn't create mastering voice");
    431    }
    432
    433    SDL_zero(waveformat);
    434    if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
    435        waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    436    } else {
    437        waveformat.wFormatTag = WAVE_FORMAT_PCM;
    438    }
    439    waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
    440    waveformat.nChannels = this->spec.channels;
    441    waveformat.nSamplesPerSec = this->spec.freq;
    442    waveformat.nBlockAlign =
    443        waveformat.nChannels * (waveformat.wBitsPerSample / 8);
    444    waveformat.nAvgBytesPerSec =
    445        waveformat.nSamplesPerSec * waveformat.nBlockAlign;
    446    waveformat.cbSize = sizeof(waveformat);
    447
    448#ifdef __WINRT__
    449    // DLudwig: for now, make XAudio2 do sample rate conversion, just to
    450    // get the loopwave test to work.
    451    //
    452    // TODO, WinRT: consider removing WinRT-specific source-voice creation code from SDL_xaudio2.c
    453    result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
    454                                        0,
    455                                        1.0f, &callbacks, NULL, NULL);
    456#else
    457    result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
    458                                        XAUDIO2_VOICE_NOSRC |
    459                                        XAUDIO2_VOICE_NOPITCH,
    460                                        1.0f, &callbacks, NULL, NULL);
    461
    462#endif
    463    if (result != S_OK) {
    464        XAUDIO2_CloseDevice(this);
    465        return SDL_SetError("XAudio2: Couldn't create source voice");
    466    }
    467    this->hidden->source = source;
    468
    469    /* Start everything playing! */
    470    result = IXAudio2_StartEngine(ixa2);
    471    if (result != S_OK) {
    472        XAUDIO2_CloseDevice(this);
    473        return SDL_SetError("XAudio2: Couldn't start engine");
    474    }
    475
    476    result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW);
    477    if (result != S_OK) {
    478        XAUDIO2_CloseDevice(this);
    479        return SDL_SetError("XAudio2: Couldn't start source voice");
    480    }
    481
    482    return 0; /* good to go. */
    483}
    484
    485static void
    486XAUDIO2_Deinitialize(void)
    487{
    488#if defined(__WIN32__)
    489    WIN_CoUninitialize();
    490#endif
    491}
    492
    493#endif  /* SDL_XAUDIO2_HAS_SDK */
    494
    495
    496static int
    497XAUDIO2_Init(SDL_AudioDriverImpl * impl)
    498{
    499#ifndef SDL_XAUDIO2_HAS_SDK
    500    SDL_SetError("XAudio2: SDL was built without XAudio2 support (old DirectX SDK).");
    501    return 0;  /* no XAudio2 support, ever. Update your SDK! */
    502#else
    503    /* XAudio2Create() is a macro that uses COM; we don't load the .dll */
    504    IXAudio2 *ixa2 = NULL;
    505#if defined(__WIN32__)
    506    // TODO, WinRT: Investigate using CoInitializeEx here
    507    if (FAILED(WIN_CoInitialize())) {
    508        SDL_SetError("XAudio2: CoInitialize() failed");
    509        return 0;
    510    }
    511#endif
    512
    513    if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
    514#if defined(__WIN32__)
    515        WIN_CoUninitialize();
    516#endif
    517        SDL_SetError("XAudio2: XAudio2Create() failed at initialization");
    518        return 0;  /* not available. */
    519    }
    520    IXAudio2_Release(ixa2);
    521
    522    /* Set the function pointers */
    523    impl->DetectDevices = XAUDIO2_DetectDevices;
    524    impl->OpenDevice = XAUDIO2_OpenDevice;
    525    impl->PlayDevice = XAUDIO2_PlayDevice;
    526    impl->WaitDevice = XAUDIO2_WaitDevice;
    527    impl->WaitDone = XAUDIO2_WaitDone;
    528    impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
    529    impl->CloseDevice = XAUDIO2_CloseDevice;
    530    impl->Deinitialize = XAUDIO2_Deinitialize;
    531
    532    return 1;   /* this audio target is available. */
    533#endif
    534}
    535
    536AudioBootStrap XAUDIO2_bootstrap = {
    537    "xaudio2", "XAudio2", XAUDIO2_Init, 0
    538};
    539
    540#endif  /* SDL_AUDIO_DRIVER_XAUDIO2 */
    541
    542/* vi: set ts=4 sw=4 expandtab: */