ds3dvirtualbuffer.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. //
  2. // ds3dvirtualbuffer.cpp
  3. //
  4. // Classes representing virtual DirectSound3D buffers that may or may not have
  5. // a real DirectSoundBuffer associated with them at any given moment.
  6. //
  7. #include "pch.h"
  8. #include "soundbase.h"
  9. #include "ds3dutil.h"
  10. #include "ds3dbuffer.h"
  11. #include "ds3dvirtualbuffer.h"
  12. namespace SoundEngine {
  13. /////////////////////////////////////////////////////////////////////////////
  14. //
  15. // DS3DBufferPositionTracker
  16. //
  17. /////////////////////////////////////////////////////////////////////////////
  18. // Create a position tracker for a sound which plays until it reaches the
  19. // end (for non-looping sounds) or has stop called upon it.
  20. BufferPositionTracker::BufferPositionTracker(ISoundPCMData* pdata, bool bLooping) :
  21. m_dwBytesPerSec(pdata->GetBytesPerSec()),
  22. m_dwLength(pdata->GetSize()),
  23. m_dwPositionMask(~(pdata->GetBytesPerSample() - 1)),
  24. m_dwPosition(0),
  25. m_bLooping(bLooping),
  26. m_bEnding(false),
  27. m_bASR(false)
  28. {
  29. };
  30. // Creates a position tracker for an ASR buffer.
  31. BufferPositionTracker::BufferPositionTracker(ISoundPCMData* pdata,
  32. DWORD dwLoopStart, DWORD dwLoopLength) :
  33. m_dwBytesPerSec(pdata->GetBytesPerSec()),
  34. m_dwLength(pdata->GetSize()),
  35. m_dwPositionMask(~(pdata->GetBytesPerSample() - 1)),
  36. m_dwPosition(0),
  37. m_bASR(true),
  38. m_bEnding(false),
  39. m_dwLoopStart(dwLoopStart),
  40. m_dwLoopLength(dwLoopLength)
  41. {
  42. // make sure the loop is contained in the buffer
  43. ZAssert(m_dwLoopStart + m_dwLoopLength <= m_dwLength);
  44. };
  45. // the sound has been given a stop.
  46. void BufferPositionTracker::Stop(bool bForceNow)
  47. {
  48. if (!m_bASR || bForceNow)
  49. {
  50. // stop it now.
  51. m_dwPosition = m_dwLoopLength;
  52. }
  53. m_bEnding = true;
  54. };
  55. // the given number of miliseconds have elapsed
  56. void BufferPositionTracker::AddTime(DWORD dwElapsedTime)
  57. {
  58. DWORD dwElapsedBytes =
  59. (DWORD)(dwElapsedTime * (__int64)m_dwBytesPerSec / 1000);
  60. if (!m_bASR)
  61. {
  62. m_dwPosition += dwElapsedBytes;
  63. // if we've passed the end...
  64. if (m_dwPosition > m_dwLength)
  65. {
  66. // loop for a looping sound
  67. if (m_bLooping && !m_bEnding)
  68. {
  69. m_dwPosition %= m_dwLength;
  70. }
  71. else
  72. {
  73. // stop the position at the end of the sound.
  74. m_dwPosition = m_dwLength;
  75. }
  76. }
  77. }
  78. else // m_bASR
  79. {
  80. if (m_bEnding)
  81. {
  82. bool bWasInAttack = m_dwPosition < m_dwLoopStart;
  83. m_dwPosition += dwElapsedBytes;
  84. // if we were in the attack portion, make sure we jump
  85. // directly to the release portion of the wave.
  86. if (bWasInAttack && (m_dwPosition > m_dwLoopStart))
  87. {
  88. m_dwPosition += m_dwLoopLength;
  89. }
  90. // stop the position at the end of the sound.
  91. if (m_dwPosition > m_dwLength)
  92. {
  93. m_dwPosition = m_dwLength;
  94. }
  95. }
  96. else
  97. {
  98. m_dwPosition += dwElapsedBytes;
  99. // loop if appropriate
  100. if (m_dwPosition >= m_dwLoopStart + m_dwLoopLength)
  101. {
  102. m_dwPosition =
  103. ((m_dwPosition - m_dwLoopStart) % m_dwLoopLength)
  104. + m_dwLoopStart;
  105. }
  106. }
  107. }
  108. };
  109. // Resets the position to the given one, presumably retrieved from a
  110. // playing buffer.
  111. void BufferPositionTracker::SetPosition(DWORD dwPosition)
  112. {
  113. m_dwPosition = min(dwPosition, m_dwLength);
  114. };
  115. /////////////////////////////////////////////////////////////////////////////
  116. //
  117. // DSVirtualSoundBuffer
  118. //
  119. /////////////////////////////////////////////////////////////////////////////
  120. // prepare a sound buffer with the given quality and 3D support using the
  121. // direct sound object pointed to by pDirectSound.
  122. HRESULT DSVirtualSoundBuffer::PrepareBuffer(IDirectSound* pDirectSound,
  123. ISoundEngine::Quality quality, bool bAllowHardware, bool bSupport3D)
  124. {
  125. HRESULT hr;
  126. if (m_pds3dbuffer == NULL)
  127. {
  128. if (m_bufferPositionTracker.IsASR())
  129. {
  130. DS3DASRSoundBuffer* pds3dASRBuffer = new DS3DASRSoundBuffer();
  131. // initialize the buffer.
  132. hr = pds3dASRBuffer->Init(pDirectSound, m_pdata,
  133. m_bufferPositionTracker.GetLoopOffset(),
  134. m_bufferPositionTracker.GetLoopLength(),
  135. bSupport3D, quality, bAllowHardware);
  136. if (ZFailed(hr)) return hr;
  137. m_pds3dbuffer = pds3dASRBuffer;
  138. }
  139. // stream sounds that are larger than 1 MB or so.
  140. else if (m_pdata->GetSize() > 1000000)
  141. {
  142. DS3DStreamingSoundBuffer* pds3DStreamingBuffer = new DS3DStreamingSoundBuffer();
  143. // initialize the buffer.
  144. hr = pds3DStreamingBuffer->Init(pDirectSound, m_pdata,
  145. m_bufferPositionTracker.IsLooping(), bSupport3D, quality, bAllowHardware);
  146. if (ZFailed(hr)) return hr;
  147. m_pds3dbuffer = pds3DStreamingBuffer;
  148. }
  149. else
  150. {
  151. DS3DStaticSoundBuffer* pds3DStaticBuffer = new DS3DStaticSoundBuffer();
  152. // initialize the buffer.
  153. hr = pds3DStaticBuffer->Init(pDirectSound, m_pdata,
  154. m_bufferPositionTracker.IsLooping(), bSupport3D, quality, bAllowHardware);
  155. if (FAILED(hr)) return hr;
  156. m_pds3dbuffer = pds3DStaticBuffer;
  157. }
  158. }
  159. m_quality = quality;
  160. m_bAllowHardware = bAllowHardware;
  161. // update the buffers's volume and pitch as needed.
  162. hr = m_pds3dbuffer->UpdateState(m_fGain, m_fPitch, false);
  163. if (ZFailed(hr)) return hr;
  164. return S_OK;
  165. }
  166. // Creates a virtual sound buffer for a normal sound, possibly looping
  167. DSVirtualSoundBuffer::DSVirtualSoundBuffer(ISoundPCMData* pdata, bool bLooping) :
  168. m_bufferPositionTracker(pdata, bLooping),
  169. m_pdata(pdata),
  170. m_bStarted(false),
  171. m_bStopped(false),
  172. m_bRetryStop(false),
  173. m_bBufferPlaying(false),
  174. m_bAudible(false),
  175. m_fAge(0),
  176. m_fPriority(1.0f),
  177. m_fGain(0.0f),
  178. m_fPitch(1.0f),
  179. m_fDynamicPriority(0.0f)
  180. {
  181. };
  182. // Creates a virtual sound buffer for an ASR sound
  183. DSVirtualSoundBuffer::DSVirtualSoundBuffer(ISoundPCMData* pdata, DWORD dwLoopOffset, DWORD dwLoopLength) :
  184. m_bufferPositionTracker(pdata, dwLoopOffset, dwLoopLength),
  185. m_pdata(pdata),
  186. m_bStarted(false),
  187. m_bStopped(false),
  188. m_bRetryStop(false),
  189. m_bBufferPlaying(false),
  190. m_bAudible(false),
  191. m_fAge(0),
  192. m_fPriority(1.0f),
  193. m_fGain(0.0f),
  194. m_fPitch(1.0f),
  195. m_fDynamicPriority(0.0f)
  196. {
  197. };
  198. // Recalculates the sounds position, loudness, whether it's playing, etc..
  199. HRESULT DSVirtualSoundBuffer::Update(DWORD dwTimeElapsed,
  200. const Vector& vectListenerPosition, float fRolloffFactor)
  201. {
  202. HRESULT hr;
  203. bool bWasStopped = m_bStopped;
  204. // if we tried to stop the sound and failed, try again now
  205. if (m_bRetryStop && m_bBufferPlaying)
  206. m_pds3dbuffer->Stop(true);
  207. // Start the sound and calculate the end time, if we have not done so
  208. // yet.
  209. if (!m_bStarted)
  210. {
  211. m_bStarted = true;
  212. }
  213. else
  214. {
  215. // update the play position
  216. m_bufferPositionTracker.AddTime((DWORD)(dwTimeElapsed * m_fPitch));
  217. m_fAge += dwTimeElapsed/1000.0f;
  218. }
  219. // if the buffer is playing, try to figure out its state.
  220. if (m_bBufferPlaying)
  221. {
  222. bool bPlaying, bBufferLost;
  223. hr = m_pds3dbuffer->GetStatus(bPlaying, bBufferLost);
  224. if (ZFailed(hr)) return hr;
  225. if (bBufferLost)
  226. {
  227. // we are obviously no longer playing
  228. m_bBufferPlaying = false;
  229. }
  230. else
  231. {
  232. // otherwise, the buffer knows if we are still playing
  233. m_bStopped = !bPlaying;
  234. }
  235. }
  236. // if we don't have a buffer playing, try to calculate if this sound
  237. // would still be playing if it were audible.
  238. if (!m_bBufferPlaying)
  239. {
  240. m_bStopped = m_bufferPositionTracker.IsStopped();
  241. }
  242. else
  243. {
  244. // update the playing sound's volume and pitch, as needed.
  245. hr = m_pds3dbuffer->UpdateState(m_fGain, m_fPitch);
  246. if (ZFailed(hr)) return hr;
  247. }
  248. // if this has just stopped
  249. if (m_bStopped && !bWasStopped)
  250. {
  251. // free the sound buffer to conserve resources
  252. m_pds3dbuffer = NULL;
  253. m_bBufferPlaying = FALSE;
  254. // if there is an event source for the sound finishing, trigger it.
  255. if (m_peventsourceFinished != NULL)
  256. {
  257. m_peventsourceFinished->Trigger();
  258. }
  259. }
  260. // set this sound to be audible if it is playing and has an attenuation
  261. // of less than 90dB.
  262. m_bAudible = !m_bStopped && (m_fGain > c_fMinAudible);
  263. // set the dynamic priority to be the aproximate volume plus the static
  264. // priority, with a 0.1 dB priority boost for physically playing sounds
  265. // and up to a 0.2 dB boost for sounds less than 2 seconds old. This
  266. // should help insure that, all other things being equal, a playing
  267. // sound will continue playing and a younger sound will replace an
  268. // identical older sound.
  269. //
  270. // Repeating weapon sounds is an example of where this behavior is most
  271. // important.
  272. m_fDynamicPriority = m_fGain + m_fPriority
  273. + (m_bBufferPlaying ? 0.1f : 0)
  274. + max(0.0f, 1.0f * (2 - m_fAge));
  275. return S_OK;
  276. };
  277. // Creates and starts a real dsound buffer for this sound
  278. HRESULT DSVirtualSoundBuffer::StartBuffer(IDirectSound* pDirectSound,
  279. ISoundEngine::Quality quality, bool bAllowHardware)
  280. {
  281. HRESULT hr;
  282. // if we have a buffer playing, we have nothing more to do
  283. if (m_bBufferPlaying)
  284. {
  285. ZAssert(false); // I want to know about this
  286. return S_FALSE;
  287. }
  288. // Create a sound buffer with the given quality (or reuse the one we
  289. // have, if appropriate).
  290. hr = PrepareBuffer(pDirectSound, quality, bAllowHardware, false);
  291. if (FAILED(hr)) return hr;
  292. // start the sound.
  293. hr = m_pds3dbuffer->Start(m_bufferPositionTracker.GetPosition(),
  294. m_bufferPositionTracker.IsStopping());
  295. if (FAILED(hr)) return hr;
  296. m_bBufferPlaying = true;
  297. return S_OK;
  298. };
  299. // forcibly stops the given buffer.
  300. HRESULT DSVirtualSoundBuffer::StopBuffer()
  301. {
  302. HRESULT hr;
  303. // if we don't have a buffer playing, we have nothing to do
  304. if (!m_bBufferPlaying)
  305. {
  306. ZAssert(false); // I want to know about this
  307. return S_FALSE;
  308. }
  309. // try to stop the sound...
  310. hr = m_pds3dbuffer->Stop(true);
  311. if (ZFailed(hr)) return hr;
  312. // syncronize our buffer tracker with the final position of the buffer.
  313. DWORD dwPosition;
  314. hr = m_pds3dbuffer->GetPosition(dwPosition);
  315. if (ZFailed(hr)) return hr;
  316. if (dwPosition != 0) // 0 could be a wraparound for a static buffer
  317. m_bufferPositionTracker.SetPosition(dwPosition);
  318. m_bBufferPlaying = false;
  319. return S_OK;
  320. }
  321. // Sets the gain, from 0 to -100 dB
  322. HRESULT DSVirtualSoundBuffer::SetGain(float fGain)
  323. {
  324. if (fGain > 0)
  325. {
  326. ZAssert(false);
  327. return E_INVALIDARG;
  328. }
  329. m_fGain = max(fGain, -100.0f);
  330. return S_OK;
  331. };
  332. // Sets the pitch shift, where 1.0 is normal, 0.5 is half of normal speed,
  333. // and 2.0 is twice normal speed.
  334. HRESULT DSVirtualSoundBuffer::SetPitch(float fPitch)
  335. {
  336. fPitch = max(fPitch, DSBFREQUENCY_MIN / m_pdata->GetSampleRate());
  337. fPitch = min(fPitch, DSBFREQUENCY_MAX / m_pdata->GetSampleRate());
  338. m_fPitch = fPitch;
  339. return S_OK;
  340. };
  341. // sets the priority - used as a addition to volume when choosing which
  342. // sounds are most important to play.
  343. HRESULT DSVirtualSoundBuffer::SetPriority(float fPriority)
  344. {
  345. m_fPriority = fPriority;
  346. return S_OK;
  347. };
  348. // Stops the sound. If bForceNow is true the sound will stop ASAP,
  349. // possibly popping. If it is false some sounds may play a trail-off
  350. // sound or fade away.
  351. HRESULT DSVirtualSoundBuffer::Stop(bool bForceNow)
  352. {
  353. HRESULT hr;
  354. if (m_bBufferPlaying)
  355. {
  356. // REVIEW: fade out at higher quality settings?
  357. hr = m_pds3dbuffer->Stop(bForceNow);
  358. if (ZFailed(hr))
  359. {
  360. m_bRetryStop = true;
  361. return hr;
  362. }
  363. }
  364. m_bufferPositionTracker.Stop(bForceNow);
  365. return S_OK;
  366. };
  367. // returns S_OK if the sound is currently playing, S_FALSE otherwise.
  368. HRESULT DSVirtualSoundBuffer::IsPlaying()
  369. {
  370. return m_bStopped ? S_FALSE : S_OK;
  371. };
  372. // Gets an event which fires when the sound finishes playing (for any
  373. // reason)
  374. IEventSource* DSVirtualSoundBuffer::GetFinishEventSource()
  375. {
  376. // if we don't have an event source, create one.
  377. if (m_peventsourceFinished == NULL)
  378. {
  379. m_peventsourceFinished = new EventSourceImpl();
  380. }
  381. return m_peventsourceFinished;
  382. };
  383. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  384. TRef<ISoundTweakable> DSVirtualSoundBuffer::GetISoundTweakable()
  385. {
  386. return this;
  387. };
  388. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  389. TRef<ISoundTweakable3D> DSVirtualSoundBuffer::GetISoundTweakable3D()
  390. {
  391. return NULL;
  392. };
  393. #ifdef _DEBUG
  394. // return a human-readable description of the object, prepending
  395. // strIndent to the beginning of each line.
  396. ZString DSVirtualSoundBuffer::DebugDump(const ZString& strIndent)
  397. {
  398. return strIndent + "DSVirtualSoundBuffer: Priority " + ZString(m_fDynamicPriority)
  399. + ", Age " + ZString(m_fAge)
  400. + ", gain " + ZString(m_fGain)
  401. + ", pitch " + ZString(m_fPitch) + "\n"
  402. + SoundDebugDump(m_pdata, strIndent + " ")
  403. + ((m_pds3dbuffer) ? SoundDebugDump(m_pds3dbuffer, strIndent + " ") : "");
  404. }
  405. #endif
  406. /////////////////////////////////////////////////////////////////////////////
  407. //
  408. // DS3DVirtualSoundBuffer
  409. //
  410. /////////////////////////////////////////////////////////////////////////////
  411. // constructs a simple 3D capable sound
  412. DS3DVirtualSoundBuffer::DS3DVirtualSoundBuffer(ISoundPCMData* pdata, bool bLooping,
  413. TRef<ISoundPositionSource> psource) :
  414. DSVirtualSoundBuffer(pdata, bLooping),
  415. m_pposSource(psource),
  416. m_b3D(false),
  417. m_bListenerRelative(false),
  418. m_bPlayingIn3D(true),
  419. m_fMinimumDistance(20.0f),
  420. m_fInnerAngle(360),
  421. m_fOuterAngle(360),
  422. m_fOutsideGain(0.0f),
  423. m_vectPosition(0,0,0),
  424. m_vectVelocity(0,0,0),
  425. m_vectOrientation(0,0,1)
  426. {
  427. };
  428. // constructs an ASR 3D capable sound
  429. DS3DVirtualSoundBuffer::DS3DVirtualSoundBuffer(ISoundPCMData* pdata, DWORD dwLoopOffset,
  430. DWORD dwLoopLength, TRef<ISoundPositionSource> psource) :
  431. DSVirtualSoundBuffer(pdata, dwLoopOffset, dwLoopLength),
  432. m_pposSource(psource),
  433. m_b3D(false),
  434. m_bListenerRelative(false),
  435. m_bPlayingIn3D(true),
  436. m_fMinimumDistance(20.0f),
  437. m_fInnerAngle(360),
  438. m_fOuterAngle(360),
  439. m_fOutsideGain(0.0f),
  440. m_vectPosition(0,0,0),
  441. m_vectVelocity(0,0,0),
  442. m_vectOrientation(0,0,1)
  443. {
  444. };
  445. // prepare a sound buffer with the given quality and 3D support using the
  446. // direct sound object pointed to by pDirectSound.
  447. HRESULT DS3DVirtualSoundBuffer::PrepareBuffer(IDirectSound* pDirectSound,
  448. ISoundEngine::Quality quality, bool bAllowHardware, bool bSupport3D)
  449. {
  450. HRESULT hr;
  451. // If the old and new quality are not the same, we need a new buffer
  452. // because the 3D quality is set on a per-buffer basis.
  453. if (quality != m_quality || m_bAllowHardware != bAllowHardware)
  454. m_pds3dbuffer = NULL;
  455. // do all of the 2D buffer creation stuff.
  456. hr = DSVirtualSoundBuffer::PrepareBuffer(pDirectSound, quality, bAllowHardware, true);
  457. if (FAILED(hr)) return hr;
  458. // update the playing sound's 3D state, as needed.
  459. hr = m_pds3dbuffer->UpdateState3D(
  460. m_vectPosition, m_vectVelocity, m_vectOrientation,
  461. m_fMinimumDistance,
  462. m_fInnerAngle, m_fOuterAngle, m_fOutsideGain,
  463. m_bPlayingIn3D,
  464. m_bListenerRelative,
  465. false
  466. );
  467. if (ZFailed(hr)) return hr;
  468. return S_OK;
  469. };
  470. // Recalculates the sounds position, loudness, whether it's playing, etc..
  471. HRESULT DS3DVirtualSoundBuffer::Update(DWORD dwTimeElapsed,
  472. const Vector& vectListenerPosition, float fRolloffFactor)
  473. {
  474. HRESULT hr;
  475. // if the source no longer exists, stop the sound.
  476. hr = m_pposSource->IsPlaying();
  477. if (ZFailed(hr)) return hr;
  478. bool bSourceIsValid = hr == S_OK;
  479. if (!bSourceIsValid)
  480. {
  481. Stop(true);
  482. }
  483. // most of what DSVirtualSoundBuffer does is perfectly fine
  484. hr = DSVirtualSoundBuffer::Update(dwTimeElapsed, vectListenerPosition,
  485. fRolloffFactor);
  486. if (ZFailed(hr)) return hr;
  487. if (bSourceIsValid)
  488. {
  489. // get the position of the source.
  490. hr = m_pposSource->GetPosition(m_vectPosition);
  491. if (ZFailed(hr)) return hr;
  492. // find out whether the position is listener relative
  493. hr = m_pposSource->IsListenerRelative();
  494. if (ZFailed(hr)) return hr;
  495. m_bListenerRelative = hr == S_OK;
  496. // optimization: don't do 3D processing if the source position is
  497. // identical to the listener's position.
  498. m_bPlayingIn3D = m_b3D
  499. && m_vectPosition
  500. != (m_bListenerRelative ? Vector(0,0,0) : vectListenerPosition);
  501. if (m_bPlayingIn3D)
  502. {
  503. // get the velocity of the source
  504. hr = m_pposSource->GetVelocity(m_vectVelocity);
  505. if (ZFailed(hr)) return hr;
  506. // get the position of the listener relative to this source
  507. Vector vectRelListener = m_bListenerRelative
  508. ? -m_vectPosition : (vectListenerPosition - m_vectPosition);
  509. // get the distance to the listener
  510. float fDistanceSquared = vectRelListener.LengthSquared();
  511. // calculate some useful constants
  512. float fMinimumDistanceSquared = m_fMinimumDistance * m_fMinimumDistance;
  513. float fMaximumDistanceSquared = fMinimumDistanceSquared
  514. * c_fMinToMaxDistanceRatio * c_fMinToMaxDistanceRatio;
  515. // if we are outside of the maximum distance for this sound
  516. if (fDistanceSquared > fMaximumDistanceSquared || !m_bAudible)
  517. {
  518. // this sound is silent
  519. m_bAudible = false;
  520. m_fDynamicPriority += -100;
  521. }
  522. else
  523. {
  524. // calculate how much softer the sound is (in dB) based on distance
  525. float fDistanceGain = min(0.0f, (float)(
  526. fRolloffFactor * -10 * log(fDistanceSquared/fMinimumDistanceSquared)
  527. ));
  528. // if this has a sound cone
  529. float fConeGain = 0;
  530. if (m_fOutsideGain != 0.0f && m_fInnerAngle != 360
  531. && !m_bListenerRelative) // TODO: HACK: we don't have the info
  532. // to do the right thing for listener
  533. // relative sounds yet.
  534. {
  535. const float fRadiansToDegrees = 57.2957795f;
  536. // get the orientation of the source
  537. hr = m_pposSource->GetOrientation(m_vectOrientation);
  538. if (ZFailed(hr)) return hr;
  539. ZAssertIsUnitVector(m_vectOrientation);
  540. // calculate the angle between the source and the listener
  541. // (using the identity v1 * v2 = |v1||v2|cos(angle) )
  542. float fPhi = (float)acos(m_vectOrientation * vectRelListener
  543. / sqrt(fDistanceSquared)) * fRadiansToDegrees;
  544. // adjust the cone attenuation to reflect this.
  545. if (fPhi > m_fInnerAngle)
  546. {
  547. if (fPhi < m_fOuterAngle)
  548. {
  549. // do a linear fade between the outside and inside
  550. // gains.
  551. fConeGain = m_fOutsideGain
  552. * (fPhi - m_fInnerAngle)
  553. / (m_fOuterAngle - m_fInnerAngle);
  554. }
  555. else
  556. {
  557. fConeGain = m_fOutsideGain;
  558. }
  559. }
  560. }
  561. float fTotalGain = m_fGain + fDistanceGain + fConeGain;
  562. // update the audibility
  563. m_bAudible = fTotalGain > c_fMinAudible;
  564. // add in the distance and cone attenuations to the priority
  565. m_fDynamicPriority += fDistanceGain + fConeGain;
  566. }
  567. }
  568. };
  569. // if we have a buffer...
  570. if (m_bBufferPlaying)
  571. {
  572. // update the playing sound's 3D state, as needed.
  573. hr = m_pds3dbuffer->UpdateState3D(
  574. m_vectPosition, m_vectVelocity, m_vectOrientation,
  575. m_fMinimumDistance,
  576. m_fInnerAngle, m_fOuterAngle, m_fOutsideGain,
  577. m_bPlayingIn3D,
  578. m_bListenerRelative
  579. );
  580. if (ZFailed(hr)) return hr;
  581. }
  582. return S_OK;
  583. }
  584. // toggles 3D Positioning on and off for the given sound.
  585. HRESULT DS3DVirtualSoundBuffer::Set3D(bool b3D)
  586. {
  587. m_b3D = b3D;
  588. return S_OK;
  589. };
  590. // Sets the distance at which the sound will be at max volume. This
  591. // effects how quickly the sound drops off with distance.
  592. HRESULT DS3DVirtualSoundBuffer::SetMinimumDistance(float fMinimumDistance)
  593. {
  594. // Any distance greater than 0 is OK.
  595. if (fMinimumDistance <= 0)
  596. {
  597. ZAssert(false);
  598. return E_INVALIDARG;
  599. }
  600. m_fMinimumDistance = fMinimumDistance;
  601. return S_OK;
  602. };
  603. // Sets a sound cone of size fInnerAngle (in degrees) where the volume is at
  604. // normal levels, outside of which it fades down by fOutsideGain
  605. // (range of 0 to -100 db) at fOuterAngle (degrees) and beyond.
  606. HRESULT DS3DVirtualSoundBuffer::SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
  607. {
  608. // check the parameters
  609. if ((fInnerAngle < 0 || fInnerAngle > 360)
  610. || (fOuterAngle < 0 || fOuterAngle > 360)
  611. || (fOuterAngle < fInnerAngle)
  612. || (fOutsideGain < -100 || fOutsideGain > 0)
  613. )
  614. {
  615. ZAssert(false);
  616. return E_INVALIDARG;
  617. };
  618. m_fInnerAngle = fInnerAngle;
  619. m_fOuterAngle = fOuterAngle;
  620. m_fOutsideGain = fOutsideGain;
  621. return S_OK;
  622. };
  623. // Sets the gain, from 0 to -100 dB
  624. HRESULT DS3DVirtualSoundBuffer::SetGain(float fGain)
  625. {
  626. return DSVirtualSoundBuffer::SetGain(fGain);
  627. };
  628. // Sets the pitch shift, where 1.0 is normal, 0.5 is half of normal speed,
  629. // and 2.0 is twice normal speed.
  630. HRESULT DS3DVirtualSoundBuffer::SetPitch(float fPitch)
  631. {
  632. return DSVirtualSoundBuffer::SetPitch(fPitch);
  633. };
  634. // sets the priority - used as a addition to volume when choosing which
  635. // sounds are most important to play.
  636. HRESULT DS3DVirtualSoundBuffer::SetPriority(float fPriority)
  637. {
  638. return DSVirtualSoundBuffer::SetPriority(fPriority);
  639. };
  640. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  641. TRef<ISoundTweakable> DS3DVirtualSoundBuffer::GetISoundTweakable()
  642. {
  643. return DSVirtualSoundBuffer::GetISoundTweakable();
  644. };
  645. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  646. TRef<ISoundTweakable3D> DS3DVirtualSoundBuffer::GetISoundTweakable3D()
  647. {
  648. return this;
  649. };
  650. };