soundtemplates.cpp 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091
  1. //
  2. // soundtemplates.cpp
  3. //
  4. // Several useful sound template implementations.
  5. //
  6. #include "pch.h"
  7. #include "soundbase.h"
  8. #include "soundutil.h"
  9. #include "soundtemplates.h"
  10. namespace SoundEngine {
  11. // a template for wave files
  12. class WaveFileTemplate : public ISoundTemplate
  13. {
  14. private:
  15. TRef<ISoundPCMData> m_pdata;
  16. ZString m_strFilename;
  17. public:
  18. // tries to initialize the object with the given file.
  19. HRESULT Init(const ZString& strFilename)
  20. {
  21. m_strFilename = strFilename;
  22. // check to make sure the file exists.
  23. HANDLE hFile;
  24. hFile = CreateFile(strFilename, 0, FILE_SHARE_READ, NULL,
  25. OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
  26. if (INVALID_HANDLE_VALUE == hFile)
  27. return STG_E_FILENOTFOUND;
  28. else
  29. {
  30. CloseHandle(hFile);
  31. return S_OK;
  32. }
  33. };
  34. // tries to initialize the object with the given data object.
  35. HRESULT Init(ISoundPCMData* pdata)
  36. {
  37. if (!pdata)
  38. return E_INVALIDARG;
  39. m_pdata = pdata;
  40. return S_OK;
  41. };
  42. // Creates a new instance of the given sound
  43. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  44. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
  45. {
  46. // if we have not loaded this sound yet...
  47. if (!m_pdata)
  48. {
  49. // try to load it.
  50. ZAssert(!m_strFilename.IsEmpty());
  51. if (FAILED(LoadWaveFile(m_pdata, m_strFilename)))
  52. {
  53. if (ZFailed(CreateDummyPCMData(m_pdata)))
  54. return E_FAIL;
  55. }
  56. }
  57. if (!pbufferSource)
  58. {
  59. ZAssert(false);
  60. return E_POINTER;
  61. }
  62. return pbufferSource->CreateStaticBuffer(psoundNew, m_pdata, false, psource);
  63. }
  64. };
  65. // creates a sound template for the given wave file
  66. HRESULT CreateWaveFileSoundTemplate(TRef<ISoundTemplate>& pstDest, const ZString& strFilename)
  67. {
  68. TRef<WaveFileTemplate> ptemplate = new WaveFileTemplate();
  69. HRESULT hr = ptemplate->Init(strFilename);
  70. if (ZSucceeded(hr))
  71. pstDest = ptemplate;
  72. return hr;
  73. };
  74. // creates a dummy sound template
  75. // (NOTE: Not optimized - fix or use only for error handling!)
  76. HRESULT CreateDummySoundTemplate(TRef<ISoundTemplate>& pstDest)
  77. {
  78. HRESULT hr;
  79. TRef<ISoundPCMData> pdata;
  80. hr = CreateDummyPCMData(pdata);
  81. if (ZFailed(hr))
  82. return hr;
  83. TRef<WaveFileTemplate> ptemplate = new WaveFileTemplate();
  84. hr = ptemplate->Init(pdata);
  85. if (ZSucceeded(hr))
  86. pstDest = ptemplate;
  87. return hr;
  88. };
  89. // a template turning another template into a 3D sound
  90. class ThreeDSoundTemplate : public ISoundTemplate
  91. {
  92. private:
  93. TRef<ISoundTemplate> m_pstBase;
  94. float m_fMinimumDistance;
  95. public:
  96. // tries to initialize the object with the given file.
  97. HRESULT Init(ISoundTemplate* pstSource, float fMinimumDistance)
  98. {
  99. if (!pstSource)
  100. {
  101. ZAssert(false);
  102. return E_POINTER;
  103. }
  104. if (fMinimumDistance <= 0)
  105. {
  106. ZAssert(false);
  107. return E_INVALIDARG;
  108. }
  109. m_pstBase = pstSource;
  110. m_fMinimumDistance = fMinimumDistance;
  111. return S_OK;
  112. };
  113. // Creates a new instance of the given sound
  114. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  115. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  116. {
  117. // REVIEW: Should we silently allow the 2D case?
  118. // we have to have a source to be 3D.
  119. /*if (!psource)
  120. {
  121. ZAssert(false);
  122. return E_INVALIDARG;
  123. }*/
  124. HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
  125. if (ZFailed(hr)) return hr;
  126. TRef<ISoundTweakable3D> ptweak3d = psoundNew->GetISoundTweakable3D();
  127. // we need to have a 3D sound for this to work.
  128. if (psource && !ptweak3d)
  129. {
  130. ZAssert(false);
  131. return E_FAIL;
  132. }
  133. if (ptweak3d)
  134. {
  135. hr = ptweak3d->Set3D(true);
  136. if (ZFailed(hr)) return hr;
  137. hr = ptweak3d->SetMinimumDistance(m_fMinimumDistance);
  138. if (ZFailed(hr)) return hr;
  139. }
  140. return S_OK;
  141. }
  142. };
  143. // creates a sound template which uses the given sound template to create
  144. // sounds and turns then into a 3D sounds with the given minimum distance.
  145. HRESULT Create3DSoundTemplate(TRef<ISoundTemplate>& pstDest,
  146. ISoundTemplate* pstSource, float fMinimumDistance)
  147. {
  148. TRef<ThreeDSoundTemplate> ptemplate = new ThreeDSoundTemplate();
  149. HRESULT hr = ptemplate->Init(pstSource, fMinimumDistance);
  150. if (ZSucceeded(hr))
  151. pstDest = ptemplate;
  152. return hr;
  153. };
  154. // a template which adds a sound cone to another template
  155. class SoundConeTemplate : public ISoundTemplate
  156. {
  157. private:
  158. TRef<ISoundTemplate> m_pstBase;
  159. float m_fInnerAngle, m_fOuterAngle, m_fOutsideGain;
  160. public:
  161. // tries to initialize the object with the given file.
  162. HRESULT Init(ISoundTemplate* pstSource, float fInnerAngle, float fOuterAngle, float fOutsideGain)
  163. {
  164. if (!pstSource)
  165. {
  166. ZAssert(false);
  167. return E_POINTER;
  168. }
  169. if ((fInnerAngle < 0 || fInnerAngle > 360)
  170. || (fOuterAngle < 0 || fOuterAngle > 360)
  171. || (fOutsideGain < -100 || fOutsideGain > 0)
  172. )
  173. {
  174. ZAssert(false);
  175. return E_INVALIDARG;
  176. }
  177. m_pstBase = pstSource;
  178. m_fInnerAngle = fInnerAngle;
  179. m_fOuterAngle = fOuterAngle;
  180. m_fOutsideGain = fOutsideGain;
  181. return S_OK;
  182. };
  183. // Creates a new instance of the given sound
  184. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  185. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  186. {
  187. // we have to have a source to be 3D.
  188. if (!psource)
  189. {
  190. ZAssert(false);
  191. return E_INVALIDARG;
  192. }
  193. HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
  194. if (ZFailed(hr)) return hr;
  195. TRef<ISoundTweakable3D> ptweak3d = psoundNew->GetISoundTweakable3D();
  196. // we need to have a 3D sound for this to work.
  197. if (!ptweak3d)
  198. {
  199. ZAssert(false);
  200. return E_FAIL;
  201. }
  202. hr = ptweak3d->SetCone(m_fInnerAngle, m_fOuterAngle, m_fOutsideGain);
  203. return S_OK;
  204. }
  205. };
  206. // creates a sound template which adds a sound cone to the given sound
  207. HRESULT CreateSoundConeTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  208. float fInnerAngle, float fOuterAngle, float fOutsideGain)
  209. {
  210. TRef<SoundConeTemplate> ptemplate = new SoundConeTemplate();
  211. HRESULT hr = ptemplate->Init(pstSource, fInnerAngle, fOuterAngle, fOutsideGain);
  212. if (ZSucceeded(hr))
  213. pstDest = ptemplate;
  214. return hr;
  215. };
  216. // a template adjusting the pitch of another template
  217. class SoundPitchTemplate : public ISoundTemplate
  218. {
  219. private:
  220. TRef<ISoundTemplate> m_pstBase;
  221. float m_fPitch;
  222. public:
  223. // a sound instance wrapper which adjusts pitch changes
  224. class PitchInstance : public SoundInstanceWrapper, public SoundTweakableWrapper
  225. {
  226. float m_fPitch;
  227. public:
  228. PitchInstance(float fPitch, ISoundInstance* pinstance,
  229. ISoundTweakable* ptweak) :
  230. SoundInstanceWrapper(pinstance),
  231. SoundTweakableWrapper(ptweak),
  232. m_fPitch(fPitch)
  233. {};
  234. // Sets the pitch shift, where 1.0 is normal, 0.5 is half of normal speed,
  235. // and 2.0 is twice normal speed.
  236. HRESULT SetPitch(float fPitch)
  237. {
  238. return SoundTweakableWrapper::SetPitch(m_fPitch * fPitch);
  239. };
  240. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  241. TRef<ISoundTweakable> GetISoundTweakable()
  242. {
  243. return this;
  244. };
  245. };
  246. public:
  247. HRESULT Init(ISoundTemplate* pstSource, float fPitch)
  248. {
  249. if (!pstSource)
  250. {
  251. ZAssert(false);
  252. return E_POINTER;
  253. }
  254. if (fPitch < 0.25f || fPitch > 4.0f)
  255. {
  256. ZAssert(false);
  257. return E_INVALIDARG;
  258. }
  259. m_pstBase = pstSource;
  260. m_fPitch = fPitch;
  261. return S_OK;
  262. };
  263. // Creates a new instance of the given sound
  264. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  265. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  266. {
  267. HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
  268. if (ZFailed(hr)) return hr;
  269. TRef<ISoundTweakable> ptweak = psoundNew->GetISoundTweakable();
  270. // we need to have a tweakable sound for this to work.
  271. if (!ptweak)
  272. {
  273. ZAssert(false);
  274. return E_FAIL;
  275. }
  276. hr = ptweak->SetPitch(m_fPitch);
  277. if (ZFailed(hr)) return hr;
  278. psoundNew = new PitchInstance(m_fPitch, psoundNew, ptweak);
  279. return S_OK;
  280. }
  281. };
  282. // creates a sound template which shifts the pitch of the created sound
  283. HRESULT CreatePitchSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  284. float fPitch)
  285. {
  286. TRef<SoundPitchTemplate> ptemplate = new SoundPitchTemplate();
  287. HRESULT hr = ptemplate->Init(pstSource, fPitch);
  288. if (ZSucceeded(hr))
  289. pstDest = ptemplate;
  290. return hr;
  291. };
  292. // a template adjusting the gain of another template
  293. class SoundGainTemplate : public ISoundTemplate
  294. {
  295. private:
  296. TRef<ISoundTemplate> m_pstBase;
  297. float m_fGain;
  298. public:
  299. // a sound instance wrapper which adjusts Gain changes
  300. class GainInstance : public SoundInstanceWrapper, public SoundTweakableWrapper
  301. {
  302. float m_fGain;
  303. public:
  304. GainInstance(float fGain, ISoundInstance* pinstance,
  305. ISoundTweakable* ptweak) :
  306. SoundInstanceWrapper(pinstance),
  307. SoundTweakableWrapper(ptweak),
  308. m_fGain(fGain)
  309. {};
  310. // Sets the Gain shift, where 1.0 is normal, 0.5 is half of normal speed,
  311. // and 2.0 is twice normal speed.
  312. HRESULT SetGain(float fGain)
  313. {
  314. return SoundTweakableWrapper::SetGain(m_fGain + fGain);
  315. };
  316. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  317. TRef<ISoundTweakable> GetISoundTweakable()
  318. {
  319. return this;
  320. };
  321. };
  322. public:
  323. HRESULT Init(ISoundTemplate* pstSource, float fGain)
  324. {
  325. if (!pstSource)
  326. {
  327. ZAssert(false);
  328. return E_POINTER;
  329. }
  330. if (fGain > 0.0f || fGain < -100.0f)
  331. {
  332. ZAssert(false);
  333. return E_INVALIDARG;
  334. }
  335. m_pstBase = pstSource;
  336. m_fGain = fGain;
  337. return S_OK;
  338. };
  339. // Creates a new instance of the given sound
  340. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  341. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  342. {
  343. HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
  344. if (ZFailed(hr)) return hr;
  345. TRef<ISoundTweakable> ptweak = psoundNew->GetISoundTweakable();
  346. // we need to have a tweakable sound for this to work.
  347. if (!ptweak)
  348. {
  349. ZAssert(false);
  350. return E_FAIL;
  351. }
  352. hr = ptweak->SetGain(m_fGain);
  353. if (ZFailed(hr)) return hr;
  354. psoundNew = new GainInstance(m_fGain, psoundNew, ptweak);
  355. return S_OK;
  356. }
  357. };
  358. // creates a sound template which adjusts the gain of the created sound
  359. HRESULT CreateGainSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  360. float fGain)
  361. {
  362. TRef<SoundGainTemplate> ptemplate = new SoundGainTemplate();
  363. HRESULT hr = ptemplate->Init(pstSource, fGain);
  364. if (ZSucceeded(hr))
  365. pstDest = ptemplate;
  366. return hr;
  367. };
  368. // a template adjusting the Priority of another template
  369. class SoundPriorityTemplate : public ISoundTemplate
  370. {
  371. private:
  372. TRef<ISoundTemplate> m_pstBase;
  373. float m_fPriority;
  374. public:
  375. // a sound instance wrapper which adjusts Priority changes
  376. class PriorityInstance : public SoundInstanceWrapper, public SoundTweakableWrapper
  377. {
  378. float m_fPriority;
  379. public:
  380. PriorityInstance(float fPriority, ISoundInstance* pinstance,
  381. ISoundTweakable* ptweak) :
  382. SoundInstanceWrapper(pinstance),
  383. SoundTweakableWrapper(ptweak),
  384. m_fPriority(fPriority)
  385. {};
  386. // Sets the Priority shift, where 1.0 is normal, 0.5 is half of normal speed,
  387. // and 2.0 is twice normal speed.
  388. HRESULT SetPriority(float fPriority)
  389. {
  390. return SoundTweakableWrapper::SetPriority(m_fPriority + fPriority);
  391. };
  392. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  393. TRef<ISoundTweakable> GetISoundTweakable()
  394. {
  395. return this;
  396. };
  397. };
  398. public:
  399. HRESULT Init(ISoundTemplate* pstSource, float fPriority)
  400. {
  401. if (!pstSource)
  402. {
  403. ZAssert(false);
  404. return E_POINTER;
  405. }
  406. m_pstBase = pstSource;
  407. m_fPriority = fPriority;
  408. return S_OK;
  409. };
  410. // Creates a new instance of the given sound
  411. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  412. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  413. {
  414. HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
  415. if (ZFailed(hr)) return hr;
  416. TRef<ISoundTweakable> ptweak = psoundNew->GetISoundTweakable();
  417. // we need to have a tweakable sound for this to work.
  418. if (!ptweak)
  419. {
  420. ZAssert(false);
  421. return E_FAIL;
  422. }
  423. hr = ptweak->SetPriority(m_fPriority);
  424. if (ZFailed(hr)) return hr;
  425. psoundNew = new PriorityInstance(m_fPriority, psoundNew, ptweak);
  426. return S_OK;
  427. }
  428. };
  429. // creates a sound template which adjusts the Priority of the created sound
  430. HRESULT CreatePrioritySoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  431. float fPriority)
  432. {
  433. TRef<SoundPriorityTemplate> ptemplate = new SoundPriorityTemplate();
  434. HRESULT hr = ptemplate->Init(pstSource, fPriority);
  435. if (ZSucceeded(hr))
  436. pstDest = ptemplate;
  437. return hr;
  438. };
  439. class LoopingSoundTemplate : public ISoundTemplate
  440. {
  441. TRef<ISoundTemplate> m_pstBase;
  442. // a wrapper for a buffer source which forces all sounds to be looping sounds
  443. class LoopingSoundBufferSource : public SoundBufferSourceWrapper
  444. {
  445. public:
  446. LoopingSoundBufferSource(ISoundBufferSource* pBase) :
  447. SoundBufferSourceWrapper(pBase) {};
  448. // Creates a static sound buffer of the given wave file.
  449. // The sound will loop until stopped.
  450. HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
  451. ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
  452. {
  453. return m_pBase->CreateStaticBuffer(psoundNew, pcmdata, true, psource);
  454. }
  455. };
  456. public:
  457. // tries to initialize the object with the given file.
  458. HRESULT Init(ISoundTemplate* pstSource)
  459. {
  460. if (!pstSource)
  461. {
  462. ZAssert(false);
  463. return E_POINTER;
  464. }
  465. m_pstBase = pstSource;
  466. return S_OK;
  467. };
  468. // Creates a new instance of the given sound
  469. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  470. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
  471. {
  472. if (!pbufferSource)
  473. {
  474. ZAssert(false);
  475. return E_POINTER;
  476. }
  477. TRef<ISoundBufferSource> pbufferLoopingSource = new LoopingSoundBufferSource(pbufferSource);
  478. return m_pstBase->CreateSound(psoundNew, pbufferLoopingSource, psource);
  479. };
  480. };
  481. // creates a looping sound from the given template
  482. HRESULT CreateLoopingSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource)
  483. {
  484. TRef<LoopingSoundTemplate> ptemplate = new LoopingSoundTemplate();
  485. HRESULT hr = ptemplate->Init(pstSource);
  486. if (ZSucceeded(hr))
  487. pstDest = ptemplate;
  488. return hr;
  489. };
  490. class ASRSoundTemplate : public ISoundTemplate
  491. {
  492. TRef<ISoundTemplate> m_pstBase;
  493. float m_fLoopStart, m_fLoopLength;
  494. // a wrapper for a buffer source which forces all sounds to be ASR sounds
  495. class ASRSoundBufferSource : public SoundBufferSourceWrapper
  496. {
  497. float m_fLoopStart, m_fLoopLength;
  498. public:
  499. ASRSoundBufferSource(ISoundBufferSource* pBase, float fLoopStart,
  500. float fLoopLength) :
  501. SoundBufferSourceWrapper(pBase),
  502. m_fLoopStart(fLoopStart),
  503. m_fLoopLength(fLoopLength)
  504. {};
  505. // Creates a static sound buffer of the given wave file.
  506. // The sound will loop until stopped.
  507. HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
  508. ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
  509. {
  510. unsigned uBytesPerSec = pcmdata->GetBytesPerSec();
  511. float fLength = pcmdata->GetSize()/float(uBytesPerSec);
  512. if (fLength < m_fLoopStart + m_fLoopLength)
  513. {
  514. ZAssert(false);
  515. return E_INVALIDARG;
  516. }
  517. unsigned uLoopOffset = unsigned(m_fLoopStart * uBytesPerSec);
  518. unsigned uLoopLength = unsigned(m_fLoopLength * uBytesPerSec);
  519. // fix the loop offset and length to be on sample boundaries
  520. unsigned uBytesPerSample = pcmdata->GetBytesPerSample();
  521. uLoopOffset -= uLoopOffset % uBytesPerSample;
  522. uLoopLength -= uLoopLength % uBytesPerSample;
  523. return m_pBase->CreateASRBuffer(psoundNew, pcmdata,
  524. uLoopOffset, uLoopLength, psource);
  525. }
  526. };
  527. public:
  528. // tries to initialize the object with the given file.
  529. HRESULT Init(ISoundTemplate* pstSource, float fLoopStart, float fLoopLength)
  530. {
  531. if (!pstSource)
  532. {
  533. ZAssert(false);
  534. return E_POINTER;
  535. }
  536. if (fLoopStart < 0.0f || fLoopLength <= 0.0f)
  537. {
  538. ZAssert(false);
  539. return E_INVALIDARG;
  540. }
  541. m_fLoopStart = fLoopStart;
  542. m_fLoopLength = fLoopLength;
  543. m_pstBase = pstSource;
  544. return S_OK;
  545. };
  546. // Creates a new instance of the given sound
  547. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  548. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
  549. {
  550. if (!pbufferSource)
  551. {
  552. ZAssert(false);
  553. return E_POINTER;
  554. }
  555. TRef<ISoundBufferSource> pbufferASRSource =
  556. new ASRSoundBufferSource(pbufferSource, m_fLoopStart, m_fLoopLength);
  557. return m_pstBase->CreateSound(psoundNew, pbufferASRSource, psource);
  558. };
  559. };
  560. // creates a ASR sound from the given template
  561. HRESULT CreateASRSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  562. float fLoopStart, float fLoopLength)
  563. {
  564. TRef<ASRSoundTemplate> ptemplate = new ASRSoundTemplate();
  565. HRESULT hr = ptemplate->Init(pstSource, fLoopStart, fLoopLength);
  566. if (ZSucceeded(hr))
  567. pstDest = ptemplate;
  568. return hr;
  569. };
  570. // a generic wrapper for an ISoundInstance which just delegates everything to
  571. // the object it wraps.
  572. // (not very useful by itself, but useful as a base class)
  573. class SoundPairWrapper : public ISoundInstance, public ISoundTweakable3D, public IEventSink
  574. {
  575. protected:
  576. TRef<ISoundInstance> m_pBase1;
  577. TRef<ISoundInstance> m_pBase2;
  578. TRef<ISoundTweakable> m_ptweak1;
  579. TRef<ISoundTweakable> m_ptweak2;
  580. TRef<ISoundTweakable3D> m_ptweak3D1;
  581. TRef<ISoundTweakable3D> m_ptweak3D2;
  582. TRef<EventSourceImpl> m_peventsourceFinished;
  583. TRef<IEventSink> m_peventDelegate;
  584. int m_nNumChildrenFinished;
  585. public:
  586. SoundPairWrapper(ISoundInstance* pBase1, ISoundInstance* pBase2) :
  587. m_pBase1(pBase1), m_pBase2(pBase2), m_nNumChildrenFinished(0)
  588. {
  589. ZAssert(pBase1 != NULL);
  590. ZAssert(pBase2 != NULL);
  591. };
  592. ~SoundPairWrapper()
  593. {
  594. // if we have an event source, we've hooked the event sources for the
  595. // two child sound instances.
  596. if (m_peventDelegate != NULL)
  597. {
  598. // unhook ourselves.
  599. m_pBase1->GetFinishEventSource()->RemoveSink(m_peventDelegate);
  600. m_pBase2->GetFinishEventSource()->RemoveSink(m_peventDelegate);
  601. }
  602. }
  603. // Stops the sound. If bForceNow is true the sound will stop ASAP,
  604. // possibly popping. If it is false some sounds may play a trail-off
  605. // sound or fade away.
  606. virtual HRESULT Stop(bool bForceNow = false)
  607. {
  608. HRESULT hr = m_pBase1->Stop(bForceNow);
  609. if (ZFailed(hr)) return hr;
  610. return m_pBase2->Stop(bForceNow);
  611. };
  612. // returns S_OK if the sound is currently playing, S_FALSE otherwise.
  613. virtual HRESULT IsPlaying()
  614. {
  615. // return S_OK if either sound is still playing
  616. HRESULT hr = m_pBase1->IsPlaying();
  617. if (ZFailed(hr) || hr == S_OK) return hr;
  618. return m_pBase2->IsPlaying();
  619. };
  620. // Gets an event which fires when the sound finishes playing (for any
  621. // reason)
  622. virtual IEventSource* GetFinishEventSource()
  623. {
  624. if (m_peventsourceFinished == NULL)
  625. {
  626. m_peventsourceFinished = new EventSourceImpl();
  627. m_peventDelegate = IEventSink::CreateDelegate(this);
  628. m_pBase1->GetFinishEventSource()->AddSink(m_peventDelegate);
  629. m_pBase2->GetFinishEventSource()->AddSink(m_peventDelegate);
  630. }
  631. return m_peventsourceFinished;
  632. };
  633. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  634. virtual TRef<ISoundTweakable> GetISoundTweakable()
  635. {
  636. // try to get the child sounds' tweakable interfaces.
  637. if (!m_ptweak1 || !m_ptweak2)
  638. {
  639. m_ptweak1 = m_pBase1->GetISoundTweakable();
  640. m_ptweak2 = m_pBase2->GetISoundTweakable();
  641. }
  642. // this sound is only tweakable if both it's child sounds are tweakable
  643. if (!m_ptweak1 || !m_ptweak2)
  644. {
  645. return NULL;
  646. }
  647. else
  648. {
  649. return this;
  650. }
  651. }
  652. virtual TRef<ISoundTweakable3D> GetISoundTweakable3D()
  653. {
  654. // try to get the child sounds' tweakable interfaces.
  655. if (!m_ptweak3D1 || !m_ptweak3D2)
  656. {
  657. m_ptweak3D1 = m_pBase1->GetISoundTweakable3D();
  658. m_ptweak3D2 = m_pBase2->GetISoundTweakable3D();
  659. }
  660. // this sound is only tweak3Dable if both it's child sounds are tweak3Dable
  661. if (!m_ptweak3D1 || !m_ptweak3D2)
  662. {
  663. return NULL;
  664. }
  665. else
  666. {
  667. return this;
  668. }
  669. }
  670. // forward a child-finished event.
  671. virtual bool OnEvent(IEventSource* pevent)
  672. {
  673. m_nNumChildrenFinished++;
  674. ZAssert(m_nNumChildrenFinished <= 2);
  675. // fire when the last child finishes
  676. if (m_nNumChildrenFinished >= 2)
  677. {
  678. m_peventsourceFinished->Trigger();
  679. }
  680. return true;
  681. };
  682. //
  683. // ISoundTweakable
  684. //
  685. // Sets the gain, from 0 to -100 dB
  686. virtual HRESULT SetGain(float fGain)
  687. {
  688. HRESULT hr = m_ptweak1->SetGain(fGain);
  689. if (ZFailed(hr)) return hr;
  690. return m_ptweak2->SetGain(fGain);
  691. };
  692. // Sets the pitch shift, where 1.0 is normal, 0.5 is half of normal speed,
  693. // and 2.0 is twice normal speed.
  694. virtual HRESULT SetPitch(float fPitch)
  695. {
  696. HRESULT hr = m_ptweak1->SetPitch(fPitch);
  697. if (ZFailed(hr)) return hr;
  698. return m_ptweak2->SetPitch(fPitch);
  699. }
  700. // sets the priority - used as a addition to volume when choosing which
  701. // sounds are most important to play.
  702. virtual HRESULT SetPriority(float fPriority)
  703. {
  704. HRESULT hr = m_ptweak1->SetPriority(fPriority);
  705. if (ZFailed(hr)) return hr;
  706. return m_ptweak2->SetPriority(fPriority);
  707. }
  708. //
  709. // ISoundTweakable3D
  710. //
  711. // toggles 3D Positioning on and off for the given sound.
  712. virtual HRESULT Set3D(bool b3D)
  713. {
  714. HRESULT hr = m_ptweak3D1->Set3D(b3D);
  715. if (ZFailed(hr)) return hr;
  716. return m_ptweak3D2->Set3D(b3D);
  717. }
  718. // Sets the distance at which the sound will be at max volume. This
  719. // effects how quickly the sound drops off with distance.
  720. virtual HRESULT SetMinimumDistance(float fMinimumDistance)
  721. {
  722. HRESULT hr = m_ptweak3D1->SetMinimumDistance(fMinimumDistance);
  723. if (ZFailed(hr)) return hr;
  724. return m_ptweak3D2->SetMinimumDistance(fMinimumDistance);
  725. }
  726. // Sets a sound cone of size fInnerAngle (in degrees) where the volume is at
  727. // normal levels, outside of which it fades down by fOutsideGain
  728. // (range of 0 to -100 db) at fOuterAngle (degrees) and beyond.
  729. virtual HRESULT SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
  730. {
  731. HRESULT hr = m_ptweak3D1->SetCone(fInnerAngle, fOuterAngle, fOutsideGain);
  732. if (ZFailed(hr)) return hr;
  733. return m_ptweak3D2->SetCone(fInnerAngle, fOuterAngle, fOutsideGain);
  734. }
  735. };
  736. // a template for merging two templates into one
  737. class PairedSoundTemplate : public ISoundTemplate
  738. {
  739. private:
  740. TRef<ISoundTemplate> m_pstBase1;
  741. TRef<ISoundTemplate> m_pstBase2;
  742. public:
  743. // tries to initialize the object with the given file.
  744. HRESULT Init(ISoundTemplate* pstSource1, ISoundTemplate* pstSource2)
  745. {
  746. if (!pstSource1 || !pstSource2)
  747. {
  748. ZAssert(false);
  749. return E_POINTER;
  750. }
  751. m_pstBase1 = pstSource1;
  752. m_pstBase2 = pstSource2;
  753. return S_OK;
  754. };
  755. // Creates a new instance of the given sound
  756. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  757. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  758. {
  759. TRef<ISoundInstance> psound1, psound2;
  760. HRESULT hr;
  761. hr = m_pstBase1->CreateSound(psound1, pbufferSource, psource);
  762. if (ZFailed(hr)) return hr;
  763. hr = m_pstBase2->CreateSound(psound2, pbufferSource, psource);
  764. if (ZFailed(hr)) return hr;
  765. if (!psound1 || !psound2)
  766. {
  767. ZAssert(false);
  768. return E_FAIL;
  769. }
  770. psoundNew = new SoundPairWrapper(psound1, psound2);
  771. return S_OK;
  772. }
  773. };
  774. // creates a sound template which plays two sounds at once
  775. HRESULT CreatePairedSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource1,
  776. ISoundTemplate* pstSource2)
  777. {
  778. TRef<PairedSoundTemplate> ptemplate = new PairedSoundTemplate();
  779. HRESULT hr = ptemplate->Init(pstSource1, pstSource2);
  780. if (ZSucceeded(hr))
  781. pstDest = ptemplate;
  782. return hr;
  783. };
  784. // a class to generate data for repeat-fire sounds
  785. class RepeatFirePCMData : public ISoundPCMData
  786. {
  787. // the base data we are working from
  788. TRef<ISoundPCMData> m_pdataBase;
  789. // a cached version of the sound playing and repeating once.
  790. void* m_pvData;
  791. // the length of the sound, in bytes
  792. DWORD m_dwSize;
  793. // the repeat delay, in bytes of sound
  794. DWORD m_dwRepeatLength;
  795. // constructor
  796. RepeatFirePCMData(ISoundPCMData* pdata, DWORD dwRepeatLength)
  797. {
  798. ZAssert(pdata);
  799. ZAssert(dwRepeatLength > 0);
  800. m_pvData = NULL;
  801. m_pdataBase = pdata;
  802. m_dwRepeatLength = dwRepeatLength;
  803. m_dwSize = m_pdataBase->GetSize() + m_dwRepeatLength * 2;
  804. }
  805. //
  806. // caching support
  807. //
  808. // a cache of previously generated repeat-fire sounds
  809. typedef std::pair<ISoundPCMData*, DWORD> RepeatFireCacheKey;
  810. typedef std::map<RepeatFireCacheKey, TRef<RepeatFirePCMData> >
  811. RepeatFireDataCache;
  812. static RepeatFireDataCache s_cache;
  813. // A ref count on the cache.
  814. // for proper destruction order we need to free the cache when the last
  815. // repeat fire template is released.
  816. static int s_nCacheRefCount;
  817. public:
  818. // adds a reference to the internal cache of repeat fire data
  819. static void AddCacheRef()
  820. {
  821. ++s_nCacheRefCount;
  822. }
  823. // releases a reference to the internal cache of repeat fire data
  824. static void ReleaseCacheRef()
  825. {
  826. --s_nCacheRefCount;
  827. ZAssert(s_nCacheRefCount >= 0);
  828. if (s_nCacheRefCount == 0 && !s_cache.empty())
  829. {
  830. s_cache.clear();
  831. }
  832. }
  833. // creates a repeat fire data source for the given sample and repeat delay
  834. // (in seconds).
  835. static RepeatFirePCMData* Create(ISoundPCMData* pdata, float fRepeatDelay)
  836. {
  837. ZAssert(s_nCacheRefCount > 0);
  838. if (!pdata)
  839. return NULL;
  840. DWORD dwRepeatLength = (DWORD)(fRepeatDelay * (pdata->GetBytesPerSec()))
  841. & ~(pdata->GetBytesPerSample() - 1);
  842. // if the repeat length is over 16 Meg, don't even try it.
  843. if (dwRepeatLength > 0x1000000 || dwRepeatLength == 0)
  844. return NULL;
  845. // otherwise, try to find it in the cache.
  846. RepeatFireDataCache::iterator iter;
  847. RepeatFireCacheKey key(pdata, dwRepeatLength);
  848. iter = s_cache.find(key);
  849. // if there was not a data source like this in the cache, create one
  850. // and add it.
  851. if (iter == s_cache.end())
  852. {
  853. iter = (s_cache.insert(std::pair<RepeatFireCacheKey, TRef<RepeatFirePCMData> >(
  854. key, new RepeatFirePCMData(pdata, dwRepeatLength)
  855. ))).first;
  856. }
  857. return (*iter).second;
  858. }
  859. // frees the sound
  860. ~RepeatFirePCMData()
  861. {
  862. // if we generated the data, delete it.
  863. if (m_pvData)
  864. delete [] (char*)m_pvData;
  865. }
  866. // gets the loop offset and loop length (which are equal) for this sound.
  867. DWORD GetLoopStart()
  868. {
  869. return m_dwRepeatLength*2;
  870. }
  871. // gets the loop offset and loop length (which are equal) for this sound.
  872. DWORD GetLoopLength()
  873. {
  874. return m_dwRepeatLength;
  875. }
  876. //
  877. // ISoundPCMData
  878. //
  879. // Gets the number of channels in the data
  880. virtual unsigned GetNumberOfChannels()
  881. {
  882. return m_pdataBase->GetNumberOfChannels();
  883. };
  884. // Gets the number of bits per sample
  885. virtual unsigned GetBitsPerSample()
  886. {
  887. return m_pdataBase->GetBitsPerSample();
  888. };
  889. // Gets the default frequency (in samples per second) of the data
  890. virtual unsigned GetSampleRate()
  891. {
  892. return m_pdataBase->GetSampleRate();
  893. }
  894. // Gets the size of the data
  895. virtual unsigned GetSize()
  896. {
  897. return m_dwSize;
  898. }
  899. // Copies the specified portion of the data
  900. virtual void GetData(void* dest, unsigned nOffset, unsigned nLength)
  901. {
  902. // if we have not created an ASR version of the data, do so
  903. if (!m_pvData)
  904. {
  905. m_pvData = new char[m_dwSize];
  906. // copy the old data to generate the second sound.
  907. m_pdataBase->GetData((char*)(m_pvData) + m_dwRepeatLength * 2, 0,
  908. m_dwSize - m_dwRepeatLength * 2);
  909. // copy the chunk of the first sound before the second sound starts
  910. memcpy(m_pvData, (char*)(m_pvData) + m_dwRepeatLength * 2, m_dwRepeatLength);
  911. // for the part where they overlap, add the two together by adding
  912. // each sample to the sample m_dwRepeatLength bytes after it.
  913. // if the samples are 8 bits
  914. if (m_pdataBase->GetBitsPerSample() == 8)
  915. {
  916. // add them as unsigned bytes with a 0x7F offset
  917. unsigned char* pcSample2Start =
  918. (unsigned char*)(m_pvData) + m_dwRepeatLength;
  919. unsigned char* pcSample3Start =
  920. (unsigned char*)(m_pvData) + m_dwRepeatLength * 2;
  921. unsigned char* pcSample1End =
  922. (unsigned char*)(m_pvData) + (m_dwSize - m_dwRepeatLength * 2);
  923. unsigned char* pcSample2End =
  924. (unsigned char*)(m_pvData) + (m_dwSize - m_dwRepeatLength);
  925. unsigned char* pcAdd;
  926. for (pcAdd = pcSample2Start; pcAdd < pcSample3Start; ++pcAdd)
  927. {
  928. // add them with saturation
  929. *pcAdd = (unsigned char)(max(0, min(0xFF,
  930. (int)pcAdd[m_dwRepeatLength] + (int)pcAdd[m_dwRepeatLength*2] - 0x7F
  931. )));
  932. }
  933. for (; pcAdd < pcSample1End; ++pcAdd)
  934. {
  935. // add them with saturation
  936. *pcAdd = (unsigned char)(max(0, min(0xFF,
  937. (int)pcAdd[0] + (int)pcAdd[m_dwRepeatLength]
  938. + (int)pcAdd[m_dwRepeatLength*2] - 0x7F*2
  939. )));
  940. }
  941. for (; pcAdd < pcSample2End; ++pcAdd)
  942. {
  943. // add them with saturation
  944. *pcAdd = (unsigned char)(max(0, min(0xFF,
  945. (int)pcAdd[0] + (int)pcAdd[m_dwRepeatLength] - 0x7F
  946. )));
  947. }
  948. }
  949. else
  950. {
  951. ZAssert(m_pdataBase->GetBitsPerSample() == 16);
  952. ZAssert(m_dwRepeatLength % 2 == 0);
  953. ZAssert(m_dwSize % 2 == 0);
  954. // add them as signed 16 bit shorts
  955. short* pwSample2Start =
  956. (short*)(m_pvData) + m_dwRepeatLength/2;
  957. short* pwSample3Start =
  958. (short*)(m_pvData) + m_dwRepeatLength;
  959. short* pwSample1End =
  960. (short*)(m_pvData) + (m_dwSize - m_dwRepeatLength*2)/2;
  961. short* pwSample2End =
  962. (short*)(m_pvData) + (m_dwSize - m_dwRepeatLength)/2;
  963. short* pwAdd;
  964. for (pwAdd = pwSample2Start; pwAdd < pwSample2Start; ++pwAdd)
  965. {
  966. // add them with saturation
  967. *pwAdd = (short)(max(-0x8000, min(0x7FFF,
  968. (int)pwAdd[m_dwRepeatLength/2] + (int)pwAdd[m_dwRepeatLength]
  969. )));
  970. }
  971. for (; pwAdd < pwSample1End; ++pwAdd)
  972. {
  973. // add them with saturation
  974. *pwAdd = (short)(max(-0x8000, min(0x7FFF,
  975. (int)pwAdd[0] + (int)pwAdd[m_dwRepeatLength/2]
  976. + (int)pwAdd[m_dwRepeatLength]
  977. )));
  978. }
  979. for (; pwAdd < pwSample2End; ++pwAdd)
  980. {
  981. // add them with saturation
  982. *pwAdd = (short)(max(-0x8000, min(0x7FFF,
  983. (int)pwAdd[0] + (int)pwAdd[m_dwRepeatLength/2]
  984. )));
  985. }
  986. }
  987. }
  988. ZAssert(nOffset + nLength <= m_dwSize);
  989. memcpy(dest, ((BYTE*)m_pvData) + nOffset, nLength);
  990. };
  991. };
  992. RepeatFirePCMData::RepeatFireDataCache RepeatFirePCMData::s_cache;
  993. int RepeatFirePCMData::s_nCacheRefCount = 0;
  994. // a class for a repeat-fire weapon
  995. class RepeatFireTemplate : public ISoundTemplate
  996. {
  997. TRef<ISoundTemplate> m_pstBase;
  998. float m_fRepeatRate;
  999. // a wrapper for a buffer source which forces all sounds to be ASR sounds
  1000. class RepeatFireBufferSource : public SoundBufferSourceWrapper
  1001. {
  1002. float m_fRepeatRate;
  1003. TRef<RepeatFirePCMData> m_ppcmdataRepeatLast;
  1004. TRef<ISoundPCMData> m_ppcmdataBaseLast;
  1005. public:
  1006. RepeatFireBufferSource(ISoundBufferSource* pBase, float fRepeatRate) :
  1007. SoundBufferSourceWrapper(pBase),
  1008. m_fRepeatRate(fRepeatRate)
  1009. {};
  1010. // Creates a static sound buffer of the given wave file.
  1011. // The sound will loop until stopped.
  1012. HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
  1013. ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
  1014. {
  1015. unsigned uBytesPerSec = pcmdata->GetBytesPerSec();
  1016. // create the repeating version of this sound
  1017. // optimization: most of the time we will be playing the same sound
  1018. // that we played last time, so there's no need to do the full lookup.
  1019. TRef<RepeatFirePCMData> ppcmdataRepeat;
  1020. if (pcmdata == m_ppcmdataBaseLast)
  1021. {
  1022. ppcmdataRepeat = m_ppcmdataRepeatLast;
  1023. }
  1024. else
  1025. {
  1026. m_ppcmdataBaseLast = pcmdata;
  1027. ppcmdataRepeat = m_ppcmdataRepeatLast =
  1028. RepeatFirePCMData::Create(pcmdata, m_fRepeatRate);
  1029. }
  1030. if (!ppcmdataRepeat)
  1031. {
  1032. ZAssert(false);
  1033. return E_FAIL;
  1034. }
  1035. return m_pBase->CreateASRBuffer(psoundNew, ppcmdataRepeat,
  1036. ppcmdataRepeat->GetLoopStart(),
  1037. ppcmdataRepeat->GetLoopLength(),
  1038. psource
  1039. );
  1040. }
  1041. };
  1042. public:
  1043. // make sure the repeat fire cache is valid while we are alive
  1044. RepeatFireTemplate()
  1045. {
  1046. RepeatFirePCMData::AddCacheRef();
  1047. };
  1048. ~RepeatFireTemplate()
  1049. {
  1050. RepeatFirePCMData::ReleaseCacheRef();
  1051. };
  1052. // tries to initialize the object with the given file.
  1053. HRESULT Init(ISoundTemplate* pstSource, float fRepeatRate)
  1054. {
  1055. if (!pstSource)
  1056. {
  1057. ZAssert(false);
  1058. return E_POINTER;
  1059. }
  1060. if (fRepeatRate <= 0.0f)
  1061. {
  1062. ZAssert(false);
  1063. return E_INVALIDARG;
  1064. }
  1065. m_fRepeatRate = fRepeatRate;
  1066. m_pstBase = pstSource;
  1067. return S_OK;
  1068. };
  1069. // Creates a new instance of the given sound
  1070. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  1071. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
  1072. {
  1073. if (!pbufferSource)
  1074. {
  1075. ZAssert(false);
  1076. return E_POINTER;
  1077. }
  1078. TRef<ISoundBufferSource> pbufferRepeatFireSource =
  1079. new RepeatFireBufferSource(pbufferSource, m_fRepeatRate);
  1080. return m_pstBase->CreateSound(psoundNew, pbufferRepeatFireSource, psource);
  1081. };
  1082. };
  1083. // Creates an ASR sound from the given template for a weapon that fires every n
  1084. // seconds. This assumes the sound for a single shot falls off dramaticly after
  1085. // 2n seconds, and plays some tricks based on this like only playing the two
  1086. // most recent sounds.
  1087. HRESULT CreateRepeatingFireSoundTemplate(TRef<ISoundTemplate>& pstDest,
  1088. ISoundTemplate* pstSource,
  1089. float fFireDelay)
  1090. {
  1091. TRef<RepeatFireTemplate> ptemplate = new RepeatFireTemplate();
  1092. HRESULT hr = ptemplate->Init(pstSource, fFireDelay);
  1093. if (ZSucceeded(hr))
  1094. pstDest = ptemplate;
  1095. return hr;
  1096. };
  1097. // provides a template for picking a random sound to play
  1098. class RandomSoundTemplate : public IRandomSoundTemplate
  1099. {
  1100. private:
  1101. // a map from probability lower bound to template
  1102. typedef std::map<float, ZAdapt<TRef<ISoundTemplate> > > TemplateMap;
  1103. TemplateMap m_templates;
  1104. // the current sum of all weights
  1105. float m_fTotalWeight;
  1106. // the last sound template we used (used to avoid repeats)
  1107. ISoundTemplate* m_pstLast;
  1108. public:
  1109. RandomSoundTemplate() :
  1110. m_fTotalWeight(0),
  1111. m_pstLast(NULL)
  1112. {
  1113. }
  1114. // adds the given template as a possible sound to play, with the given
  1115. // weight. The weight can be any arbitrary number, but a sound with weight
  1116. // f will play half as often as a sound with weight 2*f.
  1117. HRESULT AddSoundTemplate(ISoundTemplate* pstSource, float fWeight)
  1118. {
  1119. if (!pstSource)
  1120. {
  1121. ZAssert(false);
  1122. return E_POINTER;
  1123. }
  1124. if (fWeight <= 0.0f)
  1125. {
  1126. ZAssert(false);
  1127. return E_INVALIDARG;
  1128. }
  1129. m_templates.insert(std::pair<float, ZAdapt<TRef<ISoundTemplate> > >(
  1130. m_fTotalWeight, TRef<ISoundTemplate>(pstSource)));
  1131. m_fTotalWeight += fWeight;
  1132. return S_OK;
  1133. };
  1134. // Creates a new instance of the given sound
  1135. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  1136. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
  1137. {
  1138. ISoundTemplate* pstNew;
  1139. ZAssert(m_templates.size() > 0);
  1140. do
  1141. {
  1142. float fChoice = (rand()+1) * m_fTotalWeight / (RAND_MAX + 1);
  1143. pstNew = (TRef<ISoundTemplate>&)(*(--(m_templates.lower_bound(fChoice)))).second;
  1144. }
  1145. while (m_templates.size() > 2 && pstNew == m_pstLast);
  1146. pstNew->CreateSound(psoundNew, pbufferSource, psource);
  1147. m_pstLast = pstNew;
  1148. return S_OK;
  1149. };
  1150. };
  1151. // creates a sound template which randomly chooses from the sound templates
  1152. // it contains each time a sound is played.
  1153. HRESULT CreateRandomSoundTemplate(TRef<IRandomSoundTemplate>& pstDest)
  1154. {
  1155. pstDest = new RandomSoundTemplate();
  1156. return S_OK;
  1157. };
  1158. // a template for a sound which plays intermittently
  1159. class IntermittentSoundTemplate : public ISoundTemplate
  1160. {
  1161. private:
  1162. TRef<ISoundTemplate> m_pstBase;
  1163. float m_fPeriod;
  1164. bool m_bMultipleSounds;
  1165. public:
  1166. // a sound source which contains intermittently playing sounds
  1167. class IntermittentInstance : public StubbedTweakableSoundInstance
  1168. {
  1169. float m_fPeriodMS;
  1170. TRef<ISoundTemplate> m_ptemplate;
  1171. bool m_bMultipleSounds;
  1172. TRef<ISoundBufferSource> m_pbufferSource;
  1173. typedef std::list<ZAdapt<TRef<ISoundInstance> > > SoundInstanceList;
  1174. SoundInstanceList m_listPlayingSounds;
  1175. public:
  1176. IntermittentInstance(float fPeriod, bool bMultipleSounds,
  1177. ISoundTemplate* ptemplate, ISoundBufferSource* pbufferSource,
  1178. ISoundPositionSource* pposSource) :
  1179. StubbedTweakableSoundInstance(pposSource),
  1180. m_fPeriodMS(fPeriod*1000),
  1181. m_bMultipleSounds(bMultipleSounds),
  1182. m_ptemplate(ptemplate),
  1183. m_pbufferSource(pbufferSource)
  1184. {
  1185. // grab the update event for the sound buffer source
  1186. m_pbufferSource->GetUpdateEventSource()->AddSink(this);
  1187. };
  1188. // fires the m_peventsourceStopped event if bHasStopped is true
  1189. virtual void Update(DWORD dwElapsedTime, bool bHasStopped)
  1190. {
  1191. StubbedTweakableSoundInstance::Update(dwElapsedTime, bHasStopped);
  1192. if (!bHasStopped)
  1193. {
  1194. // clean up the playing sounds list
  1195. // (polling is inefficent - oh well)
  1196. SoundInstanceList::iterator iter;
  1197. for (iter = m_listPlayingSounds.begin();
  1198. iter != m_listPlayingSounds.end();)
  1199. {
  1200. if ((*iter)->IsPlaying() == S_OK)
  1201. {
  1202. ++iter;
  1203. }
  1204. else
  1205. {
  1206. iter = m_listPlayingSounds.erase(iter);
  1207. }
  1208. }
  1209. // if we can play a sound this frame
  1210. if (m_bMultipleSounds || m_listPlayingSounds.empty())
  1211. {
  1212. // calculate the probability of playing a sound in this
  1213. // frame.
  1214. // KGJV: typecasted pow args to keep compiler happy
  1215. float fProbability = 1 - (float)pow((float)2, -((float)dwElapsedTime/m_fPeriodMS));
  1216. if (rand() < fProbability * RAND_MAX)
  1217. {
  1218. // start a new sound
  1219. TRef<ISoundInstance> pNewSound;
  1220. if (ZSucceeded(m_ptemplate->CreateSound(
  1221. pNewSound, m_pbufferSource, m_pposSource)))
  1222. {
  1223. m_listPlayingSounds.push_back(pNewSound);
  1224. UpdateSound(pNewSound);
  1225. }
  1226. }
  1227. }
  1228. }
  1229. }
  1230. // Stops the sound. If bForceNow is true the sound will stop ASAP,
  1231. // possibly popping. If it is false some sounds may play a trail-off
  1232. // sound or fade away.
  1233. HRESULT Stop(bool bForceNow)
  1234. {
  1235. ZSucceeded(StubbedTweakableSoundInstance::Stop(bForceNow));
  1236. // pass on the stop event to all playing sounds
  1237. SoundInstanceList::iterator iter;
  1238. for (iter = m_listPlayingSounds.begin();
  1239. iter != m_listPlayingSounds.end(); ++iter)
  1240. {
  1241. ZSucceeded((*iter)->Stop(bForceNow));
  1242. }
  1243. return S_OK;
  1244. };
  1245. //
  1246. // ISoundTweakable
  1247. //
  1248. // Sets the gain, from 0 to -100 dB
  1249. virtual HRESULT SetGain(float fGain)
  1250. {
  1251. ZSucceeded(StubbedTweakableSoundInstance::SetGain(fGain));
  1252. // pass on the stop event to all playing sounds
  1253. SoundInstanceList::iterator iter;
  1254. for (iter = m_listPlayingSounds.begin();
  1255. iter != m_listPlayingSounds.end(); ++iter)
  1256. {
  1257. ZSucceeded((*iter)->GetISoundTweakable()->SetGain(fGain));
  1258. }
  1259. return S_OK;
  1260. };
  1261. // Sets the pitch shift, where 1.0 is normal, 0.5 is half of normal speed,
  1262. // and 2.0 is twice normal speed.
  1263. virtual HRESULT SetPitch(float fPitch)
  1264. {
  1265. ZSucceeded(StubbedTweakableSoundInstance::SetPitch(fPitch));
  1266. // pass on the stop event to all playing sounds
  1267. SoundInstanceList::iterator iter;
  1268. for (iter = m_listPlayingSounds.begin();
  1269. iter != m_listPlayingSounds.end(); ++iter)
  1270. {
  1271. ZSucceeded((*iter)->GetISoundTweakable()->SetPitch(fPitch));
  1272. }
  1273. return S_OK;
  1274. };
  1275. // sets the priority - used as a addition to volume when choosing which
  1276. // sounds are most important to play.
  1277. virtual HRESULT SetPriority(float fPriority)
  1278. {
  1279. ZSucceeded(StubbedTweakableSoundInstance::SetPriority(fPriority));
  1280. // pass on the stop event to all playing sounds
  1281. SoundInstanceList::iterator iter;
  1282. for (iter = m_listPlayingSounds.begin();
  1283. iter != m_listPlayingSounds.end(); ++iter)
  1284. {
  1285. ZSucceeded((*iter)->GetISoundTweakable()->SetPriority(fPriority));
  1286. }
  1287. return S_OK;
  1288. };
  1289. //
  1290. // ISoundTweakable3D
  1291. //
  1292. // toggles 3D Positioning on and off for the given sound.
  1293. virtual HRESULT Set3D(bool b3D)
  1294. {
  1295. ZSucceeded(StubbedTweakableSoundInstance::Set3D(b3D));
  1296. // pass on the stop event to all playing sounds
  1297. SoundInstanceList::iterator iter;
  1298. for (iter = m_listPlayingSounds.begin();
  1299. iter != m_listPlayingSounds.end(); ++iter)
  1300. {
  1301. TRef<ISoundTweakable3D> ptweak3d = (*iter)->GetISoundTweakable3D();
  1302. if (ptweak3d)
  1303. ZSucceeded(ptweak3d->Set3D(b3D));
  1304. }
  1305. return S_OK;
  1306. };
  1307. // Sets the distance at which the sound will be at max volume. This
  1308. // effects how quickly the sound drops off with distance.
  1309. virtual HRESULT SetMinimumDistance(float fMinimumDistance)
  1310. {
  1311. ZSucceeded(StubbedTweakableSoundInstance::SetMinimumDistance(fMinimumDistance));
  1312. // pass on the stop event to all playing sounds
  1313. SoundInstanceList::iterator iter;
  1314. for (iter = m_listPlayingSounds.begin();
  1315. iter != m_listPlayingSounds.end(); ++iter)
  1316. {
  1317. TRef<ISoundTweakable3D> ptweak3d = (*iter)->GetISoundTweakable3D();
  1318. if (ptweak3d)
  1319. ZSucceeded(ptweak3d->SetMinimumDistance(fMinimumDistance));
  1320. }
  1321. return S_OK;
  1322. };
  1323. // Sets a sound cone of size fInnerAngle (in degrees) where the volume is at
  1324. // normal levels, outside of which it fades down by fOutsideGain
  1325. // (range of 0 to -100 db) at fOuterAngle (degrees) and beyond.
  1326. virtual HRESULT SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
  1327. {
  1328. ZSucceeded(StubbedTweakableSoundInstance::SetCone(fInnerAngle, fOuterAngle, fOutsideGain));
  1329. // pass on the stop event to all playing sounds
  1330. SoundInstanceList::iterator iter;
  1331. for (iter = m_listPlayingSounds.begin();
  1332. iter != m_listPlayingSounds.end(); ++iter)
  1333. {
  1334. TRef<ISoundTweakable3D> ptweak3d = (*iter)->GetISoundTweakable3D();
  1335. if (ptweak3d)
  1336. ZSucceeded(ptweak3d->SetCone(fInnerAngle, fOuterAngle, fOutsideGain));
  1337. }
  1338. return S_OK;
  1339. };
  1340. };
  1341. public:
  1342. HRESULT Init(ISoundTemplate* pstSource, float fPeriod, bool bMultipleSounds)
  1343. {
  1344. if (!pstSource)
  1345. {
  1346. ZAssert(false);
  1347. return E_POINTER;
  1348. }
  1349. m_pstBase = pstSource;
  1350. m_fPeriod = fPeriod;
  1351. m_bMultipleSounds = bMultipleSounds;
  1352. return S_OK;
  1353. };
  1354. // Creates a new instance of the given sound
  1355. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  1356. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  1357. {
  1358. // we need to have a tweakable sound for this to work.
  1359. if (!pbufferSource)
  1360. {
  1361. ZAssert(false);
  1362. return E_POINTER;
  1363. }
  1364. psoundNew = new IntermittentInstance(m_fPeriod, m_bMultipleSounds,
  1365. m_pstBase, pbufferSource, psource);
  1366. return S_OK;
  1367. }
  1368. };
  1369. // creates a sound template for a sound which plays intermittently, and has a 50%
  1370. // chance of playing over a period of fPeriod seconds. If bMultipleSounds is
  1371. // true, this allows multiple sounds to play at the same time.
  1372. HRESULT CreateIntermittentSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  1373. float fPeriod, bool bMultipleSounds)
  1374. {
  1375. TRef<IntermittentSoundTemplate> ptemplate = new IntermittentSoundTemplate();
  1376. HRESULT hr = ptemplate->Init(pstSource, fPeriod, bMultipleSounds);
  1377. if (ZSucceeded(hr))
  1378. pstDest = ptemplate;
  1379. return hr;
  1380. }
  1381. // a sound instance with a delayed start
  1382. class DelayedSoundInstance : public ISoundInstance, public IEventSink
  1383. {
  1384. protected:
  1385. TRef<ISoundInstance> m_pBase;
  1386. TRef<ISoundTemplate> m_pstSource;
  1387. TRef<ISoundBufferSource> m_pbufferSource;
  1388. TRef<ISoundPositionSource> m_psource;
  1389. TRef<EventSourceImpl> m_peventsource;
  1390. TRef<IEventSink> m_psinkDelegate;
  1391. public:
  1392. DelayedSoundInstance(ISoundTemplate* pstSource,
  1393. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource) :
  1394. m_pstSource(pstSource),
  1395. m_pbufferSource(pbufferSource),
  1396. m_psource(psource)
  1397. {
  1398. ZAssert(m_pstSource != NULL);
  1399. ZAssert(m_pbufferSource != NULL);
  1400. }
  1401. ~DelayedSoundInstance()
  1402. {
  1403. if (m_psinkDelegate)
  1404. {
  1405. m_pBase->GetFinishEventSource()->RemoveSink(m_psinkDelegate);
  1406. m_psinkDelegate = NULL;
  1407. }
  1408. }
  1409. // Starts the sound.
  1410. virtual HRESULT Start()
  1411. {
  1412. HRESULT hr = m_pstSource->CreateSound(m_pBase, m_pbufferSource, m_psource);
  1413. if (SUCCEEDED(hr))
  1414. {
  1415. m_psinkDelegate = IEventSink::CreateDelegate(this);
  1416. m_pBase->GetFinishEventSource()->AddSink(m_psinkDelegate);
  1417. m_pstSource = NULL;
  1418. m_pbufferSource = NULL;
  1419. m_psource = NULL;
  1420. }
  1421. return hr;
  1422. }
  1423. // Stops the sound. If bForceNow is true the sound will stop ASAP,
  1424. // possibly popping. If it is false some sounds may play a trail-off
  1425. // sound or fade away.
  1426. virtual HRESULT Stop(bool bForceNow = false)
  1427. {
  1428. // if the sound has not been started yet, force a start so that we
  1429. // will get the finish event.
  1430. if (m_pBase)
  1431. return m_pBase->Stop();
  1432. else if (m_peventsource)
  1433. {
  1434. m_peventsource->Trigger();
  1435. m_pstSource = NULL;
  1436. }
  1437. return S_OK;
  1438. }
  1439. // a sound finished
  1440. virtual bool OnEvent(IEventSource* pevent)
  1441. {
  1442. m_pBase = NULL;
  1443. m_psinkDelegate = NULL;
  1444. if (m_peventsource)
  1445. m_peventsource->Trigger();
  1446. return false;
  1447. }
  1448. // returns S_OK if the sound is currently playing, S_FALSE otherwise.
  1449. virtual HRESULT IsPlaying()
  1450. {
  1451. if (m_pBase)
  1452. return m_pBase->IsPlaying();
  1453. else
  1454. return m_pstSource ? S_OK : S_FALSE;
  1455. }
  1456. // Gets an event which fires when the sound finishes playing (for any
  1457. // reason)
  1458. virtual IEventSource* GetFinishEventSource()
  1459. {
  1460. if (m_peventsource)
  1461. return m_peventsource;
  1462. else if (m_pBase)
  1463. return m_pBase->GetFinishEventSource();
  1464. else
  1465. return m_peventsource = new EventSourceImpl();
  1466. }
  1467. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  1468. virtual TRef<ISoundTweakable> GetISoundTweakable()
  1469. {
  1470. assert(false); // not fully implemented
  1471. if (m_pBase)
  1472. return m_pBase->GetISoundTweakable();
  1473. else
  1474. return NULL;
  1475. }
  1476. virtual TRef<ISoundTweakable3D> GetISoundTweakable3D()
  1477. {
  1478. assert(false); // not fully implemented
  1479. if (m_pBase)
  1480. return m_pBase->GetISoundTweakable3D();
  1481. else
  1482. return NULL;
  1483. }
  1484. };
  1485. // a helper class for serialized sounds which handles the serialization part
  1486. class SoundMutex : public ISoundMutex, public IEventSink
  1487. {
  1488. private:
  1489. // the set of sounds pending on this mutex and the coresponding timeout
  1490. // and (timeout - priority) values for that sound.
  1491. struct TemplateData
  1492. {
  1493. TRef<DelayedSoundInstance> pinstance;
  1494. Time timeExpiration;
  1495. Time timeUrgency;
  1496. TemplateData()
  1497. {}
  1498. TemplateData(TRef<DelayedSoundInstance> pinstance_,
  1499. Time timeExpiration_, Time timeUrgency_)
  1500. : pinstance(pinstance_), timeExpiration(timeExpiration_),
  1501. timeUrgency(timeUrgency_)
  1502. {}
  1503. };
  1504. typedef std::multimap<ZAdapt<TRef<ISoundTemplate> >, TemplateData> TemplateMap;
  1505. TemplateMap m_templatesPending;
  1506. // the currently playing template (only used for avoiding duplicate sounds)
  1507. TRef<ISoundTemplate> m_pstCurrent;
  1508. // the currently playing sound instance
  1509. TRef<ISoundInstance> m_psoundCurrent;
  1510. TRef<IEventSource> m_peventSoundFinished;
  1511. // start the given sound template and do the required book keeping.
  1512. HRESULT StartSound(TRef<ISoundInstance>& psoundNew,
  1513. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource,
  1514. ISoundTemplate* pstSource)
  1515. {
  1516. ZAssert(!m_pstCurrent);
  1517. ZAssert(!m_peventSoundFinished);
  1518. ZAssert(!m_psoundCurrent);
  1519. HRESULT hr = pstSource->CreateSound(psoundNew, pbufferSource, psource);
  1520. if (SUCCEEDED(hr))
  1521. {
  1522. if (!psoundNew)
  1523. {
  1524. ZAssert(false);
  1525. return E_FAIL;
  1526. }
  1527. m_pstCurrent = pstSource;
  1528. m_psoundCurrent = psoundNew;
  1529. m_peventSoundFinished = psoundNew->GetFinishEventSource();
  1530. m_peventSoundFinished->AddSink(this);
  1531. }
  1532. return hr;
  1533. }
  1534. void QueueNextSound()
  1535. {
  1536. ZAssert(!m_pstCurrent);
  1537. ZAssert(!m_peventSoundFinished);
  1538. ZAssert(!m_psoundCurrent);
  1539. Time now = Time::Now();
  1540. TemplateMap::iterator iterTemplate = m_templatesPending.begin();
  1541. TemplateMap::iterator iterMostUrgentSound = m_templatesPending.end();
  1542. float fMaxUrgency = -1e10f; // just a big negative value
  1543. // find the sound closest to expiring which hasn't expired yet,
  1544. // adjusting for priority.
  1545. while (iterTemplate != m_templatesPending.end())
  1546. {
  1547. if ((*iterTemplate).second.timeExpiration - now < 0.0f)
  1548. iterTemplate = m_templatesPending.erase(iterTemplate);
  1549. else
  1550. {
  1551. float fUrgency = now - (*iterTemplate).second.timeUrgency;
  1552. if (fUrgency > fMaxUrgency)
  1553. {
  1554. fMaxUrgency = fUrgency;
  1555. iterMostUrgentSound = iterTemplate;
  1556. }
  1557. ++iterTemplate;
  1558. }
  1559. }
  1560. if (iterMostUrgentSound != m_templatesPending.end())
  1561. {
  1562. HRESULT hr = (*iterMostUrgentSound).second.pinstance->Start();
  1563. if (SUCCEEDED(hr))
  1564. {
  1565. m_pstCurrent = (*iterMostUrgentSound).first;
  1566. m_peventSoundFinished =
  1567. (*iterMostUrgentSound).second.pinstance->GetFinishEventSource();
  1568. m_peventSoundFinished->AddSink(this);
  1569. m_psoundCurrent = (*iterMostUrgentSound).second.pinstance;
  1570. }
  1571. m_templatesPending.erase(iterMostUrgentSound);
  1572. if (FAILED(hr))
  1573. {
  1574. // try again with the next sound
  1575. QueueNextSound();
  1576. }
  1577. }
  1578. }
  1579. public:
  1580. HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  1581. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource,
  1582. ISoundTemplate* pstSource, float fTimeout, float fPriority, bool bSingleInstance)
  1583. {
  1584. // if we are not currently playing a sound with this mutex...
  1585. if (!m_pstCurrent)
  1586. {
  1587. // just play the sound.
  1588. return StartSound(psoundNew, pbufferSource, psource, pstSource);
  1589. }
  1590. else
  1591. {
  1592. if (bSingleInstance)
  1593. {
  1594. // if we are already playing the sound, there's nothing more
  1595. // that we need to do.
  1596. if (m_pstCurrent == pstSource)
  1597. {
  1598. psoundNew = m_psoundCurrent;
  1599. return S_OK;
  1600. }
  1601. // look for any existing instances of the sound
  1602. TemplateMap::iterator iterOld = m_templatesPending.find(TRef<ISoundTemplate>(pstSource));
  1603. if (iterOld != m_templatesPending.end())
  1604. {
  1605. // we found an existing instance of the sound. Update it's timeout
  1606. // (but leave it's priority at the same elevated level)
  1607. (*iterOld).second.timeExpiration = Time::Now() + fTimeout;
  1608. psoundNew = (*iterOld).second.pinstance;
  1609. return S_OK;
  1610. }
  1611. }
  1612. // add the sound to the sound template list
  1613. Time timeExpiration = Time::Now() + fTimeout;
  1614. TRef<DelayedSoundInstance> pinstance = new DelayedSoundInstance(pstSource, pbufferSource, psource);
  1615. m_templatesPending.insert(TemplateMap::value_type(TRef<ISoundTemplate>(pstSource),
  1616. TemplateData(pinstance, timeExpiration, timeExpiration - fPriority)));
  1617. psoundNew = pinstance;
  1618. return S_OK;
  1619. }
  1620. }
  1621. // a sound finished
  1622. virtual bool OnEvent(IEventSource* pevent)
  1623. {
  1624. m_pstCurrent = NULL;
  1625. m_psoundCurrent = NULL;
  1626. m_peventSoundFinished = NULL;
  1627. QueueNextSound();
  1628. return false;
  1629. }
  1630. // erases any pending sounds without playing them
  1631. virtual HRESULT Reset()
  1632. {
  1633. m_pstCurrent = NULL;
  1634. m_psoundCurrent = NULL;
  1635. if (m_peventSoundFinished)
  1636. m_peventSoundFinished->RemoveSink(this);
  1637. m_peventSoundFinished = NULL;
  1638. m_templatesPending.clear();
  1639. return S_OK;
  1640. }
  1641. };
  1642. // Creates a mutex used for the serial sound template below. Each
  1643. // mutex represents a single syncronization point - sounds with the same mutex
  1644. // won't overlap, but sounds with different mutexes won't be prevented from
  1645. // overlapping.
  1646. HRESULT CreateSoundMutex(TRef<ISoundMutex>& pmutex)
  1647. {
  1648. pmutex = new SoundMutex();
  1649. return S_OK;
  1650. }
  1651. // a template turning another template into a serialized sound
  1652. class SerializedSoundTemplate : public ISoundTemplate
  1653. {
  1654. private:
  1655. TRef<ISoundTemplate> m_pstBase;
  1656. TRef<SoundMutex> m_pmutex;
  1657. float m_fTimeout;
  1658. float m_fPriority;
  1659. bool m_bSingleInstance;
  1660. public:
  1661. // tries to initialize the object with the given file.
  1662. HRESULT Init(ISoundTemplate* pstSource, ISoundMutex* pmutex,
  1663. float fTimeout, float fPriority, bool bSingleInstance)
  1664. {
  1665. if (!pstSource)
  1666. {
  1667. ZAssert(false);
  1668. return E_POINTER;
  1669. }
  1670. if (fTimeout < 0)
  1671. {
  1672. ZAssert(false);
  1673. return E_INVALIDARG;
  1674. }
  1675. CastTo(m_pmutex, pmutex);
  1676. if (!m_pmutex)
  1677. {
  1678. ZAssert(false);
  1679. return E_INVALIDARG;
  1680. }
  1681. m_pstBase = pstSource;
  1682. m_fTimeout = fTimeout;
  1683. m_fPriority = fPriority;
  1684. m_bSingleInstance = bSingleInstance;
  1685. return S_OK;
  1686. };
  1687. // Creates a new instance of the given sound
  1688. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  1689. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
  1690. {
  1691. return m_pmutex->CreateSound(psoundNew, pbufferSource, psource,
  1692. m_pstBase, m_fTimeout, m_fPriority, m_bSingleInstance);
  1693. }
  1694. };
  1695. // Creates a sound template for a sound which will not be played at the same
  1696. // time as any other sound created by a serial sound template with the same
  1697. // mutex. If bSingleInstance is true, it will only be added if there is not
  1698. // an existing copy of a sound template with that source. fTimeout specifies
  1699. // the amount of time a sound can be delayed before it is discarded
  1700. HRESULT CreateSerializedSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
  1701. ISoundMutex* pmutex, float fTimeout, float fPriority,
  1702. bool bSingleInstance)
  1703. {
  1704. TRef<SerializedSoundTemplate> ptemplate = new SerializedSoundTemplate();
  1705. HRESULT hr = ptemplate->Init(pstSource, pmutex, fTimeout, fPriority, bSingleInstance);
  1706. if (ZSucceeded(hr))
  1707. pstDest = ptemplate;
  1708. return hr;
  1709. }
  1710. };