• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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