1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: snd_win32.cpp 4129 2010-03-01 13:09:25Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #define DIRECTSOUND_VERSION		0x0900
29 #include "winlocal.h"
30 #include <dsound.h>
31 #include "eax.h"
32 
33 #include "gamedefs.h"
34 #include "snd_local.h"
35 
36 // MACROS ------------------------------------------------------------------
37 
38 // TYPES -------------------------------------------------------------------
39 
40 class VDirectSoundDevice : public VSoundDevice
41 {
42 public:
43 	enum { STRM_LEN = 8 * 1024 };
44 
45 	struct FBuffer
46 	{
47 		bool					Playing;	//	Is buffer taken.
48 		int						SoundID;	//	Sound loaded in buffer.
49 		LPDIRECTSOUNDBUFFER8		Buffer;		//	DirectSound buffer.
50 		double					FreeTime;	//	Time buffer was stopped.
51 	};
52 
53 	bool					SupportEAX;
54 
55 	LPDIRECTSOUND8				DSound;
56 	LPDIRECTSOUNDBUFFER8		PrimarySoundBuffer;
57 	LPDIRECTSOUND3DLISTENER8	Listener;
58 	IKsPropertySet*				PropertySet;
59 
60 	FBuffer*				Buffers;
61 	int						NumBuffers;		// number of buffers available
62 
63 	LPDIRECTSOUNDBUFFER8	StrmBuffer;
64 	int						StrmNextUpdatePart;
65 	void*					StrmLockBuffer1;
66 	void*					StrmLockBuffer2;
67 	DWORD					StrmLockSize1;
68 	DWORD					StrmLockSize2;
69 
70 	bool Init();
71 	int SetChannels(int);
72 	void Shutdown();
73 	void Tick(float);
74 	int PlaySound(int, float, float, float, bool);
75 	int PlaySound3D(int, const TVec&, const TVec&, float, float, bool);
76 	void UpdateChannel(int, float, float);
77 	void UpdateChannel3D(int, const TVec&, const TVec&);
78 	bool IsChannelPlaying(int);
79 	void StopChannel(int);
80 	void UpdateListener(const TVec&, const TVec&, const TVec&, const TVec&,
81 		const TVec&, VReverbInfo*);
82 
83 	bool OpenStream(int, int, int);
84 	void CloseStream();
85 	int GetStreamAvailable();
86 	short* GetStreamBuffer();
87 	void SetStreamData(short*, int);
88 	void SetStreamVolume(float);
89 	void PauseStream();
90 	void ResumeStream();
91 
92 	const char* DS_Error(HRESULT);
93 
94 	int CreateBuffer(int);
95 };
96 
97 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
98 
99 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
100 
101 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
102 
103 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
104 
105 // PUBLIC DATA DEFINITIONS -------------------------------------------------
106 
107 IMPLEMENT_SOUND_DEVICE(VDirectSoundDevice, SNDDRV_Default, "Default",
108 	"DirectSound sound device", NULL);
109 
110 // PRIVATE DATA DEFINITIONS ------------------------------------------------
111 
112 static VCvarF		s3d_distance_unit("s3d_distance_unit", "32.0", CVAR_Archive);
113 static VCvarF		s3d_doppler_factor("s3d_doppler_factor", "1.0", CVAR_Archive);
114 static VCvarF		s3d_rolloff_factor("s3d_rolloff_factor", "1.0", CVAR_Archive);
115 static VCvarF		s3d_min_distance("s3d_min_distance", "64.0", CVAR_Archive);
116 static VCvarF		s3d_max_distance("s3d_max_distance", "2024.0", CVAR_Archive);
117 static VCvarI		eax_environment("eax_environment", "0");
118 
119 // CODE --------------------------------------------------------------------
120 
121 //==========================================================================
122 //
123 //  VDirectSoundDevice::Init
124 //
125 // 	Inits sound
126 //
127 //==========================================================================
128 
Init()129 bool VDirectSoundDevice::Init()
130 {
131 	guard(VDirectSoundDevice::Init);
132 	HRESULT			result;
133 	DSBUFFERDESC	dsbdesc;
134 	WAVEFORMATEX	wfx;
135 	DSCAPS			caps;
136 
137 	Buffers = NULL;
138 	NumBuffers = 0;
139 	SupportEAX = false;
140 	DSound = NULL;
141 	PrimarySoundBuffer = NULL;
142 	Listener = NULL;
143 	PropertySet = NULL;
144 	StrmBuffer = NULL;
145 	StrmNextUpdatePart = 0;
146 
147 	GCon->Log(NAME_Init, "======================================");
148 	GCon->Log(NAME_Init, "Initialising DirectSound driver.");
149 
150 	// Create DirectSound object
151 	result = CoCreateInstance(CLSID_DirectSound8, NULL,
152 		CLSCTX_INPROC_SERVER, IID_IDirectSound8, (void**)&DSound);
153 	if (result != DS_OK)
154 		Sys_Error("Failed to create DirectSound object");
155 
156 	result = DSound->Initialize(NULL);
157 	if (result == DSERR_NODRIVER)
158 	{
159 		//	User don't have a sound card
160 		DSound->Release();
161 		DSound = NULL;
162 		GCon->Log(NAME_Init, "Sound driver not found");
163 		return false;
164 	}
165 	if (result != DS_OK)
166 		Sys_Error("Failed to initialise DirectSound object\n%s", DS_Error(result));
167 
168 	// Set the cooperative level
169 	result = DSound->SetCooperativeLevel(hwnd, DSSCL_EXCLUSIVE);
170 	if (result != DS_OK)
171 		Sys_Error("Failed to set sound cooperative level\n%s", DS_Error(result));
172 
173 	//	Check for 3D sound hardware
174 	memset(&caps, 0, sizeof(caps));
175 	caps.dwSize = sizeof(caps);
176 	DSound->GetCaps(&caps);
177 	if (caps.dwFreeHw3DStaticBuffers && caps.dwFreeHwMixingStaticBuffers &&
178 		GArgs.CheckParm("-3dsound"))
179 	{
180 		Sound3D = true;
181 		GCon->Log(NAME_Init, "3D sound on");
182 	}
183 
184 	//	Create primary buffer
185 	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
186 	dsbdesc.dwSize        = sizeof(DSBUFFERDESC);
187 	dsbdesc.dwFlags       = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
188 	dsbdesc.dwBufferBytes = 0;
189 	dsbdesc.lpwfxFormat   = NULL;
190 	if (Sound3D)
191 	{
192 		dsbdesc.dwFlags |= DSBCAPS_CTRL3D;
193 	}
194 
195 	result = DSound->CreateSoundBuffer(&dsbdesc, (LPDIRECTSOUNDBUFFER *)&PrimarySoundBuffer, NULL);
196 	if (result != DS_OK)
197 		Sys_Error("Failed to create primary sound buffer\n%s", DS_Error(result));
198 
199 	// Set up wave format
200 	memset(&wfx, 0, sizeof(WAVEFORMATEX));
201 	wfx.wFormatTag		= WAVE_FORMAT_PCM;
202 	wfx.wBitsPerSample	= WORD(caps.dwFlags & DSCAPS_PRIMARY16BIT ? 16 : 8);
203 	wfx.nChannels		= caps.dwFlags & DSCAPS_PRIMARYSTEREO ? 2 : 1;
204 	//wfx.nSamplesPerSec	= 11025;
205 	wfx.nSamplesPerSec	= 44100;
206 	wfx.nBlockAlign		= WORD(wfx.wBitsPerSample / 8 * wfx.nChannels);
207 	wfx.nAvgBytesPerSec	= wfx.nSamplesPerSec * wfx.nBlockAlign;
208 	wfx.cbSize			= 0;
209 
210 	result = PrimarySoundBuffer->SetFormat(&wfx);
211 	if (result != DS_OK)
212 		Sys_Error("I_InitSound: Failed to set wave format of primary buffer\n%s", DS_Error(result));
213 
214 	// Get listener interface
215 	if (Sound3D)
216 	{
217 		result = PrimarySoundBuffer->QueryInterface(IID_IDirectSound3DListener, (LPVOID *)&Listener);
218 		if (FAILED(result))
219 		{
220 			Sys_Error("Failed to get Listener");
221 		}
222 
223 		LPDIRECTSOUNDBUFFER		tempBuffer;
224 		WAVEFORMATEX			pcmwf;
225 
226 		// Set up wave format structure.
227 		memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
228 		pcmwf.wFormatTag      = WAVE_FORMAT_PCM;
229 		pcmwf.nChannels       = 1;
230 		pcmwf.nSamplesPerSec  = 44100;
231 		pcmwf.wBitsPerSample  = WORD(8);
232 		pcmwf.nBlockAlign     = WORD(pcmwf.wBitsPerSample / 8 * pcmwf.nChannels);
233 		pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
234 
235 		// Set up DSBUFFERDESC structure.
236 		memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));  // Zero it out.
237 		dsbdesc.dwSize        = sizeof(DSBUFFERDESC);
238 		dsbdesc.dwFlags       = DSBCAPS_CTRLVOLUME |
239 			DSBCAPS_CTRLFREQUENCY | DSBCAPS_STATIC |
240 			DSBCAPS_CTRL3D | DSBCAPS_LOCHARDWARE;
241 		dsbdesc.dwBufferBytes = 44100;
242 		dsbdesc.lpwfxFormat   = &pcmwf;
243 
244 		if (SUCCEEDED(DSound->CreateSoundBuffer(&dsbdesc, &tempBuffer, NULL)))
245 		{
246 			if (FAILED(tempBuffer->QueryInterface(IID_IKsPropertySet,
247 				(void **)&PropertySet)))
248 			{
249 				GCon->Log(NAME_Init, "IKsPropertySet failed");
250 			}
251 			else
252 			{
253 				GCon->Log(NAME_Init, "IKsPropertySet acquired");
254 
255 				ULONG Support;
256 				result = PropertySet->QuerySupport(
257 					DSPROPSETID_EAX_ListenerProperties,
258 					DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &Support);
259 				if (FAILED(result) ||
260 					(Support & (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) !=
261 					(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))
262 				{
263 					GCon->Log(NAME_Init, "EAX 2.0 not supported");
264 					PropertySet->Release();
265 					PropertySet = NULL;
266 				}
267 				else
268 				{
269 					GCon->Log(NAME_Init, "EAX 2.0 supported");
270 					SupportEAX = true;
271 				}
272 			}
273 			tempBuffer->Release();
274 		}
275 
276 		Listener->SetDistanceFactor(1.0 / s3d_distance_unit, DS3D_IMMEDIATE);
277 		Listener->SetDopplerFactor(s3d_doppler_factor, DS3D_IMMEDIATE);
278 		Listener->SetRolloffFactor(s3d_rolloff_factor, DS3D_IMMEDIATE);
279 	}
280 	return true;
281 	unguard;
282 }
283 
284 //==========================================================================
285 //
286 //	VDirectSoundDevice::SetChannels
287 //
288 //==========================================================================
289 
SetChannels(int InNumChannels)290 int VDirectSoundDevice::SetChannels(int InNumChannels)
291 {
292 	guard(VDirectSoundDevice::SetChannels);
293 	DSCAPS caps;
294 	memset(&caps, 0, sizeof(caps));
295 	caps.dwSize = sizeof(caps);
296 	DSound->GetCaps(&caps);
297 
298 	if (Sound3D)
299 		NumBuffers = caps.dwFreeHw3DStaticBuffers;
300 	else
301 		NumBuffers = caps.dwFreeHwMixingStaticBuffers;
302 	if (!NumBuffers)
303 	{
304 		GCon->Log(NAME_Init, "No HW channels available");
305 		NumBuffers = 8;
306 	}
307 	Buffers = new FBuffer[NumBuffers];
308 	memset(Buffers, 0, sizeof(FBuffer) * NumBuffers);
309 	GCon->Logf(NAME_Init, "Using %d sound buffers", NumBuffers);
310 
311 	int Ret = InNumChannels;
312 	if (Ret > NumBuffers)
313 	{
314 		Ret = NumBuffers;
315 	}
316 	return Ret;
317 	unguard;
318 }
319 
320 //==========================================================================
321 //
322 //	VDirectSoundDevice::Shutdown
323 //
324 //==========================================================================
325 
Shutdown()326 void VDirectSoundDevice::Shutdown()
327 {
328 	guard(VDirectSoundDevice::Shutdown);
329 	//	Shutdown sound
330 	if (DSound)
331 	{
332 		DSound->Release();
333 		DSound = NULL;
334 	}
335 	if (Buffers)
336 	{
337 		delete[] Buffers;
338 		Buffers = NULL;
339 	}
340 	unguard;
341 }
342 
343 //==========================================================================
344 //
345 //  VDirectSoundDevice::Tick
346 //
347 //==========================================================================
348 
Tick(float)349 void VDirectSoundDevice::Tick(float)
350 {
351 }
352 
353 //==========================================================================
354 //
355 //	VDirectSoundDevice::DS_Error
356 //
357 //==========================================================================
358 
DS_Error(HRESULT result)359 const char* VDirectSoundDevice::DS_Error(HRESULT result)
360 {
361 	static char	errmsg[128];
362 
363 	switch(result)
364 	{
365 	case DS_OK:
366 		return "The request completed successfully.";
367 
368 	case DSERR_ALLOCATED:
369 		return "The request failed because resources, such as a priority level, were already in use by another caller.";
370 
371 	case DSERR_ALREADYINITIALIZED:
372 		return "The object is already initialised.";
373 
374 	case DSERR_BADFORMAT:
375 		return "The specified wave format is not supported.";
376 
377 	case DSERR_BUFFERLOST:
378 		return "The buffer memory has been lost and must be restored.";
379 
380 	case DSERR_CONTROLUNAVAIL:
381 		return "The control (volume, pan, and so forth) requested by the caller is not available.";
382 
383 	case DSERR_GENERIC:
384 		return "An undetermined error occurred inside the DirectSound subsystem.";
385 
386 	case DSERR_INVALIDCALL:
387 		return "This function is not valid for the current state of this object.";
388 
389 	case DSERR_INVALIDPARAM:
390 		return "An invalid parameter was passed to the returning function.";
391 
392 	case DSERR_NOAGGREGATION:
393 		return "The object does not support aggregation.";
394 
395 	case DSERR_NODRIVER:
396 		return "No sound driver is available for use.";
397 
398 	case DSERR_OTHERAPPHASPRIO:
399 		return "This value is obsolete and is not used.";
400 
401 	case DSERR_OUTOFMEMORY:
402 		return "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request.";
403 
404 	case DSERR_PRIOLEVELNEEDED:
405 		return "The caller does not have the priority level required for the function to succeed.";
406 
407 	case DSERR_UNINITIALIZED:
408 		return "The IDirectSound::Initialise method has not been called or has not been called successfully before other methods were called.";
409 
410 	case DSERR_UNSUPPORTED:
411 		return "The function called is not supported at this time.";
412 
413 	default:
414 		sprintf(errmsg,"Unknown Error Code: %04X", result);
415 		return errmsg;
416 	}
417 }
418 
419 //==========================================================================
420 //
421 //	VDirectSoundDevice::CreateBuffer
422 //
423 //==========================================================================
424 
CreateBuffer(int sound_id)425 int VDirectSoundDevice::CreateBuffer(int sound_id)
426 {
427 	HRESULT					result;
428 	LPDIRECTSOUNDBUFFER8	dsbuffer;
429 	DSBUFFERDESC			dsbdesc;
430 	WAVEFORMATEX			pcmwf;
431 	void					*buffer;
432 	void					*buff2;
433 	DWORD					size1;
434 	DWORD					size2;
435 	int						i;
436 
437 	for (i = 0; i < NumBuffers; i++)
438 	{
439 		if (!Buffers[i].Playing && Buffers[i].SoundID == sound_id)
440 		{
441 			return i;
442 		}
443 	}
444 
445 	//	Check, that sound lump is loaded
446 	if (!GSoundManager->LoadSound(sound_id))
447 	{
448 		//	Missing sound.
449 		return -1;
450 	}
451 	sfxinfo_t &sfx = GSoundManager->S_sfx[sound_id];
452 
453 	// Set up wave format structure.
454 	memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
455 	pcmwf.wFormatTag = WAVE_FORMAT_PCM;
456 	pcmwf.nChannels = 1;
457 	pcmwf.nSamplesPerSec = sfx.SampleRate;
458 	pcmwf.wBitsPerSample = WORD(sfx.SampleBits);
459 	pcmwf.nBlockAlign = WORD(pcmwf.wBitsPerSample / 8 * pcmwf.nChannels);
460 	pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
461 
462 	// Set up DSBUFFERDESC structure.
463 	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));  // Zero it out.
464 	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
465 	dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY |
466 		DSBCAPS_STATIC;
467 	dsbdesc.dwBufferBytes = sfx.DataSize;
468 	dsbdesc.lpwfxFormat = &pcmwf;
469 	if (Sound3D)
470 	{
471 		dsbdesc.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_LOCHARDWARE;
472 	}
473 	else
474 	{
475 		dsbdesc.dwFlags |= DSBCAPS_CTRLPAN;
476 	}
477 
478 	int Handle;
479 	for (Handle = 0; Handle < NumBuffers; Handle++)
480 	{
481 		if (!Buffers[Handle].SoundID)
482 			break;
483 	}
484 	result = DSound->CreateSoundBuffer(&dsbdesc, (LPDIRECTSOUNDBUFFER *)&dsbuffer, NULL);
485 	if (Handle == NumBuffers || result != DS_OK)
486 	{
487 		int		best = -1;
488 		double	least_time = 999999999.0;
489 
490 		for (i = 0; i < NumBuffers; i++)
491 		{
492 			if (Buffers[i].SoundID && !Buffers[i].Playing &&
493 				Buffers[i].FreeTime < least_time)
494 			{
495 				best = i;
496 				least_time = Buffers[i].FreeTime;
497 			}
498 		}
499 		if (best != -1)
500 		{
501 			Buffers[best].Buffer->Release();
502 			Buffers[best].SoundID = 0;
503 			if (Handle == NumBuffers)
504 				Handle = best;
505 			if (result != DS_OK)
506 				result = DSound->CreateSoundBuffer(&dsbdesc, (LPDIRECTSOUNDBUFFER *)&dsbuffer, NULL);
507 		}
508 		else
509 		{
510 			//	All channels are busy.
511 			return -1;
512 		}
513 	}
514 
515 	if (result != DS_OK)
516 	{
517 		GCon->Log(NAME_Dev, "Failed to create sound buffer");
518 		GCon->Log(NAME_Dev, DS_Error(result));
519 
520 		//	We don't need to keep lump static
521 		GSoundManager->DoneWithLump(sound_id);
522 
523 		return -1;
524 	}
525 
526 	dsbuffer->Lock(0, sfx.DataSize,
527 		&buffer, &size1, &buff2, &size2, DSBLOCK_ENTIREBUFFER);
528 	memcpy(buffer, sfx.Data, sfx.DataSize);
529 	dsbuffer->Unlock(buffer, sfx.DataSize, buff2, size2);
530 
531 	//	We don't need to keep lump static
532 	GSoundManager->DoneWithLump(sound_id);
533 
534 	Buffers[Handle].Buffer = dsbuffer;
535 	Buffers[Handle].SoundID = sound_id;
536 	return Handle;
537 }
538 
539 //==========================================================================
540 //
541 //	VDirectSoundDevice::PlaySound
542 //
543 // 	This function adds a sound to the list of currently active sounds, which
544 // is maintained as a given number of internal channels.
545 //
546 //==========================================================================
547 
PlaySound(int sound_id,float vol,float sep,float pitch,bool Loop)548 int VDirectSoundDevice::PlaySound(int sound_id, float vol, float sep,
549 	float pitch, bool Loop)
550 {
551 	guard(VDirectSoundDevice::PlaySound);
552 	HRESULT					result;
553 
554 	int Handle = CreateBuffer(sound_id);
555 	if (Handle == -1)
556 	{
557 		return -1;
558 	}
559 	Buffers[Handle].Buffer->SetFrequency((DWORD)(
560 		GSoundManager->S_sfx[sound_id].SampleRate * pitch));
561 	Buffers[Handle].Buffer->SetCurrentPosition(0);
562 
563 	if (Sound3D)
564 	{
565 		LPDIRECTSOUND3DBUFFER8	Buf3D;
566 
567 		Buffers[Handle].Buffer->SetVolume((LONG)(4096.0 * (vol - 1.0)));
568 
569 		result = Buffers[Handle].Buffer->QueryInterface(
570 			IID_IDirectSound3DBuffer8, (LPVOID *)&Buf3D);
571 		if (FAILED(result))
572 		{
573 			Sys_Error("Failed to get 3D buffer");
574 		}
575 
576 		Buf3D->SetMode(DS3DMODE_HEADRELATIVE, DS3D_IMMEDIATE);
577 		Buf3D->SetPosition(0.0, -16.0, 0.0, DS3D_IMMEDIATE);
578 		Buf3D->SetMinDistance(s3d_min_distance, DS3D_IMMEDIATE);
579 		Buf3D->SetMaxDistance(s3d_max_distance, DS3D_IMMEDIATE);
580 		Buf3D->Release();
581 	}
582 	else
583 	{
584 		Buffers[Handle].Buffer->SetVolume((int)(vol * 3000) - 3000);
585 		Buffers[Handle].Buffer->SetPan((int)(sep * 2000));
586 	}
587 
588 	result = Buffers[Handle].Buffer->Play(0, 0, Loop ? DSBPLAY_LOOPING : 0);
589 	if (result != DS_OK)
590 	{
591 		GCon->Log(NAME_Dev, "Failed to play buffer");
592 		GCon->Log(NAME_Dev, DS_Error(result));
593 	}
594 	Buffers[Handle].Playing = true;
595 	return Handle;
596 	unguard;
597 }
598 
599 //==========================================================================
600 //
601 //	VDirectSoundDevice::PlaySound3D
602 //
603 // 	This function adds a sound to the list of currently active sounds, which
604 // is maintained as a given number of internal channels.
605 //
606 //==========================================================================
607 
PlaySound3D(int sound_id,const TVec & origin,const TVec & velocity,float volume,float pitch,bool Loop)608 int VDirectSoundDevice::PlaySound3D(int sound_id, const TVec &origin,
609 	const TVec &velocity, float volume, float pitch, bool Loop)
610 {
611 	guard(VDirectSoundDevice::PlaySound3D);
612 	HRESULT					result;
613 	LPDIRECTSOUND3DBUFFER8	Buf3D;
614 
615 	int Handle = CreateBuffer(sound_id);
616 	if (Handle == -1)
617 	{
618 		return -1;
619 	}
620 	Buffers[Handle].Buffer->SetFrequency((DWORD)(
621 		GSoundManager->S_sfx[sound_id].SampleRate * pitch));
622 	Buffers[Handle].Buffer->SetCurrentPosition(0);
623 
624 	Buffers[Handle].Buffer->SetVolume((LONG)(4096.0 * (volume - 1.0)));
625 
626 	result = Buffers[Handle].Buffer->QueryInterface(
627 		IID_IDirectSound3DBuffer8, (LPVOID *)&Buf3D);
628 	if (FAILED(result))
629 	{
630 		Sys_Error("Failed to get 3D buffer");
631 	}
632 
633 	Buf3D->SetPosition(origin.x, origin.z, origin.y, DS3D_IMMEDIATE);
634 	Buf3D->SetVelocity(velocity.x, velocity.z, velocity.y, DS3D_IMMEDIATE);
635 	Buf3D->SetMinDistance(s3d_min_distance, DS3D_IMMEDIATE);
636 	Buf3D->SetMaxDistance(s3d_max_distance, DS3D_IMMEDIATE);
637 	Buf3D->Release();
638 
639 	result = Buffers[Handle].Buffer->Play(0, 0, Loop ? DSBPLAY_LOOPING : 0);
640 	if (result != DS_OK)
641 	{
642 		GCon->Log(NAME_Dev, "Failed to play buffer");
643 		GCon->Log(NAME_Dev, DS_Error(result));
644 	}
645 	Buffers[Handle].Playing = true;
646 	return Handle;
647 	unguard;
648 }
649 
650 //==========================================================================
651 //
652 //  VDirectSoundDevice::UpdateChannel
653 //
654 //==========================================================================
655 
UpdateChannel(int Handle,float vol,float sep)656 void VDirectSoundDevice::UpdateChannel(int Handle, float vol, float sep)
657 {
658 	guard(VDirectSoundDevice::UpdateChannel);
659 	if (Handle == -1)
660 	{
661 		return;
662 	}
663 	//	Update params
664 	if (!Sound3D)
665 	{
666 		Buffers[Handle].Buffer->SetVolume((int)(vol * 3000) - 3000);
667 		Buffers[Handle].Buffer->SetPan((int)(sep * 2000));
668 	}
669 	unguard;
670 }
671 
672 //==========================================================================
673 //
674 //	VDirectSoundDevice::UpdateChannel3D
675 //
676 //==========================================================================
677 
UpdateChannel3D(int Handle,const TVec & Org,const TVec & Vel)678 void VDirectSoundDevice::UpdateChannel3D(int Handle, const TVec& Org,
679 	const TVec& Vel)
680 {
681 	guard(VDirectSoundDevice::PlaySound3D);
682 	HRESULT					result;
683 	LPDIRECTSOUND3DBUFFER8	Buf3D;
684 
685 	if (Handle == -1)
686 	{
687 		return;
688 	}
689 	result = Buffers[Handle].Buffer->QueryInterface(
690 		IID_IDirectSound3DBuffer8, (LPVOID *)&Buf3D);
691 	if (FAILED(result))
692 	{
693 		return;
694 	}
695 	Buf3D->SetPosition(Org.x, Org.z, Org.y, DS3D_DEFERRED);
696 	Buf3D->SetVelocity(Vel.x, Vel.z, Vel.y, DS3D_DEFERRED);
697 	Buf3D->Release();
698 	unguard;
699 }
700 
701 //==========================================================================
702 //
703 //	VDirectSoundDevice::IsChannelPlaying
704 //
705 //==========================================================================
706 
IsChannelPlaying(int Handle)707 bool VDirectSoundDevice::IsChannelPlaying(int Handle)
708 {
709 	guard(VDirectSoundDevice::IsChannelPlaying);
710 	if (Handle == -1)
711 	{
712 		return false;
713 	}
714 	if (Buffers[Handle].Buffer)
715 	{
716 		DWORD	Status;
717 
718 		Buffers[Handle].Buffer->GetStatus(&Status);
719 
720 		if (Status & DSBSTATUS_PLAYING)
721 		{
722 			return true;
723 		}
724 	}
725 	return false;
726 	unguard;
727 }
728 
729 //==========================================================================
730 //
731 //  VDirectSoundDevice::StopChannel
732 //
733 //	Stop the sound. Necessary to prevent runaway chainsaw, and to stop
734 // rocket launches when an explosion occurs.
735 //	All sounds MUST be stopped;
736 //
737 //==========================================================================
738 
StopChannel(int Handle)739 void VDirectSoundDevice::StopChannel(int Handle)
740 {
741 	if (Handle == -1)
742 	{
743 		return;
744 	}
745 
746 	//	Stop buffer
747 	Buffers[Handle].Buffer->Stop();
748 
749 	//	Mark buffer as not playing for reuse.
750 	Buffers[Handle].FreeTime = Sys_Time();
751 	Buffers[Handle].Playing = false;
752 }
753 
754 //==========================================================================
755 //
756 //	VDirectSoundDevice::UpdateListener
757 //
758 //==========================================================================
759 
UpdateListener(const TVec & org,const TVec & vel,const TVec & fwd,const TVec &,const TVec & up,VReverbInfo * Env)760 void VDirectSoundDevice::UpdateListener(const TVec& org, const TVec& vel,
761 	const TVec& fwd, const TVec&, const TVec& up, VReverbInfo* Env)
762 {
763 	guard(VDirectSoundDevice::UpdateListener);
764 	//	Set position, velocity and orientation.
765 	Listener->SetPosition(org.x, org.z, org.y, DS3D_DEFERRED);
766 	Listener->SetVelocity(vel.x, vel.z, vel.y, DS3D_DEFERRED);
767 	Listener->SetOrientation(fwd.x, fwd.z, fwd.y, up.x, up.z, up.y, DS3D_DEFERRED);
768 
769 	//	Set factor values.
770 	Listener->SetDistanceFactor(1.0 / s3d_distance_unit, DS3D_DEFERRED);
771 	Listener->SetDopplerFactor(s3d_doppler_factor, DS3D_DEFERRED);
772 	Listener->SetRolloffFactor(s3d_rolloff_factor, DS3D_DEFERRED);
773 
774 	if (SupportEAX)
775 	{
776 		//	Set environment properties.
777 		EAXLISTENERPROPERTIES Prop;
778 		Prop.lRoom = Env->Props.Room;
779 		Prop.lRoomHF = Env->Props.RoomHF;
780 		Prop.flRoomRolloffFactor = Env->Props.RoomRolloffFactor;
781 		Prop.flDecayTime = Env->Props.DecayTime;
782 		Prop.flDecayHFRatio = Env->Props.DecayHFRatio;
783 		Prop.lReflections = Env->Props.Reflections;
784 		Prop.flReflectionsDelay = Env->Props.ReflectionsDelay;
785 		Prop.lReverb = Env->Props.Reverb;
786 		Prop.flReverbDelay = Env->Props.ReverbDelay;
787 		Prop.dwEnvironment = Env->Props.Environment;
788 		Prop.flEnvironmentSize = Env->Props.EnvironmentSize;
789 		Prop.flEnvironmentDiffusion = Env->Props.EnvironmentDiffusion;
790 		Prop.flAirAbsorptionHF = Env->Props.AirAbsorptionHF;
791 		Prop.dwFlags = Env->Props.Flags & 0x3f;
792 		PropertySet->Set(DSPROPSETID_EAX_ListenerProperties,
793 			DSPROPERTY_EAXLISTENER_ALLPARAMETERS |
794 			DSPROPERTY_EAXLISTENER_DEFERRED, NULL, 0, &Prop, sizeof(Prop));
795 
796 		if (Env->Id == 1)
797 		{
798 			DWORD envId = eax_environment;
799 			if (envId < 0 || envId >= EAX_ENVIRONMENT_COUNT)
800 				envId = EAX_ENVIRONMENT_GENERIC;
801 			PropertySet->Set(DSPROPSETID_EAX_ListenerProperties,
802 				DSPROPERTY_EAXLISTENER_ENVIRONMENT |
803 				DSPROPERTY_EAXLISTENER_DEFERRED, NULL, 0, &envId, sizeof(DWORD));
804 
805 			float envSize = GAudio->EAX_CalcEnvSize();
806 			PropertySet->Set(DSPROPSETID_EAX_ListenerProperties,
807 				DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE |
808 				DSPROPERTY_EAXLISTENER_DEFERRED, NULL, 0, &envSize, sizeof(float));
809 		}
810 	}
811 
812 	//	Commit settings.
813 	Listener->CommitDeferredSettings();
814 	unguard;
815 }
816 
817 //==========================================================================
818 //
819 //	VDirectSoundDevice::OpenStream
820 //
821 //==========================================================================
822 
OpenStream(int Rate,int Bits,int Channels)823 bool VDirectSoundDevice::OpenStream(int Rate, int Bits, int Channels)
824 {
825 	guard(VDirectSoundDevice::OpenStream);
826 	HRESULT					result;
827 	DSBUFFERDESC			dsbdesc;
828 	WAVEFORMATEX			pcmwf;
829 	int						i;
830 
831 	// Set up wave format structure.
832 	memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
833 	pcmwf.wFormatTag = WAVE_FORMAT_PCM;
834 	pcmwf.nChannels = Channels;
835 	pcmwf.nSamplesPerSec = Rate;
836 	pcmwf.wBitsPerSample = WORD(Bits);
837 	pcmwf.nBlockAlign = WORD(pcmwf.wBitsPerSample / 8 * pcmwf.nChannels);
838 	pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
839 
840 	// Set up DSBUFFERDESC structure.
841 	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));  // Zero it out.
842 	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
843 	dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME |
844 		DSBCAPS_STATIC;
845 	dsbdesc.dwBufferBytes = STRM_LEN * 4;
846 	dsbdesc.lpwfxFormat = &pcmwf;
847 
848 	result = DSound->CreateSoundBuffer(&dsbdesc, (LPDIRECTSOUNDBUFFER *)&StrmBuffer, NULL);
849 	if (result != DS_OK)
850 	{
851 		int		best = -1;
852 		double	least_time = 999999999.0;
853 
854 		for (i = 0; i < NumBuffers; i++)
855 		{
856 			if (Buffers[i].SoundID && !Buffers[i].Playing &&
857 				Buffers[i].FreeTime < least_time)
858 			{
859 				best = i;
860 				least_time = Buffers[i].FreeTime;
861 			}
862 		}
863 		if (best != -1)
864 		{
865 			Buffers[best].Buffer->Release();
866 			Buffers[best].SoundID = 0;
867 			result = DSound->CreateSoundBuffer(&dsbdesc, (LPDIRECTSOUNDBUFFER *)&StrmBuffer, NULL);
868 		}
869 	}
870 	if (result != DS_OK)
871 	{
872 		GCon->Log(NAME_Dev, "Failed to create sound buffer");
873 		GCon->Log(NAME_Dev, DS_Error(result));
874 		return false;
875 	}
876 	return true;
877 	unguard;
878 }
879 
880 //==========================================================================
881 //
882 //	VDirectSoundDevice::CloseStream
883 //
884 //==========================================================================
885 
CloseStream()886 void VDirectSoundDevice::CloseStream()
887 {
888 	guard(VDirectSoundDevice::CloseStream);
889 	if (StrmBuffer)
890 	{
891 		StrmBuffer->Stop();
892 		StrmBuffer->Release();
893 		StrmBuffer = NULL;
894 	}
895 	unguard;
896 }
897 
898 //==========================================================================
899 //
900 //	VDirectSoundDevice::GetStreamAvailable
901 //
902 //==========================================================================
903 
GetStreamAvailable()904 int VDirectSoundDevice::GetStreamAvailable()
905 {
906 	guard(VDirectSoundDevice::GetStreamAvailable);
907 	DWORD	Status;
908 	DWORD	PlayPos;
909 	DWORD	WritePos;
910 
911 	StrmBuffer->GetStatus(&Status);
912 	if (!(Status & DSBSTATUS_PLAYING))
913 	{
914 		//	Not playing, lock entire buffer.
915 		StrmBuffer->Lock(0, STRM_LEN * 4, &StrmLockBuffer1, &StrmLockSize1,
916 			&StrmLockBuffer2, &StrmLockSize2, DSBLOCK_ENTIREBUFFER);
917 		return StrmLockSize1 / 4;
918 	}
919 	StrmBuffer->GetCurrentPosition(&PlayPos, &WritePos);
920 	int PlayPart = PlayPos / (STRM_LEN);
921 	if (PlayPart != StrmNextUpdatePart)
922 	{
923 		StrmBuffer->Lock(StrmNextUpdatePart * STRM_LEN, STRM_LEN,
924 			&StrmLockBuffer1, &StrmLockSize1, &StrmLockBuffer2,
925 			&StrmLockSize2, 0);
926 		return StrmLockSize1 / 4;
927 	}
928 	return 0;
929 	unguard;
930 }
931 
932 //==========================================================================
933 //
934 //	VDirectSoundDevice::SetStreamData
935 //
936 //==========================================================================
937 
GetStreamBuffer()938 short* VDirectSoundDevice::GetStreamBuffer()
939 {
940 	guard(VDirectSoundDevice::GetStreamBuffer);
941 	return (short*)StrmLockBuffer1;
942 	unguard;
943 }
944 
945 //==========================================================================
946 //
947 //	VDirectSoundDevice::SetStreamData
948 //
949 //==========================================================================
950 
SetStreamData(short *,int)951 void VDirectSoundDevice::SetStreamData(short*, int)
952 {
953 	guard(VDirectSoundDevice::SetStreamData);
954 	DWORD	Status;
955 
956 	StrmBuffer->Unlock(StrmLockBuffer1, StrmLockSize1, StrmLockBuffer2, StrmLockSize2);
957 	StrmBuffer->GetStatus(&Status);
958 	if (!(Status & DSBSTATUS_PLAYING))
959 	{
960 		StrmBuffer->SetCurrentPosition(0);
961 		StrmBuffer->Play(0, 0, DSBPLAY_LOOPING);
962 	}
963 	StrmNextUpdatePart = (StrmNextUpdatePart + 1) & 3;
964 	unguard;
965 }
966 
967 //==========================================================================
968 //
969 //	VDirectSoundDevice::SetStreamVolume
970 //
971 //==========================================================================
972 
SetStreamVolume(float Volume)973 void VDirectSoundDevice::SetStreamVolume(float Volume)
974 {
975 	guard(VDirectSoundDevice::SetStreamVolume);
976 	if (StrmBuffer)
977 	{
978 		StrmBuffer->SetVolume(int(4000 * (Volume - 1.0)));
979 	}
980 	unguard;
981 }
982 
983 //==========================================================================
984 //
985 //	VDirectSoundDevice::PauseStream
986 //
987 //==========================================================================
988 
PauseStream()989 void VDirectSoundDevice::PauseStream()
990 {
991 	guard(VDirectSoundDevice::PauseStream);
992 	if (StrmBuffer)
993 	{
994 		StrmBuffer->Stop();
995 	}
996 	unguard;
997 }
998 
999 //==========================================================================
1000 //
1001 //	VDirectSoundDevice::ResumeStream
1002 //
1003 //==========================================================================
1004 
ResumeStream()1005 void VDirectSoundDevice::ResumeStream()
1006 {
1007 	guard(VDirectSoundDevice::ResumeStream);
1008 	if (StrmBuffer)
1009 	{
1010 		StrmBuffer->Play(0, 0, DSBPLAY_LOOPING);
1011 	}
1012 	unguard;
1013 }
1014