SDL 2.0
SDL_winmm.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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/* 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 */
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 = CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
379 if (this->hidden->audio_sem == NULL) {
380 return SDL_SetError("Couldn't create semaphore");
381 }
382
383 /* Create the sound buffers */
384 this->hidden->mixbuf =
385 (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
386 if (this->hidden->mixbuf == NULL) {
387 return SDL_OutOfMemory();
388 }
389
390 SDL_zero(this->hidden->wavebuf);
391 for (i = 0; i < NUM_BUFFERS; ++i) {
392 this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
393 this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
394 this->hidden->wavebuf[i].lpData =
395 (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
396
397 if (iscapture) {
398 result = waveInPrepareHeader(this->hidden->hin,
399 &this->hidden->wavebuf[i],
400 sizeof(this->hidden->wavebuf[i]));
401 if (result != MMSYSERR_NOERROR) {
402 return SetMMerror("waveInPrepareHeader()", result);
403 }
404
405 result = waveInAddBuffer(this->hidden->hin,
406 &this->hidden->wavebuf[i],
407 sizeof(this->hidden->wavebuf[i]));
408 if (result != MMSYSERR_NOERROR) {
409 return SetMMerror("waveInAddBuffer()", result);
410 }
411 } else {
412 result = waveOutPrepareHeader(this->hidden->hout,
413 &this->hidden->wavebuf[i],
414 sizeof(this->hidden->wavebuf[i]));
415 if (result != MMSYSERR_NOERROR) {
416 return SetMMerror("waveOutPrepareHeader()", result);
417 }
418 }
419 }
420
421 if (iscapture) {
422 result = waveInStart(this->hidden->hin);
423 if (result != MMSYSERR_NOERROR) {
424 return SetMMerror("waveInStart()", result);
425 }
426 }
427
428 return 0; /* Ready to go! */
429}
430
431
432static int
433WINMM_Init(SDL_AudioDriverImpl * impl)
434{
435 /* Set the function pointers */
436 impl->DetectDevices = WINMM_DetectDevices;
437 impl->OpenDevice = WINMM_OpenDevice;
438 impl->PlayDevice = WINMM_PlayDevice;
439 impl->WaitDevice = WINMM_WaitDevice;
440 impl->GetDeviceBuf = WINMM_GetDeviceBuf;
441 impl->CaptureFromDevice = WINMM_CaptureFromDevice;
442 impl->FlushCapture = WINMM_FlushCapture;
443 impl->CloseDevice = WINMM_CloseDevice;
444
446
447 return 1; /* this audio target is available. */
448}
449
451 "winmm", "Windows Waveform Audio", WINMM_Init, 0
452};
453
454#endif /* SDL_AUDIO_DRIVER_WINMM */
455
456/* vi: set ts=4 sw=4 expandtab: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:169
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1647
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1659
#define AUDIO_F32
Definition: SDL_audio.h:114
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define AUDIO_S16
Definition: SDL_audio.h:96
#define AUDIO_U8
Definition: SDL_audio.h:89
#define SDL_AUDIO_ISFLOAT(x)
Definition: SDL_audio.h:76
#define AUDIO_S32
Definition: SDL_audio.h:105
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
unsigned int size_t
#define SDL_SetError
#define SDL_malloc
#define SDL_strlen
#define SDL_free
#define SDL_memcpy
#define SDL_snprintf
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
GLuint GLfloat * val
GLuint64EXT * result
GLenum GLsizei len
GLuint buffer
#define NUM_BUFFERS
Definition: SDL_openslES.h:31
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
#define SDL_static_cast(type, expression)
Definition: SDL_stdinc.h:138
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
uint8_t Uint8
Definition: SDL_stdinc.h:179
AudioBootStrap WINMM_bootstrap
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_AudioSpec spec
Definition: loopwave.c:31
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:75
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
Uint32 size
Definition: SDL_audio.h:186
Uint8 channels
Definition: SDL_audio.h:182
SDL_AudioFormat format
Definition: SDL_audio.h:181
WAVEFORMATEX * waveformat
Definition: SDL_wasapi.h:43