Mercurial > sdl-ios-xcode
comparison src/audio/windx5/SDL_dx5audio.c @ 2049:5f6550e5184f
Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Tue, 17 Oct 2006 09:15:21 +0000 |
parents | 4ad1e863d100 |
children | bbc89e09503f |
comparison
equal
deleted
inserted
replaced
2048:6067c7f9a672 | 2049:5f6550e5184f |
---|---|
26 #include "SDL_timer.h" | 26 #include "SDL_timer.h" |
27 #include "SDL_audio.h" | 27 #include "SDL_audio.h" |
28 #include "../SDL_audio_c.h" | 28 #include "../SDL_audio_c.h" |
29 #include "SDL_dx5audio.h" | 29 #include "SDL_dx5audio.h" |
30 | 30 |
31 /* Define this if you want to use DirectX 6 DirectSoundNotify interface */ | 31 /* !!! FIXME: move this somewhere that other drivers can use it... */ |
32 //#define USE_POSITION_NOTIFY | 32 #if defined(_WIN32_WCE) |
33 #define WINDOWS_OS_NAME "Windows CE/PocketPC" | |
34 #elif defined(WIN64) | |
35 #define WINDOWS_OS_NAME "Win64" | |
36 #else | |
37 #define WINDOWS_OS_NAME "Win32" | |
38 #endif | |
33 | 39 |
34 /* DirectX function pointers for audio */ | 40 /* DirectX function pointers for audio */ |
35 HRESULT(WINAPI * DSoundCreate) (LPGUID, LPDIRECTSOUND *, LPUNKNOWN); | |
36 | |
37 /* Audio driver functions */ | |
38 static int DX5_OpenAudio(_THIS, SDL_AudioSpec * spec); | |
39 static void DX5_ThreadInit(_THIS); | |
40 static void DX5_WaitAudio_BusyWait(_THIS); | |
41 #ifdef USE_POSITION_NOTIFY | |
42 static void DX6_WaitAudio_EventWait(_THIS); | |
43 #endif | |
44 static void DX5_PlayAudio(_THIS); | |
45 static Uint8 *DX5_GetAudioBuf(_THIS); | |
46 static void DX5_WaitDone(_THIS); | |
47 static void DX5_CloseAudio(_THIS); | |
48 | |
49 /* Audio driver bootstrap functions */ | |
50 | |
51 static int | |
52 Audio_Available(void) | |
53 { | |
54 HINSTANCE DSoundDLL; | |
55 int dsound_ok; | |
56 | |
57 /* Version check DSOUND.DLL (Is DirectX okay?) */ | |
58 dsound_ok = 0; | |
59 DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL")); | |
60 if (DSoundDLL != NULL) { | |
61 /* We just use basic DirectSound, we're okay */ | |
62 /* Yay! */ | |
63 /* Unfortunately, the sound drivers on NT have | |
64 higher latencies than the audio buffers used | |
65 by many SDL applications, so there are gaps | |
66 in the audio - it sounds terrible. Punt for now. | |
67 */ | |
68 OSVERSIONINFO ver; | |
69 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); | |
70 GetVersionEx(&ver); | |
71 switch (ver.dwPlatformId) { | |
72 case VER_PLATFORM_WIN32_NT: | |
73 if (ver.dwMajorVersion > 4) { | |
74 /* Win2K */ | |
75 dsound_ok = 1; | |
76 } else { | |
77 /* WinNT */ | |
78 dsound_ok = 0; | |
79 } | |
80 break; | |
81 default: | |
82 /* Win95 or Win98 */ | |
83 dsound_ok = 1; | |
84 break; | |
85 } | |
86 /* Now check for DirectX 5 or better - otherwise | |
87 * we will fail later in DX5_OpenAudio without a chance | |
88 * to fall back to the DIB driver. */ | |
89 if (dsound_ok) { | |
90 /* DirectSoundCaptureCreate was added in DX5 */ | |
91 if (!GetProcAddress(DSoundDLL, TEXT("DirectSoundCaptureCreate"))) | |
92 dsound_ok = 0; | |
93 | |
94 } | |
95 /* Clean up.. */ | |
96 FreeLibrary(DSoundDLL); | |
97 } | |
98 return (dsound_ok); | |
99 } | |
100 | |
101 /* Functions for loading the DirectX functions dynamically */ | |
102 static HINSTANCE DSoundDLL = NULL; | 41 static HINSTANCE DSoundDLL = NULL; |
103 | 42 static HRESULT (WINAPI *DSoundCreate)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN) = NULL; |
104 static void | 43 |
105 DX5_Unload(void) | 44 static void |
45 DSOUND_Unload(void) | |
106 { | 46 { |
107 if (DSoundDLL != NULL) { | 47 if (DSoundDLL != NULL) { |
108 FreeLibrary(DSoundDLL); | 48 FreeLibrary(DSoundDLL); |
109 DSoundCreate = NULL; | 49 } |
110 DSoundDLL = NULL; | 50 |
111 } | 51 DSoundCreate = NULL; |
112 } | 52 DSoundDLL = NULL; |
53 } | |
54 | |
55 | |
113 static int | 56 static int |
114 DX5_Load(void) | 57 DSOUND_Load(void) |
115 { | 58 { |
116 int status; | 59 int loaded = 0; |
117 | 60 |
118 DX5_Unload(); | 61 DSOUND_Unload(); |
62 | |
119 DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL")); | 63 DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL")); |
120 if (DSoundDLL != NULL) { | 64 if (DSoundDLL == NULL) { |
121 DSoundCreate = (void *) GetProcAddress(DSoundDLL, | 65 SDL_SetError("DirectSound: failed to load DSOUND.DLL"); |
66 } else { | |
67 /* Now make sure we have DirectX 5 or better... */ | |
68 /* (DirectSoundCaptureCreate was added in DX5) */ | |
69 if (!GetProcAddress(DSoundDLL, TEXT("DirectSoundCaptureCreate"))) { | |
70 SDL_SetError("DirectSound: System doesn't appear to have DX5."); | |
71 } else { | |
72 DSoundCreate = (void *) GetProcAddress(DSoundDLL, | |
122 TEXT("DirectSoundCreate")); | 73 TEXT("DirectSoundCreate")); |
123 } | 74 } |
124 if (DSoundDLL && DSoundCreate) { | 75 |
125 status = 0; | 76 if (!DSoundCreate) { |
126 } else { | 77 SDL_SetError("DirectSound: Failed to find DirectSoundCreate"); |
127 DX5_Unload(); | 78 } else { |
128 status = -1; | 79 loaded = 1; |
129 } | 80 } |
130 return status; | 81 } |
131 } | 82 |
132 | 83 if (!loaded) { |
133 static void | 84 DSOUND_Unload(); |
134 Audio_DeleteDevice(SDL_AudioDevice * device) | 85 } |
135 { | 86 |
136 DX5_Unload(); | 87 return loaded; |
137 SDL_free(device->hidden); | 88 } |
138 SDL_free(device); | 89 |
139 } | |
140 | |
141 static SDL_AudioDevice * | |
142 Audio_CreateDevice(int devindex) | |
143 { | |
144 SDL_AudioDevice *this; | |
145 | |
146 /* Load DirectX */ | |
147 if (DX5_Load() < 0) { | |
148 return (NULL); | |
149 } | |
150 | |
151 /* Initialize all variables that we clean on shutdown */ | |
152 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); | |
153 if (this) { | |
154 SDL_memset(this, 0, (sizeof *this)); | |
155 this->hidden = (struct SDL_PrivateAudioData *) | |
156 SDL_malloc((sizeof *this->hidden)); | |
157 } | |
158 if ((this == NULL) || (this->hidden == NULL)) { | |
159 SDL_OutOfMemory(); | |
160 if (this) { | |
161 SDL_free(this); | |
162 } | |
163 return (0); | |
164 } | |
165 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
166 | |
167 /* Set the function pointers */ | |
168 this->OpenAudio = DX5_OpenAudio; | |
169 this->ThreadInit = DX5_ThreadInit; | |
170 this->WaitAudio = DX5_WaitAudio_BusyWait; | |
171 this->PlayAudio = DX5_PlayAudio; | |
172 this->GetAudioBuf = DX5_GetAudioBuf; | |
173 this->WaitDone = DX5_WaitDone; | |
174 this->CloseAudio = DX5_CloseAudio; | |
175 | |
176 this->free = Audio_DeleteDevice; | |
177 | |
178 return this; | |
179 } | |
180 | |
181 AudioBootStrap DSOUND_bootstrap = { | |
182 "dsound", "Win95/98/2000 DirectSound", | |
183 Audio_Available, Audio_CreateDevice | |
184 }; | |
185 | 90 |
186 static void | 91 static void |
187 SetDSerror(const char *function, int code) | 92 SetDSerror(const char *function, int code) |
188 { | 93 { |
189 static const char *error; | 94 static const char *error; |
190 static char errbuf[1024]; | 95 static char errbuf[1024]; |
191 | 96 |
192 errbuf[0] = 0; | 97 errbuf[0] = 0; |
193 switch (code) { | 98 switch (code) { |
194 case E_NOINTERFACE: | 99 case E_NOINTERFACE: |
195 error = | 100 error = "Unsupported interface -- Is DirectX 5.0 or later installed?"; |
196 "Unsupported interface\n-- Is DirectX 5.0 or later installed?"; | |
197 break; | 101 break; |
198 case DSERR_ALLOCATED: | 102 case DSERR_ALLOCATED: |
199 error = "Audio device in use"; | 103 error = "Audio device in use"; |
200 break; | 104 break; |
201 case DSERR_BADFORMAT: | 105 case DSERR_BADFORMAT: |
239 } | 143 } |
240 | 144 |
241 /* DirectSound needs to be associated with a window */ | 145 /* DirectSound needs to be associated with a window */ |
242 static HWND mainwin = NULL; | 146 static HWND mainwin = NULL; |
243 /* */ | 147 /* */ |
148 | |
244 void | 149 void |
245 DX5_SoundFocus(HWND hwnd) | 150 DSOUND_SoundFocus(HWND hwnd) |
246 { | 151 { |
152 /* !!! FIXME: probably broken with multi-window support in SDL 1.3 ... */ | |
247 mainwin = hwnd; | 153 mainwin = hwnd; |
248 } | 154 } |
249 | 155 |
250 static void | 156 static void |
251 DX5_ThreadInit(_THIS) | 157 DSOUND_ThreadInit(_THIS) |
252 { | 158 { |
253 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); | 159 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); |
254 } | 160 } |
255 | 161 |
256 static void | 162 static void |
257 DX5_WaitAudio_BusyWait(_THIS) | 163 DSOUND_WaitDevice(_THIS) |
258 { | 164 { |
259 DWORD status; | 165 DWORD status = 0; |
260 DWORD cursor, junk; | 166 DWORD cursor = 0; |
261 HRESULT result; | 167 DWORD junk = 0; |
168 HRESULT result = DS_OK; | |
262 | 169 |
263 /* Semi-busy wait, since we have no way of getting play notification | 170 /* Semi-busy wait, since we have no way of getting play notification |
264 on a primary mixing buffer located in hardware (DirectX 5.0) | 171 on a primary mixing buffer located in hardware (DirectX 5.0) |
265 */ | 172 */ |
266 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor); | 173 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
174 &junk, &cursor); | |
267 if (result != DS_OK) { | 175 if (result != DS_OK) { |
268 if (result == DSERR_BUFFERLOST) { | 176 if (result == DSERR_BUFFERLOST) { |
269 IDirectSoundBuffer_Restore(mixbuf); | 177 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
270 } | 178 } |
271 #ifdef DEBUG_SOUND | 179 #ifdef DEBUG_SOUND |
272 SetDSerror("DirectSound GetCurrentPosition", result); | 180 SetDSerror("DirectSound GetCurrentPosition", result); |
273 #endif | 181 #endif |
274 return; | 182 return; |
275 } | 183 } |
276 | 184 |
277 while ((cursor / mixlen) == lastchunk) { | 185 while ((cursor / this->hidden->mixlen) == this->hidden->lastchunk) { |
278 /* FIXME: find out how much time is left and sleep that long */ | 186 /* FIXME: find out how much time is left and sleep that long */ |
279 SDL_Delay(1); | 187 SDL_Delay(1); |
280 | 188 |
281 /* Try to restore a lost sound buffer */ | 189 /* Try to restore a lost sound buffer */ |
282 IDirectSoundBuffer_GetStatus(mixbuf, &status); | 190 IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status); |
283 if ((status & DSBSTATUS_BUFFERLOST)) { | 191 if ((status & DSBSTATUS_BUFFERLOST)) { |
284 IDirectSoundBuffer_Restore(mixbuf); | 192 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
285 IDirectSoundBuffer_GetStatus(mixbuf, &status); | 193 IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status); |
286 if ((status & DSBSTATUS_BUFFERLOST)) { | 194 if ((status & DSBSTATUS_BUFFERLOST)) { |
287 break; | 195 break; |
288 } | 196 } |
289 } | 197 } |
290 if (!(status & DSBSTATUS_PLAYING)) { | 198 if (!(status & DSBSTATUS_PLAYING)) { |
291 result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING); | 199 result = IDirectSoundBuffer_Play(this->hidden->mixbuf, 0, 0, |
200 DSBPLAY_LOOPING); | |
292 if (result == DS_OK) { | 201 if (result == DS_OK) { |
293 continue; | 202 continue; |
294 } | 203 } |
295 #ifdef DEBUG_SOUND | 204 #ifdef DEBUG_SOUND |
296 SetDSerror("DirectSound Play", result); | 205 SetDSerror("DirectSound Play", result); |
297 #endif | 206 #endif |
298 return; | 207 return; |
299 } | 208 } |
300 | 209 |
301 /* Find out where we are playing */ | 210 /* Find out where we are playing */ |
302 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, | 211 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
303 &junk, &cursor); | 212 &junk, &cursor); |
304 if (result != DS_OK) { | 213 if (result != DS_OK) { |
305 SetDSerror("DirectSound GetCurrentPosition", result); | 214 SetDSerror("DirectSound GetCurrentPosition", result); |
306 return; | 215 return; |
307 } | 216 } |
308 } | 217 } |
309 } | 218 } |
310 | 219 |
311 #ifdef USE_POSITION_NOTIFY | 220 static void |
312 static void | 221 DSOUND_PlayDevice(_THIS) |
313 DX6_WaitAudio_EventWait(_THIS) | |
314 { | |
315 DWORD status; | |
316 HRESULT result; | |
317 | |
318 /* Try to restore a lost sound buffer */ | |
319 IDirectSoundBuffer_GetStatus(mixbuf, &status); | |
320 if ((status & DSBSTATUS_BUFFERLOST)) { | |
321 IDirectSoundBuffer_Restore(mixbuf); | |
322 IDirectSoundBuffer_GetStatus(mixbuf, &status); | |
323 if ((status & DSBSTATUS_BUFFERLOST)) { | |
324 return; | |
325 } | |
326 } | |
327 if (!(status & DSBSTATUS_PLAYING)) { | |
328 result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING); | |
329 if (result != DS_OK) { | |
330 #ifdef DEBUG_SOUND | |
331 SetDSerror("DirectSound Play", result); | |
332 #endif | |
333 return; | |
334 } | |
335 } | |
336 WaitForSingleObject(audio_event, INFINITE); | |
337 } | |
338 #endif /* USE_POSITION_NOTIFY */ | |
339 | |
340 static void | |
341 DX5_PlayAudio(_THIS) | |
342 { | 222 { |
343 /* Unlock the buffer, allowing it to play */ | 223 /* Unlock the buffer, allowing it to play */ |
344 if (locked_buf) { | 224 if (this->hidden->locked_buf) { |
345 IDirectSoundBuffer_Unlock(mixbuf, locked_buf, mixlen, NULL, 0); | 225 IDirectSoundBuffer_Unlock(this->hidden->mixbuf, |
226 this->hidden->locked_buf, | |
227 this->hidden->mixlen, NULL, 0); | |
346 } | 228 } |
347 | 229 |
348 } | 230 } |
349 | 231 |
350 static Uint8 * | 232 static Uint8 * |
351 DX5_GetAudioBuf(_THIS) | 233 DSOUND_GetDeviceBuf(_THIS) |
352 { | 234 { |
353 DWORD cursor, junk; | 235 DWORD cursor = 0; |
354 HRESULT result; | 236 DWORD junk = 0; |
355 DWORD rawlen; | 237 HRESULT result = DS_OK; |
238 DWORD rawlen = 0; | |
356 | 239 |
357 /* Figure out which blocks to fill next */ | 240 /* Figure out which blocks to fill next */ |
358 locked_buf = NULL; | 241 this->hidden->locked_buf = NULL; |
359 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor); | 242 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
243 &junk, &cursor); | |
360 if (result == DSERR_BUFFERLOST) { | 244 if (result == DSERR_BUFFERLOST) { |
361 IDirectSoundBuffer_Restore(mixbuf); | 245 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
362 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, | 246 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
363 &junk, &cursor); | 247 &junk, &cursor); |
364 } | 248 } |
365 if (result != DS_OK) { | 249 if (result != DS_OK) { |
366 SetDSerror("DirectSound GetCurrentPosition", result); | 250 SetDSerror("DirectSound GetCurrentPosition", result); |
367 return (NULL); | 251 return (NULL); |
368 } | 252 } |
369 cursor /= mixlen; | 253 cursor /= this->hidden->mixlen; |
370 #ifdef DEBUG_SOUND | 254 #ifdef DEBUG_SOUND |
371 /* Detect audio dropouts */ | 255 /* Detect audio dropouts */ |
372 { | 256 { |
373 DWORD spot = cursor; | 257 DWORD spot = cursor; |
374 if (spot < lastchunk) { | 258 if (spot < this->hidden->lastchunk) { |
375 spot += NUM_BUFFERS; | 259 spot += this->hidden->num_buffers; |
376 } | 260 } |
377 if (spot > lastchunk + 1) { | 261 if (spot > this->hidden->lastchunk + 1) { |
378 fprintf(stderr, "Audio dropout, missed %d fragments\n", | 262 fprintf(stderr, "Audio dropout, missed %d fragments\n", |
379 (spot - (lastchunk + 1))); | 263 (spot - (this->hidden->lastchunk + 1))); |
380 } | 264 } |
381 } | 265 } |
382 #endif | 266 #endif |
383 lastchunk = cursor; | 267 this->hidden->lastchunk = cursor; |
384 cursor = (cursor + 1) % NUM_BUFFERS; | 268 cursor = (cursor + 1) % this->hidden->num_buffers; |
385 cursor *= mixlen; | 269 cursor *= this->hidden->mixlen; |
386 | 270 |
387 /* Lock the audio buffer */ | 271 /* Lock the audio buffer */ |
388 result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen, | 272 result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor, |
389 (LPVOID *) & locked_buf, &rawlen, NULL, | 273 this->hidden->mixlen, |
390 &junk, 0); | 274 (LPVOID *) &this->hidden->locked_buf, |
275 &rawlen, NULL, &junk, 0); | |
391 if (result == DSERR_BUFFERLOST) { | 276 if (result == DSERR_BUFFERLOST) { |
392 IDirectSoundBuffer_Restore(mixbuf); | 277 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
393 result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen, | 278 result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor, |
394 (LPVOID *) & locked_buf, &rawlen, | 279 this->hidden->mixlen, |
395 NULL, &junk, 0); | 280 (LPVOID *) &this->hidden->locked_buf, |
281 &rawlen, NULL, &junk, 0); | |
396 } | 282 } |
397 if (result != DS_OK) { | 283 if (result != DS_OK) { |
398 SetDSerror("DirectSound Lock", result); | 284 SetDSerror("DirectSound Lock", result); |
399 return (NULL); | 285 return (NULL); |
400 } | 286 } |
401 return (locked_buf); | 287 return (this->hidden->locked_buf); |
402 } | 288 } |
403 | 289 |
404 static void | 290 static void |
405 DX5_WaitDone(_THIS) | 291 DSOUND_WaitDone(_THIS) |
406 { | 292 { |
407 Uint8 *stream; | 293 Uint8 *stream = DSOUND_GetDeviceBuf(this); |
408 | 294 |
409 /* Wait for the playing chunk to finish */ | 295 /* Wait for the playing chunk to finish */ |
410 stream = this->GetAudioBuf(this); | |
411 if (stream != NULL) { | 296 if (stream != NULL) { |
412 SDL_memset(stream, silence, mixlen); | 297 SDL_memset(stream, this->spec.silence, this->hidden->mixlen); |
413 this->PlayAudio(this); | 298 DSOUND_PlayDevice(this); |
414 } | 299 } |
415 this->WaitAudio(this); | 300 DSOUND_WaitDevice(this); |
416 | 301 |
417 /* Stop the looping sound buffer */ | 302 /* Stop the looping sound buffer */ |
418 IDirectSoundBuffer_Stop(mixbuf); | 303 IDirectSoundBuffer_Stop(this->hidden->mixbuf); |
419 } | 304 } |
420 | 305 |
421 static void | 306 static void |
422 DX5_CloseAudio(_THIS) | 307 DSOUND_CloseDevice(_THIS) |
423 { | 308 { |
424 if (sound != NULL) { | 309 if (this->hidden != NULL) { |
425 if (mixbuf != NULL) { | 310 if (this->hidden->sound != NULL) { |
426 /* Clean up the audio buffer */ | 311 if (this->hidden->mixbuf != NULL) { |
427 IDirectSoundBuffer_Release(mixbuf); | 312 /* Clean up the audio buffer */ |
428 mixbuf = NULL; | 313 IDirectSoundBuffer_Release(this->hidden->mixbuf); |
429 } | 314 this->hidden->mixbuf = NULL; |
430 if (audio_event != NULL) { | 315 } |
431 CloseHandle(audio_event); | 316 IDirectSound_Release(this->hidden->sound); |
432 audio_event = NULL; | 317 this->hidden->sound = NULL; |
433 } | 318 } |
434 IDirectSound_Release(sound); | 319 |
435 sound = NULL; | 320 SDL_free(this->hidden); |
436 } | 321 this->hidden = NULL; |
437 } | 322 } |
438 | 323 } |
439 #ifdef USE_PRIMARY_BUFFER | |
440 /* This function tries to create a primary audio buffer, and returns the | |
441 number of audio chunks available in the created buffer. | |
442 */ | |
443 static int | |
444 CreatePrimary(LPDIRECTSOUND sndObj, HWND focus, | |
445 LPDIRECTSOUNDBUFFER * sndbuf, WAVEFORMATEX * wavefmt, | |
446 Uint32 chunksize) | |
447 { | |
448 HRESULT result; | |
449 DSBUFFERDESC format; | |
450 DSBCAPS caps; | |
451 int numchunks; | |
452 | |
453 /* Try to set primary mixing privileges */ | |
454 result = IDirectSound_SetCooperativeLevel(sndObj, focus, | |
455 DSSCL_WRITEPRIMARY); | |
456 if (result != DS_OK) { | |
457 #ifdef DEBUG_SOUND | |
458 SetDSerror("DirectSound SetCooperativeLevel", result); | |
459 #endif | |
460 return (-1); | |
461 } | |
462 | |
463 /* Try to create the primary buffer */ | |
464 SDL_memset(&format, 0, sizeof(format)); | |
465 format.dwSize = sizeof(format); | |
466 format.dwFlags = (DSBCAPS_PRIMARYBUFFER | DSBCAPS_GETCURRENTPOSITION2); | |
467 format.dwFlags |= DSBCAPS_STICKYFOCUS; | |
468 #ifdef USE_POSITION_NOTIFY | |
469 format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; | |
470 #endif | |
471 result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL); | |
472 if (result != DS_OK) { | |
473 #ifdef DEBUG_SOUND | |
474 SetDSerror("DirectSound CreateSoundBuffer", result); | |
475 #endif | |
476 return (-1); | |
477 } | |
478 | |
479 /* Check the size of the fragment buffer */ | |
480 SDL_memset(&caps, 0, sizeof(caps)); | |
481 caps.dwSize = sizeof(caps); | |
482 result = IDirectSoundBuffer_GetCaps(*sndbuf, &caps); | |
483 if (result != DS_OK) { | |
484 #ifdef DEBUG_SOUND | |
485 SetDSerror("DirectSound GetCaps", result); | |
486 #endif | |
487 IDirectSoundBuffer_Release(*sndbuf); | |
488 return (-1); | |
489 } | |
490 if ((chunksize > caps.dwBufferBytes) || | |
491 ((caps.dwBufferBytes % chunksize) != 0)) { | |
492 /* The primary buffer size is not a multiple of 'chunksize' | |
493 -- this hopefully doesn't happen when 'chunksize' is a | |
494 power of 2. | |
495 */ | |
496 IDirectSoundBuffer_Release(*sndbuf); | |
497 SDL_SetError | |
498 ("Primary buffer size is: %d, cannot break it into chunks of %d bytes\n", | |
499 caps.dwBufferBytes, chunksize); | |
500 return (-1); | |
501 } | |
502 numchunks = (caps.dwBufferBytes / chunksize); | |
503 | |
504 /* Set the primary audio format */ | |
505 result = IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt); | |
506 if (result != DS_OK) { | |
507 #ifdef DEBUG_SOUND | |
508 SetDSerror("DirectSound SetFormat", result); | |
509 #endif | |
510 IDirectSoundBuffer_Release(*sndbuf); | |
511 return (-1); | |
512 } | |
513 return (numchunks); | |
514 } | |
515 #endif /* USE_PRIMARY_BUFFER */ | |
516 | 324 |
517 /* This function tries to create a secondary audio buffer, and returns the | 325 /* This function tries to create a secondary audio buffer, and returns the |
518 number of audio chunks available in the created buffer. | 326 number of audio chunks available in the created buffer. |
519 */ | 327 */ |
520 static int | 328 static int |
521 CreateSecondary(LPDIRECTSOUND sndObj, HWND focus, | 329 CreateSecondary(_THIS, HWND focus, WAVEFORMATEX *wavefmt) |
522 LPDIRECTSOUNDBUFFER * sndbuf, WAVEFORMATEX * wavefmt, | 330 { |
523 Uint32 chunksize) | 331 LPDIRECTSOUND sndObj = this->hidden->sound; |
524 { | 332 LPDIRECTSOUNDBUFFER *sndbuf = this->hidden->mixbuf; |
333 Uint32 chunksize = this->spec.size; | |
525 const int numchunks = 8; | 334 const int numchunks = 8; |
526 HRESULT result; | 335 HRESULT result = DS_OK; |
527 DSBUFFERDESC format; | 336 DSBUFFERDESC format; |
528 LPVOID pvAudioPtr1, pvAudioPtr2; | 337 LPVOID pvAudioPtr1, pvAudioPtr2; |
529 DWORD dwAudioBytes1, dwAudioBytes2; | 338 DWORD dwAudioBytes1, dwAudioBytes2; |
530 | 339 |
531 /* Try to set primary mixing privileges */ | 340 /* Try to set primary mixing privileges */ |
536 result = IDirectSound_SetCooperativeLevel(sndObj, | 345 result = IDirectSound_SetCooperativeLevel(sndObj, |
537 GetDesktopWindow(), | 346 GetDesktopWindow(), |
538 DSSCL_NORMAL); | 347 DSSCL_NORMAL); |
539 } | 348 } |
540 if (result != DS_OK) { | 349 if (result != DS_OK) { |
541 #ifdef DEBUG_SOUND | |
542 SetDSerror("DirectSound SetCooperativeLevel", result); | 350 SetDSerror("DirectSound SetCooperativeLevel", result); |
543 #endif | |
544 return (-1); | 351 return (-1); |
545 } | 352 } |
546 | 353 |
547 /* Try to create the secondary buffer */ | 354 /* Try to create the secondary buffer */ |
548 SDL_memset(&format, 0, sizeof(format)); | 355 SDL_memset(&format, 0, sizeof(format)); |
549 format.dwSize = sizeof(format); | 356 format.dwSize = sizeof(format); |
550 format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; | 357 format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; |
551 #ifdef USE_POSITION_NOTIFY | |
552 format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; | |
553 #endif | |
554 if (!focus) { | 358 if (!focus) { |
555 format.dwFlags |= DSBCAPS_GLOBALFOCUS; | 359 format.dwFlags |= DSBCAPS_GLOBALFOCUS; |
556 } else { | 360 } else { |
557 format.dwFlags |= DSBCAPS_STICKYFOCUS; | 361 format.dwFlags |= DSBCAPS_STICKYFOCUS; |
558 } | 362 } |
576 result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, | 380 result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, |
577 (LPVOID *) & pvAudioPtr1, &dwAudioBytes1, | 381 (LPVOID *) & pvAudioPtr1, &dwAudioBytes1, |
578 (LPVOID *) & pvAudioPtr2, &dwAudioBytes2, | 382 (LPVOID *) & pvAudioPtr2, &dwAudioBytes2, |
579 DSBLOCK_ENTIREBUFFER); | 383 DSBLOCK_ENTIREBUFFER); |
580 if (result == DS_OK) { | 384 if (result == DS_OK) { |
581 if (wavefmt->wBitsPerSample == 8) { | 385 SDL_memset(pvAudioPtr1, this->spec.silence, dwAudioBytes1); |
582 SDL_memset(pvAudioPtr1, 0x80, dwAudioBytes1); | |
583 } else { | |
584 SDL_memset(pvAudioPtr1, 0x00, dwAudioBytes1); | |
585 } | |
586 IDirectSoundBuffer_Unlock(*sndbuf, | 386 IDirectSoundBuffer_Unlock(*sndbuf, |
587 (LPVOID) pvAudioPtr1, dwAudioBytes1, | 387 (LPVOID) pvAudioPtr1, dwAudioBytes1, |
588 (LPVOID) pvAudioPtr2, dwAudioBytes2); | 388 (LPVOID) pvAudioPtr2, dwAudioBytes2); |
589 } | 389 } |
590 | 390 |
591 /* We're ready to go */ | 391 /* We're ready to go */ |
592 return (numchunks); | 392 return (numchunks); |
593 } | 393 } |
594 | 394 |
595 /* This function tries to set position notify events on the mixing buffer */ | |
596 #ifdef USE_POSITION_NOTIFY | |
597 static int | 395 static int |
598 CreateAudioEvent(_THIS) | 396 DSOUND_OpenDevice(_THIS, const char *devname, int iscapture) |
599 { | |
600 LPDIRECTSOUNDNOTIFY notify; | |
601 DSBPOSITIONNOTIFY *notify_positions; | |
602 int i, retval; | |
603 HRESULT result; | |
604 | |
605 /* Default to fail on exit */ | |
606 retval = -1; | |
607 notify = NULL; | |
608 | |
609 /* Query for the interface */ | |
610 result = IDirectSoundBuffer_QueryInterface(mixbuf, | |
611 &IID_IDirectSoundNotify, | |
612 (void *) ¬ify); | |
613 if (result != DS_OK) { | |
614 goto done; | |
615 } | |
616 | |
617 /* Allocate the notify structures */ | |
618 notify_positions = (DSBPOSITIONNOTIFY *) SDL_malloc(NUM_BUFFERS * | |
619 sizeof | |
620 (*notify_positions)); | |
621 if (notify_positions == NULL) { | |
622 goto done; | |
623 } | |
624 | |
625 /* Create the notify event */ | |
626 audio_event = CreateEvent(NULL, FALSE, FALSE, NULL); | |
627 if (audio_event == NULL) { | |
628 goto done; | |
629 } | |
630 | |
631 /* Set up the notify structures */ | |
632 for (i = 0; i < NUM_BUFFERS; ++i) { | |
633 notify_positions[i].dwOffset = i * mixlen; | |
634 notify_positions[i].hEventNotify = audio_event; | |
635 } | |
636 result = IDirectSoundNotify_SetNotificationPositions(notify, | |
637 NUM_BUFFERS, | |
638 notify_positions); | |
639 if (result == DS_OK) { | |
640 retval = 0; | |
641 } | |
642 done: | |
643 if (notify != NULL) { | |
644 IDirectSoundNotify_Release(notify); | |
645 } | |
646 return (retval); | |
647 } | |
648 #endif /* USE_POSITION_NOTIFY */ | |
649 | |
650 static int | |
651 DX5_OpenAudio(_THIS, SDL_AudioSpec * spec) | |
652 { | 397 { |
653 HRESULT result; | 398 HRESULT result; |
654 WAVEFORMATEX waveformat; | 399 WAVEFORMATEX waveformat; |
655 | 400 int valid_format = 0; |
656 /* Set basic WAVE format parameters */ | 401 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); |
402 | |
403 /* !!! FIXME: handle devname */ | |
404 /* !!! FIXME: handle iscapture */ | |
405 | |
406 /* Initialize all variables that we clean on shutdown */ | |
407 this->hidden = (struct SDL_PrivateAudioData *) | |
408 SDL_malloc((sizeof *this->hidden)); | |
409 if (this->hidden == NULL) { | |
410 SDL_OutOfMemory(); | |
411 return 0; | |
412 } | |
413 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
414 | |
415 while ((!valid_format) && (test_format)) { | |
416 switch (test_format) { | |
417 case AUDIO_U8: | |
418 case AUDIO_S16: | |
419 case AUDIO_S32: | |
420 this->spec.format = test_format; | |
421 valid_format = 1; | |
422 break; | |
423 } | |
424 test_format = SDL_NextAudioFormat(); | |
425 } | |
426 | |
427 if (!valid_format) { | |
428 DSOUND_CloseDevice(this); | |
429 SDL_SetError("DirectSound: Unsupported audio format"); | |
430 return 0; | |
431 } | |
432 | |
657 SDL_memset(&waveformat, 0, sizeof(waveformat)); | 433 SDL_memset(&waveformat, 0, sizeof(waveformat)); |
658 waveformat.wFormatTag = WAVE_FORMAT_PCM; | 434 waveformat.wFormatTag = WAVE_FORMAT_PCM; |
659 | 435 waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format); |
660 /* Determine the audio parameters from the AudioSpec */ | 436 waveformat.nChannels = this->spec.channels; |
661 switch (SDL_AUDIO_BITSIZE(spec->format)) { | 437 waveformat.nSamplesPerSec = this->spec.freq; |
662 case 8: | |
663 /* Unsigned 8 bit audio data */ | |
664 spec->format = AUDIO_U8; | |
665 silence = 0x80; | |
666 waveformat.wBitsPerSample = 8; | |
667 break; | |
668 case 16: | |
669 /* Signed 16 bit audio data */ | |
670 spec->format = AUDIO_S16; | |
671 silence = 0x00; | |
672 waveformat.wBitsPerSample = 16; | |
673 break; | |
674 case 32: | |
675 /* Signed 32 bit audio data */ | |
676 spec->format = AUDIO_S32; | |
677 silence = 0x00; | |
678 waveformat.wBitsPerSample = 32; | |
679 break; | |
680 default: | |
681 SDL_SetError("Unsupported audio format"); | |
682 return (-1); | |
683 } | |
684 waveformat.nChannels = spec->channels; | |
685 waveformat.nSamplesPerSec = spec->freq; | |
686 waveformat.nBlockAlign = | 438 waveformat.nBlockAlign = |
687 waveformat.nChannels * (waveformat.wBitsPerSample / 8); | 439 waveformat.nChannels * (waveformat.wBitsPerSample / 8); |
688 waveformat.nAvgBytesPerSec = | 440 waveformat.nAvgBytesPerSec = |
689 waveformat.nSamplesPerSec * waveformat.nBlockAlign; | 441 waveformat.nSamplesPerSec * waveformat.nBlockAlign; |
690 | 442 |
691 /* Update the fragment size as size in bytes */ | 443 /* Update the fragment size as size in bytes */ |
692 SDL_CalculateAudioSpec(spec); | 444 SDL_CalculateAudioSpec(&this->spec); |
693 | 445 |
694 /* Open the audio device */ | 446 /* Open the audio device */ |
695 result = DSoundCreate(NULL, &sound, NULL); | 447 result = DSoundCreate(NULL, &this->hidden->sound, NULL); |
696 if (result != DS_OK) { | 448 if (result != DS_OK) { |
449 DSOUND_CloseDevice(this); | |
697 SetDSerror("DirectSoundCreate", result); | 450 SetDSerror("DirectSoundCreate", result); |
698 return (-1); | 451 return 0; |
699 } | 452 } |
700 | 453 |
701 /* Create the audio buffer to which we write */ | 454 /* Create the audio buffer to which we write */ |
702 NUM_BUFFERS = -1; | 455 this->hidden->num_buffers = CreateSecondary(this, mainwin, &waveformat); |
703 #ifdef USE_PRIMARY_BUFFER | 456 if (this->hidden->num_buffers < 0) { |
704 if (mainwin) { | 457 DSOUND_CloseDevice(this); |
705 NUM_BUFFERS = CreatePrimary(sound, mainwin, &mixbuf, | 458 return 0; |
706 &waveformat, spec->size); | 459 } |
707 } | 460 |
708 #endif /* USE_PRIMARY_BUFFER */ | 461 /* The buffer will auto-start playing in DSOUND_WaitDevice() */ |
709 if (NUM_BUFFERS < 0) { | 462 this->hidden->mixlen = this->spec.size; |
710 NUM_BUFFERS = CreateSecondary(sound, mainwin, &mixbuf, | 463 |
711 &waveformat, spec->size); | 464 return 1; /* good to go. */ |
712 if (NUM_BUFFERS < 0) { | 465 } |
713 return (-1); | 466 |
714 } | 467 |
715 #ifdef DEBUG_SOUND | 468 static void |
716 fprintf(stderr, "Using secondary audio buffer\n"); | 469 DSOUND_Deinitialize(void) |
717 #endif | 470 { |
718 } | 471 DSOUND_Unload(); |
719 #ifdef DEBUG_SOUND | 472 } |
720 else | 473 |
721 fprintf(stderr, "Using primary audio buffer\n"); | 474 |
722 #endif | 475 static int |
723 | 476 DSOUND_Init(SDL_AudioDriverImpl *impl) |
724 /* The buffer will auto-start playing in DX5_WaitAudio() */ | 477 { |
725 lastchunk = 0; | 478 OSVERSIONINFO ver; |
726 mixlen = spec->size; | 479 |
727 | 480 /* |
728 #ifdef USE_POSITION_NOTIFY | 481 * Unfortunately, the sound drivers on NT have higher latencies than the |
729 /* See if we can use DirectX 6 event notification */ | 482 * audio buffers used by many SDL applications, so there are gaps in the |
730 if (CreateAudioEvent(this) == 0) { | 483 * audio - it sounds terrible. Punt for now. |
731 this->WaitAudio = DX6_WaitAudio_EventWait; | 484 */ |
732 } else { | 485 SDL_memset(&ver, '\0', sizeof (OSVERSIONINFO)); |
733 this->WaitAudio = DX5_WaitAudio_BusyWait; | 486 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
734 } | 487 GetVersionEx(&ver); |
735 #endif | 488 if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) |
736 return (0); | 489 if (ver.dwMajorVersion <= 4) { |
737 } | 490 return 0; /* NT4.0 or earlier. Disable dsound support. */ |
491 } | |
492 } | |
493 | |
494 if (!DSOUND_Load()) { | |
495 return 0; | |
496 } | |
497 | |
498 /* Set the function pointers */ | |
499 impl->OpenDevice = DSOUND_OpenDevice; | |
500 impl->PlayDevice = DSOUND_PlayDevice; | |
501 impl->WaitDevice = DSOUND_WaitDevice; | |
502 impl->WaitDone = DSOUND_WaitDone; | |
503 impl->ThreadInit = DSOUND_ThreadInit; | |
504 impl->GetDeviceBuf = DSOUND_GetDeviceBuf; | |
505 impl->CloseDevice = DSOUND_CloseDevice; | |
506 impl->Deinitialize = DSOUND_Deinitialize; | |
507 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME */ | |
508 | |
509 return 1; | |
510 } | |
511 | |
512 AudioBootStrap DSOUND_bootstrap = { | |
513 "dsound", WINDOWS_OS_NAME "DirectSound", DSOUND_Init, 0 | |
514 }; | |
738 | 515 |
739 /* vi: set ts=4 sw=4 expandtab: */ | 516 /* vi: set ts=4 sw=4 expandtab: */ |