sdl

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

SDL_winmm.c (14243B)


      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_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 /* MinGW32 mmsystem.h doesn't include these structures */
     36 #if defined(__MINGW32__) && defined(_MMSYSTEM_H)
     37 
     38 typedef struct tagWAVEINCAPS2W 
     39 {
     40     WORD wMid;
     41     WORD wPid;
     42     MMVERSION vDriverVersion;
     43     WCHAR szPname[MAXPNAMELEN];
     44     DWORD dwFormats;
     45     WORD wChannels;
     46     WORD wReserved1;
     47     GUID ManufacturerGuid;
     48     GUID ProductGuid;
     49     GUID NameGuid;
     50 } WAVEINCAPS2W,*PWAVEINCAPS2W,*NPWAVEINCAPS2W,*LPWAVEINCAPS2W;
     51 
     52 typedef struct tagWAVEOUTCAPS2W
     53 {
     54     WORD wMid;
     55     WORD wPid;
     56     MMVERSION vDriverVersion;
     57     WCHAR szPname[MAXPNAMELEN];
     58     DWORD dwFormats;
     59     WORD wChannels;
     60     WORD wReserved1;
     61     DWORD dwSupport;
     62     GUID ManufacturerGuid;
     63     GUID ProductGuid;
     64     GUID NameGuid;
     65 } WAVEOUTCAPS2W,*PWAVEOUTCAPS2W,*NPWAVEOUTCAPS2W,*LPWAVEOUTCAPS2W;
     66 
     67 #endif /* defined(__MINGW32__) && defined(_MMSYSTEM_H) */
     68 
     69 #ifndef WAVE_FORMAT_IEEE_FLOAT
     70 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
     71 #endif
     72 
     73 #define DETECT_DEV_IMPL(iscap, typ, capstyp) \
     74 static void DetectWave##typ##Devs(void) { \
     75     const UINT iscapture = iscap ? 1 : 0; \
     76     const UINT devcount = wave##typ##GetNumDevs(); \
     77     capstyp##2W caps; \
     78     UINT i; \
     79     for (i = 0; i < devcount; i++) { \
     80         if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
     81             char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \
     82             if (name != NULL) { \
     83                 SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \
     84                 SDL_free(name); \
     85             } \
     86         } \
     87     } \
     88 }
     89 
     90 DETECT_DEV_IMPL(SDL_FALSE, Out, WAVEOUTCAPS)
     91 DETECT_DEV_IMPL(SDL_TRUE, In, WAVEINCAPS)
     92 
     93 static void
     94 WINMM_DetectDevices(void)
     95 {
     96     DetectWaveInDevs();
     97     DetectWaveOutDevs();
     98 }
     99 
    100 static void CALLBACK
    101 CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
    102           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    103 {
    104     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    105 
    106     /* Only service "buffer is filled" messages */
    107     if (uMsg != WIM_DATA)
    108         return;
    109 
    110     /* Signal that we have a new buffer of data */
    111     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    112 }
    113 
    114 
    115 /* The Win32 callback for filling the WAVE device */
    116 static void CALLBACK
    117 FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
    118           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    119 {
    120     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    121 
    122     /* Only service "buffer done playing" messages */
    123     if (uMsg != WOM_DONE)
    124         return;
    125 
    126     /* Signal that we are done playing a buffer */
    127     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    128 }
    129 
    130 static int
    131 SetMMerror(char *function, MMRESULT code)
    132 {
    133     int len;
    134     char errbuf[MAXERRORLENGTH];
    135     wchar_t werrbuf[MAXERRORLENGTH];
    136 
    137     SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
    138     len = SDL_static_cast(int, SDL_strlen(errbuf));
    139 
    140     waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
    141     WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
    142                         MAXERRORLENGTH - len, NULL, NULL);
    143 
    144     return SDL_SetError("%s", errbuf);
    145 }
    146 
    147 static void
    148 WINMM_WaitDevice(_THIS)
    149 {
    150     /* Wait for an audio chunk to finish */
    151     WaitForSingleObject(this->hidden->audio_sem, INFINITE);
    152 }
    153 
    154 static Uint8 *
    155 WINMM_GetDeviceBuf(_THIS)
    156 {
    157     return (Uint8 *) (this->hidden->
    158                       wavebuf[this->hidden->next_buffer].lpData);
    159 }
    160 
    161 static void
    162 WINMM_PlayDevice(_THIS)
    163 {
    164     /* Queue it up */
    165     waveOutWrite(this->hidden->hout,
    166                  &this->hidden->wavebuf[this->hidden->next_buffer],
    167                  sizeof(this->hidden->wavebuf[0]));
    168     this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
    169 }
    170 
    171 static int
    172 WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
    173 {
    174     const int nextbuf = this->hidden->next_buffer;
    175     MMRESULT result;
    176 
    177     SDL_assert(buflen == this->spec.size);
    178 
    179     /* Wait for an audio chunk to finish */
    180     WaitForSingleObject(this->hidden->audio_sem, INFINITE);
    181 
    182     /* Copy it to caller's buffer... */
    183     SDL_memcpy(buffer, this->hidden->wavebuf[nextbuf].lpData, this->spec.size);
    184 
    185     /* requeue the buffer that just finished. */
    186     result = waveInAddBuffer(this->hidden->hin,
    187                              &this->hidden->wavebuf[nextbuf],
    188                              sizeof (this->hidden->wavebuf[nextbuf]));
    189     if (result != MMSYSERR_NOERROR) {
    190         return -1;  /* uhoh! Disable the device. */
    191     }
    192 
    193     /* queue the next buffer in sequence, next time. */
    194     this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
    195     return this->spec.size;
    196 }
    197 
    198 static void
    199 WINMM_FlushCapture(_THIS)
    200 {
    201     /* Wait for an audio chunk to finish */
    202     if (WaitForSingleObject(this->hidden->audio_sem, 0) == WAIT_OBJECT_0) {
    203         const int nextbuf = this->hidden->next_buffer;
    204         /* requeue the buffer that just finished without reading from it. */
    205         waveInAddBuffer(this->hidden->hin,
    206                         &this->hidden->wavebuf[nextbuf],
    207                         sizeof (this->hidden->wavebuf[nextbuf]));
    208         this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
    209     }
    210 }
    211 
    212 static void
    213 WINMM_CloseDevice(_THIS)
    214 {
    215     int i;
    216 
    217     if (this->hidden->hout) {
    218         waveOutReset(this->hidden->hout);
    219 
    220         /* Clean up mixing buffers */
    221         for (i = 0; i < NUM_BUFFERS; ++i) {
    222             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
    223                 waveOutUnprepareHeader(this->hidden->hout,
    224                                        &this->hidden->wavebuf[i],
    225                                        sizeof (this->hidden->wavebuf[i]));
    226             }
    227         }
    228 
    229         waveOutClose(this->hidden->hout);
    230     }
    231 
    232     if (this->hidden->hin) {
    233         waveInReset(this->hidden->hin);
    234 
    235         /* Clean up mixing buffers */
    236         for (i = 0; i < NUM_BUFFERS; ++i) {
    237             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
    238                 waveInUnprepareHeader(this->hidden->hin,
    239                                        &this->hidden->wavebuf[i],
    240                                        sizeof (this->hidden->wavebuf[i]));
    241             }
    242         }
    243         waveInClose(this->hidden->hin);
    244     }
    245 
    246     if (this->hidden->audio_sem) {
    247         CloseHandle(this->hidden->audio_sem);
    248     }
    249 
    250     SDL_free(this->hidden->mixbuf);
    251     SDL_free(this->hidden);
    252 }
    253 
    254 static SDL_bool
    255 PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture)
    256 {
    257     SDL_zerop(pfmt);
    258 
    259     if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
    260         pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    261     } else {
    262         pfmt->wFormatTag = WAVE_FORMAT_PCM;
    263     }
    264     pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
    265 
    266     pfmt->nChannels = this->spec.channels;
    267     pfmt->nSamplesPerSec = this->spec.freq;
    268     pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
    269     pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
    270 
    271     if (iscapture) {
    272         return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
    273     } else {
    274         return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
    275     }
    276 }
    277 
    278 static int
    279 WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
    280 {
    281     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
    282     int valid_datatype = 0;
    283     MMRESULT result;
    284     WAVEFORMATEX waveformat;
    285     UINT devId = WAVE_MAPPER;  /* WAVE_MAPPER == choose system's default */
    286     UINT i;
    287 
    288     if (handle != NULL) {  /* specific device requested? */
    289         /* -1 because we increment the original value to avoid NULL. */
    290         const size_t val = ((size_t) handle) - 1;
    291         devId = (UINT) val;
    292     }
    293 
    294     /* Initialize all variables that we clean on shutdown */
    295     this->hidden = (struct SDL_PrivateAudioData *)
    296         SDL_malloc((sizeof *this->hidden));
    297     if (this->hidden == NULL) {
    298         return SDL_OutOfMemory();
    299     }
    300     SDL_zerop(this->hidden);
    301 
    302     /* Initialize the wavebuf structures for closing */
    303     for (i = 0; i < NUM_BUFFERS; ++i)
    304         this->hidden->wavebuf[i].dwUser = 0xFFFF;
    305 
    306     if (this->spec.channels > 2)
    307         this->spec.channels = 2;        /* !!! FIXME: is this right? */
    308 
    309     while ((!valid_datatype) && (test_format)) {
    310         switch (test_format) {
    311         case AUDIO_U8:
    312         case AUDIO_S16:
    313         case AUDIO_S32:
    314         case AUDIO_F32:
    315             this->spec.format = test_format;
    316             if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
    317                 valid_datatype = 1;
    318             } else {
    319                 test_format = SDL_NextAudioFormat();
    320             }
    321             break;
    322 
    323         default:
    324             test_format = SDL_NextAudioFormat();
    325             break;
    326         }
    327     }
    328 
    329     if (!valid_datatype) {
    330         return SDL_SetError("Unsupported audio format");
    331     }
    332 
    333     /* Update the fragment size as size in bytes */
    334     SDL_CalculateAudioSpec(&this->spec);
    335 
    336     /* Open the audio device */
    337     if (iscapture) {
    338         result = waveInOpen(&this->hidden->hin, devId, &waveformat,
    339                              (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
    340                              CALLBACK_FUNCTION);
    341         if (result != MMSYSERR_NOERROR) {
    342             return SetMMerror("waveInOpen()", result);
    343         }
    344     } else {
    345         result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
    346                              (DWORD_PTR) FillSound, (DWORD_PTR) this,
    347                              CALLBACK_FUNCTION);
    348         if (result != MMSYSERR_NOERROR) {
    349             return SetMMerror("waveOutOpen()", result);
    350         }
    351     }
    352 
    353 #ifdef SOUND_DEBUG
    354     /* Check the sound device we retrieved */
    355     {
    356         if (iscapture) {
    357             WAVEINCAPS caps;
    358             result = waveInGetDevCaps((UINT) this->hidden->hout,
    359                                       &caps, sizeof (caps));
    360             if (result != MMSYSERR_NOERROR) {
    361                 return SetMMerror("waveInGetDevCaps()", result);
    362             }
    363             printf("Audio device: %s\n", caps.szPname);
    364         } else {
    365             WAVEOUTCAPS caps;
    366             result = waveOutGetDevCaps((UINT) this->hidden->hout,
    367                                        &caps, sizeof(caps));
    368             if (result != MMSYSERR_NOERROR) {
    369                 return SetMMerror("waveOutGetDevCaps()", result);
    370             }
    371             printf("Audio device: %s\n", caps.szPname);
    372         }
    373     }
    374 #endif
    375 
    376     /* Create the audio buffer semaphore */
    377     this->hidden->audio_sem = CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
    378     if (this->hidden->audio_sem == NULL) {
    379         return SDL_SetError("Couldn't create semaphore");
    380     }
    381 
    382     /* Create the sound buffers */
    383     this->hidden->mixbuf =
    384         (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
    385     if (this->hidden->mixbuf == NULL) {
    386         return SDL_OutOfMemory();
    387     }
    388 
    389     SDL_zeroa(this->hidden->wavebuf);
    390     for (i = 0; i < NUM_BUFFERS; ++i) {
    391         this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
    392         this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
    393         this->hidden->wavebuf[i].lpData =
    394             (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
    395 
    396         if (iscapture) {
    397             result = waveInPrepareHeader(this->hidden->hin,
    398                                           &this->hidden->wavebuf[i],
    399                                           sizeof(this->hidden->wavebuf[i]));
    400             if (result != MMSYSERR_NOERROR) {
    401                 return SetMMerror("waveInPrepareHeader()", result);
    402             }
    403 
    404             result = waveInAddBuffer(this->hidden->hin,
    405                                      &this->hidden->wavebuf[i],
    406                                      sizeof(this->hidden->wavebuf[i]));
    407             if (result != MMSYSERR_NOERROR) {
    408                 return SetMMerror("waveInAddBuffer()", result);
    409             }
    410         } else {
    411             result = waveOutPrepareHeader(this->hidden->hout,
    412                                           &this->hidden->wavebuf[i],
    413                                           sizeof(this->hidden->wavebuf[i]));
    414             if (result != MMSYSERR_NOERROR) {
    415                 return SetMMerror("waveOutPrepareHeader()", result);
    416             }
    417         }
    418     }
    419 
    420     if (iscapture) {
    421         result = waveInStart(this->hidden->hin);
    422         if (result != MMSYSERR_NOERROR) {
    423             return SetMMerror("waveInStart()", result);
    424         }
    425     }
    426 
    427     return 0;                   /* Ready to go! */
    428 }
    429 
    430 
    431 static int
    432 WINMM_Init(SDL_AudioDriverImpl * impl)
    433 {
    434     /* Set the function pointers */
    435     impl->DetectDevices = WINMM_DetectDevices;
    436     impl->OpenDevice = WINMM_OpenDevice;
    437     impl->PlayDevice = WINMM_PlayDevice;
    438     impl->WaitDevice = WINMM_WaitDevice;
    439     impl->GetDeviceBuf = WINMM_GetDeviceBuf;
    440     impl->CaptureFromDevice = WINMM_CaptureFromDevice;
    441     impl->FlushCapture = WINMM_FlushCapture;
    442     impl->CloseDevice = WINMM_CloseDevice;
    443 
    444     impl->HasCaptureSupport = SDL_TRUE;
    445 
    446     return 1;   /* this audio target is available. */
    447 }
    448 
    449 AudioBootStrap WINMM_bootstrap = {
    450     "winmm", "Windows Waveform Audio", WINMM_Init, 0
    451 };
    452 
    453 #endif /* SDL_AUDIO_DRIVER_WINMM */
    454 
    455 /* vi: set ts=4 sw=4 expandtab: */