Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5131 clevermous 1
/*
2
Copyright (C) 1996-1997 Id Software, Inc.
3
 
4
This program is free software; you can redistribute it and/or
5
modify it under the terms of the GNU General Public License
6
as published by the Free Software Foundation; either version 2
7
of the License, or (at your option) any later version.
8
 
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
 
13
See the GNU General Public License for more details.
14
 
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
 
19
*/
20
#include "quakedef.h"
21
#include "winquake.h"
22
 
23
#define iDirectSoundCreate(a,b,c)	pDirectSoundCreate(a,b,c)
24
 
25
HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
26
 
27
// 64K is > 1 second at 16-bit, 22050 Hz
28
#define	WAV_BUFFERS				64
29
#define	WAV_MASK				0x3F
30
#define	WAV_BUFFER_SIZE			0x0400
31
#define SECONDARY_BUFFER_SIZE	0x10000
32
 
33
typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
34
 
35
static qboolean	wavonly;
36
static qboolean	dsound_init;
37
static qboolean	wav_init;
38
static qboolean	snd_firsttime = true, snd_isdirect, snd_iswave;
39
static qboolean	primary_format_set;
40
 
41
static int	sample16;
42
static int	snd_sent, snd_completed;
43
 
44
 
45
/*
46
 * Global variables. Must be visible to window-procedure function
47
 *  so it can unlock and free the data block after it has been played.
48
 */
49
 
50
HANDLE		hData;
51
HPSTR		lpData, lpData2;
52
 
53
HGLOBAL		hWaveHdr;
54
LPWAVEHDR	lpWaveHdr;
55
 
56
HWAVEOUT    hWaveOut;
57
 
58
WAVEOUTCAPS	wavecaps;
59
 
60
DWORD	gSndBufSize;
61
 
62
MMTIME		mmstarttime;
63
 
64
LPDIRECTSOUND pDS;
65
LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
66
 
67
HINSTANCE hInstDS;
68
 
69
qboolean SNDDMA_InitDirect (void);
70
qboolean SNDDMA_InitWav (void);
71
 
72
 
73
/*
74
==================
75
S_BlockSound
76
==================
77
*/
78
void S_BlockSound (void)
79
{
80
 
81
// DirectSound takes care of blocking itself
82
	if (snd_iswave)
83
	{
84
		snd_blocked++;
85
 
86
		if (snd_blocked == 1)
87
		{
88
			waveOutReset (hWaveOut);
89
		}
90
	}
91
}
92
 
93
 
94
/*
95
==================
96
S_UnblockSound
97
==================
98
*/
99
void S_UnblockSound (void)
100
{
101
 
102
// DirectSound takes care of blocking itself
103
	if (snd_iswave)
104
	{
105
		snd_blocked--;
106
	}
107
}
108
 
109
 
110
/*
111
==================
112
FreeSound
113
==================
114
*/
115
void FreeSound (void)
116
{
117
	int		i;
118
 
119
	if (pDSBuf)
120
	{
121
		pDSBuf->lpVtbl->Stop(pDSBuf);
122
		pDSBuf->lpVtbl->Release(pDSBuf);
123
	}
124
 
125
// only release primary buffer if it's not also the mixing buffer we just released
126
	if (pDSPBuf && (pDSBuf != pDSPBuf))
127
	{
128
		pDSPBuf->lpVtbl->Release(pDSPBuf);
129
	}
130
 
131
	if (pDS)
132
	{
133
		pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
134
		pDS->lpVtbl->Release(pDS);
135
	}
136
 
137
	if (hWaveOut)
138
	{
139
		waveOutReset (hWaveOut);
140
 
141
		if (lpWaveHdr)
142
		{
143
			for (i=0 ; i< WAV_BUFFERS ; i++)
144
				waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
145
		}
146
 
147
		waveOutClose (hWaveOut);
148
 
149
		if (hWaveHdr)
150
		{
151
			GlobalUnlock(hWaveHdr);
152
			GlobalFree(hWaveHdr);
153
		}
154
 
155
		if (hData)
156
		{
157
			GlobalUnlock(hData);
158
			GlobalFree(hData);
159
		}
160
 
161
	}
162
 
163
	pDS = NULL;
164
	pDSBuf = NULL;
165
	pDSPBuf = NULL;
166
	hWaveOut = 0;
167
	hData = 0;
168
	hWaveHdr = 0;
169
	lpData = NULL;
170
	lpWaveHdr = NULL;
171
	dsound_init = false;
172
	wav_init = false;
173
}
174
 
175
 
176
/*
177
==================
178
SNDDMA_InitDirect
179
 
180
Direct-Sound support
181
==================
182
*/
183
sndinitstat SNDDMA_InitDirect (void)
184
{
185
	DSBUFFERDESC	dsbuf;
186
	DSBCAPS			dsbcaps;
187
	DWORD			dwSize, dwWrite;
188
	DSCAPS			dscaps;
189
	WAVEFORMATEX	format, pformat;
190
	HRESULT			hresult;
191
	int				reps;
192
 
193
	memset ((void *)&sn, 0, sizeof (sn));
194
 
195
	shm = &sn;
196
 
197
	shm->channels = 2;
198
	shm->samplebits = 16;
199
	shm->speed = 11025;
200
 
201
	memset (&format, 0, sizeof(format));
202
	format.wFormatTag = WAVE_FORMAT_PCM;
203
    format.nChannels = shm->channels;
204
    format.wBitsPerSample = shm->samplebits;
205
    format.nSamplesPerSec = shm->speed;
206
    format.nBlockAlign = format.nChannels
207
		*format.wBitsPerSample / 8;
208
    format.cbSize = 0;
209
    format.nAvgBytesPerSec = format.nSamplesPerSec
210
		*format.nBlockAlign;
211
 
212
	if (!hInstDS)
213
	{
214
		hInstDS = LoadLibrary("dsound.dll");
215
 
216
		if (hInstDS == NULL)
217
		{
218
			Con_SafePrintf ("Couldn't load dsound.dll\n");
219
			return SIS_FAILURE;
220
		}
221
 
222
		pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
223
 
224
		if (!pDirectSoundCreate)
225
		{
226
			Con_SafePrintf ("Couldn't get DS proc addr\n");
227
			return SIS_FAILURE;
228
		}
229
	}
230
 
231
	while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
232
	{
233
		if (hresult != DSERR_ALLOCATED)
234
		{
235
			Con_SafePrintf ("DirectSound create failed\n");
236
			return SIS_FAILURE;
237
		}
238
 
239
		if (MessageBox (NULL,
240
						"The sound hardware is in use by another app.\n\n"
241
					    "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
242
						"Sound not available",
243
						MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
244
		{
245
			Con_SafePrintf ("DirectSoundCreate failure\n"
246
							"  hardware already in use\n");
247
			return SIS_NOTAVAIL;
248
		}
249
	}
250
 
251
	dscaps.dwSize = sizeof(dscaps);
252
 
253
	if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps))
254
	{
255
		Con_SafePrintf ("Couldn't get DS caps\n");
256
	}
257
 
258
	if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
259
	{
260
		Con_SafePrintf ("No DirectSound driver installed\n");
261
		FreeSound ();
262
		return SIS_FAILURE;
263
	}
264
 
265
	if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
266
	{
267
		Con_SafePrintf ("Set coop level failed\n");
268
		FreeSound ();
269
		return SIS_FAILURE;
270
	}
271
 
272
// get access to the primary buffer, if possible, so we can set the
273
// sound hardware format
274
	memset (&dsbuf, 0, sizeof(dsbuf));
275
	dsbuf.dwSize = sizeof(DSBUFFERDESC);
276
	dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
277
	dsbuf.dwBufferBytes = 0;
278
	dsbuf.lpwfxFormat = NULL;
279
 
280
	memset(&dsbcaps, 0, sizeof(dsbcaps));
281
	dsbcaps.dwSize = sizeof(dsbcaps);
282
	primary_format_set = false;
283
 
284
	if (!COM_CheckParm ("-snoforceformat"))
285
	{
286
		if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
287
		{
288
			pformat = format;
289
 
290
			if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
291
			{
292
				if (snd_firsttime)
293
					Con_SafePrintf ("Set primary sound buffer format: no\n");
294
			}
295
			else
296
			{
297
				if (snd_firsttime)
298
					Con_SafePrintf ("Set primary sound buffer format: yes\n");
299
 
300
				primary_format_set = true;
301
			}
302
		}
303
	}
304
 
305
	if (!primary_format_set || !COM_CheckParm ("-primarysound"))
306
	{
307
	// create the secondary buffer we'll actually work with
308
		memset (&dsbuf, 0, sizeof(dsbuf));
309
		dsbuf.dwSize = sizeof(DSBUFFERDESC);
310
		dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
311
		dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
312
		dsbuf.lpwfxFormat = &format;
313
 
314
		memset(&dsbcaps, 0, sizeof(dsbcaps));
315
		dsbcaps.dwSize = sizeof(dsbcaps);
316
 
317
		if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
318
		{
319
			Con_SafePrintf ("DS:CreateSoundBuffer Failed");
320
			FreeSound ();
321
			return SIS_FAILURE;
322
		}
323
 
324
		shm->channels = format.nChannels;
325
		shm->samplebits = format.wBitsPerSample;
326
		shm->speed = format.nSamplesPerSec;
327
 
328
		if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
329
		{
330
			Con_SafePrintf ("DS:GetCaps failed\n");
331
			FreeSound ();
332
			return SIS_FAILURE;
333
		}
334
 
335
		if (snd_firsttime)
336
			Con_SafePrintf ("Using secondary sound buffer\n");
337
	}
338
	else
339
	{
340
		if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
341
		{
342
			Con_SafePrintf ("Set coop level failed\n");
343
			FreeSound ();
344
			return SIS_FAILURE;
345
		}
346
 
347
		if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
348
		{
349
			Con_Printf ("DS:GetCaps failed\n");
350
			return SIS_FAILURE;
351
		}
352
 
353
		pDSBuf = pDSPBuf;
354
		Con_SafePrintf ("Using primary sound buffer\n");
355
	}
356
 
357
	// Make sure mixer is active
358
	pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
359
 
360
	if (snd_firsttime)
361
		Con_SafePrintf("   %d channel(s)\n"
362
		               "   %d bits/sample\n"
363
					   "   %d bytes/sec\n",
364
					   shm->channels, shm->samplebits, shm->speed);
365
 
366
	gSndBufSize = dsbcaps.dwBufferBytes;
367
 
368
// initialize the buffer
369
	reps = 0;
370
 
371
	while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
372
	{
373
		if (hresult != DSERR_BUFFERLOST)
374
		{
375
			Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
376
			FreeSound ();
377
			return SIS_FAILURE;
378
		}
379
 
380
		if (++reps > 10000)
381
		{
382
			Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
383
			FreeSound ();
384
			return SIS_FAILURE;
385
		}
386
 
387
	}
388
 
389
	memset(lpData, 0, dwSize);
390
//		lpData[4] = lpData[5] = 0x7f;	// force a pop for debugging
391
 
392
	pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
393
 
394
	/* we don't want anyone to access the buffer directly w/o locking it first. */
395
	lpData = NULL;
396
 
397
	pDSBuf->lpVtbl->Stop(pDSBuf);
398
	pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
399
	pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
400
 
401
	shm->soundalive = true;
402
	shm->splitbuffer = false;
403
	shm->samples = gSndBufSize/(shm->samplebits/8);
404
	shm->samplepos = 0;
405
	shm->submission_chunk = 1;
406
	shm->buffer = (unsigned char *) lpData;
407
	sample16 = (shm->samplebits/8) - 1;
408
 
409
	dsound_init = true;
410
 
411
	return SIS_SUCCESS;
412
}
413
 
414
 
415
/*
416
==================
417
SNDDM_InitWav
418
 
419
Crappy windows multimedia base
420
==================
421
*/
422
qboolean SNDDMA_InitWav (void)
423
{
424
	WAVEFORMATEX  format;
425
	int				i;
426
	HRESULT			hr;
427
 
428
	snd_sent = 0;
429
	snd_completed = 0;
430
 
431
	shm = &sn;
432
 
433
	shm->channels = 2;
434
	shm->samplebits = 16;
435
	shm->speed = 11025;
436
 
437
	memset (&format, 0, sizeof(format));
438
	format.wFormatTag = WAVE_FORMAT_PCM;
439
	format.nChannels = shm->channels;
440
	format.wBitsPerSample = shm->samplebits;
441
	format.nSamplesPerSec = shm->speed;
442
	format.nBlockAlign = format.nChannels
443
		*format.wBitsPerSample / 8;
444
	format.cbSize = 0;
445
	format.nAvgBytesPerSec = format.nSamplesPerSec
446
		*format.nBlockAlign;
447
 
448
	/* Open a waveform device for output using window callback. */
449
	while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
450
					&format,
451
					0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
452
	{
453
		if (hr != MMSYSERR_ALLOCATED)
454
		{
455
			Con_SafePrintf ("waveOutOpen failed\n");
456
			return false;
457
		}
458
 
459
		if (MessageBox (NULL,
460
						"The sound hardware is in use by another app.\n\n"
461
					    "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
462
						"Sound not available",
463
						MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
464
		{
465
			Con_SafePrintf ("waveOutOpen failure;\n"
466
							"  hardware already in use\n");
467
			return false;
468
		}
469
	}
470
 
471
	/*
472
	 * Allocate and lock memory for the waveform data. The memory
473
	 * for waveform data must be globally allocated with
474
	 * GMEM_MOVEABLE and GMEM_SHARE flags.
475
 
476
	*/
477
	gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
478
	hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
479
	if (!hData)
480
	{
481
		Con_SafePrintf ("Sound: Out of memory.\n");
482
		FreeSound ();
483
		return false;
484
	}
485
	lpData = GlobalLock(hData);
486
	if (!lpData)
487
	{
488
		Con_SafePrintf ("Sound: Failed to lock.\n");
489
		FreeSound ();
490
		return false;
491
	}
492
	memset (lpData, 0, gSndBufSize);
493
 
494
	/*
495
	 * Allocate and lock memory for the header. This memory must
496
	 * also be globally allocated with GMEM_MOVEABLE and
497
	 * GMEM_SHARE flags.
498
	 */
499
	hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
500
		(DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
501
 
502
	if (hWaveHdr == NULL)
503
	{
504
		Con_SafePrintf ("Sound: Failed to Alloc header.\n");
505
		FreeSound ();
506
		return false;
507
	}
508
 
509
	lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
510
 
511
	if (lpWaveHdr == NULL)
512
	{
513
		Con_SafePrintf ("Sound: Failed to lock header.\n");
514
		FreeSound ();
515
		return false;
516
	}
517
 
518
	memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
519
 
520
	/* After allocation, set up and prepare headers. */
521
	for (i=0 ; i
522
	{
523
		lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
524
		lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
525
 
526
		if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
527
				MMSYSERR_NOERROR)
528
		{
529
			Con_SafePrintf ("Sound: failed to prepare wave headers\n");
530
			FreeSound ();
531
			return false;
532
		}
533
	}
534
 
535
	shm->soundalive = true;
536
	shm->splitbuffer = false;
537
	shm->samples = gSndBufSize/(shm->samplebits/8);
538
	shm->samplepos = 0;
539
	shm->submission_chunk = 1;
540
	shm->buffer = (unsigned char *) lpData;
541
	sample16 = (shm->samplebits/8) - 1;
542
 
543
	wav_init = true;
544
 
545
	return true;
546
}
547
 
548
/*
549
==================
550
SNDDMA_Init
551
 
552
Try to find a sound device to mix for.
553
Returns false if nothing is found.
554
==================
555
*/
556
 
557
int SNDDMA_Init(void)
558
{
559
	sndinitstat	stat;
560
 
561
	if (COM_CheckParm ("-wavonly"))
562
		wavonly = true;
563
 
564
	dsound_init = wav_init = 0;
565
 
566
	stat = SIS_FAILURE;	// assume DirectSound won't initialize
567
 
568
	/* Init DirectSound */
569
	if (!wavonly)
570
	{
571
		if (snd_firsttime || snd_isdirect)
572
		{
573
			stat = SNDDMA_InitDirect ();;
574
 
575
			if (stat == SIS_SUCCESS)
576
			{
577
				snd_isdirect = true;
578
 
579
				if (snd_firsttime)
580
					Con_SafePrintf ("DirectSound initialized\n");
581
			}
582
			else
583
			{
584
				snd_isdirect = false;
585
				Con_SafePrintf ("DirectSound failed to init\n");
586
			}
587
		}
588
	}
589
 
590
// if DirectSound didn't succeed in initializing, try to initialize
591
// waveOut sound, unless DirectSound failed because the hardware is
592
// already allocated (in which case the user has already chosen not
593
// to have sound)
594
	if (!dsound_init && (stat != SIS_NOTAVAIL))
595
	{
596
		if (snd_firsttime || snd_iswave)
597
		{
598
 
599
			snd_iswave = SNDDMA_InitWav ();
600
 
601
			if (snd_iswave)
602
			{
603
				if (snd_firsttime)
604
					Con_SafePrintf ("Wave sound initialized\n");
605
			}
606
			else
607
			{
608
				Con_SafePrintf ("Wave sound failed to init\n");
609
			}
610
		}
611
	}
612
 
613
	snd_firsttime = false;
614
 
615
	if (!dsound_init && !wav_init)
616
	{
617
		if (snd_firsttime)
618
			Con_SafePrintf ("No sound device initialized\n");
619
 
620
		return 0;
621
	}
622
 
623
	return 1;
624
}
625
 
626
/*
627
==============
628
SNDDMA_GetDMAPos
629
 
630
return the current sample position (in mono samples read)
631
inside the recirculating dma buffer, so the mixing code will know
632
how many sample are required to fill it up.
633
===============
634
*/
635
int SNDDMA_GetDMAPos(void)
636
{
637
	MMTIME	mmtime;
638
	int		s;
639
	DWORD	dwWrite;
640
 
641
	if (dsound_init)
642
	{
643
		mmtime.wType = TIME_SAMPLES;
644
		pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
645
		s = mmtime.u.sample - mmstarttime.u.sample;
646
	}
647
	else if (wav_init)
648
	{
649
		s = snd_sent * WAV_BUFFER_SIZE;
650
	}
651
 
652
 
653
	s >>= sample16;
654
 
655
	s &= (shm->samples-1);
656
 
657
	return s;
658
}
659
 
660
/*
661
==============
662
SNDDMA_Submit
663
 
664
Send sound to device if buffer isn't really the dma buffer
665
===============
666
*/
667
void SNDDMA_Submit(void)
668
{
669
	LPWAVEHDR	h;
670
	int			wResult;
671
 
672
	if (!wav_init)
673
		return;
674
 
675
	//
676
	// find which sound blocks have completed
677
	//
678
	while (1)
679
	{
680
		if ( snd_completed == snd_sent )
681
		{
682
			Con_DPrintf ("Sound overrun\n");
683
			break;
684
		}
685
 
686
		if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
687
		{
688
			break;
689
		}
690
 
691
		snd_completed++;	// this buffer has been played
692
	}
693
 
694
	//
695
	// submit two new sound blocks
696
	//
697
	while (((snd_sent - snd_completed) >> sample16) < 4)
698
	{
699
		h = lpWaveHdr + ( snd_sent&WAV_MASK );
700
 
701
		snd_sent++;
702
		/*
703
		 * Now the data block can be sent to the output device. The
704
		 * waveOutWrite function returns immediately and waveform
705
		 * data is sent to the output device in the background.
706
		 */
707
		wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
708
 
709
		if (wResult != MMSYSERR_NOERROR)
710
		{
711
			Con_SafePrintf ("Failed to write block to device\n");
712
			FreeSound ();
713
			return;
714
		}
715
	}
716
}
717
 
718
/*
719
==============
720
SNDDMA_Shutdown
721
 
722
Reset the sound device for exiting
723
===============
724
*/
725
void SNDDMA_Shutdown(void)
726
{
727
	FreeSound ();
728
}
729