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