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