|
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 /* Audio driver bootstrap functions */ |
|
49 |
|
50 static int Audio_Available(void) |
|
51 { |
|
52 return(1); |
|
53 } |
|
54 |
|
55 static void Audio_DeleteDevice(SDL_AudioDevice *device) |
|
56 { |
|
57 SDL_free(device->hidden); |
|
58 SDL_free(device); |
|
59 } |
|
60 |
|
61 static SDL_AudioDevice *Audio_CreateDevice(int devindex) |
|
62 { |
|
63 SDL_AudioDevice *this; |
|
64 |
|
65 /* Initialize all variables that we clean on shutdown */ |
|
66 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); |
|
67 if ( this ) { |
|
68 SDL_memset(this, 0, (sizeof *this)); |
|
69 this->hidden = (struct SDL_PrivateAudioData *) |
|
70 SDL_malloc((sizeof *this->hidden)); |
|
71 } |
|
72 if ( (this == NULL) || (this->hidden == NULL) ) { |
|
73 SDL_OutOfMemory(); |
|
74 if ( this ) { |
|
75 SDL_free(this); |
|
76 } |
|
77 return(0); |
|
78 } |
|
79 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); |
|
80 |
|
81 /* Set the function pointers */ |
|
82 this->OpenAudio = DIB_OpenAudio; |
|
83 this->ThreadInit = DIB_ThreadInit; |
|
84 this->WaitAudio = DIB_WaitAudio; |
|
85 this->PlayAudio = DIB_PlayAudio; |
|
86 this->GetAudioBuf = DIB_GetAudioBuf; |
|
87 this->WaitDone = DIB_WaitDone; |
|
88 this->CloseAudio = DIB_CloseAudio; |
|
89 |
|
90 this->free = Audio_DeleteDevice; |
|
91 |
|
92 return this; |
|
93 } |
|
94 |
|
95 AudioBootStrap WAVEOUT_bootstrap = { |
|
96 "waveout", "Win95/98/NT/2000 WaveOut", |
|
97 Audio_Available, Audio_CreateDevice |
|
98 }; |
|
99 |
|
100 |
|
101 /* The Win32 callback for filling the WAVE device */ |
|
102 static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, |
|
103 DWORD dwParam1, DWORD dwParam2) |
|
104 { |
|
105 SDL_AudioDevice *this = (SDL_AudioDevice *)dwInstance; |
|
106 |
|
107 /* Only service "buffer done playing" messages */ |
|
108 if ( uMsg != WOM_DONE ) |
|
109 return; |
|
110 |
|
111 /* Signal that we are done playing a buffer */ |
|
112 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300) |
|
113 ReleaseSemaphoreCE(audio_sem, 1, NULL); |
|
114 #else |
|
115 ReleaseSemaphore(audio_sem, 1, NULL); |
|
116 #endif |
|
117 } |
|
118 |
|
119 static void SetMMerror(char *function, MMRESULT code) |
|
120 { |
|
121 size_t len; |
|
122 char errbuf[MAXERRORLENGTH]; |
|
123 #ifdef _WIN32_WCE |
|
124 wchar_t werrbuf[MAXERRORLENGTH]; |
|
125 #endif |
|
126 |
|
127 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function); |
|
128 len = SDL_strlen(errbuf); |
|
129 |
|
130 #ifdef _WIN32_WCE |
|
131 /* UNICODE version */ |
|
132 waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH-len); |
|
133 WideCharToMultiByte(CP_ACP,0,werrbuf,-1,errbuf+len,MAXERRORLENGTH-len,NULL,NULL); |
|
134 #else |
|
135 waveOutGetErrorText(code, errbuf+len, (UINT)(MAXERRORLENGTH-len)); |
|
136 #endif |
|
137 |
|
138 SDL_SetError("%s",errbuf); |
|
139 } |
|
140 |
|
141 /* Set high priority for the audio thread */ |
|
142 static void DIB_ThreadInit(_THIS) |
|
143 { |
|
144 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); |
|
145 } |
|
146 |
|
147 void DIB_WaitAudio(_THIS) |
|
148 { |
|
149 /* Wait for an audio chunk to finish */ |
|
150 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300) |
|
151 WaitForSemaphoreCE(audio_sem, INFINITE); |
|
152 #else |
|
153 WaitForSingleObject(audio_sem, INFINITE); |
|
154 #endif |
|
155 } |
|
156 |
|
157 Uint8 *DIB_GetAudioBuf(_THIS) |
|
158 { |
|
159 Uint8 *retval; |
|
160 |
|
161 retval = (Uint8 *)(wavebuf[next_buffer].lpData); |
|
162 return retval; |
|
163 } |
|
164 |
|
165 void DIB_PlayAudio(_THIS) |
|
166 { |
|
167 /* Queue it up */ |
|
168 waveOutWrite(sound, &wavebuf[next_buffer], sizeof(wavebuf[0])); |
|
169 next_buffer = (next_buffer+1)%NUM_BUFFERS; |
|
170 } |
|
171 |
|
172 void DIB_WaitDone(_THIS) |
|
173 { |
|
174 int i, left; |
|
175 |
|
176 do { |
|
177 left = NUM_BUFFERS; |
|
178 for ( i=0; i<NUM_BUFFERS; ++i ) { |
|
179 if ( wavebuf[i].dwFlags & WHDR_DONE ) { |
|
180 --left; |
|
181 } |
|
182 } |
|
183 if ( left > 0 ) { |
|
184 SDL_Delay(100); |
|
185 } |
|
186 } while ( left > 0 ); |
|
187 } |
|
188 |
|
189 void DIB_CloseAudio(_THIS) |
|
190 { |
|
191 int i; |
|
192 |
|
193 /* Close up audio */ |
|
194 if ( audio_sem ) { |
|
195 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300) |
|
196 CloseSynchHandle(audio_sem); |
|
197 #else |
|
198 CloseHandle(audio_sem); |
|
199 #endif |
|
200 } |
|
201 if ( sound ) { |
|
202 waveOutClose(sound); |
|
203 } |
|
204 |
|
205 /* Clean up mixing buffers */ |
|
206 for ( i=0; i<NUM_BUFFERS; ++i ) { |
|
207 if ( wavebuf[i].dwUser != 0xFFFF ) { |
|
208 waveOutUnprepareHeader(sound, &wavebuf[i], |
|
209 sizeof(wavebuf[i])); |
|
210 wavebuf[i].dwUser = 0xFFFF; |
|
211 } |
|
212 } |
|
213 /* Free raw mixing buffer */ |
|
214 if ( mixbuf != NULL ) { |
|
215 SDL_free(mixbuf); |
|
216 mixbuf = NULL; |
|
217 } |
|
218 } |
|
219 |
|
220 int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec) |
|
221 { |
|
222 MMRESULT result; |
|
223 int i; |
|
224 WAVEFORMATEX waveformat; |
|
225 |
|
226 /* Initialize the wavebuf structures for closing */ |
|
227 sound = NULL; |
|
228 audio_sem = NULL; |
|
229 for ( i = 0; i < NUM_BUFFERS; ++i ) |
|
230 wavebuf[i].dwUser = 0xFFFF; |
|
231 mixbuf = NULL; |
|
232 |
|
233 /* Set basic WAVE format parameters */ |
|
234 SDL_memset(&waveformat, 0, sizeof(waveformat)); |
|
235 waveformat.wFormatTag = WAVE_FORMAT_PCM; |
|
236 |
|
237 /* Determine the audio parameters from the AudioSpec */ |
|
238 switch ( spec->format & 0xFF ) { |
|
239 case 8: |
|
240 /* Unsigned 8 bit audio data */ |
|
241 spec->format = AUDIO_U8; |
|
242 waveformat.wBitsPerSample = 8; |
|
243 break; |
|
244 case 16: |
|
245 /* Signed 16 bit audio data */ |
|
246 spec->format = AUDIO_S16; |
|
247 waveformat.wBitsPerSample = 16; |
|
248 break; |
|
249 default: |
|
250 SDL_SetError("Unsupported audio format"); |
|
251 return(-1); |
|
252 } |
|
253 waveformat.nChannels = spec->channels; |
|
254 waveformat.nSamplesPerSec = spec->freq; |
|
255 waveformat.nBlockAlign = |
|
256 waveformat.nChannels * (waveformat.wBitsPerSample/8); |
|
257 waveformat.nAvgBytesPerSec = |
|
258 waveformat.nSamplesPerSec * waveformat.nBlockAlign; |
|
259 |
|
260 /* Check the buffer size -- minimum of 1/4 second (word aligned) */ |
|
261 if ( spec->samples < (spec->freq/4) ) |
|
262 spec->samples = ((spec->freq/4)+3)&~3; |
|
263 |
|
264 /* Update the fragment size as size in bytes */ |
|
265 SDL_CalculateAudioSpec(spec); |
|
266 |
|
267 /* Open the audio device */ |
|
268 result = waveOutOpen(&sound, WAVE_MAPPER, &waveformat, |
|
269 (DWORD_PTR)FillSound, (DWORD_PTR)this, CALLBACK_FUNCTION); |
|
270 if ( result != MMSYSERR_NOERROR ) { |
|
271 SetMMerror("waveOutOpen()", result); |
|
272 return(-1); |
|
273 } |
|
274 |
|
275 #ifdef SOUND_DEBUG |
|
276 /* Check the sound device we retrieved */ |
|
277 { |
|
278 WAVEOUTCAPS caps; |
|
279 |
|
280 result = waveOutGetDevCaps((UINT)sound, &caps, sizeof(caps)); |
|
281 if ( result != MMSYSERR_NOERROR ) { |
|
282 SetMMerror("waveOutGetDevCaps()", result); |
|
283 return(-1); |
|
284 } |
|
285 printf("Audio device: %s\n", caps.szPname); |
|
286 } |
|
287 #endif |
|
288 |
|
289 /* Create the audio buffer semaphore */ |
|
290 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300) |
|
291 audio_sem = CreateSemaphoreCE(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL); |
|
292 #else |
|
293 audio_sem = CreateSemaphore(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL); |
|
294 #endif |
|
295 if ( audio_sem == NULL ) { |
|
296 SDL_SetError("Couldn't create semaphore"); |
|
297 return(-1); |
|
298 } |
|
299 |
|
300 /* Create the sound buffers */ |
|
301 mixbuf = (Uint8 *)SDL_malloc(NUM_BUFFERS*spec->size); |
|
302 if ( mixbuf == NULL ) { |
|
303 SDL_SetError("Out of memory"); |
|
304 return(-1); |
|
305 } |
|
306 for ( i = 0; i < NUM_BUFFERS; ++i ) { |
|
307 SDL_memset(&wavebuf[i], 0, sizeof(wavebuf[i])); |
|
308 wavebuf[i].lpData = (LPSTR) &mixbuf[i*spec->size]; |
|
309 wavebuf[i].dwBufferLength = spec->size; |
|
310 wavebuf[i].dwFlags = WHDR_DONE; |
|
311 result = waveOutPrepareHeader(sound, &wavebuf[i], |
|
312 sizeof(wavebuf[i])); |
|
313 if ( result != MMSYSERR_NOERROR ) { |
|
314 SetMMerror("waveOutPrepareHeader()", result); |
|
315 return(-1); |
|
316 } |
|
317 } |
|
318 |
|
319 /* Ready to go! */ |
|
320 next_buffer = 0; |
|
321 return(0); |
|
322 } |