1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /* Allow access to a raw mixing buffer */
25
26 #define WIN32_LEAN_AND_MEAN
27 #include <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_dibaudio.h"
34 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
35 #include "win_ce_semaphore.h"
36 #endif
37
38
39 /* Audio driver functions */
40 static int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec);
41 static void DIB_ThreadInit(_THIS);
42 static void DIB_WaitAudio(_THIS);
43 static Uint8 *DIB_GetAudioBuf(_THIS);
44 static void DIB_PlayAudio(_THIS);
45 static void DIB_WaitDone(_THIS);
46 static void DIB_CloseAudio(_THIS);
47
48 int volatile dibaudio_thread_debug = 0;
49 int volatile dibaudio_cb_debug = 0;
50 int volatile dibaudio_main_debug = 0;
51
52 /* Audio driver bootstrap functions */
53
Audio_Available(void)54 static int Audio_Available(void)
55 {
56 return(1);
57 }
58
Audio_DeleteDevice(SDL_AudioDevice * device)59 static void Audio_DeleteDevice(SDL_AudioDevice *device)
60 {
61 SDL_free(device->hidden);
62 SDL_free(device);
63 }
64
Audio_CreateDevice(int devindex)65 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
66 {
67 SDL_AudioDevice *this;
68
69 /* Initialize all variables that we clean on shutdown */
70 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
71 if ( this ) {
72 SDL_memset(this, 0, (sizeof *this));
73 this->hidden = (struct SDL_PrivateAudioData *)
74 SDL_malloc((sizeof *this->hidden));
75 }
76 if ( (this == NULL) || (this->hidden == NULL) ) {
77 SDL_OutOfMemory();
78 if ( this ) {
79 SDL_free(this);
80 }
81 return(0);
82 }
83 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
84
85 /* Set the function pointers */
86 this->OpenAudio = DIB_OpenAudio;
87 this->ThreadInit = DIB_ThreadInit;
88 this->WaitAudio = DIB_WaitAudio;
89 this->PlayAudio = DIB_PlayAudio;
90 this->GetAudioBuf = DIB_GetAudioBuf;
91 this->WaitDone = DIB_WaitDone;
92 this->CloseAudio = DIB_CloseAudio;
93
94 this->free = Audio_DeleteDevice;
95
96 return this;
97 }
98
99 AudioBootStrap WAVEOUT_bootstrap = {
100 "waveout", "Win95/98/NT/2000 WaveOut",
101 Audio_Available, Audio_CreateDevice
102 };
103
104
105 /* The Win32 callback for filling the WAVE device */
FillSound(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD dwParam1,DWORD dwParam2)106 static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
107 DWORD dwParam1, DWORD dwParam2)
108 {
109 SDL_AudioDevice *this = (SDL_AudioDevice *)dwInstance;
110
111 /* Only service "buffer done playing" messages */
112 if ( uMsg != WOM_DONE )
113 return;
114
115 /* Signal that we are done playing a buffer */
116 dibaudio_cb_debug = 1;
117 EnterCriticalSection( &audio_cs );
118 dibaudio_cb_debug = 2;
119 SetEvent( audio_event );
120 dibaudio_cb_debug = 3;
121 LeaveCriticalSection( &audio_cs );
122 dibaudio_cb_debug = 4;
123 }
124
SetMMerror(char * function,MMRESULT code)125 static void SetMMerror(char *function, MMRESULT code)
126 {
127 size_t len;
128 char errbuf[MAXERRORLENGTH];
129 #ifdef _WIN32_WCE
130 wchar_t werrbuf[MAXERRORLENGTH];
131 #endif
132
133 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
134 len = SDL_strlen(errbuf);
135
136 #ifdef _WIN32_WCE
137 /* UNICODE version */
138 waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH-len);
139 WideCharToMultiByte(CP_ACP,0,werrbuf,-1,errbuf+len,MAXERRORLENGTH-len,NULL,NULL);
140 #else
141 waveOutGetErrorText(code, errbuf+len, (UINT)(MAXERRORLENGTH-len));
142 #endif
143
144 SDL_SetError("%s",errbuf);
145 }
146
147 /* Set high priority for the audio thread */
DIB_ThreadInit(_THIS)148 static void DIB_ThreadInit(_THIS)
149 {
150 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
151 }
152
DIB_WaitAudio(_THIS)153 void DIB_WaitAudio(_THIS)
154 {
155 /* Wait for an audio chunk to finish */
156 dibaudio_thread_debug = 1;
157 WaitForSingleObject(audio_event, INFINITE);
158 dibaudio_thread_debug = 2;
159 ResetEvent( audio_event );
160 dibaudio_thread_debug = 3;
161 }
162
DIB_GetAudioBuf(_THIS)163 Uint8 *DIB_GetAudioBuf(_THIS)
164 {
165 Uint8 *retval;
166
167 dibaudio_thread_debug = 4;
168 EnterCriticalSection( &audio_cs );
169 dibaudio_thread_debug = 5;
170 retval = (Uint8 *)(wavebuf[next_buffer].lpData);
171 return retval;
172 }
173
DIB_PlayAudio(_THIS)174 void DIB_PlayAudio(_THIS)
175 {
176 /* Queue it up */
177 dibaudio_thread_debug = 6;
178 waveOutWrite(sound, &wavebuf[next_buffer], sizeof(wavebuf[0]));
179 dibaudio_thread_debug = 7;
180 next_buffer = (next_buffer+1)%NUM_BUFFERS;
181 LeaveCriticalSection( &audio_cs );
182 dibaudio_thread_debug = 8;
183 }
184
DIB_WaitDone(_THIS)185 void DIB_WaitDone(_THIS)
186 {
187 int i, left, tries = 5;
188
189 dibaudio_thread_debug = 9;
190
191 /* give some time for the wave output to send the last buffer,
192 * but don't hang if one was cancelled
193 */
194 do {
195 left = NUM_BUFFERS;
196 for ( i=0; i<NUM_BUFFERS; ++i ) {
197 if ( wavebuf[i].dwFlags & WHDR_DONE ) {
198 --left;
199 }
200 }
201 if ( left == 0 )
202 break;
203
204 SDL_Delay(100);
205 } while ( --tries > 0 );
206
207 dibaudio_thread_debug = 10;
208 }
209
DIB_CloseAudio(_THIS)210 void DIB_CloseAudio(_THIS)
211 {
212 int i;
213
214 /* Close up audio */
215 if ( audio_event != INVALID_HANDLE_VALUE ) {
216 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
217 CloseSynchHandle(audio_event);
218 #else
219 CloseHandle(audio_event);
220 #endif
221 }
222 if ( sound ) {
223 waveOutReset(sound);
224 }
225
226 /* Clean up mixing buffers */
227 for ( i=0; i<NUM_BUFFERS; ++i ) {
228 if ( wavebuf[i].dwUser != 0xFFFF ) {
229 waveOutUnprepareHeader(sound, &wavebuf[i],
230 sizeof(wavebuf[i]));
231 wavebuf[i].dwUser = 0xFFFF;
232 }
233 }
234 /* Free raw mixing buffer */
235 if ( mixbuf != NULL ) {
236 SDL_free(mixbuf);
237 mixbuf = NULL;
238 }
239
240 if ( sound )
241 waveOutClose(sound);
242 }
243
DIB_OpenAudio(_THIS,SDL_AudioSpec * spec)244 int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec)
245 {
246 MMRESULT result;
247 int i;
248 WAVEFORMATEX waveformat;
249
250 /* Initialize the wavebuf structures for closing */
251 sound = NULL;
252 InitializeCriticalSection( &audio_cs );
253 audio_event = INVALID_HANDLE_VALUE;
254 for ( i = 0; i < NUM_BUFFERS; ++i )
255 wavebuf[i].dwUser = 0xFFFF;
256 mixbuf = NULL;
257
258 /* Set basic WAVE format parameters */
259 SDL_memset(&waveformat, 0, sizeof(waveformat));
260 waveformat.wFormatTag = WAVE_FORMAT_PCM;
261
262 /* Determine the audio parameters from the AudioSpec */
263 switch ( spec->format & 0xFF ) {
264 case 8:
265 /* Unsigned 8 bit audio data */
266 spec->format = AUDIO_U8;
267 waveformat.wBitsPerSample = 8;
268 break;
269 case 16:
270 /* Signed 16 bit audio data */
271 spec->format = AUDIO_S16;
272 waveformat.wBitsPerSample = 16;
273 break;
274 default:
275 SDL_SetError("Unsupported audio format");
276 return(-1);
277 }
278 waveformat.nChannels = spec->channels;
279 waveformat.nSamplesPerSec = spec->freq;
280 waveformat.nBlockAlign =
281 waveformat.nChannels * (waveformat.wBitsPerSample/8);
282 waveformat.nAvgBytesPerSec =
283 waveformat.nSamplesPerSec * waveformat.nBlockAlign;
284
285 /* Check the buffer size -- minimum of 1/4 second (word aligned) */
286 if ( spec->samples < (spec->freq/4) )
287 spec->samples = ((spec->freq/4)+3)&~3;
288
289 /* Update the fragment size as size in bytes */
290 SDL_CalculateAudioSpec(spec);
291
292 /* Open the audio device */
293 result = waveOutOpen(&sound, WAVE_MAPPER, &waveformat,
294 (DWORD_PTR)FillSound, (DWORD_PTR)this, CALLBACK_FUNCTION);
295 if ( result != MMSYSERR_NOERROR ) {
296 SetMMerror("waveOutOpen()", result);
297 return(-1);
298 }
299
300 #ifdef SOUND_DEBUG
301 /* Check the sound device we retrieved */
302 {
303 WAVEOUTCAPS caps;
304
305 result = waveOutGetDevCaps((UINT)sound, &caps, sizeof(caps));
306 if ( result != MMSYSERR_NOERROR ) {
307 SetMMerror("waveOutGetDevCaps()", result);
308 return(-1);
309 }
310 printf("Audio device: %s\n", caps.szPname);
311 }
312 #endif
313
314 /* Create the audio buffer semaphore */
315 audio_event = CreateEvent( NULL, TRUE, FALSE, NULL );
316 if ( audio_event == NULL ) {
317 SDL_SetError("Couldn't create semaphore");
318 return(-1);
319 }
320
321 /* Create the sound buffers */
322 mixbuf = (Uint8 *)SDL_malloc(NUM_BUFFERS*spec->size);
323 if ( mixbuf == NULL ) {
324 SDL_SetError("Out of memory");
325 return(-1);
326 }
327 for ( i = 0; i < NUM_BUFFERS; ++i ) {
328 SDL_memset(&wavebuf[i], 0, sizeof(wavebuf[i]));
329 wavebuf[i].lpData = (LPSTR) &mixbuf[i*spec->size];
330 wavebuf[i].dwBufferLength = spec->size;
331 wavebuf[i].dwFlags = WHDR_DONE;
332 result = waveOutPrepareHeader(sound, &wavebuf[i],
333 sizeof(wavebuf[i]));
334 if ( result != MMSYSERR_NOERROR ) {
335 SetMMerror("waveOutPrepareHeader()", result);
336 return(-1);
337 }
338 }
339
340 /* Ready to go! */
341 next_buffer = 0;
342 return(0);
343 }
344