SDL  2.0
SDL_winmm.c
Go to the documentation of this file.
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 
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
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
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
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
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 *
122 WINMM_GetDeviceBuf(_THIS)
123 {
124  return (Uint8 *) (this->hidden->
125  wavebuf[this->hidden->next_buffer].lpData);
126 }
127 
128 static void
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
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
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
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
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
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 */
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
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 
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: */
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1611
GLuint64EXT * result
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:75
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:79
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:78
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
AudioBootStrap WINMM_bootstrap
#define SDL_zerop(x)
Definition: SDL_stdinc.h:362
GLenum GLsizei len
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1623
SDL_AudioSpec spec
Definition: loopwave.c:35
#define SDL_AUDIO_ISFLOAT(x)
Definition: SDL_audio.h:76
unsigned int size_t
#define AUDIO_U8
Definition: SDL_audio.h:89
#define SDL_memcpy
GLuint GLfloat * val
Uint8 channels
Definition: SDL_audio.h:172
#define _THIS
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
void SDL_free(void *mem)
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
#define AUDIO_S32
Definition: SDL_audio.h:105
#define SDL_static_cast(type, expression)
Definition: SDL_stdinc.h:113
#define SDL_zero(x)
Definition: SDL_stdinc.h:361
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1632
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
Uint32 size
Definition: SDL_audio.h:176
#define SDL_assert(condition)
Definition: SDL_assert.h:167
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:76
#define NUM_BUFFERS
Definition: SDL_pspaudio.h:30
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:82
#define SDL_SetError
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:85
#define SDL_strlen
SDL_AudioFormat format
Definition: SDL_audio.h:171
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:83
GLuint buffer
#define AUDIO_S16
Definition: SDL_audio.h:96
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:81
#define SDL_snprintf
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:90
#define SDL_malloc
#define AUDIO_F32
Definition: SDL_audio.h:114