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