Mercurial > sdl-ios-xcode
comparison src/audio/windx5/SDL_dx5audio.c @ 3842:6b04ca2f1016 SDL-ryan-multiple-audio-device
Moved DirectSound audio target to SDL 1.3 API and did some house cleaning.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Tue, 17 Oct 2006 06:37:42 +0000 |
parents | c8b3d3d13ed1 |
children | 66fb40445587 |
comparison
equal
deleted
inserted
replaced
3841:3479f939987c | 3842:6b04ca2f1016 |
---|---|
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 } | 90 |
140 | 91 |
141 static SDL_AudioDevice * | 92 |
142 Audio_CreateDevice(int devindex) | 93 static int |
143 { | 94 DSOUND_Available(void) |
144 SDL_AudioDevice *this; | 95 { |
145 | 96 int dsound_ok = 1; |
146 /* Load DirectX */ | 97 OSVERSIONINFO ver; |
147 if (DX5_Load() < 0) { | 98 |
148 return (NULL); | 99 /* |
149 } | 100 * Unfortunately, the sound drivers on NT have higher latencies than the |
150 | 101 * audio buffers used by many SDL applications, so there are gaps in the |
151 /* Initialize all variables that we clean on shutdown */ | 102 * audio - it sounds terrible. Punt for now. |
152 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); | 103 */ |
153 if (this) { | 104 SDL_memset(&ver, '\0', sizeof (OSVERSIONINFO)); |
154 SDL_memset(this, 0, (sizeof *this)); | 105 ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
155 this->hidden = (struct SDL_PrivateAudioData *) | 106 GetVersionEx(&ver); |
156 SDL_malloc((sizeof *this->hidden)); | 107 if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) |
157 } | 108 if (ver.dwMajorVersion <= 4) { |
158 if ((this == NULL) || (this->hidden == NULL)) { | 109 dsound_ok = 0; /* NT4.0 or earlier. Disable dsound support. */ |
159 SDL_OutOfMemory(); | 110 } |
160 if (this) { | 111 } |
161 SDL_free(this); | 112 |
162 } | 113 if (dsound_ok) { |
163 return (0); | 114 dsound_ok = DSOUND_Load(); /* make sure we really have DX5. */ |
164 } | 115 DSOUND_Unload(); |
165 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | 116 } |
166 | 117 |
167 /* Set the function pointers */ | 118 return (dsound_ok); |
168 this->OpenAudio = DX5_OpenAudio; | 119 } |
169 this->ThreadInit = DX5_ThreadInit; | 120 |
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, 0 | |
184 }; | |
185 | 121 |
186 static void | 122 static void |
187 SetDSerror(const char *function, int code) | 123 SetDSerror(const char *function, int code) |
188 { | 124 { |
189 static const char *error; | 125 static const char *error; |
190 static char errbuf[1024]; | 126 static char errbuf[1024]; |
191 | 127 |
192 errbuf[0] = 0; | 128 errbuf[0] = 0; |
193 switch (code) { | 129 switch (code) { |
194 case E_NOINTERFACE: | 130 case E_NOINTERFACE: |
195 error = | 131 error = "Unsupported interface -- Is DirectX 5.0 or later installed?"; |
196 "Unsupported interface\n-- Is DirectX 5.0 or later installed?"; | |
197 break; | 132 break; |
198 case DSERR_ALLOCATED: | 133 case DSERR_ALLOCATED: |
199 error = "Audio device in use"; | 134 error = "Audio device in use"; |
200 break; | 135 break; |
201 case DSERR_BADFORMAT: | 136 case DSERR_BADFORMAT: |
239 } | 174 } |
240 | 175 |
241 /* DirectSound needs to be associated with a window */ | 176 /* DirectSound needs to be associated with a window */ |
242 static HWND mainwin = NULL; | 177 static HWND mainwin = NULL; |
243 /* */ | 178 /* */ |
179 | |
244 void | 180 void |
245 DX5_SoundFocus(HWND hwnd) | 181 DSOUND_SoundFocus(HWND hwnd) |
246 { | 182 { |
183 /* !!! FIXME: probably broken with multi-window support in SDL 1.3 ... */ | |
247 mainwin = hwnd; | 184 mainwin = hwnd; |
248 } | 185 } |
249 | 186 |
250 static void | 187 static void |
251 DX5_ThreadInit(_THIS) | 188 DSOUND_ThreadInit(_THIS) |
252 { | 189 { |
253 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); | 190 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); |
254 } | 191 } |
255 | 192 |
256 static void | 193 static void |
257 DX5_WaitAudio_BusyWait(_THIS) | 194 DSOUND_WaitDevice(_THIS) |
258 { | 195 { |
259 DWORD status; | 196 DWORD status = 0; |
260 DWORD cursor, junk; | 197 DWORD cursor = 0; |
261 HRESULT result; | 198 DWORD junk = 0; |
199 HRESULT result = DS_OK; | |
262 | 200 |
263 /* Semi-busy wait, since we have no way of getting play notification | 201 /* Semi-busy wait, since we have no way of getting play notification |
264 on a primary mixing buffer located in hardware (DirectX 5.0) | 202 on a primary mixing buffer located in hardware (DirectX 5.0) |
265 */ | 203 */ |
266 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor); | 204 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
205 &junk, &cursor); | |
267 if (result != DS_OK) { | 206 if (result != DS_OK) { |
268 if (result == DSERR_BUFFERLOST) { | 207 if (result == DSERR_BUFFERLOST) { |
269 IDirectSoundBuffer_Restore(mixbuf); | 208 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
270 } | 209 } |
271 #ifdef DEBUG_SOUND | 210 #ifdef DEBUG_SOUND |
272 SetDSerror("DirectSound GetCurrentPosition", result); | 211 SetDSerror("DirectSound GetCurrentPosition", result); |
273 #endif | 212 #endif |
274 return; | 213 return; |
275 } | 214 } |
276 | 215 |
277 while ((cursor / mixlen) == lastchunk) { | 216 while ((cursor / this->hidden->mixlen) == this->hidden->lastchunk) { |
278 /* FIXME: find out how much time is left and sleep that long */ | 217 /* FIXME: find out how much time is left and sleep that long */ |
279 SDL_Delay(1); | 218 SDL_Delay(1); |
280 | 219 |
281 /* Try to restore a lost sound buffer */ | 220 /* Try to restore a lost sound buffer */ |
282 IDirectSoundBuffer_GetStatus(mixbuf, &status); | 221 IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status); |
283 if ((status & DSBSTATUS_BUFFERLOST)) { | 222 if ((status & DSBSTATUS_BUFFERLOST)) { |
284 IDirectSoundBuffer_Restore(mixbuf); | 223 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
285 IDirectSoundBuffer_GetStatus(mixbuf, &status); | 224 IDirectSoundBuffer_GetStatus(this->hidden->mixbuf, &status); |
286 if ((status & DSBSTATUS_BUFFERLOST)) { | 225 if ((status & DSBSTATUS_BUFFERLOST)) { |
287 break; | 226 break; |
288 } | 227 } |
289 } | 228 } |
290 if (!(status & DSBSTATUS_PLAYING)) { | 229 if (!(status & DSBSTATUS_PLAYING)) { |
291 result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING); | 230 result = IDirectSoundBuffer_Play(this->hidden->mixbuf, 0, 0, |
231 DSBPLAY_LOOPING); | |
292 if (result == DS_OK) { | 232 if (result == DS_OK) { |
293 continue; | 233 continue; |
294 } | 234 } |
295 #ifdef DEBUG_SOUND | 235 #ifdef DEBUG_SOUND |
296 SetDSerror("DirectSound Play", result); | 236 SetDSerror("DirectSound Play", result); |
297 #endif | 237 #endif |
298 return; | 238 return; |
299 } | 239 } |
300 | 240 |
301 /* Find out where we are playing */ | 241 /* Find out where we are playing */ |
302 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, | 242 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
303 &junk, &cursor); | 243 &junk, &cursor); |
304 if (result != DS_OK) { | 244 if (result != DS_OK) { |
305 SetDSerror("DirectSound GetCurrentPosition", result); | 245 SetDSerror("DirectSound GetCurrentPosition", result); |
306 return; | 246 return; |
307 } | 247 } |
308 } | 248 } |
309 } | 249 } |
310 | 250 |
311 #ifdef USE_POSITION_NOTIFY | 251 static void |
312 static void | 252 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 { | 253 { |
343 /* Unlock the buffer, allowing it to play */ | 254 /* Unlock the buffer, allowing it to play */ |
344 if (locked_buf) { | 255 if (this->hidden->locked_buf) { |
345 IDirectSoundBuffer_Unlock(mixbuf, locked_buf, mixlen, NULL, 0); | 256 IDirectSoundBuffer_Unlock(this->hidden->mixbuf, |
257 this->hidden->locked_buf, | |
258 this->hidden->mixlen, NULL, 0); | |
346 } | 259 } |
347 | 260 |
348 } | 261 } |
349 | 262 |
350 static Uint8 * | 263 static Uint8 * |
351 DX5_GetAudioBuf(_THIS) | 264 DSOUND_GetDeviceBuf(_THIS) |
352 { | 265 { |
353 DWORD cursor, junk; | 266 DWORD cursor = 0; |
354 HRESULT result; | 267 DWORD junk = 0; |
355 DWORD rawlen; | 268 HRESULT result = DS_OK; |
269 DWORD rawlen = 0; | |
356 | 270 |
357 /* Figure out which blocks to fill next */ | 271 /* Figure out which blocks to fill next */ |
358 locked_buf = NULL; | 272 this->hidden->locked_buf = NULL; |
359 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor); | 273 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
274 &junk, &cursor); | |
360 if (result == DSERR_BUFFERLOST) { | 275 if (result == DSERR_BUFFERLOST) { |
361 IDirectSoundBuffer_Restore(mixbuf); | 276 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
362 result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, | 277 result = IDirectSoundBuffer_GetCurrentPosition(this->hidden->mixbuf, |
363 &junk, &cursor); | 278 &junk, &cursor); |
364 } | 279 } |
365 if (result != DS_OK) { | 280 if (result != DS_OK) { |
366 SetDSerror("DirectSound GetCurrentPosition", result); | 281 SetDSerror("DirectSound GetCurrentPosition", result); |
367 return (NULL); | 282 return (NULL); |
368 } | 283 } |
369 cursor /= mixlen; | 284 cursor /= this->hidden->mixlen; |
370 #ifdef DEBUG_SOUND | 285 #ifdef DEBUG_SOUND |
371 /* Detect audio dropouts */ | 286 /* Detect audio dropouts */ |
372 { | 287 { |
373 DWORD spot = cursor; | 288 DWORD spot = cursor; |
374 if (spot < lastchunk) { | 289 if (spot < this->hidden->lastchunk) { |
375 spot += NUM_BUFFERS; | 290 spot += this->hidden->num_buffers; |
376 } | 291 } |
377 if (spot > lastchunk + 1) { | 292 if (spot > this->hidden->lastchunk + 1) { |
378 fprintf(stderr, "Audio dropout, missed %d fragments\n", | 293 fprintf(stderr, "Audio dropout, missed %d fragments\n", |
379 (spot - (lastchunk + 1))); | 294 (spot - (this->hidden->lastchunk + 1))); |
380 } | 295 } |
381 } | 296 } |
382 #endif | 297 #endif |
383 lastchunk = cursor; | 298 this->hidden->lastchunk = cursor; |
384 cursor = (cursor + 1) % NUM_BUFFERS; | 299 cursor = (cursor + 1) % this->hidden->num_buffers; |
385 cursor *= mixlen; | 300 cursor *= this->hidden->mixlen; |
386 | 301 |
387 /* Lock the audio buffer */ | 302 /* Lock the audio buffer */ |
388 result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen, | 303 result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor, |
389 (LPVOID *) & locked_buf, &rawlen, NULL, | 304 this->hidden->mixlen, |
390 &junk, 0); | 305 (LPVOID *) &this->hidden->locked_buf, |
306 &rawlen, NULL, &junk, 0); | |
391 if (result == DSERR_BUFFERLOST) { | 307 if (result == DSERR_BUFFERLOST) { |
392 IDirectSoundBuffer_Restore(mixbuf); | 308 IDirectSoundBuffer_Restore(this->hidden->mixbuf); |
393 result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen, | 309 result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor, |
394 (LPVOID *) & locked_buf, &rawlen, | 310 this->hidden->mixlen, |
395 NULL, &junk, 0); | 311 (LPVOID *) &this->hidden->locked_buf, |
312 &rawlen, NULL, &junk, 0); | |
396 } | 313 } |
397 if (result != DS_OK) { | 314 if (result != DS_OK) { |
398 SetDSerror("DirectSound Lock", result); | 315 SetDSerror("DirectSound Lock", result); |
399 return (NULL); | 316 return (NULL); |
400 } | 317 } |
401 return (locked_buf); | 318 return (this->hidden->locked_buf); |
402 } | 319 } |
403 | 320 |
404 static void | 321 static void |
405 DX5_WaitDone(_THIS) | 322 DSOUND_WaitDone(_THIS) |
406 { | 323 { |
407 Uint8 *stream; | 324 Uint8 *stream = DSOUND_GetDeviceBuf(this); |
408 | 325 |
409 /* Wait for the playing chunk to finish */ | 326 /* Wait for the playing chunk to finish */ |
410 stream = this->GetAudioBuf(this); | |
411 if (stream != NULL) { | 327 if (stream != NULL) { |
412 SDL_memset(stream, silence, mixlen); | 328 SDL_memset(stream, this->spec.silence, this->hidden->mixlen); |
413 this->PlayAudio(this); | 329 DSOUND_PlayDevice(this); |
414 } | 330 } |
415 this->WaitAudio(this); | 331 DSOUND_WaitDevice(this); |
416 | 332 |
417 /* Stop the looping sound buffer */ | 333 /* Stop the looping sound buffer */ |
418 IDirectSoundBuffer_Stop(mixbuf); | 334 IDirectSoundBuffer_Stop(this->hidden->mixbuf); |
419 } | 335 } |
420 | 336 |
421 static void | 337 static void |
422 DX5_CloseAudio(_THIS) | 338 DSOUND_CloseDevice(_THIS) |
423 { | 339 { |
424 if (sound != NULL) { | 340 if (this->hidden != NULL) { |
425 if (mixbuf != NULL) { | 341 if (this->hidden->sound != NULL) { |
426 /* Clean up the audio buffer */ | 342 if (this->hidden->mixbuf != NULL) { |
427 IDirectSoundBuffer_Release(mixbuf); | 343 /* Clean up the audio buffer */ |
428 mixbuf = NULL; | 344 IDirectSoundBuffer_Release(this->hidden->mixbuf); |
429 } | 345 this->hidden->mixbuf = NULL; |
430 if (audio_event != NULL) { | 346 } |
431 CloseHandle(audio_event); | 347 IDirectSound_Release(this->hidden->sound); |
432 audio_event = NULL; | 348 this->hidden->sound = NULL; |
433 } | 349 } |
434 IDirectSound_Release(sound); | 350 |
435 sound = NULL; | 351 SDL_free(this->hidden); |
436 } | 352 this->hidden = NULL; |
437 } | 353 } |
438 | 354 } |
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 | 355 |
517 /* This function tries to create a secondary audio buffer, and returns the | 356 /* This function tries to create a secondary audio buffer, and returns the |
518 number of audio chunks available in the created buffer. | 357 number of audio chunks available in the created buffer. |
519 */ | 358 */ |
520 static int | 359 static int |
521 CreateSecondary(LPDIRECTSOUND sndObj, HWND focus, | 360 CreateSecondary(_THIS, HWND focus, WAVEFORMATEX *wavefmt) |
522 LPDIRECTSOUNDBUFFER * sndbuf, WAVEFORMATEX * wavefmt, | 361 { |
523 Uint32 chunksize) | 362 LPDIRECTSOUND sndObj = this->hidden->sound; |
524 { | 363 LPDIRECTSOUNDBUFFER *sndbuf = this->hidden->mixbuf; |
364 Uint32 chunksize = this->spec.size; | |
525 const int numchunks = 8; | 365 const int numchunks = 8; |
526 HRESULT result; | 366 HRESULT result = DS_OK; |
527 DSBUFFERDESC format; | 367 DSBUFFERDESC format; |
528 LPVOID pvAudioPtr1, pvAudioPtr2; | 368 LPVOID pvAudioPtr1, pvAudioPtr2; |
529 DWORD dwAudioBytes1, dwAudioBytes2; | 369 DWORD dwAudioBytes1, dwAudioBytes2; |
530 | 370 |
531 /* Try to set primary mixing privileges */ | 371 /* Try to set primary mixing privileges */ |
536 result = IDirectSound_SetCooperativeLevel(sndObj, | 376 result = IDirectSound_SetCooperativeLevel(sndObj, |
537 GetDesktopWindow(), | 377 GetDesktopWindow(), |
538 DSSCL_NORMAL); | 378 DSSCL_NORMAL); |
539 } | 379 } |
540 if (result != DS_OK) { | 380 if (result != DS_OK) { |
541 #ifdef DEBUG_SOUND | |
542 SetDSerror("DirectSound SetCooperativeLevel", result); | 381 SetDSerror("DirectSound SetCooperativeLevel", result); |
543 #endif | |
544 return (-1); | 382 return (-1); |
545 } | 383 } |
546 | 384 |
547 /* Try to create the secondary buffer */ | 385 /* Try to create the secondary buffer */ |
548 SDL_memset(&format, 0, sizeof(format)); | 386 SDL_memset(&format, 0, sizeof(format)); |
549 format.dwSize = sizeof(format); | 387 format.dwSize = sizeof(format); |
550 format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; | 388 format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; |
551 #ifdef USE_POSITION_NOTIFY | |
552 format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; | |
553 #endif | |
554 if (!focus) { | 389 if (!focus) { |
555 format.dwFlags |= DSBCAPS_GLOBALFOCUS; | 390 format.dwFlags |= DSBCAPS_GLOBALFOCUS; |
556 } else { | 391 } else { |
557 format.dwFlags |= DSBCAPS_STICKYFOCUS; | 392 format.dwFlags |= DSBCAPS_STICKYFOCUS; |
558 } | 393 } |
576 result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, | 411 result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, |
577 (LPVOID *) & pvAudioPtr1, &dwAudioBytes1, | 412 (LPVOID *) & pvAudioPtr1, &dwAudioBytes1, |
578 (LPVOID *) & pvAudioPtr2, &dwAudioBytes2, | 413 (LPVOID *) & pvAudioPtr2, &dwAudioBytes2, |
579 DSBLOCK_ENTIREBUFFER); | 414 DSBLOCK_ENTIREBUFFER); |
580 if (result == DS_OK) { | 415 if (result == DS_OK) { |
581 if (wavefmt->wBitsPerSample == 8) { | 416 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, | 417 IDirectSoundBuffer_Unlock(*sndbuf, |
587 (LPVOID) pvAudioPtr1, dwAudioBytes1, | 418 (LPVOID) pvAudioPtr1, dwAudioBytes1, |
588 (LPVOID) pvAudioPtr2, dwAudioBytes2); | 419 (LPVOID) pvAudioPtr2, dwAudioBytes2); |
589 } | 420 } |
590 | 421 |
591 /* We're ready to go */ | 422 /* We're ready to go */ |
592 return (numchunks); | 423 return (numchunks); |
593 } | 424 } |
594 | 425 |
595 /* This function tries to set position notify events on the mixing buffer */ | |
596 #ifdef USE_POSITION_NOTIFY | |
597 static int | 426 static int |
598 CreateAudioEvent(_THIS) | 427 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 { | 428 { |
653 HRESULT result; | 429 HRESULT result; |
654 WAVEFORMATEX waveformat; | 430 WAVEFORMATEX waveformat; |
655 | 431 int valid_format = 0; |
656 /* Set basic WAVE format parameters */ | 432 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); |
433 | |
434 /* !!! FIXME: handle devname */ | |
435 /* !!! FIXME: handle iscapture */ | |
436 | |
437 /* Initialize all variables that we clean on shutdown */ | |
438 this->hidden = (struct SDL_PrivateAudioData *) | |
439 SDL_malloc((sizeof *this->hidden)); | |
440 if (this->hidden == NULL) { | |
441 SDL_OutOfMemory(); | |
442 return 0; | |
443 } | |
444 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
445 | |
446 while ((!valid_format) && (test_format)) { | |
447 switch (test_format) { | |
448 case AUDIO_U8: | |
449 case AUDIO_S16: | |
450 case AUDIO_S32: | |
451 this->spec.format = test_format; | |
452 valid_format = 1; | |
453 break; | |
454 } | |
455 test_format = SDL_NextAudioFormat(); | |
456 } | |
457 | |
458 if (!valid_format) { | |
459 DSOUND_CloseDevice(this); | |
460 SDL_SetError("DirectSound: Unsupported audio format"); | |
461 return 0; | |
462 } | |
463 | |
657 SDL_memset(&waveformat, 0, sizeof(waveformat)); | 464 SDL_memset(&waveformat, 0, sizeof(waveformat)); |
658 waveformat.wFormatTag = WAVE_FORMAT_PCM; | 465 waveformat.wFormatTag = WAVE_FORMAT_PCM; |
659 | 466 waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format); |
660 /* Determine the audio parameters from the AudioSpec */ | 467 waveformat.nChannels = this->spec.channels; |
661 switch (SDL_AUDIO_BITSIZE(spec->format)) { | 468 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 = | 469 waveformat.nBlockAlign = |
687 waveformat.nChannels * (waveformat.wBitsPerSample / 8); | 470 waveformat.nChannels * (waveformat.wBitsPerSample / 8); |
688 waveformat.nAvgBytesPerSec = | 471 waveformat.nAvgBytesPerSec = |
689 waveformat.nSamplesPerSec * waveformat.nBlockAlign; | 472 waveformat.nSamplesPerSec * waveformat.nBlockAlign; |
690 | 473 |
691 /* Update the fragment size as size in bytes */ | 474 /* Update the fragment size as size in bytes */ |
692 SDL_CalculateAudioSpec(spec); | 475 SDL_CalculateAudioSpec(&this->spec); |
693 | 476 |
694 /* Open the audio device */ | 477 /* Open the audio device */ |
695 result = DSoundCreate(NULL, &sound, NULL); | 478 result = DSoundCreate(NULL, &this->hidden->sound, NULL); |
696 if (result != DS_OK) { | 479 if (result != DS_OK) { |
480 DSOUND_CloseDevice(this); | |
697 SetDSerror("DirectSoundCreate", result); | 481 SetDSerror("DirectSoundCreate", result); |
698 return (-1); | 482 return 0; |
699 } | 483 } |
700 | 484 |
701 /* Create the audio buffer to which we write */ | 485 /* Create the audio buffer to which we write */ |
702 NUM_BUFFERS = -1; | 486 this->hidden->num_buffers = CreateSecondary(this, mainwin, &waveformat); |
703 #ifdef USE_PRIMARY_BUFFER | 487 if (this->hidden->num_buffers < 0) { |
704 if (mainwin) { | 488 DSOUND_CloseDevice(this); |
705 NUM_BUFFERS = CreatePrimary(sound, mainwin, &mixbuf, | 489 return 0; |
706 &waveformat, spec->size); | 490 } |
707 } | 491 |
708 #endif /* USE_PRIMARY_BUFFER */ | 492 /* The buffer will auto-start playing in DSOUND_WaitDevice() */ |
709 if (NUM_BUFFERS < 0) { | 493 this->hidden->mixlen = this->spec.size; |
710 NUM_BUFFERS = CreateSecondary(sound, mainwin, &mixbuf, | 494 |
711 &waveformat, spec->size); | 495 return 1; /* good to go. */ |
712 if (NUM_BUFFERS < 0) { | 496 } |
713 return (-1); | 497 |
714 } | 498 |
715 #ifdef DEBUG_SOUND | 499 static void |
716 fprintf(stderr, "Using secondary audio buffer\n"); | 500 DSOUND_Deinitialize(void) |
717 #endif | 501 { |
718 } | 502 DSOUND_Unload(); |
719 #ifdef DEBUG_SOUND | 503 } |
720 else | 504 |
721 fprintf(stderr, "Using primary audio buffer\n"); | 505 |
722 #endif | 506 static int |
723 | 507 DSOUND_Init(SDL_AudioDriverImpl *impl) |
724 /* The buffer will auto-start playing in DX5_WaitAudio() */ | 508 { |
725 lastchunk = 0; | 509 /* Load DirectX */ |
726 mixlen = spec->size; | 510 if (DSOUND_Load() < 0) { |
727 | 511 return 0; |
728 #ifdef USE_POSITION_NOTIFY | 512 } |
729 /* See if we can use DirectX 6 event notification */ | 513 |
730 if (CreateAudioEvent(this) == 0) { | 514 /* Set the function pointers */ |
731 this->WaitAudio = DX6_WaitAudio_EventWait; | 515 impl->OpenDevice = DSOUND_OpenDevice; |
732 } else { | 516 impl->PlayDevice = DSOUND_PlayDevice; |
733 this->WaitAudio = DX5_WaitAudio_BusyWait; | 517 impl->WaitDevice = DSOUND_WaitDevice; |
734 } | 518 impl->WaitDone = DSOUND_WaitDone; |
735 #endif | 519 impl->ThreadInit = DSOUND_ThreadInit; |
736 return (0); | 520 impl->GetDeviceBuf = DSOUND_GetDeviceBuf; |
737 } | 521 impl->CloseDevice = DSOUND_CloseDevice; |
522 impl->Deinitialize = DSOUND_Deinitialize; | |
523 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME */ | |
524 | |
525 return 1; | |
526 } | |
527 | |
528 AudioBootStrap DSOUND_bootstrap = { | |
529 "dsound", WINDOWS_OS_NAME "DirectSound", | |
530 DSOUND_Available, DSOUND_Init, 0 | |
531 }; | |
738 | 532 |
739 /* vi: set ts=4 sw=4 expandtab: */ | 533 /* vi: set ts=4 sw=4 expandtab: */ |