SoundThing.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // SoundThing.cpp
  19. // Project: Nostril (aka Postal)
  20. //
  21. // History:
  22. // 02/24/97 MJR Stole infrastructure from Jon's AnimThing.
  23. //
  24. // 03/07/97 JMI Now remembers last channel played with and will attempt
  25. // to abort the sample, if this object is killed.
  26. // This obejct now responds to the delete message.
  27. //
  28. // 03/07/97 JMI Was only checking messages when the sound looped.
  29. //
  30. // 03/13/97 JMI Load now takes a version number.
  31. //
  32. // 03/25/97 JMI Changed EDIT_GUI_FILE to 8.3 compliant name.
  33. //
  34. // 04/10/97 BRH Updated this to work with the new multi layer attribute
  35. // maps.
  36. //
  37. // 04/22/97 JMI When I added the remembering of the last channel played,
  38. // I forgot to actually remember the last channel played.
  39. // Duh! Fixed.
  40. //
  41. // 05/29/97 JMI Removed ASSERT on m_pRealm->m_pAttribMap which no longer
  42. // exists.
  43. //
  44. // 06/17/97 JMI Converted all occurrences of rand() to GetRand() and
  45. // srand() to SeedRand().
  46. //
  47. // 06/29/97 JMI Converted EditRect(), EditRender(), and/or Render() to
  48. // use Map3Dto2D().
  49. //
  50. // 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
  51. // for 2D image components.
  52. //
  53. // 07/17/97 JMI Changed RSnd*'s to SampleMaster::SoundInstances.
  54. // Now uses new SampleMaster interface for volume and play
  55. // instance reference.
  56. //
  57. // 07/18/97 JMI Got rid of bogus immitation PlaySample functions.
  58. // Now there is one PlaySample() function. Also, you now
  59. // MUST specify a category and you don't have to specify a
  60. // SoundInstance ptr to specify a volume.
  61. // Also, added user edittable member m_lVolumeHalfLife.
  62. //
  63. // 07/25/97 MJR Added the loading/saving of x,y,z.
  64. //
  65. // 07/28/97 JMI Now varies volume as sound plays (based on distance to
  66. // ear).
  67. // Also, makes sure the next sound does not start until at
  68. // least after the sound sample should be done.
  69. //
  70. // 08/01/97 JMI Added looping parameters.
  71. // Also, fixed bug where &m_b* were casted to long* (but
  72. // they are not 32 bits).
  73. //
  74. // 08/04/97 JMI Made hiding and showing of id 499, in UseLoopingGuiCall,
  75. // _slightly_ more efficient.
  76. //
  77. // 08/04/97 JMI Added m_sAmbient indicating whether or not this sound
  78. // is ambient (i.e., non-essential).
  79. // Also, implemented a special random number generator
  80. // strictly for sound things so they can be merry and random
  81. // and not de-synchronize.
  82. //
  83. // 08/05/97 JMI Changed priority to use Z position rather than 2D
  84. // projected Y position.
  85. //
  86. // 08/09/97 JMI Added better UI for 'loop back from end' usage.
  87. // Now offers a checkbox option to loop infinitely.
  88. //
  89. // 08/11/97 JMI Added RelayVolume() so CSoundRelays can update their
  90. // CSoundThing parents.
  91. //
  92. // 08/12/97 JMI Now can browse for files with paths relative to the
  93. // samples SAK.
  94. //
  95. // 09/27/99 JMI Eliminated boolean performance warnings.
  96. //
  97. //////////////////////////////////////////////////////////////////////////////
  98. //
  99. // This CThing-derived class will play sounds with various options.
  100. //
  101. //////////////////////////////////////////////////////////////////////////////
  102. #define SOUNDTHING_CPP
  103. #include "RSPiX.h"
  104. #include "SoundThing.h"
  105. #include "game.h"
  106. // This class has its own GetRandom() to keep it from de-synching the game.
  107. #ifdef GetRandom
  108. #undef GetRandom
  109. #endif
  110. #ifdef GetRand
  111. #undef GetRand
  112. #endif
  113. #ifdef MOBILE //Arm RAND_MAX is a full int, code expecting a short!!
  114. #define RAND_MAX 0x7fff
  115. #endif
  116. ////////////////////////////////////////////////////////////////////////////////
  117. // Macros/types/etc.
  118. ////////////////////////////////////////////////////////////////////////////////
  119. #define IMAGE_FILE "soundthing.bmp"
  120. #define GUI_FILE_NAME "res/editor/Sound.gui"
  121. #define END_OF_TIME 0x7fffffff // Unfortunately, we won't be dead (or,
  122. // considering it's always just 24 days
  123. // off, fortunately). The program, will
  124. // break, however.
  125. ////////////////////////////////////////////////////////////////////////////////
  126. // Class statics.
  127. ////////////////////////////////////////////////////////////////////////////////
  128. short CSoundThing::ms_sFileCount = 0; // File count.
  129. long CSoundThing::ms_lGetRandomSeed = 0; // Seed for get random.
  130. ////////////////////////////////////////////////////////////////////////////////
  131. // Load object (should call base class version!)
  132. ////////////////////////////////////////////////////////////////////////////////
  133. short CSoundThing::Load( // Returns 0 if successfull, non-zero otherwise
  134. RFile* pFile, // In: File to load from
  135. bool bEditMode, // In: True for edit mode, false otherwise
  136. short sFileCount, // In: File count (unique per file, never 0)
  137. ULONG ulFileVersion) // In: Version of file format to load.
  138. {
  139. short sResult = CThing::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  140. if (sResult == 0)
  141. {
  142. // If new file . . .
  143. if (sFileCount != ms_sFileCount)
  144. {
  145. ms_sFileCount = sFileCount;
  146. // Do one time stuff.
  147. // Reseed local random.
  148. ms_lGetRandomSeed = 0;
  149. }
  150. switch (ulFileVersion)
  151. {
  152. default:
  153. case 40:
  154. pFile->Read(&m_sAmbient);
  155. case 39:
  156. case 38:
  157. pFile->Read(&m_sPurgeSampleWhenDone );
  158. pFile->Read(&m_sUseLooping );
  159. pFile->Read(&m_lStopLoopingTime );
  160. pFile->Read(&m_lNumLoopBacks );
  161. pFile->Read(&m_lLoopBackTo );
  162. pFile->Read(&m_lLoopBackFrom );
  163. case 37:
  164. case 36:
  165. case 35:
  166. case 34:
  167. pFile->Read(&m_dX);
  168. pFile->Read(&m_dY);
  169. pFile->Read(&m_dZ);
  170. case 33:
  171. case 32:
  172. case 31:
  173. pFile->Read(&m_lVolumeHalfLife);
  174. case 30:
  175. case 29:
  176. case 28:
  177. case 27:
  178. case 26:
  179. case 25:
  180. case 24:
  181. case 23:
  182. case 22:
  183. case 21:
  184. case 20:
  185. case 19:
  186. case 18:
  187. case 17:
  188. case 16:
  189. case 15:
  190. case 14:
  191. case 13:
  192. case 12:
  193. case 11:
  194. case 10:
  195. case 9:
  196. case 8:
  197. case 7:
  198. case 6:
  199. case 5:
  200. case 4:
  201. case 3:
  202. case 2:
  203. case 1:
  204. long lBool;
  205. pFile->Read(&lBool);
  206. m_bInitiallyEnabled = lBool ? true : false;
  207. pFile->Read(&lBool);
  208. m_bInitiallyRepeats = lBool ? true : false;
  209. pFile->Read(m_lMinTime, 2);
  210. pFile->Read(m_lRndTime, 2);
  211. pFile->Read(m_szResName);
  212. break;
  213. }
  214. // Make sure there were no file errors or format errors . . .
  215. if (!pFile->Error() && sResult == 0)
  216. {
  217. sResult = Init();
  218. }
  219. else
  220. {
  221. sResult = -1;
  222. TRACE("CSoundThing::Load(): Error reading from file!\n");
  223. }
  224. }
  225. return sResult;
  226. }
  227. ////////////////////////////////////////////////////////////////////////////////
  228. // Save object (should call base class version!)
  229. ////////////////////////////////////////////////////////////////////////////////
  230. short CSoundThing::Save( // Returns 0 if successfull, non-zero otherwise
  231. RFile* pFile, // In: File to save to
  232. short sFileCount) // In: File count (unique per file, never 0)
  233. {
  234. short sResult = CThing::Save(pFile, sFileCount);
  235. if (sResult == 0)
  236. {
  237. pFile->Write(m_sAmbient);
  238. pFile->Write(m_sPurgeSampleWhenDone );
  239. pFile->Write(m_sUseLooping );
  240. pFile->Write(m_lStopLoopingTime );
  241. pFile->Write(m_lNumLoopBacks );
  242. pFile->Write(m_lLoopBackTo );
  243. pFile->Write(m_lLoopBackFrom );
  244. pFile->Write(m_dX);
  245. pFile->Write(m_dY);
  246. pFile->Write(m_dZ);
  247. pFile->Write(m_lVolumeHalfLife);
  248. pFile->Write((long)m_bInitiallyEnabled);
  249. pFile->Write((long)m_bInitiallyRepeats);
  250. pFile->Write(m_lMinTime, 2);
  251. pFile->Write(m_lRndTime, 2);
  252. pFile->Write(m_szResName);
  253. // Make sure there were no file errors
  254. sResult = pFile->Error();
  255. }
  256. return sResult;
  257. }
  258. ////////////////////////////////////////////////////////////////////////////////
  259. // Startup object
  260. ////////////////////////////////////////////////////////////////////////////////
  261. short CSoundThing::Startup(void) // Returns 0 if successfull, non-zero otherwise
  262. {
  263. // Set the collective volume to zero to start.
  264. m_lCollectiveVolume = 0;
  265. return 0;
  266. }
  267. ////////////////////////////////////////////////////////////////////////////////
  268. // Shutdown object
  269. ////////////////////////////////////////////////////////////////////////////////
  270. short CSoundThing::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
  271. {
  272. return 0;
  273. }
  274. ////////////////////////////////////////////////////////////////////////////////
  275. // Suspend object
  276. ////////////////////////////////////////////////////////////////////////////////
  277. void CSoundThing::Suspend(void)
  278. {
  279. m_sSuspend++;
  280. }
  281. ////////////////////////////////////////////////////////////////////////////////
  282. // Resume object
  283. ////////////////////////////////////////////////////////////////////////////////
  284. void CSoundThing::Resume(void)
  285. {
  286. m_sSuspend--;
  287. // If we're actually going to start updating again....
  288. if (m_sSuspend == 0)
  289. {
  290. // This is kind of cheesy, but it will work to some degree
  291. m_lLastStartTime = m_pRealm->m_time.GetGameTime();
  292. }
  293. }
  294. ////////////////////////////////////////////////////////////////////////////////
  295. // Update object
  296. ////////////////////////////////////////////////////////////////////////////////
  297. void CSoundThing::Update(void)
  298. {
  299. if (!m_sSuspend)
  300. {
  301. // Get current time
  302. long lCurTime = m_pRealm->m_time.GetGameTime();
  303. // If enabled and (non-ambient or ambients allowed) . . .
  304. if (m_bEnabled == true && (m_sAmbient == FALSE || g_GameSettings.m_sPlayAmbientSounds) )
  305. {
  306. // If current time hits next starting time (or if we're in the init state)
  307. if ((lCurTime >= m_lNextStartTime) || (m_sWhichTime < 0))
  308. {
  309. long lSampleDuration = 0;
  310. long lLoopStartTime;
  311. long lLoopEndTime = m_lLoopBackFrom;
  312. if (m_sUseLooping)
  313. {
  314. lLoopStartTime = m_lLoopBackTo;
  315. }
  316. else
  317. {
  318. // Indicate no looping.
  319. lLoopStartTime = -1;
  320. }
  321. // If first time (not init) OR nth time with repeat enabled . . .
  322. // Play the sound (unless it's the init state)
  323. if (m_sWhichTime == 0 || (m_sWhichTime > 0 && m_bRepeats == true) )
  324. {
  325. // If using loop parameters . . .
  326. if (m_sUseLooping)
  327. {
  328. // Stop looping existing sound.
  329. StopLoopingSample(m_siChannel);
  330. }
  331. bool bPurge;
  332. if (m_sPurgeSampleWhenDone)
  333. {
  334. bPurge = true;
  335. }
  336. else
  337. {
  338. bPurge = false;
  339. }
  340. PlaySample( // Returns nothing.
  341. // Does not fail.
  342. m_id, // In: Identifier of sample you want played.
  343. SampleMaster::Ambient, // In: Sound Volume Category for user adjustment
  344. DistanceToVolume(m_dX, m_dY, m_dZ, m_lVolumeHalfLife), // In: Initial Sound Volume (0 - 255)
  345. &m_siChannel, // Out: Handle for adjusting sound volume
  346. &lSampleDuration, // Out: Sample duration in ms, if not NULL.
  347. lLoopStartTime, // In: Where to loop back to in milliseconds.
  348. // -1 indicates no looping (unless m_sLoop is
  349. // explicitly set).
  350. lLoopEndTime, // In: Where to loop back from in milliseconds.
  351. // If less than 1, the end + lLoopEndTime is used.
  352. bPurge); // In: Call ReleaseAndPurge rather than Release after playing
  353. ///////////////////////////////////////////////////////////////////////////////////////////////////
  354. // NOTE: We have to use the exact logic RSnd does to determine the loop points so we can correctly
  355. // determine the amount of time until we stop looping.
  356. ///////////////////////////////////////////////////////////////////////////////////////////////////
  357. // If using the end . . .
  358. if (lLoopEndTime < 1)
  359. {
  360. // Use the duration plus the specified negative time.
  361. lLoopEndTime += lSampleDuration;
  362. }
  363. // Fix these values in case we're in release mode.
  364. if (lLoopStartTime > lSampleDuration)
  365. {
  366. lLoopStartTime = lSampleDuration;
  367. }
  368. if (lLoopEndTime > lSampleDuration)
  369. {
  370. lLoopEndTime = lSampleDuration;
  371. }
  372. if (lLoopStartTime < 0 && m_sUseLooping)
  373. {
  374. lLoopStartTime = 0;
  375. }
  376. if (lLoopStartTime > lLoopEndTime)
  377. {
  378. lLoopStartTime = lLoopEndTime;
  379. }
  380. // If using loop parameters and loop backs are not infinite . . .
  381. if (m_sUseLooping && m_lNumLoopBacks >= 0)
  382. {
  383. // Calculate time until we stop looping . . .
  384. m_lStopLoopingTime = lCurTime + (m_lNumLoopBacks + 1) * (lLoopEndTime - lLoopStartTime) + lLoopStartTime;
  385. }
  386. else
  387. {
  388. // Set time way into the future so we don't bother for a while.
  389. m_lStopLoopingTime = END_OF_TIME;
  390. }
  391. }
  392. // Save current start time (use lCurTime in case we're in the init state)
  393. m_lLastStartTime = lCurTime;
  394. // Inc which time to use (don't go past 1)
  395. if (m_sWhichTime < 1)
  396. m_sWhichTime++;
  397. // Pick random time between 0 and specified random time
  398. long lRnd = (long)(((float)GetRand() / (float)RAND_MAX) * (float)m_lRndTime[m_sWhichTime]);
  399. // Make sure this at least the length of the sample.
  400. long lWaitDuration = MAX(long(m_lMinTime[m_sWhichTime] + lRnd), lSampleDuration);
  401. // Calculate next starting time
  402. m_lNextStartTime = m_lLastStartTime + lWaitDuration;
  403. }
  404. // Relay our volume (we act as just another satellite).
  405. RelayVolume(DistanceToVolume(m_dX, m_dY, m_dZ, m_lVolumeHalfLife) );
  406. }
  407. // If time to stop looping . . .
  408. if (lCurTime >= m_lStopLoopingTime)
  409. {
  410. if (m_sUseLooping)
  411. {
  412. // Stop looping existing sound.
  413. StopLoopingSample(m_siChannel);
  414. }
  415. // Set time way into the future so we don't bother for a while.
  416. m_lStopLoopingTime = END_OF_TIME;
  417. }
  418. // Process messages.
  419. ProcessMessages();
  420. switch (m_state)
  421. {
  422. case State_Happy:
  423. break;
  424. case State_Delete:
  425. // Banzai!
  426. delete this;
  427. return ;
  428. }
  429. }
  430. }
  431. ////////////////////////////////////////////////////////////////////////////////
  432. // Render object
  433. ////////////////////////////////////////////////////////////////////////////////
  434. void CSoundThing::Render(void)
  435. {
  436. // Render our sound. What a cheezy way of trying to justify putting this
  437. // here.
  438. // Adjust volume of last play instance. Clip just in case.
  439. SetInstanceVolume(m_siChannel, MIN(255L, m_lCollectiveVolume) );
  440. // Reset volume.
  441. m_lCollectiveVolume = 0;
  442. }
  443. ////////////////////////////////////////////////////////////////////////////////
  444. // Called by editor to init new object at specified position
  445. ////////////////////////////////////////////////////////////////////////////////
  446. short CSoundThing::EditNew( // Returns 0 if successfull, non-zero otherwise
  447. short sX, // In: New x coord
  448. short sY, // In: New y coord
  449. short sZ) // In: New z coord
  450. {
  451. short sResult = 0;
  452. // Use specified position
  453. m_dX = (double)sX;
  454. m_dY = (double)sY;
  455. m_dZ = (double)sZ;
  456. sResult = EditModify();
  457. return sResult;
  458. }
  459. ////////////////////////////////////////////////////////////////////////////////
  460. // Helper function/macro for changing a GUIs text value.
  461. ////////////////////////////////////////////////////////////////////////////////
  462. inline void SetGuiItemVal( // Returns nothing.
  463. RGuiItem* pguiRoot, // In: GUI Root.
  464. long lId, // In: ID of item whose text we'll change.
  465. long lVal) // In: New value.
  466. {
  467. RGuiItem* pgui = pguiRoot->GetItemFromId(lId);
  468. if (pgui)
  469. {
  470. pgui->SetText("%ld", lVal);
  471. pgui->Compose();
  472. }
  473. }
  474. ////////////////////////////////////////////////////////////////////////////////
  475. // Callback from multibtn checkbox.
  476. ////////////////////////////////////////////////////////////////////////////////
  477. static void CheckEnableGuiCall( // Returns nothing.
  478. RGuiItem* pgui_pmb) // In: GUI pointer to the multi button that was
  479. // pressed.
  480. {
  481. ASSERT(pgui_pmb->m_type == RGuiItem::MultiBtn);
  482. RMultiBtn* pmb = (RMultiBtn*)pgui_pmb;
  483. // Show based on value stored in GUI.
  484. short sVisible = pmb->m_ulUserData;
  485. // If unchecked . . .
  486. if (pmb->m_sState == 1)
  487. {
  488. // Opposite show/hide state.
  489. sVisible = !sVisible;
  490. }
  491. RGuiItem* pguiLoopSettingsContainer = pmb->GetParent()->GetItemFromId(pmb->m_ulUserInstance);
  492. if (pguiLoopSettingsContainer)
  493. {
  494. pguiLoopSettingsContainer->SetVisible(sVisible);
  495. }
  496. }
  497. ////////////////////////////////////////////////////////////////////////////////
  498. // Callback from GUI to browse and fill another GUI with result, if a relative
  499. // path could be made.
  500. ////////////////////////////////////////////////////////////////////////////////
  501. static void BrowseCall( // Returns nothing.
  502. RGuiItem* pgui) // In: GUI pointer that was pressed.
  503. {
  504. ASSERT(pgui->GetParent() );
  505. RGuiItem* pguiName = pgui->GetParent()->GetItemFromId(pgui->m_ulUserInstance);
  506. if (pguiName)
  507. {
  508. char szTitle[256];
  509. sprintf(szTitle, g_pszGenericBrowseFor_s_Title, "sound file");
  510. // Create full system path from existing RSPiX subpath.
  511. char szSystemPath[RSP_MAX_PATH];
  512. char* pszSakpath = g_resmgrSamples.GetBasePath();
  513. if (pguiName->m_szText[0] == '\0')
  514. {
  515. pguiName->SetText(".");
  516. }
  517. strcpy(szSystemPath, FullPathCustom(pszSakpath, pguiName->m_szText) );
  518. short sResult;
  519. do {
  520. sResult = SubPathOpenBox( // Returns 0 on success, negative on error, 1 if
  521. // not subpathable (i.e., returned path is full path).
  522. pszSakpath, // In: Full path to be relative to (system format).
  523. szTitle, // In: Title of box.
  524. szSystemPath, // In: Default filename (system format).
  525. szSystemPath, // Out: User's choice (system format).
  526. sizeof(szSystemPath), // In: Amount of memory pointed to by pszChosenFileName.
  527. "wav"); // In: If not NULL, '.' delimited extension based filename
  528. // filter specification. Ex: ".cpp.h.exe.lib" or "cpp.h.exe.lib"
  529. // Note: Cannot use '.' in filter. Preceding '.' ignored.
  530. if (sResult > 0)
  531. {
  532. rspMsgBox(
  533. RSP_MB_ICN_STOP | RSP_MB_BUT_OK,
  534. g_pszAppName,
  535. g_pszGenericMustBeRelativePath_s,
  536. pszSakpath);
  537. }
  538. } while (sResult > 0);
  539. // If successful in getting a relative path . . .
  540. if (sResult == 0)
  541. {
  542. // Udpate GUI.
  543. pguiName->SetText("%s", rspPathFromSystem(szSystemPath) );
  544. pguiName->Compose();
  545. }
  546. }
  547. }
  548. ////////////////////////////////////////////////////////////////////////////////
  549. // Called by editor to modify object
  550. ////////////////////////////////////////////////////////////////////////////////
  551. short CSoundThing::EditModify(void)
  552. {
  553. short sResult = 0;
  554. // Load gui dialog
  555. RGuiItem* pgui = RGuiItem::LoadInstantiate(FullPathVD(GUI_FILE_NAME));
  556. if (pgui != NULL)
  557. {
  558. // Init "name" edit
  559. REdit* pResName = (REdit*)pgui->GetItemFromId(3);
  560. ASSERT(pResName != NULL);
  561. ASSERT(pResName->m_type == RGuiItem::Edit);
  562. pResName->SetText("%s", m_szResName);
  563. pResName->Compose();
  564. // Setup name browse button.
  565. RGuiItem* pguiNameBrowse = pgui->GetItemFromId(4);
  566. ASSERT(pguiNameBrowse);
  567. // Set callback for browsage.
  568. pguiNameBrowse->m_bcUser = BrowseCall;
  569. // Tell it what ID to fill with result.
  570. pguiNameBrowse->m_ulUserInstance = pResName->m_lId;
  571. // Init "first time" edit
  572. REdit* pFirstTime = (REdit*)pgui->GetItemFromId(100);
  573. ASSERT(pFirstTime != NULL);
  574. ASSERT(pFirstTime->m_type == RGuiItem::Edit);
  575. pFirstTime->SetText("%ld", m_lMinTime[0]);
  576. pFirstTime->Compose();
  577. // Init "first random time" edit
  578. REdit* pFirstRndTime = (REdit*)pgui->GetItemFromId(101);
  579. ASSERT(pFirstRndTime != NULL);
  580. ASSERT(pFirstRndTime->m_type == RGuiItem::Edit);
  581. pFirstRndTime->SetText("%ld", m_lRndTime[0]);
  582. pFirstRndTime->Compose();
  583. // Init "repeat time" edit
  584. REdit* pRepeatTime = (REdit*)pgui->GetItemFromId(102);
  585. ASSERT(pRepeatTime != NULL);
  586. ASSERT(pRepeatTime->m_type == RGuiItem::Edit);
  587. pRepeatTime->SetText("%ld", m_lMinTime[1]);
  588. pRepeatTime->Compose();
  589. // Init "repeat random time" edit
  590. REdit* pRepeatRndTime = (REdit*)pgui->GetItemFromId(103);
  591. ASSERT(pRepeatRndTime != NULL);
  592. ASSERT(pRepeatRndTime->m_type == RGuiItem::Edit);
  593. pRepeatRndTime->SetText("%ld", m_lRndTime[1]);
  594. pRepeatRndTime->Compose();
  595. // Init "enable" push button
  596. RPushBtn* pEnable = (RPushBtn*)pgui->GetItemFromId(200);
  597. ASSERT(pEnable != NULL);
  598. pEnable->m_state = m_bInitiallyEnabled ? RPushBtn::On : RPushBtn::Off;
  599. pEnable->Compose();
  600. // Init "repeat" push button
  601. RPushBtn* pRepeat = (RPushBtn*)pgui->GetItemFromId(201);
  602. ASSERT(pRepeat != NULL);
  603. pRepeat->m_state = m_bInitiallyRepeats ? RPushBtn::On : RPushBtn::Off;
  604. pRepeat->Compose();
  605. // Init "volume half life" edit.
  606. REdit* peditHalfLife = (REdit*)pgui->GetItemFromId(301);
  607. ASSERT(peditHalfLife != NULL);
  608. ASSERT(peditHalfLife->m_type == RGuiItem::Edit);
  609. peditHalfLife->SetText("%ld", m_lVolumeHalfLife);
  610. peditHalfLife->Compose();
  611. // Init "Purge sample when done" checkbox.
  612. RMultiBtn* pmbPurge = (RMultiBtn*)pgui->GetItemFromId(500);
  613. ASSERT(pmbPurge);
  614. ASSERT(pmbPurge->m_type == RGuiItem::MultiBtn);
  615. pmbPurge->m_sState = (m_sPurgeSampleWhenDone == FALSE) ? 1 : 2;
  616. pmbPurge->Compose();
  617. // Init "Use looping" checkbox.
  618. RMultiBtn* pmbUseLooping = (RMultiBtn*)pgui->GetItemFromId(400);
  619. ASSERT(pmbUseLooping);
  620. ASSERT(pmbUseLooping->m_type == RGuiItem::MultiBtn);
  621. pmbUseLooping->m_sState = (m_sUseLooping == FALSE) ? 1 : 2;
  622. pmbUseLooping->Compose();
  623. // Set the callback so we can tell when state changes.
  624. pmbUseLooping->m_bcUser = CheckEnableGuiCall;
  625. // Set GUI to enable/disable.
  626. pmbUseLooping->m_ulUserInstance = 499;
  627. // Set show state to use when checked.
  628. pmbUseLooping->m_ulUserData = TRUE;
  629. // Setup intially.
  630. CheckEnableGuiCall(pmbUseLooping);
  631. SetGuiItemVal(pgui, 401, m_lLoopBackTo);
  632. SetGuiItemVal(pgui, 402, (m_lLoopBackFrom == 0) ? 1 : m_lLoopBackFrom);
  633. SetGuiItemVal(pgui, 403, (m_lNumLoopBacks < 0) ? 0 : m_lNumLoopBacks);
  634. // Init "End" checkbox.
  635. RMultiBtn* pmbLoopFromEnd = (RMultiBtn*)pgui->GetItemFromId(404);
  636. ASSERT(pmbLoopFromEnd);
  637. ASSERT(pmbLoopFromEnd->m_type == RGuiItem::MultiBtn);
  638. pmbLoopFromEnd->m_sState = (m_lLoopBackFrom == 0) ? 2 : 1;
  639. pmbLoopFromEnd->Compose();
  640. // Set the callback so we can tell when state changes.
  641. pmbLoopFromEnd->m_bcUser = CheckEnableGuiCall;
  642. // Set GUI to enable/disable.
  643. pmbLoopFromEnd->m_ulUserInstance = 402;
  644. // Set show state to use when checked.
  645. pmbLoopFromEnd->m_ulUserData = FALSE;
  646. // Setup intially.
  647. CheckEnableGuiCall(pmbLoopFromEnd);
  648. // Init "Infinite" checkbox.
  649. RMultiBtn* pmbLoopInfinitely = (RMultiBtn*)pgui->GetItemFromId(405);
  650. ASSERT(pmbLoopInfinitely);
  651. ASSERT(pmbLoopInfinitely->m_type == RGuiItem::MultiBtn);
  652. pmbLoopInfinitely->m_sState = (m_lNumLoopBacks < 0) ? 2 : 1;
  653. pmbLoopInfinitely->Compose();
  654. // Set the callback so we can tell when state changes.
  655. pmbLoopInfinitely->m_bcUser = CheckEnableGuiCall;
  656. // Set GUI to enable/disable.
  657. pmbLoopInfinitely->m_ulUserInstance = 403;
  658. // Set show state to use when checked.
  659. pmbLoopInfinitely->m_ulUserData = FALSE;
  660. // Setup intially.
  661. CheckEnableGuiCall(pmbLoopInfinitely);
  662. // Init "Ambient sound" checkbox.
  663. RMultiBtn* pmbAmbient = (RMultiBtn*)pgui->GetItemFromId(600);
  664. ASSERT(pmbAmbient);
  665. ASSERT(pmbAmbient->m_type == RGuiItem::MultiBtn);
  666. pmbAmbient->m_sState = (m_sAmbient == FALSE) ? 1 : 2;
  667. pmbAmbient->Compose();
  668. // Run the dialog using this super-duper helper funciton
  669. if (DoGui(pgui) == 1)
  670. {
  671. // Get new values from dialog
  672. pResName->GetText(m_szResName, sizeof(m_szResName) );
  673. m_lMinTime[0] = pFirstTime->GetVal();
  674. m_lRndTime[0] = pFirstRndTime->GetVal();
  675. m_lMinTime[1] = pRepeatTime->GetVal();
  676. m_lRndTime[1] = pRepeatRndTime->GetVal();
  677. m_bInitiallyEnabled = (pEnable->m_state == RPushBtn::On) ? true : false;
  678. m_bInitiallyRepeats = (pRepeat->m_state == RPushBtn::On) ? true : false;
  679. m_lVolumeHalfLife = peditHalfLife->GetVal();
  680. m_sPurgeSampleWhenDone = (pmbPurge->m_sState == 2) ? TRUE : FALSE;
  681. m_sUseLooping = (pmbUseLooping->m_sState == 2) ? TRUE : FALSE;
  682. m_lLoopBackTo = pgui->GetVal(401);
  683. m_lLoopBackFrom = (pmbLoopFromEnd->m_sState == 2) ? 0 : pgui->GetVal(402);
  684. m_lNumLoopBacks = (pmbLoopInfinitely->m_sState == 2) ? -1 : pgui->GetVal(403);
  685. m_sAmbient = (pmbAmbient->m_sState == 2) ? TRUE : FALSE;
  686. }
  687. else
  688. {
  689. sResult = 1;
  690. }
  691. // Done with GUI.
  692. delete pgui;
  693. }
  694. else
  695. {
  696. sResult = -1;
  697. }
  698. // If everything's okay, init using new values
  699. if (sResult == 0)
  700. sResult = Init();
  701. return sResult;
  702. }
  703. ////////////////////////////////////////////////////////////////////////////////
  704. // Called by editor to move object to specified position
  705. ////////////////////////////////////////////////////////////////////////////////
  706. short CSoundThing::EditMove( // Returns 0 if successfull, non-zero otherwise
  707. short sX, // In: New x coord
  708. short sY, // In: New y coord
  709. short sZ) // In: New z coord
  710. {
  711. m_dX = (double)sX;
  712. m_dY = (double)sY;
  713. m_dZ = (double)sZ;
  714. return 0;
  715. }
  716. ////////////////////////////////////////////////////////////////////////////////
  717. // Called by editor to get the clickable pos/area of an object in 2D.
  718. // (virtual (Overridden here)).
  719. ////////////////////////////////////////////////////////////////////////////////
  720. void CSoundThing::EditRect( // Returns nothiing.
  721. RRect* prc) // Out: Clickable pos/area of object.
  722. {
  723. Map3Dto2D(
  724. m_dX,
  725. m_dY,
  726. m_dZ,
  727. &(prc->sX),
  728. &(prc->sY) );
  729. prc->sW = 10; // Safety.
  730. prc->sH = 10; // Safety.
  731. if (m_pImage)
  732. {
  733. prc->sW = m_pImage->m_sWidth;
  734. prc->sH = m_pImage->m_sHeight;
  735. }
  736. prc->sX -= prc->sW / 2;
  737. prc->sY -= prc->sH;
  738. }
  739. ////////////////////////////////////////////////////////////////////////////////
  740. // Called by editor to get the hotspot of an object in 2D.
  741. // (virtual (Overridden here)).
  742. ////////////////////////////////////////////////////////////////////////////////
  743. void CSoundThing::EditHotSpot( // Returns nothiing.
  744. short* psX, // Out: X coord of 2D hotspot relative to
  745. // EditRect() pos.
  746. short* psY) // Out: Y coord of 2D hotspot relative to
  747. // EditRect() pos.
  748. {
  749. *psX = 5; // Safety.
  750. *psY = 5; // Safety.
  751. if (m_pImage)
  752. {
  753. *psX = m_pImage->m_sWidth / 2;
  754. *psY = m_pImage->m_sHeight;
  755. }
  756. }
  757. ////////////////////////////////////////////////////////////////////////////////
  758. // Called by editor to update object
  759. ////////////////////////////////////////////////////////////////////////////////
  760. void CSoundThing::EditUpdate(void)
  761. {
  762. }
  763. ////////////////////////////////////////////////////////////////////////////////
  764. // Called by editor to render object
  765. ////////////////////////////////////////////////////////////////////////////////
  766. void CSoundThing::EditRender(void)
  767. {
  768. // Setup simple, non-animating sprite
  769. m_sprite.m_sInFlags = 0;
  770. Map3Dto2D(
  771. m_dX,
  772. m_dY,
  773. m_dZ,
  774. &(m_sprite.m_sX2),
  775. &(m_sprite.m_sY2) );
  776. // Priority is based on bottom edge of sprite
  777. m_sprite.m_sPriority = m_dZ;
  778. // Center on image.
  779. m_sprite.m_sX2 -= m_pImage->m_sWidth / 2;
  780. m_sprite.m_sY2 -= m_pImage->m_sHeight;
  781. m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer((short) m_dX, (short) m_dZ));
  782. m_sprite.m_pImage = m_pImage;
  783. // Update sprite in scene
  784. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  785. }
  786. ////////////////////////////////////////////////////////////////////////////////
  787. // Init object
  788. ////////////////////////////////////////////////////////////////////////////////
  789. short CSoundThing::Init(void) // Returns 0 if successfull, non-zero otherwise
  790. {
  791. short sResult = 0;
  792. Kill();
  793. m_lLastStartTime = 0;
  794. m_lNextStartTime = 0;
  795. m_sWhichTime = -1;
  796. m_bEnabled = m_bInitiallyEnabled;
  797. m_bRepeats = m_bInitiallyRepeats;
  798. // Tell samplemaster to cache (preload) this sample
  799. CacheSample(m_id);
  800. if (m_pImage == 0)
  801. {
  802. sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(IMAGE_FILE), &m_pImage);
  803. if (sResult == 0)
  804. {
  805. // This is a questionable action on a resource managed item, but it's
  806. // okay if EVERYONE wants it to be an FSPR8.
  807. if (m_pImage->Convert(RImage::FSPR8) != RImage::FSPR8)
  808. {
  809. sResult = -1;
  810. TRACE("CSoundThing::GetResource() - Couldn't convert to FSPR8\n");
  811. }
  812. }
  813. }
  814. return sResult;
  815. }
  816. ////////////////////////////////////////////////////////////////////////////////
  817. // Kill object
  818. ////////////////////////////////////////////////////////////////////////////////
  819. short CSoundThing::Kill(void) // Returns 0 if successfull, non-zero otherwise
  820. {
  821. if (m_pImage != 0)
  822. rspReleaseResource(&g_resmgrGame, &m_pImage);
  823. m_pRealm->m_scene.RemoveSprite(&m_sprite);
  824. // If we have a play instance identifier . . .
  825. if (m_siChannel != 0)
  826. {
  827. // Abort it.
  828. AbortSample(m_siChannel);
  829. // Done with ID.
  830. m_siChannel = 0;
  831. }
  832. return 0;
  833. }
  834. ////////////////////////////////////////////////////////////////////////////////
  835. // Process our message queue.
  836. ////////////////////////////////////////////////////////////////////////////////
  837. void CSoundThing::ProcessMessages(void)
  838. {
  839. // Check queue of messages.
  840. GameMessage msg;
  841. while (m_MessageQueue.DeQ(&msg) == true)
  842. {
  843. switch(msg.msg_Generic.eType)
  844. {
  845. case typeObjectDelete:
  846. m_state = State_Delete;
  847. break;
  848. }
  849. }
  850. }
  851. ////////////////////////////////////////////////////////////////////////////////
  852. // Don't call this from outside of CSoundThing. It should affect only
  853. // CSoundThing stuff.
  854. ////////////////////////////////////////////////////////////////////////////////
  855. long CSoundThing::GetRandom(void)
  856. {
  857. return (((ms_lGetRandomSeed = ms_lGetRandomSeed * 214013L + 2531011L) >> 16) & 0x7fff);
  858. }
  859. ////////////////////////////////////////////////////////////////////////////////
  860. // Relay the volume to add to this CSoundThing's collective volume.
  861. ////////////////////////////////////////////////////////////////////////////////
  862. void CSoundThing::RelayVolume( // Returns nothing.
  863. long lVolume) // In: Volume to relay.
  864. {
  865. m_lCollectiveVolume += lVolume;
  866. }
  867. ////////////////////////////////////////////////////////////////////////////////
  868. // EOF
  869. ////////////////////////////////////////////////////////////////////////////////