Cleaned up audio device detection. Cleared out a lot of cut-and-paste.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "SDL_config.h"
23 /* Allow access to a raw mixing buffer */
25 #include "../../core/windows/SDL_windows.h"
28 #include "SDL_timer.h"
29 #include "SDL_audio.h"
30 #include "../SDL_audio_c.h"
31 #include "SDL_winmm.h"
32 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
33 #include "win_ce_semaphore.h"
36 #define DETECT_DEV_IMPL(typ, capstyp) \
37 static void DetectWave##typ##Devs(SDL_AddAudioDevice addfn) { \
38 const UINT devcount = wave##typ##GetNumDevs(); \
41 for (i = 0; i < devcount; i++) { \
42 if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
43 char *name = WIN_StringToUTF8(caps.szPname); \
52 DETECT_DEV_IMPL(Out, WAVEOUTCAPS)
53 DETECT_DEV_IMPL(In, WAVEINCAPS)
56 WINMM_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
59 DetectWaveInDevs(addfn);
61 DetectWaveOutDevs(addfn);
66 CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
67 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
69 SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
71 /* Only service "buffer is filled" messages */
75 /* Signal that we have a new buffer of data */
76 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
77 ReleaseSemaphoreCE(this->hidden->audio_sem, 1, NULL);
79 ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
84 /* The Win32 callback for filling the WAVE device */
86 FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
87 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
89 SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
91 /* Only service "buffer done playing" messages */
95 /* Signal that we are done playing a buffer */
96 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
97 ReleaseSemaphoreCE(this->hidden->audio_sem, 1, NULL);
99 ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
104 SetMMerror(char *function, MMRESULT code)
107 char errbuf[MAXERRORLENGTH];
108 wchar_t werrbuf[MAXERRORLENGTH];
110 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
111 len = SDL_strlen(errbuf);
113 waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
114 WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
115 MAXERRORLENGTH - len, NULL, NULL);
117 SDL_SetError("%s", errbuf);
121 WINMM_WaitDevice(_THIS)
123 /* Wait for an audio chunk to finish */
124 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
125 WaitForSemaphoreCE(this->hidden->audio_sem, INFINITE);
127 WaitForSingleObject(this->hidden->audio_sem, INFINITE);
132 WINMM_GetDeviceBuf(_THIS)
134 return (Uint8 *) (this->hidden->
135 wavebuf[this->hidden->next_buffer].lpData);
139 WINMM_PlayDevice(_THIS)
142 waveOutWrite(this->hidden->hout,
143 &this->hidden->wavebuf[this->hidden->next_buffer],
144 sizeof(this->hidden->wavebuf[0]));
145 this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
149 WINMM_WaitDone(_THIS)
155 for (i = 0; i < NUM_BUFFERS; ++i) {
156 if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
167 WINMM_CloseDevice(_THIS)
170 if (this->hidden != NULL) {
173 if (this->hidden->audio_sem) {
174 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
175 CloseSynchHandle(this->hidden->audio_sem);
177 CloseHandle(this->hidden->audio_sem);
179 this->hidden->audio_sem = 0;
182 if (this->hidden->hin) {
183 waveInClose(this->hidden->hin);
184 this->hidden->hin = 0;
187 if (this->hidden->hout) {
188 waveOutClose(this->hidden->hout);
189 this->hidden->hout = 0;
192 /* Clean up mixing buffers */
193 for (i = 0; i < NUM_BUFFERS; ++i) {
194 if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
195 waveOutUnprepareHeader(this->hidden->hout,
196 &this->hidden->wavebuf[i],
197 sizeof(this->hidden->wavebuf[i]));
198 this->hidden->wavebuf[i].dwUser = 0xFFFF;
202 if (this->hidden->mixbuf != NULL) {
203 /* Free raw mixing buffer */
204 SDL_free(this->hidden->mixbuf);
205 this->hidden->mixbuf = NULL;
208 SDL_free(this->hidden);
214 WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
216 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
217 int valid_datatype = 0;
219 WAVEFORMATEX waveformat;
220 UINT_PTR devId = WAVE_MAPPER; /* WAVE_MAPPER == choose system's default */
224 if (devname != NULL) { /* specific device requested? */
226 const int devcount = (int) waveInGetNumDevs();
228 for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
229 result = waveInGetDevCaps(i, &caps, sizeof (caps));
230 if (result != MMSYSERR_NOERROR)
232 else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
234 else if (SDL_strcmp(devname, utf8) == 0)
235 devId = (UINT_PTR) i;
239 const int devcount = (int) waveOutGetNumDevs();
241 for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
242 result = waveOutGetDevCaps(i, &caps, sizeof (caps));
243 if (result != MMSYSERR_NOERROR)
245 else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
247 else if (SDL_strcmp(devname, utf8) == 0)
248 devId = (UINT_PTR) i;
253 if (devId == WAVE_MAPPER) {
254 SDL_SetError("Requested device not found");
259 /* Initialize all variables that we clean on shutdown */
260 this->hidden = (struct SDL_PrivateAudioData *)
261 SDL_malloc((sizeof *this->hidden));
262 if (this->hidden == NULL) {
266 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
268 /* Initialize the wavebuf structures for closing */
269 for (i = 0; i < NUM_BUFFERS; ++i)
270 this->hidden->wavebuf[i].dwUser = 0xFFFF;
272 while ((!valid_datatype) && (test_format)) {
274 this->spec.format = test_format;
275 switch (test_format) {
283 test_format = SDL_NextAudioFormat();
288 if (!valid_datatype) {
289 WINMM_CloseDevice(this);
290 SDL_SetError("Unsupported audio format");
294 /* Set basic WAVE format parameters */
295 SDL_memset(&waveformat, '\0', sizeof(waveformat));
296 waveformat.wFormatTag = WAVE_FORMAT_PCM;
297 waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
299 if (this->spec.channels > 2)
300 this->spec.channels = 2; /* !!! FIXME: is this right? */
302 waveformat.nChannels = this->spec.channels;
303 waveformat.nSamplesPerSec = this->spec.freq;
304 waveformat.nBlockAlign =
305 waveformat.nChannels * (waveformat.wBitsPerSample / 8);
306 waveformat.nAvgBytesPerSec =
307 waveformat.nSamplesPerSec * waveformat.nBlockAlign;
309 /* Check the buffer size -- minimum of 1/4 second (word aligned) */
310 if (this->spec.samples < (this->spec.freq / 4))
311 this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
313 /* Update the fragment size as size in bytes */
314 SDL_CalculateAudioSpec(&this->spec);
316 /* Open the audio device */
318 result = waveInOpen(&this->hidden->hin, devId, &waveformat,
319 (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
322 result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
323 (DWORD_PTR) FillSound, (DWORD_PTR) this,
327 if (result != MMSYSERR_NOERROR) {
328 WINMM_CloseDevice(this);
329 SetMMerror("waveOutOpen()", result);
333 /* Check the sound device we retrieved */
337 result = waveOutGetDevCaps((UINT) this->hidden->hout,
338 &caps, sizeof(caps));
339 if (result != MMSYSERR_NOERROR) {
340 WINMM_CloseDevice(this);
341 SetMMerror("waveOutGetDevCaps()", result);
344 printf("Audio device: %s\n", caps.szPname);
348 /* Create the audio buffer semaphore */
349 this->hidden->audio_sem =
350 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
351 CreateSemaphoreCE(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
353 CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
355 if (this->hidden->audio_sem == NULL) {
356 WINMM_CloseDevice(this);
357 SDL_SetError("Couldn't create semaphore");
361 /* Create the sound buffers */
362 this->hidden->mixbuf =
363 (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
364 if (this->hidden->mixbuf == NULL) {
365 WINMM_CloseDevice(this);
369 for (i = 0; i < NUM_BUFFERS; ++i) {
370 SDL_memset(&this->hidden->wavebuf[i], '\0',
371 sizeof(this->hidden->wavebuf[i]));
372 this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
373 this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
374 this->hidden->wavebuf[i].lpData =
375 (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
376 result = waveOutPrepareHeader(this->hidden->hout,
377 &this->hidden->wavebuf[i],
378 sizeof(this->hidden->wavebuf[i]));
379 if (result != MMSYSERR_NOERROR) {
380 WINMM_CloseDevice(this);
381 SetMMerror("waveOutPrepareHeader()", result);
386 return 1; /* Ready to go! */
391 WINMM_Init(SDL_AudioDriverImpl * impl)
393 /* Set the function pointers */
394 impl->DetectDevices = WINMM_DetectDevices;
395 impl->OpenDevice = WINMM_OpenDevice;
396 impl->PlayDevice = WINMM_PlayDevice;
397 impl->WaitDevice = WINMM_WaitDevice;
398 impl->WaitDone = WINMM_WaitDone;
399 impl->GetDeviceBuf = WINMM_GetDeviceBuf;
400 impl->CloseDevice = WINMM_CloseDevice;
402 return 1; /* this audio target is available. */
405 AudioBootStrap WINMM_bootstrap = {
406 "winmm", "Windows Waveform Audio", WINMM_Init, 0
409 /* vi: set ts=4 sw=4 expandtab: */