ds3dbuffer.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
  1. //
  2. // ds3dbuffer.cpp: low-level classes representing actual DirectSound3D buffers
  3. //
  4. #include "pch.h"
  5. #include "soundbase.h"
  6. #include "ds3dutil.h"
  7. #include "ds3dbuffer.h"
  8. namespace SoundEngine {
  9. /////////////////////////////////////////////////////////////////////////////
  10. //
  11. // DS3DSoundBuffer
  12. //
  13. /////////////////////////////////////////////////////////////////////////////
  14. // convert a gain in the range -100 to 0 dB to a direct sound volume
  15. inline LONG DS3DSoundBuffer::DSoundVolume(float fGain)
  16. {
  17. ZAssert((fGain >= -100) && (fGain <= 0));
  18. return (LONG)(100 * fGain);
  19. }
  20. // initializes the object, creating the DSoundBuffer itself and
  21. // initializing local variables.
  22. HRESULT DS3DSoundBuffer::CreateBuffer(IDirectSound* pDirectSound, ISoundPCMData* pdata,
  23. DWORD dwBufferSize, bool bStatic, bool bSupport3D, ISoundEngine::Quality quality,
  24. bool bAllowHardware)
  25. {
  26. HRESULT hr;
  27. // check the arguments
  28. if (!pdata || !pDirectSound)
  29. {
  30. ZAssert(false);
  31. return E_POINTER;
  32. }
  33. // set a few state variables with the new info we have
  34. m_b3D = bSupport3D;
  35. m_dwSampleRate = pdata->GetSampleRate();
  36. // describe the new buffer we want
  37. WAVEFORMATEX waveformatex;
  38. DSBUFFERDESC dsbufferdesc;
  39. waveformatex.cbSize = sizeof(waveformatex);
  40. waveformatex.wFormatTag = WAVE_FORMAT_PCM;
  41. waveformatex.nChannels = pdata->GetNumberOfChannels();
  42. waveformatex.nSamplesPerSec = m_dwSampleRate;
  43. waveformatex.wBitsPerSample = pdata->GetBitsPerSample();
  44. waveformatex.nBlockAlign = waveformatex.wBitsPerSample / 8 * waveformatex.nChannels;
  45. waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign;
  46. dsbufferdesc.dwSize = sizeof(dsbufferdesc);
  47. dsbufferdesc.dwFlags =
  48. (bSupport3D ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : 0)
  49. | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME
  50. | DSBCAPS_GETCURRENTPOSITION2
  51. | (bStatic ? DSBCAPS_STATIC : 0);
  52. dsbufferdesc.dwBufferBytes = dwBufferSize;
  53. dsbufferdesc.dwReserved = 0;
  54. dsbufferdesc.lpwfxFormat = &waveformatex;
  55. #if DIRECTSOUND_VERSION >= 0x0700
  56. if (bAllowHardware)
  57. dsbufferdesc.dwFlags |= DSBCAPS_LOCDEFER;
  58. else
  59. dsbufferdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
  60. if (bSupport3D)
  61. {
  62. switch (quality)
  63. {
  64. case ISoundEngine::minQuality:
  65. dsbufferdesc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
  66. break;
  67. case ISoundEngine::midQuality:
  68. dsbufferdesc.guid3DAlgorithm = DS3DALG_DEFAULT;
  69. break;
  70. case ISoundEngine::maxQuality:
  71. dsbufferdesc.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
  72. break;
  73. };
  74. }
  75. else
  76. {
  77. dsbufferdesc.guid3DAlgorithm = GUID_NULL;
  78. }
  79. #endif
  80. // create the new buffer
  81. hr = pDirectSound->CreateSoundBuffer(&dsbufferdesc, &m_pdirectsoundbuffer, NULL);
  82. if (FAILED(hr)) return hr;
  83. // get a handle to the 3D buffer, if this is 3D
  84. if (bSupport3D)
  85. {
  86. hr = m_pdirectsoundbuffer->QueryInterface(IID_IDirectSound3DBuffer, (void**)&m_pdirectsound3Dbuffer);
  87. if (ZFailed(hr)) return hr;
  88. }
  89. return S_OK;
  90. };
  91. // Use an exisiting to initialize this buffer. Note that the buffers will
  92. // share memory, so this only really works for static buffers.
  93. HRESULT DS3DSoundBuffer::DuplicateBuffer(IDirectSound* pDirectSound, DS3DSoundBuffer* pBuffer)
  94. {
  95. HRESULT hr;
  96. // check the arguments
  97. if (!pBuffer || !pDirectSound)
  98. {
  99. ZAssert(false);
  100. return E_POINTER;
  101. }
  102. if (!pBuffer->m_pdirectsoundbuffer)
  103. {
  104. ZAssert(false);
  105. return E_INVALIDARG;
  106. }
  107. // copy the basic info
  108. m_b3D = pBuffer->m_b3D;
  109. m_bListenerRelative = pBuffer->m_bListenerRelative;
  110. m_dwSampleRate = pBuffer->m_dwSampleRate;
  111. // duplicate the buffer
  112. hr = pDirectSound->DuplicateSoundBuffer(pBuffer->m_pdirectsoundbuffer, &m_pdirectsoundbuffer);
  113. if (FAILED(hr)) return hr;
  114. // reset the 2D info.
  115. hr = m_pdirectsoundbuffer->SetVolume(DSoundVolume(m_fGain));
  116. if (ZFailed(hr)) return hr;
  117. hr = m_pdirectsoundbuffer->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
  118. if (ZFailed(hr)) return hr;
  119. // if this is 3D
  120. if (pBuffer->m_pdirectsound3Dbuffer != NULL)
  121. {
  122. DS3DBUFFER ds3dbuf;
  123. // get a handle to the 3D buffer
  124. hr = m_pdirectsoundbuffer->QueryInterface(IID_IDirectSound3DBuffer, (void**)&m_pdirectsound3Dbuffer);
  125. if (ZFailed(hr)) return hr;
  126. // reset the 3D info.
  127. ds3dbuf.dwSize = sizeof(ds3dbuf);
  128. ConvertVector(ds3dbuf.vPosition, m_vectPosition);
  129. ConvertVector(ds3dbuf.vVelocity, m_vectVelocity);
  130. ds3dbuf.dwInsideConeAngle = (LONG)m_fInnerAngle;
  131. ds3dbuf.dwOutsideConeAngle = (LONG)m_fOuterAngle;
  132. ConvertVector(ds3dbuf.vConeOrientation, m_vectOrientation);
  133. ds3dbuf.lConeOutsideVolume = DSoundVolume(m_fOutsideGain);
  134. ds3dbuf.flMaxDistance = m_fMinimumDistance * c_fMinToMaxDistanceRatio;
  135. ds3dbuf.flMinDistance = m_fMinimumDistance;
  136. ds3dbuf.dwMode = m_b3D
  137. ? (m_bListenerRelative ? DS3DMODE_HEADRELATIVE : DS3DMODE_NORMAL)
  138. : DS3DMODE_DISABLE;
  139. hr = m_pdirectsound3Dbuffer->SetAllParameters(&ds3dbuf, DS3D_IMMEDIATE);
  140. if (ZFailed(hr)) return hr;
  141. }
  142. return S_OK;
  143. };
  144. // Start the buffer, starting as a looping buffer if requested.
  145. HRESULT DS3DSoundBuffer::StartImpl(bool bLooping)
  146. {
  147. HRESULT hr;
  148. DWORD dwFlags = bLooping ? DSBPLAY_LOOPING : 0;
  149. // DX7 bug: if the given sound is frequency shifted, we need to unshift
  150. // it before starting the buffer.
  151. if (m_fPitch != 1.0)
  152. {
  153. hr = m_pdirectsoundbuffer->SetFrequency((LONG)m_dwSampleRate);
  154. if (ZFailed(hr)) return hr;
  155. }
  156. // try starting the buffer
  157. hr = m_pdirectsoundbuffer->Play(0, 0, dwFlags);
  158. if (hr == DSERR_BUFFERLOST)
  159. {
  160. // the buffer was lost
  161. debugf("DSound buffer lost.\n");
  162. // try reloading it
  163. hr = RestoreBuffer();
  164. if (ZFailed(hr)) return hr;
  165. // try starting it again
  166. hr = m_pdirectsoundbuffer->Play(0, 0, dwFlags);
  167. }
  168. if (FAILED(hr))
  169. {
  170. ZAssert(hr == DSERR_INVALIDCALL);
  171. debugf("Error starting sound: %X\n", hr);
  172. return hr;
  173. }
  174. // DX7 bug: now reshift it since the buffer has been started
  175. if (m_fPitch != 1.0)
  176. {
  177. hr = m_pdirectsoundbuffer->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
  178. if (ZFailed(hr)) return hr;
  179. }
  180. return S_OK;
  181. }
  182. DS3DSoundBuffer::DS3DSoundBuffer() :
  183. m_vectPosition(0, 0, 0),
  184. m_vectVelocity(0, 0, 0),
  185. m_vectOrientation(1.1f, 0, 0), // should be reset before playing
  186. m_fMinimumDistance(DS3D_DEFAULTMINDISTANCE + 0.01f), // should be reset
  187. m_fInnerAngle(360), m_fOuterAngle(360), m_fOutsideGain(0),
  188. m_fGain(0),
  189. m_fPitch(1.0f),
  190. m_bListenerRelative(false)
  191. {
  192. }
  193. DS3DSoundBuffer::~DS3DSoundBuffer()
  194. {
  195. // make sure these are released before the DirectSound object.
  196. m_pdirectsoundbuffer = NULL;
  197. m_pdirectsound3Dbuffer = NULL;
  198. }
  199. // Sets the volume and pitch iff the new values differ from the old.
  200. HRESULT DS3DSoundBuffer::UpdateState(float fGain, float fPitch, bool bCanDefer)
  201. {
  202. HRESULT hr;
  203. if (m_fGain != fGain)
  204. {
  205. if (fGain < -100 || fGain > 0)
  206. {
  207. ZAssert(false);
  208. return E_INVALIDARG;
  209. }
  210. m_fGain = fGain;
  211. hr = m_pdirectsoundbuffer->SetVolume(DSoundVolume(m_fGain));
  212. if (ZFailed(hr)) return hr;
  213. }
  214. if (m_fPitch != fPitch)
  215. {
  216. if (fPitch < (DSBFREQUENCY_MIN / m_dwSampleRate)
  217. || (fPitch > DSBFREQUENCY_MAX / m_dwSampleRate))
  218. {
  219. ZAssert(false);
  220. return E_INVALIDARG;
  221. }
  222. m_fPitch = fPitch;
  223. hr = m_pdirectsoundbuffer->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
  224. if (ZFailed(hr)) return hr;
  225. }
  226. return S_OK;
  227. };
  228. // Sets the 3D info iff the new values differ from the old.
  229. HRESULT DS3DSoundBuffer::UpdateState3D(
  230. const Vector& vectPosition,
  231. const Vector& vectVelocity,
  232. const Vector& vectOrientation,
  233. float fMinimumDistance,
  234. float fInnerAngle,
  235. float fOuterAngle,
  236. float fOutsideGain,
  237. bool b3D,
  238. bool bListenerRelative,
  239. bool bCanDefer
  240. )
  241. {
  242. // if this is not a 3D-capable sound, we should not be called
  243. if (m_pdirectsound3Dbuffer == NULL)
  244. {
  245. ZAssert(FALSE);
  246. return E_NOTIMPL;
  247. }
  248. // if something has changed...
  249. if ((m_vectPosition != vectPosition)
  250. || (m_vectVelocity != vectVelocity)
  251. || (m_vectOrientation != vectOrientation)
  252. || (m_fMinimumDistance != fMinimumDistance)
  253. || (m_fInnerAngle != fInnerAngle)
  254. || (m_fOuterAngle != fOuterAngle)
  255. || (m_fOutsideGain != fOutsideGain)
  256. || (m_b3D != b3D)
  257. || (m_bListenerRelative != bListenerRelative)
  258. )
  259. {
  260. HRESULT hr;
  261. DS3DBUFFER ds3dbuf;
  262. // check the arguments
  263. ZAssertIsUnitVector(vectOrientation);
  264. if ((fMinimumDistance <= 0)
  265. || (fInnerAngle < 0 || fInnerAngle > 360)
  266. || (fOuterAngle < 0 || fOuterAngle > 360)
  267. || (fOutsideGain < -100 || fOutsideGain > 0)
  268. )
  269. {
  270. ZAssert(false);
  271. return E_INVALIDARG;
  272. }
  273. // set the cached parameters to the new info
  274. m_vectPosition = vectPosition;
  275. m_vectVelocity = vectVelocity;
  276. m_vectOrientation = vectOrientation;
  277. m_fMinimumDistance = fMinimumDistance;
  278. m_fInnerAngle = fInnerAngle;
  279. m_fOuterAngle = fOuterAngle;
  280. m_fOutsideGain = fOutsideGain;
  281. m_b3D = b3D;
  282. m_bListenerRelative = bListenerRelative;
  283. // set all of the parameters at once
  284. ds3dbuf.dwSize = sizeof(ds3dbuf);
  285. ConvertVector(ds3dbuf.vPosition, m_vectPosition);
  286. ConvertVector(ds3dbuf.vVelocity, m_vectVelocity);
  287. ds3dbuf.dwInsideConeAngle = (LONG)m_fInnerAngle;
  288. ds3dbuf.dwOutsideConeAngle = (LONG)m_fOuterAngle;
  289. ConvertVector(ds3dbuf.vConeOrientation, m_vectOrientation);
  290. ds3dbuf.lConeOutsideVolume = DSoundVolume(m_fOutsideGain);
  291. ds3dbuf.flMaxDistance = m_fMinimumDistance * c_fMinToMaxDistanceRatio;
  292. ds3dbuf.flMinDistance = m_fMinimumDistance;
  293. ds3dbuf.dwMode = m_b3D
  294. ? (m_bListenerRelative ? DS3DMODE_HEADRELATIVE : DS3DMODE_NORMAL)
  295. : DS3DMODE_DISABLE;
  296. hr = m_pdirectsound3Dbuffer->SetAllParameters(
  297. &ds3dbuf, bCanDefer ? DS3D_DEFERRED : DS3D_IMMEDIATE);
  298. if (ZFailed(hr)) return hr;
  299. }
  300. return S_OK;
  301. };
  302. // gets the current status of the buffer
  303. HRESULT DS3DSoundBuffer::GetStatus(bool& bPlaying, bool& bBufferLost)
  304. {
  305. HRESULT hr;
  306. DWORD dwStatus;
  307. hr = m_pdirectsoundbuffer->GetStatus(&dwStatus);
  308. bPlaying = (dwStatus & DSBSTATUS_PLAYING) != 0;
  309. bBufferLost = (dwStatus & DSBSTATUS_BUFFERLOST) != 0;
  310. return hr;
  311. };
  312. #ifdef _DEBUG
  313. // return a human-readable description of the object, prepending
  314. // strIndent to the beginning of each line.
  315. ZString DS3DSoundBuffer::DebugDump(const ZString& strIndent)
  316. {
  317. DWORD dwStatus;
  318. m_pdirectsoundbuffer->GetStatus(&dwStatus);
  319. return strIndent + "DS3DSoundBuffer: "
  320. + (m_b3D ? " 3D" : " 2D")
  321. + ((dwStatus & DSBSTATUS_PLAYING) ? " playing" : "")
  322. + ((dwStatus & DSBSTATUS_LOOPING) ? " looping" : "")
  323. #if DIRECTSOUND_VERSION >= 0x0700
  324. + ((dwStatus & DSBSTATUS_LOCHARDWARE) ? " in hardware" : "")
  325. + ((dwStatus & DSBSTATUS_LOCSOFTWARE) ? " in software" : "")
  326. #endif
  327. + (m_b3D ? ("\n" + strIndent + " Pos("
  328. + m_vectPosition.X() + ", " + m_vectPosition.Y() + ", "
  329. + m_vectPosition.Z() + ")")
  330. + (m_bListenerRelative ? " listener rel" : "")
  331. : ZString(""))
  332. + "\n";
  333. }
  334. #endif
  335. /////////////////////////////////////////////////////////////////////////////
  336. //
  337. // DS3DStaticSoundBuffer
  338. //
  339. /////////////////////////////////////////////////////////////////////////////
  340. // a cache of currently playing static sound buffers
  341. DS3DStaticSoundBuffer::BufferCache DS3DStaticSoundBuffer::bufferCache;
  342. // restore the contents of the buffer after a buffer loss
  343. HRESULT DS3DStaticSoundBuffer::RestoreBuffer()
  344. {
  345. HRESULT hr;
  346. // restore the buffer
  347. hr = m_pdirectsoundbuffer->Restore();
  348. if (ZFailed(hr)) return hr;
  349. // restore the contents of the buffer
  350. hr = LoadData();
  351. if (ZFailed(hr)) return hr;
  352. return S_OK;
  353. };
  354. // fills the buffer with data from the data source
  355. HRESULT DS3DStaticSoundBuffer::LoadData()
  356. {
  357. void *pvBlock1Data, *pvBlock2Data;
  358. DWORD dwBlock1Length, dwBlock2Length;
  359. HRESULT hr;
  360. // try locking the sound buffer
  361. hr = m_pdirectsoundbuffer->Lock(
  362. 0, m_pdata->GetSize(),
  363. &pvBlock1Data, &dwBlock1Length,
  364. &pvBlock2Data, &dwBlock2Length,
  365. 0
  366. );
  367. if (hr == DSERR_BUFFERLOST)
  368. {
  369. //
  370. // if the buffer was lost, try reloading it and starting it again.
  371. //
  372. // restore the buffer
  373. hr = m_pdirectsoundbuffer->Restore();
  374. if (ZFailed(hr)) return hr;
  375. // try locking it again
  376. hr = m_pdirectsoundbuffer->Lock(
  377. 0, m_pdata->GetSize(),
  378. &pvBlock1Data, &dwBlock1Length,
  379. &pvBlock2Data, &dwBlock2Length,
  380. 0
  381. );
  382. }
  383. if (ZFailed(hr)) return hr;
  384. // fill in the contents of the buffer
  385. m_pdata->GetData(pvBlock1Data, 0, dwBlock1Length);
  386. if (pvBlock2Data)
  387. {
  388. m_pdata->GetData(pvBlock2Data, dwBlock1Length, dwBlock2Length);
  389. }
  390. // unlock the buffer
  391. hr = m_pdirectsoundbuffer->Unlock(
  392. pvBlock1Data, dwBlock1Length,
  393. pvBlock2Data, dwBlock2Length
  394. );
  395. if (ZFailed(hr)) return hr;
  396. return S_OK;
  397. }
  398. // Constructor
  399. DS3DStaticSoundBuffer::DS3DStaticSoundBuffer() :
  400. m_bHasBeenPlayed(false)
  401. {
  402. m_iterSelf = bufferCache.end();
  403. }
  404. // Destructor
  405. DS3DStaticSoundBuffer::~DS3DStaticSoundBuffer()
  406. {
  407. // remove this buffer from the buffer cache.
  408. if (m_iterSelf != bufferCache.end())
  409. {
  410. bufferCache.erase(m_iterSelf);
  411. }
  412. }
  413. // Initializes this object with the given wave data, 3D support, and sound
  414. // quality.
  415. HRESULT DS3DStaticSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
  416. bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware
  417. )
  418. {
  419. HRESULT hr;
  420. m_bLooping = bLooping;
  421. m_pdata = pdata;
  422. // see if we have an instance of this buffer which we can simply clone
  423. CacheKey cacheKey(pdata, bSupport3D, quality, bAllowHardware);
  424. BufferCache::iterator iterCache = bufferCache.find(cacheKey);
  425. if (iterCache != bufferCache.end())
  426. {
  427. hr = DuplicateBuffer(pDirectSound, (*iterCache).second);
  428. if (FAILED(hr)) return hr;
  429. }
  430. else
  431. {
  432. // create a new buffer for the sound
  433. hr = CreateBuffer(pDirectSound, pdata, pdata->GetSize(), true, bSupport3D, quality, bAllowHardware);
  434. if (FAILED(hr)) return hr;
  435. // for static buffers, fill the buffer at creation time
  436. hr = LoadData();
  437. if (ZFailed(hr)) return hr;
  438. }
  439. m_iterSelf = bufferCache.insert(BufferCache::value_type(cacheKey, this));
  440. return S_OK;
  441. }
  442. // starts the given buffer playing at the given position.
  443. HRESULT DS3DStaticSoundBuffer::Start(DWORD dwPosition, bool bIsStopping)
  444. {
  445. HRESULT hr;
  446. ZAssert(dwPosition <= m_pdata->GetSize());
  447. if (m_bHasBeenPlayed || dwPosition != 0)
  448. {
  449. hr = m_pdirectsoundbuffer->SetCurrentPosition(dwPosition);
  450. if (ZFailed(hr)) return hr;
  451. }
  452. hr = StartImpl(m_bLooping);
  453. if (FAILED(hr)) return hr;
  454. m_bHasBeenPlayed = true;
  455. return S_OK;
  456. };
  457. // stops the given buffer.
  458. HRESULT DS3DStaticSoundBuffer::Stop(bool bForceNow)
  459. {
  460. return m_pdirectsoundbuffer->Stop();
  461. };
  462. // Gets the current position of the sound
  463. HRESULT DS3DStaticSoundBuffer::GetPosition(DWORD& dwPosition)
  464. {
  465. // for static sounds, the buffer position is the source position
  466. return m_pdirectsoundbuffer->GetCurrentPosition(&dwPosition, NULL);
  467. };
  468. /////////////////////////////////////////////////////////////////////////////
  469. //
  470. // DS3DStreamingSoundBuffer
  471. //
  472. /////////////////////////////////////////////////////////////////////////////
  473. // determines if a buffer cursor starting at dwStart and ending at dwEnd
  474. // would have crossed event dwTrigger.
  475. inline bool DS3DStreamingSoundBuffer::CrossedBoundary(DWORD dwTrigger, DWORD dwStart, DWORD dwEnd)
  476. {
  477. return dwTrigger < m_dwBufferSize
  478. && ((dwStart <= dwTrigger && (dwEnd >= dwTrigger || dwEnd < dwStart))
  479. || (dwStart > dwTrigger && (dwEnd < dwStart && dwEnd >= dwTrigger)));
  480. }
  481. // updates anything waiting for the play/read pointer to cross a certain
  482. // point in the buffer.
  483. void DS3DStreamingSoundBuffer::ReadPointerUpdate(DWORD dwReadOffset)
  484. {
  485. if (CrossedBoundary(m_dwStopOffset, m_dwLastReadOffset, dwReadOffset))
  486. {
  487. m_bPlayingSilence = true;
  488. }
  489. // update the last known read offset
  490. m_dwLastReadOffset = dwReadOffset;
  491. }
  492. // gets the last played source offset from the last played buffer offset.
  493. DWORD DS3DStreamingSoundBuffer::GetPlayedSourceOffset(DWORD dwLastPlayedPosition)
  494. {
  495. if (m_bPlayingSilence)
  496. {
  497. return m_pdata->GetSize();
  498. }
  499. else
  500. {
  501. // figure out the last offset where any real data was written
  502. DWORD dwWriteOffset = (m_dwStopOffset < m_dwBufferSize)
  503. ? m_dwStopOffset : m_dwWriteOffset;
  504. if (dwLastPlayedPosition > dwWriteOffset)
  505. {
  506. int nOffset = (int)(m_dwSourceOffset - m_dwBufferSize - dwWriteOffset
  507. + dwLastPlayedPosition);
  508. if (nOffset < 0)
  509. return nOffset + m_dwBufferSize;
  510. else
  511. return nOffset;
  512. }
  513. else
  514. {
  515. return m_dwSourceOffset - dwWriteOffset + dwLastPlayedPosition;
  516. }
  517. }
  518. };
  519. // restore the contents of the buffer after a buffer loss
  520. HRESULT DS3DStreamingSoundBuffer::RestoreBuffer()
  521. {
  522. HRESULT hr;
  523. // restore the buffer
  524. hr = m_pdirectsoundbuffer->Restore();
  525. if (ZFailed(hr)) return hr;
  526. // get the last known good playback position
  527. DWORD dwLastPlayedPosition;
  528. hr = m_pdirectsoundbuffer->GetCurrentPosition(&dwLastPlayedPosition, NULL);
  529. if (ZFailed(hr)) return hr;
  530. // reset the buffer write pointer to the same point
  531. m_dwWriteOffset = dwLastPlayedPosition;
  532. // update the read pointer flags, as appropriate
  533. ReadPointerUpdate(dwLastPlayedPosition);
  534. // reset the source offset to the last played position (taking into
  535. // account buffer wraparound).
  536. m_dwSourceOffset = GetPlayedSourceOffset(dwLastPlayedPosition);
  537. // restore the contents of the buffer from that position onwards.
  538. hr = UpdateBufferContents();
  539. if (ZFailed(hr)) return hr;
  540. return S_OK;
  541. };
  542. // Fills the buffer with as much data as it can handle.
  543. //
  544. // bTrustWritePtr is a bit of a hack for ASR sounds. Basically, the
  545. // problem is that when stop is called, an ASR sound needs to call
  546. // GetCurrentPosition to figure out the first safe place it can start
  547. // writing the sound's release. When UpdateBufferContents calls
  548. // GetCurrentPosition a moment later, however, it may return a slightly
  549. // larger offset for the minimum write position. We want to use the result
  550. // of the first call to GetCurrentPosition in this case, and not assert
  551. // that the write pointer is after the result returned by the second call
  552. // to GetCurrentPosition.
  553. HRESULT DS3DStreamingSoundBuffer::UpdateBufferContents(bool bTrustWritePtr)
  554. {
  555. HRESULT hr;
  556. DWORD dwReadOffset, dwMinWriteOffset;
  557. // get the boundaries of the sound buffer where we can write
  558. hr = m_pdirectsoundbuffer->GetCurrentPosition(&dwReadOffset, &dwMinWriteOffset);
  559. if (ZFailed(hr)) return hr;
  560. // make sure we are writing _after_ the last writable Offset
  561. // (messy because we have to take into account buffer wraparound)
  562. if ((((dwReadOffset < m_dwWriteOffset)
  563. && (dwMinWriteOffset < dwReadOffset || dwMinWriteOffset > m_dwWriteOffset))
  564. || ((dwReadOffset >= m_dwWriteOffset)
  565. && (dwMinWriteOffset < dwReadOffset && dwMinWriteOffset > m_dwWriteOffset))
  566. )
  567. && !bTrustWritePtr
  568. )
  569. {
  570. // Doh! We've played incorrect sound!
  571. debugf("sound buffer underflow; read %d, write %d, min write %d\n",
  572. dwReadOffset, m_dwWriteOffset, dwMinWriteOffset);
  573. // recover as best we can.
  574. m_dwWriteOffset = dwMinWriteOffset;
  575. }
  576. // Check to see if we've crossed any events waiting on the play pointer
  577. ReadPointerUpdate(dwReadOffset);
  578. // figure out the length of data we want.
  579. DWORD dwLength = ((dwReadOffset > m_dwWriteOffset)
  580. ? (dwReadOffset - m_dwWriteOffset)
  581. : (m_dwBufferSize + dwReadOffset - m_dwWriteOffset));
  582. void *pvBlock1Data, *pvBlock2Data;
  583. DWORD dwBlock1Length, dwBlock2Length;
  584. // try locking the sound buffer
  585. hr = m_pdirectsoundbuffer->Lock(
  586. m_dwWriteOffset, dwLength,
  587. &pvBlock1Data, &dwBlock1Length,
  588. &pvBlock2Data, &dwBlock2Length,
  589. 0
  590. );
  591. if (hr == DSERR_BUFFERLOST)
  592. {
  593. //
  594. // if the buffer was lost, try reloading it and starting it again.
  595. //
  596. // restore the buffer
  597. hr = m_pdirectsoundbuffer->Restore();
  598. if (ZFailed(hr)) return hr;
  599. // try locking it again
  600. hr = m_pdirectsoundbuffer->Lock(
  601. m_dwWriteOffset, dwLength,
  602. &pvBlock1Data, &dwBlock1Length,
  603. &pvBlock2Data, &dwBlock2Length,
  604. 0
  605. );
  606. }
  607. if (ZFailed(hr)) return hr;
  608. // fill in the contents of the buffer
  609. hr = StreamData(pvBlock1Data, dwBlock1Length);
  610. if (ZFailed(hr)) return hr;
  611. if (pvBlock2Data)
  612. {
  613. hr = StreamData(pvBlock2Data, dwBlock2Length);
  614. if (ZFailed(hr)) return hr;
  615. }
  616. // unlock the buffer
  617. hr = m_pdirectsoundbuffer->Unlock(
  618. pvBlock1Data, dwBlock1Length,
  619. pvBlock2Data, dwBlock2Length
  620. );
  621. if (ZFailed(hr)) return hr;
  622. return S_OK;
  623. }
  624. // streams the given length of data out to the buffer pointed to by pvBuffer
  625. HRESULT DS3DStreamingSoundBuffer::StreamData(void *pvBuffer, DWORD dwLength)
  626. {
  627. ZAssert(pvBuffer && dwLength != 0);
  628. DWORD dwLengthRemaining = dwLength;
  629. BYTE* pcWritePtr = (BYTE*)pvBuffer;
  630. DWORD dwSourceSize = m_pdata->GetSize();
  631. // just copy the source to the buffer until the buffer is filled or,
  632. // for non-looping sounds, we reach the end of the source.
  633. do {
  634. // copy data up until the end of the sound.
  635. DWORD dwCopyLength = min(dwLengthRemaining, dwSourceSize - m_dwSourceOffset);
  636. // copy the source
  637. if (dwCopyLength > 0)
  638. m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
  639. // update the pointers
  640. pcWritePtr += dwCopyLength;
  641. m_dwSourceOffset += dwCopyLength;
  642. dwLengthRemaining -= dwCopyLength;
  643. // if we are looping and we've reached the end, go back the start.
  644. if (m_dwSourceOffset == dwSourceSize && m_bLooping)
  645. m_dwSourceOffset = 0;
  646. } while (dwLengthRemaining > 0 && m_bLooping);
  647. // if we have more that we need to fill in...
  648. if (dwLengthRemaining != 0)
  649. {
  650. // it's past the end of the sound, so fill it with 0's
  651. BYTE nFillValue = (m_pdata->GetBitsPerSample() == 8) ? 0x80 : 0;
  652. memset(pcWritePtr, nFillValue, dwLengthRemaining);
  653. // mark where the sound stops
  654. if (m_dwStopOffset >= m_dwBufferSize)
  655. {
  656. m_dwStopOffset = m_dwWriteOffset + dwLength - dwLengthRemaining;
  657. }
  658. }
  659. // update the write offset
  660. m_dwWriteOffset = (m_dwWriteOffset + dwLength) % m_dwBufferSize;
  661. return S_OK;
  662. }
  663. // Initializes this object with the given wave data, 3D support, sound
  664. // quality, and buffer length (in seconds)
  665. HRESULT DS3DStreamingSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
  666. bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware,
  667. float fBufferLength
  668. )
  669. {
  670. HRESULT hr;
  671. m_bLooping = bLooping;
  672. m_pdata = pdata;
  673. m_dwBufferSize = (DWORD)(fBufferLength * pdata->GetBytesPerSec());
  674. // create a new buffer for the sound
  675. hr = CreateBuffer(pDirectSound, pdata, m_dwBufferSize, false, bSupport3D, quality, bAllowHardware);
  676. if (FAILED(hr)) return hr;
  677. return S_OK;
  678. }
  679. // Updates the contents of the streaming buffer.
  680. bool DS3DStreamingSoundBuffer::Execute()
  681. {
  682. HRESULT hr = UpdateBufferContents();
  683. ZAssert(SUCCEEDED(hr));
  684. // if we are playing silence, stop the buffer.
  685. if (m_bPlayingSilence)
  686. {
  687. HRESULT hr = m_pdirectsoundbuffer->Stop();
  688. ZAssert(SUCCEEDED(hr));
  689. return false;
  690. }
  691. else
  692. {
  693. return true;
  694. }
  695. }
  696. // Constructor
  697. DS3DStreamingSoundBuffer::DS3DStreamingSoundBuffer() :
  698. m_bHasBeenPlayed(false)
  699. {
  700. }
  701. // Destructor
  702. DS3DStreamingSoundBuffer::~DS3DStreamingSoundBuffer()
  703. {
  704. // if this has been played at least once
  705. if (m_bHasBeenPlayed)
  706. {
  707. // make sure we are not on the update list before we go away
  708. m_threadUpdate.RemoveTask(this);
  709. }
  710. }
  711. // Initializes this object with the given wave data, 3D support, and sound
  712. // quality.
  713. HRESULT DS3DStreamingSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
  714. bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware
  715. )
  716. {
  717. return Init(pDirectSound, pdata, bLooping, bSupport3D, quality, bAllowHardware, 5.0f);
  718. }
  719. // starts the given buffer playing at the given position.
  720. HRESULT DS3DStreamingSoundBuffer::Start(DWORD dwPosition, bool bIsStopping)
  721. {
  722. HRESULT hr;
  723. // make sure this is not already playing
  724. ZAssert(!m_threadUpdate.HasTask(this));
  725. if (m_bHasBeenPlayed)
  726. {
  727. hr = m_pdirectsoundbuffer->SetCurrentPosition(0);
  728. if (ZFailed(hr)) return hr;
  729. }
  730. ZAssert(dwPosition <= m_pdata->GetSize());
  731. m_dwSourceOffset = dwPosition;
  732. m_dwWriteOffset = 0;
  733. m_dwLastReadOffset = 0;
  734. m_dwStopOffset = m_dwBufferSize;
  735. m_bPlayingSilence = false;
  736. UpdateBufferContents();
  737. hr = StartImpl(true);
  738. if (FAILED(hr)) return hr;
  739. m_bHasBeenPlayed = true;
  740. // add it to the update thread's list
  741. m_threadUpdate.AddTask(this);
  742. return S_OK;
  743. };
  744. // stops the given buffer.
  745. HRESULT DS3DStreamingSoundBuffer::Stop(bool bForceNow)
  746. {
  747. // remove this from the update thread's task list
  748. m_threadUpdate.RemoveTask(this);
  749. return m_pdirectsoundbuffer->Stop();
  750. };
  751. // Gets the current position of the sound
  752. HRESULT DS3DStreamingSoundBuffer::GetPosition(DWORD& dwPosition)
  753. {
  754. DWORD dwBufferPosition;
  755. // get the buffer position is the source position
  756. HRESULT hr = m_pdirectsoundbuffer->GetCurrentPosition(&dwBufferPosition, NULL);
  757. if (ZFailed(hr)) return hr;
  758. // update the read pointer flags, as appropriate
  759. ReadPointerUpdate(dwBufferPosition);
  760. // translate from that to the source position
  761. dwPosition = GetPlayedSourceOffset(dwBufferPosition);
  762. return S_OK;
  763. };
  764. // gets the current status of the buffer
  765. HRESULT DS3DStreamingSoundBuffer::GetStatus(bool& bPlaying, bool& bBufferLost)
  766. {
  767. HRESULT hr;
  768. DWORD dwStatus;
  769. hr = m_pdirectsoundbuffer->GetStatus(&dwStatus);
  770. if (ZFailed(hr)) return hr;
  771. bPlaying = (dwStatus & DSBSTATUS_PLAYING) != 0;
  772. bBufferLost = (dwStatus & DSBSTATUS_BUFFERLOST) != 0;
  773. return S_OK;
  774. };
  775. // The update thread which handles filling the buffers as they play
  776. TaskListThread DS3DStreamingSoundBuffer::m_threadUpdate(THREAD_PRIORITY_TIME_CRITICAL, 500);
  777. /////////////////////////////////////////////////////////////////////////////
  778. //
  779. // DS3DASRSoundBuffer
  780. //
  781. /////////////////////////////////////////////////////////////////////////////
  782. // Destructor
  783. DS3DASRSoundBuffer::~DS3DASRSoundBuffer()
  784. {
  785. // if this has been played at least once
  786. if (m_bHasBeenPlayed)
  787. {
  788. // make sure we are not on the update list before we go away
  789. m_threadUpdate.RemoveTask(this);
  790. m_bHasBeenPlayed = false;
  791. }
  792. }
  793. // updates anything waiting for the play/read pointer to cross a certain
  794. // point in the buffer.
  795. void DS3DASRSoundBuffer::ReadPointerUpdate(DWORD dwReadOffset)
  796. {
  797. if (CrossedBoundary(m_dwSustainBufferOffset, m_dwLastReadOffset, dwReadOffset))
  798. {
  799. m_bPlayingSustain = true;
  800. }
  801. if (CrossedBoundary(m_dwReleaseBufferOffset, m_dwLastReadOffset, dwReadOffset))
  802. {
  803. ZAssert(m_bEnding);
  804. m_bPlayingRelease = true;
  805. }
  806. DS3DStreamingSoundBuffer::ReadPointerUpdate(dwReadOffset);
  807. }
  808. // gets the last played source offset from the last played buffer offset.
  809. DWORD DS3DASRSoundBuffer::GetPlayedSourceOffset(DWORD dwLastPlayedPosition)
  810. {
  811. if (m_bPlayingSilence)
  812. {
  813. return m_pdata->GetSize();
  814. }
  815. else
  816. {
  817. // figure out the buffer offset and source offset that are most relevant
  818. DWORD dwReferenceOffset;
  819. DWORD dwSourceOffset;
  820. // if we are in the attack portion
  821. if (!m_bPlayingSustain)
  822. {
  823. // if we have written part of the sustain section
  824. if (m_dwSustainBufferOffset < m_dwBufferSize)
  825. {
  826. // use the start of the sustain section as our reference point.
  827. dwReferenceOffset = m_dwSustainBufferOffset;
  828. dwSourceOffset = m_dwLoopOffset;
  829. }
  830. else
  831. {
  832. // use the real values
  833. dwReferenceOffset = m_dwWriteOffset;
  834. dwSourceOffset = m_dwSourceOffset;
  835. }
  836. }
  837. // if we are in the sustain portion
  838. else if (!m_bPlayingRelease)
  839. {
  840. // if we have written part of the release section
  841. if (m_dwReleaseBufferOffset < m_dwBufferSize)
  842. {
  843. // use the start of the release section as our reference point.
  844. dwReferenceOffset = m_dwReleaseBufferOffset;
  845. dwSourceOffset = m_dwLoopOffset + m_dwLoopLength;
  846. }
  847. else
  848. {
  849. // use the real values
  850. dwReferenceOffset = m_dwWriteOffset;
  851. dwSourceOffset = m_dwSourceOffset;
  852. }
  853. }
  854. // if we are in the release portion heading towards silence
  855. else
  856. {
  857. // if we have started writing silence
  858. if (m_dwStopOffset < m_dwBufferSize)
  859. {
  860. // use the end of the sound as our reference point.
  861. dwReferenceOffset = m_dwStopOffset;
  862. dwSourceOffset = m_pdata->GetSize();
  863. }
  864. else
  865. {
  866. // use the real values
  867. dwReferenceOffset = m_dwWriteOffset;
  868. dwSourceOffset = m_dwSourceOffset;
  869. }
  870. }
  871. // figure out the space between the buffer and the reference point
  872. int nDelta = int(dwReferenceOffset) - int(dwLastPlayedPosition);
  873. if (nDelta <= 0)
  874. {
  875. nDelta += m_dwBufferSize;
  876. if (nDelta < 0)
  877. {
  878. ZAssert(false);
  879. nDelta = 0;
  880. }
  881. }
  882. // use that to figure out the offset
  883. // if we are in the sustain loop
  884. if (m_bPlayingSustain && !m_bPlayingRelease)
  885. {
  886. // we need to figure out where in the sustain loop this puts us
  887. int nOffset = dwSourceOffset - (nDelta % m_dwLoopLength);
  888. if (nOffset < m_dwLoopOffset)
  889. nOffset += m_dwLoopLength;
  890. return nOffset;
  891. }
  892. else
  893. {
  894. // just adjust in the obvious way
  895. ZAssert(int(dwSourceOffset) - nDelta >= 0);
  896. return int(dwSourceOffset) - nDelta;
  897. }
  898. }
  899. };
  900. // streams the given length of data out to the buffer pointed to by pvBuffer
  901. HRESULT DS3DASRSoundBuffer::StreamData(void *pvBuffer, DWORD dwLength)
  902. {
  903. ZAssert(pvBuffer && dwLength != 0);
  904. DWORD dwSourceSize = m_pdata->GetSize();
  905. DWORD dwLengthRemaining = dwLength;
  906. BYTE* pcWritePtr = (BYTE*)pvBuffer;
  907. DWORD dwCopyLength;
  908. // if we are writing the attack portion of the sound...
  909. if (m_dwSourceOffset < m_dwLoopOffset)
  910. {
  911. // copy data up until the end of the attack or end of the write block.
  912. dwCopyLength = min(dwLengthRemaining, m_dwLoopOffset - m_dwSourceOffset);
  913. // copy the source
  914. if (dwCopyLength > 0)
  915. {
  916. m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
  917. }
  918. // update the pointers
  919. pcWritePtr += dwCopyLength;
  920. m_dwSourceOffset += dwCopyLength;
  921. dwLengthRemaining -= dwCopyLength;
  922. // If we've finished writing the attack, mark the beginning of the
  923. // sustain portion.
  924. if (m_dwSourceOffset >= m_dwLoopOffset)
  925. {
  926. ZAssert(m_dwSourceOffset == m_dwLoopOffset);
  927. m_dwSustainBufferOffset =
  928. m_dwWriteOffset + dwLength - dwLengthRemaining;
  929. }
  930. }
  931. // write 0 or more sustain loops. Do this by finishing any loop which
  932. // we have not completed, and then write additional loops if we are not
  933. // trying to end the sound.
  934. while (dwLengthRemaining > 0 && (
  935. (m_dwSourceOffset == m_dwLoopOffset && !m_bEnding)
  936. || (m_dwSourceOffset > m_dwLoopOffset
  937. && (m_dwSourceOffset < m_dwLoopOffset + m_dwLoopLength))))
  938. {
  939. // copy data up until the end of a sustain loop or end of the write block.
  940. dwCopyLength = min(dwLengthRemaining,
  941. m_dwLoopOffset + m_dwLoopLength - m_dwSourceOffset);
  942. // copy the source
  943. ZAssert(dwCopyLength > 0);
  944. m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
  945. // update the pointers
  946. pcWritePtr += dwCopyLength;
  947. m_dwSourceOffset += dwCopyLength;
  948. dwLengthRemaining -= dwCopyLength;
  949. // If we've finished an entire loop...
  950. if (m_dwSourceOffset >= m_dwLoopOffset + m_dwLoopLength)
  951. {
  952. ZAssert(m_dwSourceOffset == m_dwLoopOffset + m_dwLoopLength);
  953. // reset the source offset to the beginning of the loop
  954. m_dwSourceOffset = m_dwLoopOffset;
  955. // if this is the end of the sustain section, keep track of
  956. // that.
  957. if (m_bEnding)
  958. {
  959. m_dwReleaseBufferOffset =
  960. m_dwWriteOffset + dwLength - dwLengthRemaining;
  961. }
  962. }
  963. }
  964. // write the release section of the loop, if appropriate
  965. if (dwLengthRemaining > 0 && m_dwSourceOffset < dwSourceSize)
  966. {
  967. ZAssert(m_bEnding);
  968. ZAssert(m_dwSourceOffset == m_dwLoopOffset
  969. || (m_dwSourceOffset >= m_dwLoopOffset + m_dwLoopLength));
  970. // when we first get here, adjust the read pointer from the
  971. // beginning of the sustain loop to the begining of the release.
  972. if (m_dwSourceOffset < m_dwLoopOffset + m_dwLoopLength)
  973. m_dwSourceOffset = m_dwLoopOffset + m_dwLoopLength;
  974. // copy data up until the end or end of the write block.
  975. dwCopyLength = min(dwLengthRemaining, dwSourceSize - m_dwSourceOffset);
  976. // copy the source
  977. if (dwCopyLength > 0)
  978. {
  979. m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
  980. }
  981. // update the pointers
  982. pcWritePtr += dwCopyLength;
  983. m_dwSourceOffset += dwCopyLength;
  984. dwLengthRemaining -= dwCopyLength;
  985. // If we've finished writing the release, mark where the sound ends
  986. if (m_dwSourceOffset >= dwSourceSize)
  987. {
  988. ZAssert(m_dwSourceOffset == dwSourceSize);
  989. m_dwStopOffset = m_dwWriteOffset + dwLength - dwLengthRemaining;
  990. }
  991. }
  992. // if we have more that we need to fill in, then we need to fill with silence
  993. if (dwLengthRemaining != 0)
  994. {
  995. // it's past the end of the sound, so fill it with 0's
  996. BYTE nFillValue = (m_pdata->GetBitsPerSample() == 8) ? 0x80 : 0;
  997. memset(pcWritePtr, nFillValue, dwLengthRemaining);
  998. }
  999. // update the write offset
  1000. m_dwWriteOffset = (m_dwWriteOffset + dwLength) % m_dwBufferSize;
  1001. return S_OK;
  1002. }
  1003. // Initializes this object with the given wave data, 3D support, and sound
  1004. // quality.
  1005. HRESULT DS3DASRSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
  1006. DWORD dwLoopOffset, DWORD dwLoopLength, bool bSupport3D,
  1007. ISoundEngine::Quality quality, bool bAllowHardware
  1008. )
  1009. {
  1010. ZAssert(dwLoopOffset + dwLoopLength < pdata->GetSize());
  1011. ZAssert(dwLoopLength > 0);
  1012. HRESULT hr;
  1013. m_pdata = pdata;
  1014. m_dwLoopOffset = dwLoopOffset;
  1015. m_dwLoopLength = dwLoopLength;
  1016. // REVIEW: what size buffer would work best for these sounds?
  1017. hr = DS3DStreamingSoundBuffer::Init(
  1018. pDirectSound, pdata, false, bSupport3D, quality, bAllowHardware, 1.5f);
  1019. if (ZFailed(hr)) return hr;
  1020. return S_OK;
  1021. }
  1022. // starts the given buffer playing at the given position.
  1023. HRESULT DS3DASRSoundBuffer::Start(DWORD dwPosition, bool bIsStopping)
  1024. {
  1025. ZAssert(bIsStopping || dwPosition < m_dwLoopOffset + m_dwLoopLength);
  1026. DWORD dwSourceLength = m_pdata->GetSize();
  1027. m_dwSustainBufferOffset = m_dwBufferSize;
  1028. m_bPlayingSustain = dwPosition >= m_dwLoopOffset;
  1029. m_dwReleaseBufferOffset = m_dwBufferSize;
  1030. m_bPlayingRelease = dwPosition >= m_dwLoopOffset + m_dwLoopLength;
  1031. ZAssert(m_bPlayingRelease ? bIsStopping : true);
  1032. m_bEnding = bIsStopping;
  1033. DS3DStreamingSoundBuffer::Start(dwPosition, bIsStopping);
  1034. return S_OK;
  1035. };
  1036. // stops the given buffer.
  1037. HRESULT DS3DASRSoundBuffer::Stop(bool bForceNow)
  1038. {
  1039. if (bForceNow)
  1040. {
  1041. return DS3DStreamingSoundBuffer::Stop(bForceNow);
  1042. }
  1043. else if (!m_bEnding)
  1044. {
  1045. // remove this from the update thread's task list to avoid race
  1046. // conditions.
  1047. m_threadUpdate.RemoveTask(this);
  1048. // transition to the release as soon as possible.
  1049. // to do this, we just invalidate the buffer as far back as possible
  1050. // and rewrite it with the knowledge that we are ending the sound.
  1051. HRESULT hr;
  1052. // get the first possible write position
  1053. DWORD dwWritePosition;
  1054. hr = m_pdirectsoundbuffer->GetCurrentPosition(NULL, &dwWritePosition);
  1055. if (ZFailed(hr)) return hr;
  1056. // reset the source offset to the new write position (taking into
  1057. // account buffer wraparound).
  1058. int nWriteDelta = dwWritePosition - m_dwLastReadOffset;
  1059. if (nWriteDelta < 0)
  1060. {
  1061. nWriteDelta += m_dwBufferSize;
  1062. }
  1063. ZAssert(nWriteDelta >= 0);
  1064. DWORD dwLastPlayedSourceOffset = GetPlayedSourceOffset(m_dwLastReadOffset);
  1065. if (dwLastPlayedSourceOffset + nWriteDelta > m_dwLoopOffset)
  1066. {
  1067. m_dwSourceOffset =
  1068. (dwLastPlayedSourceOffset + nWriteDelta - m_dwLoopOffset)
  1069. % m_dwLoopLength + m_dwLoopOffset;
  1070. }
  1071. else
  1072. {
  1073. m_dwSourceOffset = dwLastPlayedSourceOffset + nWriteDelta;
  1074. }
  1075. // reset the buffer write pointer to the same point
  1076. m_dwWriteOffset = dwWritePosition;
  1077. // restore the contents of the buffer from that position onwards.
  1078. m_bEnding = true;
  1079. hr = UpdateBufferContents(true);
  1080. if (ZFailed(hr)) return hr;
  1081. // put it back in the update loop
  1082. m_threadUpdate.AddTask(this);
  1083. }
  1084. return S_OK;
  1085. };
  1086. };