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