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