SoundManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // SoundManager.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Text;
  13. using Microsoft.Xna.Framework;
  14. using Microsoft.Xna.Framework.Audio;
  15. using RobotGameData.Camera;
  16. #endregion
  17. namespace RobotGameData.Sound
  18. {
  19. #region SoundElement
  20. /// <summary>
  21. /// a sound structure with sound cue and emitter for 3D playback.
  22. /// </summary>
  23. public class SoundElement
  24. {
  25. public Cue cue = null;
  26. public AudioEmitter emitter = null;
  27. public string Name
  28. {
  29. get { return cue.Name; }
  30. }
  31. }
  32. #endregion
  33. /// <summary>
  34. /// It reads sound data and plays sound and supports 3D positional sound.
  35. /// When playing sound, it creates SoundElement class internally and
  36. /// manages a sound pool.
  37. /// In order to load sound data, the files, which have been created by
  38. /// Microsoft Cross-Platform Audio Creation Tool (XACT), such as .xgs,
  39. /// .xwb, and .xsb, are needed.
  40. /// </summary>
  41. public class SoundManager
  42. {
  43. #region Fields
  44. /// <summary>
  45. /// If set to false, all of the related functions get turned off.
  46. /// </summary>
  47. bool soundOn = true;
  48. static AudioEngine audioEngine = null;
  49. static WaveBank waveBank = null;
  50. static SoundBank soundBank = null;
  51. static Dictionary<string, AudioCategory> audioCategories =
  52. new Dictionary<string, AudioCategory>();
  53. // Keep track of all the 3D sounds that are currently playing.
  54. static List<SoundElement> activeSounds = new List<SoundElement>();
  55. // Keep track of spare SoundObject instances, so we can reuse them.
  56. // Otherwise we would have to allocate new instances each time
  57. // a sound was played, which would create unnecessary garbage.
  58. static Stack<SoundElement> soundPool = new Stack<SoundElement>();
  59. // The listener describes the ear which is hearing 3D sounds.
  60. // This is usually set to match the camera.
  61. AudioListener listener = new AudioListener();
  62. // The emitter describes an entity which is making a 3D sound.
  63. AudioEmitter emitter = new AudioEmitter();
  64. static bool pauseAll = false;
  65. #endregion
  66. #region Properties
  67. public static AudioEngine AudioEngine
  68. {
  69. get { return audioEngine; }
  70. }
  71. public static WaveBank WaveBank
  72. {
  73. get { return waveBank; }
  74. }
  75. public static SoundBank SoundBank
  76. {
  77. get { return soundBank; }
  78. }
  79. public static bool PauseAll
  80. {
  81. get { return pauseAll; }
  82. }
  83. #endregion
  84. /// <summary>
  85. /// Constructor.
  86. /// </summary>
  87. public SoundManager()
  88. {
  89. audioCategories.Clear();
  90. pauseAll = false;
  91. }
  92. /// <summary>
  93. /// applies 3D sound to the listener.
  94. /// </summary>
  95. /// <param name="position">listener position</param>
  96. /// <param name="direction">listener direction</param>
  97. /// <param name="up">listener up vector</param>
  98. /// <param name="velocity">listener velocity</param>
  99. public void ApplyEmitter(Vector3 position, Vector3 direction, Vector3 up,
  100. Vector3 velocity)
  101. {
  102. // Update listener by the current camera
  103. this.listener.Position = position;
  104. this.listener.Forward = direction;
  105. this.listener.Up = up;
  106. this.listener.Velocity = velocity;
  107. }
  108. /// <summary>
  109. /// processes the sound pool and the 3D sound playback.
  110. /// XNA's AudioEngine is updated here.
  111. /// </summary>
  112. public void Update()
  113. {
  114. if (soundOn == false)
  115. return;
  116. // Loop over all the currently playing 3D sounds.
  117. int index = 0;
  118. while (index < activeSounds.Count)
  119. {
  120. SoundElement sound = activeSounds[index];
  121. if (sound.cue.IsStopped)
  122. {
  123. // If the cue has stopped playing, dispose it.
  124. //sound.cue.Dispose();
  125. sound.cue = null;
  126. sound.emitter = null;
  127. // Store the SoundElement instance for future reuse.
  128. soundPool.Push(sound);
  129. // Remove it from the active list.
  130. activeSounds.RemoveAt(index);
  131. }
  132. else
  133. {
  134. if (sound.emitter != null)
  135. {
  136. // If the cue is still playing, update its 3D settings.
  137. Apply3D(sound);
  138. }
  139. index++;
  140. }
  141. }
  142. // Update the XACT engine.
  143. AudioEngine.Update();
  144. }
  145. /// <summary>
  146. /// stops every sound and removes every sound member.
  147. /// </summary>
  148. public void Dispose()
  149. {
  150. if (soundOn == false)
  151. return;
  152. StopSound();
  153. audioCategories.Clear();
  154. SoundBank.Dispose();
  155. WaveBank.Dispose();
  156. AudioEngine.Dispose();
  157. }
  158. /// <summary>
  159. /// initialize all sound members.
  160. /// creates and initializes the XNA's AudioEndgine.
  161. /// </summary>
  162. /// <param name="globalSettingsFileName"></param>
  163. /// <param name="waveBankFileName"></param>
  164. /// <param name="soundBankFileName"></param>
  165. public void Initialize( string globalSettingsFileName,
  166. string waveBankFileName,
  167. string soundBankFileName)
  168. {
  169. if (soundOn == false)
  170. return;
  171. // Create audio engine
  172. audioEngine = new AudioEngine(globalSettingsFileName);
  173. if (audioEngine == null)
  174. {
  175. throw new ArgumentException(
  176. "The audio engine could not be created.");
  177. }
  178. // Create wave bank
  179. waveBank = new WaveBank(audioEngine, waveBankFileName);
  180. if (waveBank == null)
  181. {
  182. throw new ArgumentException(
  183. "The wave bank could not be created.");
  184. }
  185. // Create sound bank
  186. soundBank = new SoundBank(audioEngine, soundBankFileName);
  187. if (soundBank == null)
  188. {
  189. throw new ArgumentException(
  190. "The sound bank could not be created.");
  191. }
  192. }
  193. /// <summary>
  194. /// adds a sound category.
  195. /// </summary>
  196. /// <param name="categoryName">name</param>
  197. public void AddSoundCategory(string categoryName)
  198. {
  199. if (soundOn == false)
  200. return;
  201. AudioCategory audioCategory = AudioEngine.GetCategory(categoryName);
  202. audioCategories.Add(categoryName, audioCategory);
  203. }
  204. /// <summary>
  205. /// plays the sound for 3D.
  206. /// </summary>
  207. /// <param name="soundName">entried sound name</param>
  208. /// <param name="emitter">3D emitter</param>
  209. /// <returns>playing sound cue</returns>
  210. public Cue PlaySound3D(string soundName, AudioEmitter emitter)
  211. {
  212. SoundElement sound;
  213. if (soundPool.Count > 0)
  214. {
  215. // If possible, reuse an existing Cue3D instance.
  216. sound = soundPool.Pop();
  217. }
  218. else
  219. {
  220. // Otherwise we have to allocate a new one.
  221. sound = new SoundElement();
  222. }
  223. // Fill in the cue and emitter fields.
  224. sound.cue = soundBank.GetCue(soundName);
  225. sound.emitter = emitter;
  226. // Set the 3D position of this cue, and then play it.
  227. Apply3D(sound);
  228. sound.cue.Play();
  229. // Remember that this cue is now active.
  230. activeSounds.Add(sound);
  231. return sound.cue;
  232. }
  233. /// <summary>
  234. /// plays the sound.
  235. /// </summary>
  236. /// <param name="soundName">entried sound name</param>
  237. /// <returns>playing sound cue</returns>
  238. public static Cue PlaySound(string soundName)
  239. {
  240. SoundElement sound = null;
  241. if (soundPool.Count > 0)
  242. {
  243. // If possible, reuse an existing Cue3D instance.
  244. sound = soundPool.Pop();
  245. }
  246. else
  247. {
  248. // Otherwise we have to allocate a new one.
  249. sound = new SoundElement();
  250. }
  251. // Fill in the cue and emitter fields.
  252. sound.cue = soundBank.GetCue(soundName);
  253. sound.cue.Play();
  254. // Remember that this cue is now active.
  255. activeSounds.Add(sound);
  256. return sound.cue;
  257. }
  258. /// <summary>
  259. /// pauses the sound.
  260. /// </summary>
  261. public void PauseSound()
  262. {
  263. if (soundOn == false || pauseAll )
  264. return;
  265. for (int i = 0; i < activeSounds.Count; i++)
  266. PauseSound(activeSounds[i]);
  267. pauseAll = true;
  268. }
  269. /// <summary>
  270. /// resumes the sound
  271. /// </summary>
  272. public void ResumeSound()
  273. {
  274. if (soundOn == false || pauseAll == false)
  275. return;
  276. for (int i = 0; i < activeSounds.Count; i++)
  277. ResumeSound(activeSounds[i]);
  278. pauseAll = false;
  279. }
  280. /// <summary>
  281. /// stops the sound
  282. /// </summary>
  283. /// <param name="cue">playing sound</param>
  284. public bool StopSound(Cue cue)
  285. {
  286. if (soundOn == false || cue == null)
  287. return false;
  288. if (cue.IsPaused || cue.IsPlaying)
  289. {
  290. cue.Stop(AudioStopOptions.Immediate);
  291. return true;
  292. }
  293. return false;
  294. }
  295. /// <summary>
  296. /// stop all sounds.
  297. /// </summary>
  298. public void StopSound()
  299. {
  300. if (soundOn == false)
  301. return;
  302. for (int i = 0; i < activeSounds.Count; i++)
  303. StopSound(activeSounds[i]);
  304. }
  305. /// <summary>
  306. /// sets volume.
  307. /// </summary>
  308. /// <param name="categoryName"></param>
  309. /// <param name="volume"></param>
  310. public void SetVolume(string categoryName, float volume)
  311. {
  312. if (soundOn == false)
  313. return;
  314. if (!audioCategories.ContainsKey(categoryName))
  315. {
  316. throw new InvalidOperationException("Cannot find the category : "
  317. + categoryName);
  318. }
  319. audioCategories[categoryName].SetVolume(
  320. MathHelper.Clamp(volume, 0.0f, 1.0f));
  321. }
  322. /// <summary>
  323. /// determines whether a sound is being played.
  324. /// </summary>
  325. /// <param name="cue">playing sound cue</param>
  326. /// <returns>true or false</returns>
  327. public bool IsPlaying(Cue cue)
  328. {
  329. if (soundOn == false)
  330. return false;
  331. if (cue != null)
  332. return cue.IsPlaying;
  333. return false;
  334. }
  335. /// <summary>
  336. /// determines whether a sound is being played.
  337. /// </summary>
  338. /// <param name="cue">playing sound cue</param>
  339. /// <returns>true or false</returns>
  340. public bool IsPause(Cue cue)
  341. {
  342. if (soundOn == false)
  343. return false;
  344. if (cue != null)
  345. return cue.IsPaused;
  346. return false;
  347. }
  348. /// <summary>
  349. /// applies 3D position to sound element.
  350. /// </summary>
  351. /// <param name="element"></param>
  352. private void Apply3D(SoundElement element)
  353. {
  354. emitter.Position = element.emitter.Position;
  355. emitter.Forward = element.emitter.Forward;
  356. emitter.Up = element.emitter.Up;
  357. emitter.Velocity = element.emitter.Velocity;
  358. element.cue.Apply3D(listener, emitter);
  359. }
  360. /// <summary>
  361. /// pauses the sound.
  362. /// </summary>
  363. /// <param name="sound">playing sound element</param>
  364. private bool PauseSound(SoundElement sound)
  365. {
  366. if (soundOn == false || sound == null || sound.cue == null)
  367. return false;
  368. if (sound.cue.IsPlaying)
  369. {
  370. sound.cue.Pause();
  371. return true;
  372. }
  373. return false;
  374. }
  375. /// <summary>
  376. /// resumes the sound.
  377. /// </summary>
  378. /// <param name="sound">paused sound element</param>
  379. private bool ResumeSound(SoundElement sound)
  380. {
  381. if (soundOn == false || sound == null || sound.cue == null)
  382. return false;
  383. if (sound.cue.IsPaused)
  384. {
  385. sound.cue.Resume();
  386. return true;
  387. }
  388. return false;
  389. }
  390. /// <summary>
  391. /// stops the sound.
  392. /// </summary>
  393. /// <param name="sound">playing sound element</param>
  394. private bool StopSound(SoundElement sound)
  395. {
  396. if (soundOn == false || sound == null || sound.cue == null)
  397. return false;
  398. if (sound.cue.IsPaused || sound.cue.IsPlaying)
  399. {
  400. sound.cue.Stop(AudioStopOptions.Immediate);
  401. return true;
  402. }
  403. return false;
  404. }
  405. }
  406. }