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