diff options
Diffstat (limited to 'source/3rd-party/SDL2/src/audio/winmm/SDL_winmm.c')
-rw-r--r-- | source/3rd-party/SDL2/src/audio/winmm/SDL_winmm.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/source/3rd-party/SDL2/src/audio/winmm/SDL_winmm.c b/source/3rd-party/SDL2/src/audio/winmm/SDL_winmm.c new file mode 100644 index 0000000..20426f1 --- /dev/null +++ b/source/3rd-party/SDL2/src/audio/winmm/SDL_winmm.c @@ -0,0 +1,456 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_AUDIO_DRIVER_WINMM + +/* Allow access to a raw mixing buffer */ + +#include "../../core/windows/SDL_windows.h" +#include <mmsystem.h> + +#include "SDL_assert.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "SDL_winmm.h" + +/* MinGW32 mmsystem.h doesn't include these structures */ +#if defined(__MINGW32__) && defined(_MMSYSTEM_H) + +typedef struct tagWAVEINCAPS2W +{ + WORD wMid; + WORD wPid; + MMVERSION vDriverVersion; + WCHAR szPname[MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} WAVEINCAPS2W,*PWAVEINCAPS2W,*NPWAVEINCAPS2W,*LPWAVEINCAPS2W; + +typedef struct tagWAVEOUTCAPS2W +{ + WORD wMid; + WORD wPid; + MMVERSION vDriverVersion; + WCHAR szPname[MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} WAVEOUTCAPS2W,*PWAVEOUTCAPS2W,*NPWAVEOUTCAPS2W,*LPWAVEOUTCAPS2W; + +#endif /* defined(__MINGW32__) && defined(_MMSYSTEM_H) */ + +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif + +#define DETECT_DEV_IMPL(iscap, typ, capstyp) \ +static void DetectWave##typ##Devs(void) { \ + const UINT iscapture = iscap ? 1 : 0; \ + const UINT devcount = wave##typ##GetNumDevs(); \ + capstyp##2W caps; \ + UINT i; \ + for (i = 0; i < devcount; i++) { \ + if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \ + char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \ + if (name != NULL) { \ + SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \ + SDL_free(name); \ + } \ + } \ + } \ +} + +DETECT_DEV_IMPL(SDL_FALSE, Out, WAVEOUTCAPS) +DETECT_DEV_IMPL(SDL_TRUE, In, WAVEINCAPS) + +static void +WINMM_DetectDevices(void) +{ + DetectWaveInDevs(); + DetectWaveOutDevs(); +} + +static void CALLBACK +CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance; + + /* Only service "buffer is filled" messages */ + if (uMsg != WIM_DATA) + return; + + /* Signal that we have a new buffer of data */ + ReleaseSemaphore(this->hidden->audio_sem, 1, NULL); +} + + +/* The Win32 callback for filling the WAVE device */ +static void CALLBACK +FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance; + + /* Only service "buffer done playing" messages */ + if (uMsg != WOM_DONE) + return; + + /* Signal that we are done playing a buffer */ + ReleaseSemaphore(this->hidden->audio_sem, 1, NULL); +} + +static int +SetMMerror(char *function, MMRESULT code) +{ + int len; + char errbuf[MAXERRORLENGTH]; + wchar_t werrbuf[MAXERRORLENGTH]; + + SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function); + len = SDL_static_cast(int, SDL_strlen(errbuf)); + + waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len); + WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len, + MAXERRORLENGTH - len, NULL, NULL); + + return SDL_SetError("%s", errbuf); +} + +static void +WINMM_WaitDevice(_THIS) +{ + /* Wait for an audio chunk to finish */ + WaitForSingleObject(this->hidden->audio_sem, INFINITE); +} + +static Uint8 * +WINMM_GetDeviceBuf(_THIS) +{ + return (Uint8 *) (this->hidden-> + wavebuf[this->hidden->next_buffer].lpData); +} + +static void +WINMM_PlayDevice(_THIS) +{ + /* Queue it up */ + waveOutWrite(this->hidden->hout, + &this->hidden->wavebuf[this->hidden->next_buffer], + sizeof(this->hidden->wavebuf[0])); + this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS; +} + +static int +WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen) +{ + const int nextbuf = this->hidden->next_buffer; + MMRESULT result; + + SDL_assert(buflen == this->spec.size); + + /* Wait for an audio chunk to finish */ + WaitForSingleObject(this->hidden->audio_sem, INFINITE); + + /* Copy it to caller's buffer... */ + SDL_memcpy(buffer, this->hidden->wavebuf[nextbuf].lpData, this->spec.size); + + /* requeue the buffer that just finished. */ + result = waveInAddBuffer(this->hidden->hin, + &this->hidden->wavebuf[nextbuf], + sizeof (this->hidden->wavebuf[nextbuf])); + if (result != MMSYSERR_NOERROR) { + return -1; /* uhoh! Disable the device. */ + } + + /* queue the next buffer in sequence, next time. */ + this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS; + return this->spec.size; +} + +static void +WINMM_FlushCapture(_THIS) +{ + /* Wait for an audio chunk to finish */ + if (WaitForSingleObject(this->hidden->audio_sem, 0) == WAIT_OBJECT_0) { + const int nextbuf = this->hidden->next_buffer; + /* requeue the buffer that just finished without reading from it. */ + waveInAddBuffer(this->hidden->hin, + &this->hidden->wavebuf[nextbuf], + sizeof (this->hidden->wavebuf[nextbuf])); + this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS; + } +} + +static void +WINMM_CloseDevice(_THIS) +{ + int i; + + if (this->hidden->hout) { + waveOutReset(this->hidden->hout); + + /* Clean up mixing buffers */ + for (i = 0; i < NUM_BUFFERS; ++i) { + if (this->hidden->wavebuf[i].dwUser != 0xFFFF) { + waveOutUnprepareHeader(this->hidden->hout, + &this->hidden->wavebuf[i], + sizeof (this->hidden->wavebuf[i])); + } + } + + waveOutClose(this->hidden->hout); + } + + if (this->hidden->hin) { + waveInReset(this->hidden->hin); + + /* Clean up mixing buffers */ + for (i = 0; i < NUM_BUFFERS; ++i) { + if (this->hidden->wavebuf[i].dwUser != 0xFFFF) { + waveInUnprepareHeader(this->hidden->hin, + &this->hidden->wavebuf[i], + sizeof (this->hidden->wavebuf[i])); + } + } + waveInClose(this->hidden->hin); + } + + if (this->hidden->audio_sem) { + CloseHandle(this->hidden->audio_sem); + } + + SDL_free(this->hidden->mixbuf); + SDL_free(this->hidden); +} + +static SDL_bool +PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture) +{ + SDL_zerop(pfmt); + + if (SDL_AUDIO_ISFLOAT(this->spec.format)) { + pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + } else { + pfmt->wFormatTag = WAVE_FORMAT_PCM; + } + pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format); + + pfmt->nChannels = this->spec.channels; + pfmt->nSamplesPerSec = this->spec.freq; + pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8); + pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign; + + if (iscapture) { + return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0); + } else { + return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0); + } +} + +static int +WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) +{ + SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); + int valid_datatype = 0; + MMRESULT result; + WAVEFORMATEX waveformat; + UINT devId = WAVE_MAPPER; /* WAVE_MAPPER == choose system's default */ + UINT i; + + if (handle != NULL) { /* specific device requested? */ + /* -1 because we increment the original value to avoid NULL. */ + const size_t val = ((size_t) handle) - 1; + devId = (UINT) val; + } + + /* Initialize all variables that we clean on shutdown */ + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + if (this->hidden == NULL) { + return SDL_OutOfMemory(); + } + SDL_zerop(this->hidden); + + /* Initialize the wavebuf structures for closing */ + for (i = 0; i < NUM_BUFFERS; ++i) + this->hidden->wavebuf[i].dwUser = 0xFFFF; + + if (this->spec.channels > 2) + this->spec.channels = 2; /* !!! FIXME: is this right? */ + + while ((!valid_datatype) && (test_format)) { + switch (test_format) { + case AUDIO_U8: + case AUDIO_S16: + case AUDIO_S32: + case AUDIO_F32: + this->spec.format = test_format; + if (PrepWaveFormat(this, devId, &waveformat, iscapture)) { + valid_datatype = 1; + } else { + test_format = SDL_NextAudioFormat(); + } + break; + + default: + test_format = SDL_NextAudioFormat(); + break; + } + } + + if (!valid_datatype) { + return SDL_SetError("Unsupported audio format"); + } + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(&this->spec); + + /* Open the audio device */ + if (iscapture) { + result = waveInOpen(&this->hidden->hin, devId, &waveformat, + (DWORD_PTR) CaptureSound, (DWORD_PTR) this, + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveInOpen()", result); + } + } else { + result = waveOutOpen(&this->hidden->hout, devId, &waveformat, + (DWORD_PTR) FillSound, (DWORD_PTR) this, + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveOutOpen()", result); + } + } + +#ifdef SOUND_DEBUG + /* Check the sound device we retrieved */ + { + if (iscapture) { + WAVEINCAPS caps; + result = waveInGetDevCaps((UINT) this->hidden->hout, + &caps, sizeof (caps)); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveInGetDevCaps()", result); + } + printf("Audio device: %s\n", caps.szPname); + } else { + WAVEOUTCAPS caps; + result = waveOutGetDevCaps((UINT) this->hidden->hout, + &caps, sizeof(caps)); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveOutGetDevCaps()", result); + } + printf("Audio device: %s\n", caps.szPname); + } + } +#endif + + /* Create the audio buffer semaphore */ + this->hidden->audio_sem = CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL); + if (this->hidden->audio_sem == NULL) { + return SDL_SetError("Couldn't create semaphore"); + } + + /* Create the sound buffers */ + this->hidden->mixbuf = + (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size); + if (this->hidden->mixbuf == NULL) { + return SDL_OutOfMemory(); + } + + SDL_zero(this->hidden->wavebuf); + for (i = 0; i < NUM_BUFFERS; ++i) { + this->hidden->wavebuf[i].dwBufferLength = this->spec.size; + this->hidden->wavebuf[i].dwFlags = WHDR_DONE; + this->hidden->wavebuf[i].lpData = + (LPSTR) & this->hidden->mixbuf[i * this->spec.size]; + + if (iscapture) { + result = waveInPrepareHeader(this->hidden->hin, + &this->hidden->wavebuf[i], + sizeof(this->hidden->wavebuf[i])); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveInPrepareHeader()", result); + } + + result = waveInAddBuffer(this->hidden->hin, + &this->hidden->wavebuf[i], + sizeof(this->hidden->wavebuf[i])); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveInAddBuffer()", result); + } + } else { + result = waveOutPrepareHeader(this->hidden->hout, + &this->hidden->wavebuf[i], + sizeof(this->hidden->wavebuf[i])); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveOutPrepareHeader()", result); + } + } + } + + if (iscapture) { + result = waveInStart(this->hidden->hin); + if (result != MMSYSERR_NOERROR) { + return SetMMerror("waveInStart()", result); + } + } + + return 0; /* Ready to go! */ +} + + +static int +WINMM_Init(SDL_AudioDriverImpl * impl) +{ + /* Set the function pointers */ + impl->DetectDevices = WINMM_DetectDevices; + impl->OpenDevice = WINMM_OpenDevice; + impl->PlayDevice = WINMM_PlayDevice; + impl->WaitDevice = WINMM_WaitDevice; + impl->GetDeviceBuf = WINMM_GetDeviceBuf; + impl->CaptureFromDevice = WINMM_CaptureFromDevice; + impl->FlushCapture = WINMM_FlushCapture; + impl->CloseDevice = WINMM_CloseDevice; + + impl->HasCaptureSupport = SDL_TRUE; + + return 1; /* this audio target is available. */ +} + +AudioBootStrap WINMM_bootstrap = { + "winmm", "Windows Waveform Audio", WINMM_Init, 0 +}; + +#endif /* SDL_AUDIO_DRIVER_WINMM */ + +/* vi: set ts=4 sw=4 expandtab: */ |