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: */