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_timer.h"
31 #include "SDL_audio.h"
32 #include "../SDL_audio_c.h"
33 #include "SDL_winmm.h"
34 
35 #ifndef WAVE_FORMAT_IEEE_FLOAT
36 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
37 #endif
38 
39 #define DETECT_DEV_IMPL(iscap, typ, capstyp) \
40 static void DetectWave##typ##Devs(void) { \
41  const UINT iscapture = iscap ? 1 : 0; \
42  const UINT devcount = wave##typ##GetNumDevs(); \
43  capstyp caps; \
44  UINT i; \
45  for (i = 0; i < devcount; i++) { \
46  if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
47  char *name = WIN_StringToUTF8(caps.szPname); \
48  if (name != NULL) { \
49  SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \
50  SDL_free(name); \
51  } \
52  } \
53  } \
54 }
55 
56 DETECT_DEV_IMPL(SDL_FALSE, Out, WAVEOUTCAPS)
57 DETECT_DEV_IMPL(SDL_TRUE, In, WAVEINCAPS)
58 
59 static void
60 WINMM_DetectDevices(void)
61 {
62  DetectWaveInDevs();
63  DetectWaveOutDevs();
64 }
65 
66 static void CALLBACK
67 CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
68  DWORD_PTR dwParam1, DWORD_PTR dwParam2)
69 {
70  SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
71 
72  /* Only service "buffer is filled" messages */
73  if (uMsg != WIM_DATA)
74  return;
75 
76  /* Signal that we have a new buffer of data */
77  ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
78 }
79 
80 
81 /* The Win32 callback for filling the WAVE device */
82 static void CALLBACK
83 FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
84  DWORD_PTR dwParam1, DWORD_PTR dwParam2)
85 {
86  SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
87 
88  /* Only service "buffer done playing" messages */
89  if (uMsg != WOM_DONE)
90  return;
91 
92  /* Signal that we are done playing a buffer */
93  ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
94 }
95 
96 static int
97 SetMMerror(char *function, MMRESULT code)
98 {
99  int len;
100  char errbuf[MAXERRORLENGTH];
101  wchar_t werrbuf[MAXERRORLENGTH];
102 
103  SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
104  len = SDL_static_cast(int, SDL_strlen(errbuf));
105 
106  waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
107  WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
108  MAXERRORLENGTH - len, NULL, NULL);
109 
110  return SDL_SetError("%s", errbuf);
111 }
112 
113 static void
114 WINMM_WaitDevice(_THIS)
115 {
116  /* Wait for an audio chunk to finish */
117  WaitForSingleObject(this->hidden->audio_sem, INFINITE);
118 }
119 
120 static Uint8 *
121 WINMM_GetDeviceBuf(_THIS)
122 {
123  return (Uint8 *) (this->hidden->
124  wavebuf[this->hidden->next_buffer].lpData);
125 }
126 
127 static void
128 WINMM_PlayDevice(_THIS)
129 {
130  /* Queue it up */
131  waveOutWrite(this->hidden->hout,
132  &this->hidden->wavebuf[this->hidden->next_buffer],
133  sizeof(this->hidden->wavebuf[0]));
134  this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
135 }
136 
137 static void
138 WINMM_WaitDone(_THIS)
139 {
140  int i, left;
141 
142  do {
143  left = NUM_BUFFERS;
144  for (i = 0; i < NUM_BUFFERS; ++i) {
145  if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
146  --left;
147  }
148  }
149  if (left > 0) {
150  SDL_Delay(100);
151  }
152  } while (left > 0);
153 }
154 
155 static void
156 WINMM_CloseDevice(_THIS)
157 {
158  /* Close up audio */
159  if (this->hidden != NULL) {
160  int i;
161 
162  if (this->hidden->audio_sem) {
163  CloseHandle(this->hidden->audio_sem);
164  this->hidden->audio_sem = 0;
165  }
166 
167  /* Clean up mixing buffers */
168  for (i = 0; i < NUM_BUFFERS; ++i) {
169  if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
170  waveOutUnprepareHeader(this->hidden->hout,
171  &this->hidden->wavebuf[i],
172  sizeof(this->hidden->wavebuf[i]));
173  this->hidden->wavebuf[i].dwUser = 0xFFFF;
174  }
175  }
176 
177  /* Free raw mixing buffer */
178  SDL_free(this->hidden->mixbuf);
179  this->hidden->mixbuf = NULL;
180 
181  if (this->hidden->hin) {
182  waveInClose(this->hidden->hin);
183  this->hidden->hin = 0;
184  }
185 
186  if (this->hidden->hout) {
187  waveOutClose(this->hidden->hout);
188  this->hidden->hout = 0;
189  }
190 
191  SDL_free(this->hidden);
192  this->hidden = NULL;
193  }
194 }
195 
196 static SDL_bool
197 PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture)
198 {
199  SDL_zerop(pfmt);
200 
201  if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
202  pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
203  } else {
204  pfmt->wFormatTag = WAVE_FORMAT_PCM;
205  }
206  pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
207 
208  pfmt->nChannels = this->spec.channels;
209  pfmt->nSamplesPerSec = this->spec.freq;
210  pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
211  pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
212 
213  if (iscapture) {
214  return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
215  } else {
216  return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
217  }
218 }
219 
220 static int
221 WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
222 {
223  SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
224  int valid_datatype = 0;
225  MMRESULT result;
226  WAVEFORMATEX waveformat;
227  UINT devId = WAVE_MAPPER; /* WAVE_MAPPER == choose system's default */
228  UINT i;
229 
230  if (handle != NULL) { /* specific device requested? */
231  /* -1 because we increment the original value to avoid NULL. */
232  const size_t val = ((size_t) handle) - 1;
233  devId = (UINT) val;
234  }
235 
236  /* Initialize all variables that we clean on shutdown */
237  this->hidden = (struct SDL_PrivateAudioData *)
238  SDL_malloc((sizeof *this->hidden));
239  if (this->hidden == NULL) {
240  return SDL_OutOfMemory();
241  }
242  SDL_memset(this->hidden, 0, (sizeof *this->hidden));
243 
244  /* Initialize the wavebuf structures for closing */
245  for (i = 0; i < NUM_BUFFERS; ++i)
246  this->hidden->wavebuf[i].dwUser = 0xFFFF;
247 
248  if (this->spec.channels > 2)
249  this->spec.channels = 2; /* !!! FIXME: is this right? */
250 
251  while ((!valid_datatype) && (test_format)) {
252  switch (test_format) {
253  case AUDIO_U8:
254  case AUDIO_S16:
255  case AUDIO_S32:
256  case AUDIO_F32:
257  this->spec.format = test_format;
258  if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
259  valid_datatype = 1;
260  } else {
261  test_format = SDL_NextAudioFormat();
262  }
263  break;
264 
265  default:
266  test_format = SDL_NextAudioFormat();
267  break;
268  }
269  }
270 
271  if (!valid_datatype) {
272  WINMM_CloseDevice(this);
273  return SDL_SetError("Unsupported audio format");
274  }
275 
276  /* Update the fragment size as size in bytes */
278 
279  /* Open the audio device */
280  if (iscapture) {
281  result = waveInOpen(&this->hidden->hin, devId, &waveformat,
282  (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
283  CALLBACK_FUNCTION);
284  } else {
285  result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
286  (DWORD_PTR) FillSound, (DWORD_PTR) this,
287  CALLBACK_FUNCTION);
288  }
289 
290  if (result != MMSYSERR_NOERROR) {
291  WINMM_CloseDevice(this);
292  return SetMMerror("waveOutOpen()", result);
293  }
294 #ifdef SOUND_DEBUG
295  /* Check the sound device we retrieved */
296  {
297  WAVEOUTCAPS caps;
298 
299  result = waveOutGetDevCaps((UINT) this->hidden->hout,
300  &caps, sizeof(caps));
301  if (result != MMSYSERR_NOERROR) {
302  WINMM_CloseDevice(this);
303  return SetMMerror("waveOutGetDevCaps()", result);
304  }
305  printf("Audio device: %s\n", caps.szPname);
306  }
307 #endif
308 
309  /* Create the audio buffer semaphore */
310  this->hidden->audio_sem =
311  CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
312  if (this->hidden->audio_sem == NULL) {
313  WINMM_CloseDevice(this);
314  return SDL_SetError("Couldn't create semaphore");
315  }
316 
317  /* Create the sound buffers */
318  this->hidden->mixbuf =
319  (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
320  if (this->hidden->mixbuf == NULL) {
321  WINMM_CloseDevice(this);
322  return SDL_OutOfMemory();
323  }
324  for (i = 0; i < NUM_BUFFERS; ++i) {
325  SDL_memset(&this->hidden->wavebuf[i], 0,
326  sizeof(this->hidden->wavebuf[i]));
327  this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
328  this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
329  this->hidden->wavebuf[i].lpData =
330  (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
331  result = waveOutPrepareHeader(this->hidden->hout,
332  &this->hidden->wavebuf[i],
333  sizeof(this->hidden->wavebuf[i]));
334  if (result != MMSYSERR_NOERROR) {
335  WINMM_CloseDevice(this);
336  return SetMMerror("waveOutPrepareHeader()", result);
337  }
338  }
339 
340  return 0; /* Ready to go! */
341 }
342 
343 
344 static int
345 WINMM_Init(SDL_AudioDriverImpl * impl)
346 {
347  /* Set the function pointers */
348  impl->DetectDevices = WINMM_DetectDevices;
349  impl->OpenDevice = WINMM_OpenDevice;
350  impl->PlayDevice = WINMM_PlayDevice;
351  impl->WaitDevice = WINMM_WaitDevice;
352  impl->WaitDone = WINMM_WaitDone;
353  impl->GetDeviceBuf = WINMM_GetDeviceBuf;
354  impl->CloseDevice = WINMM_CloseDevice;
355 
356  return 1; /* this audio target is available. */
357 }
358 
360  "winmm", "Windows Waveform Audio", WINMM_Init, 0
361 };
362 
363 #endif /* SDL_AUDIO_DRIVER_WINMM */
364 
365 /* vi: set ts=4 sw=4 expandtab: */
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1398
GLuint64EXT * result
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:71
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:75
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:74
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
AudioBootStrap WINMM_bootstrap
#define SDL_zerop(x)
Definition: SDL_stdinc.h:356
GLenum GLsizei len
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1410
SDL_bool
Definition: SDL_stdinc.h:126
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
GLuint GLfloat * val
Uint8 channels
Definition: SDL_audio.h:172
GLint left
#define _THIS
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:139
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:109
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1419
#define SDL_Delay
Uint32 size
Definition: SDL_audio.h:176
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:42
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:72
#define NUM_BUFFERS
Definition: SDL_pspaudio.h:30
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_SetError
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
#define SDL_strlen
SDL_AudioFormat format
Definition: SDL_audio.h:171
#define AUDIO_S16
Definition: SDL_audio.h:96
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:77
#define SDL_snprintf
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
#define SDL_malloc
#define AUDIO_F32
Definition: SDL_audio.h:114
#define SDL_memset
void(* WaitDone)(_THIS)
Definition: SDL_sysaudio.h:78