2
0

AudioSystem.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. // ----------------------------------------------------------------
  2. // From Game Programming in C++ by Sanjay Madhav
  3. // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
  4. //
  5. // Released under the BSD License
  6. // See LICENSE in root directory for full details.
  7. // ----------------------------------------------------------------
  8. #include "AudioSystem.h"
  9. #include <SDL/SDL_log.h>
  10. #include <fmod_studio.hpp>
  11. #include <fmod_errors.h>
  12. #include <vector>
  13. unsigned int AudioSystem::sNextID = 0;
  14. AudioSystem::AudioSystem(Game* game)
  15. :mGame(game)
  16. ,mSystem(nullptr)
  17. ,mLowLevelSystem(nullptr)
  18. {
  19. }
  20. AudioSystem::~AudioSystem()
  21. {
  22. }
  23. bool AudioSystem::Initialize()
  24. {
  25. // Initialize debug logging
  26. FMOD::Debug_Initialize(
  27. FMOD_DEBUG_LEVEL_ERROR, // Log only errors
  28. FMOD_DEBUG_MODE_TTY // Output to stdout
  29. );
  30. // Create FMOD studio system object
  31. FMOD_RESULT result;
  32. result = FMOD::Studio::System::create(&mSystem);
  33. if (result != FMOD_OK)
  34. {
  35. SDL_Log("Failed to create FMOD system: %s", FMOD_ErrorString(result));
  36. return false;
  37. }
  38. // Initialize FMOD studio system
  39. result = mSystem->initialize(
  40. 512, // Max number of concurrent sounds
  41. FMOD_STUDIO_INIT_NORMAL, // Use default settings
  42. FMOD_INIT_NORMAL, // Use default settings
  43. nullptr // Usually null
  44. );
  45. if (result != FMOD_OK)
  46. {
  47. SDL_Log("Failed to initialize FMOD system: %s", FMOD_ErrorString(result));
  48. return false;
  49. }
  50. // Save the low-level system pointer
  51. mSystem->getLowLevelSystem(&mLowLevelSystem);
  52. // Load the master banks (strings first)
  53. LoadBank("Assets/Master Bank.strings.bank");
  54. LoadBank("Assets/Master Bank.bank");
  55. return true;
  56. }
  57. void AudioSystem::Shutdown()
  58. {
  59. // Unload all banks
  60. UnloadAllBanks();
  61. // Shutdown FMOD system
  62. if (mSystem)
  63. {
  64. mSystem->release();
  65. }
  66. }
  67. void AudioSystem::LoadBank(const std::string& name)
  68. {
  69. // Prevent double-loading
  70. if (mBanks.find(name) != mBanks.end())
  71. {
  72. return;
  73. }
  74. // Try to load bank
  75. FMOD::Studio::Bank* bank = nullptr;
  76. FMOD_RESULT result = mSystem->loadBankFile(
  77. name.c_str(), // File name of bank
  78. FMOD_STUDIO_LOAD_BANK_NORMAL, // Normal loading
  79. &bank // Save pointer to bank
  80. );
  81. const int maxPathLength = 512;
  82. if (result == FMOD_OK)
  83. {
  84. // Add bank to map
  85. mBanks.emplace(name, bank);
  86. // Load all non-streaming sample data
  87. bank->loadSampleData();
  88. // Get the number of events in this bank
  89. int numEvents = 0;
  90. bank->getEventCount(&numEvents);
  91. if (numEvents > 0)
  92. {
  93. // Get list of event descriptions in this bank
  94. std::vector<FMOD::Studio::EventDescription*> events(numEvents);
  95. bank->getEventList(events.data(), numEvents, &numEvents);
  96. char eventName[maxPathLength];
  97. for (int i = 0; i < numEvents; i++)
  98. {
  99. FMOD::Studio::EventDescription* e = events[i];
  100. // Get the path of this event (like event:/Explosion2D)
  101. e->getPath(eventName, maxPathLength, nullptr);
  102. // Add to event map
  103. mEvents.emplace(eventName, e);
  104. }
  105. }
  106. // Get the number of buses in this bank
  107. int numBuses = 0;
  108. bank->getBusCount(&numBuses);
  109. if (numBuses > 0)
  110. {
  111. // Get list of buses in this bank
  112. std::vector<FMOD::Studio::Bus*> buses(numBuses);
  113. bank->getBusList(buses.data(), numBuses, &numBuses);
  114. char busName[512];
  115. for (int i = 0; i < numBuses; i++)
  116. {
  117. FMOD::Studio::Bus* bus = buses[i];
  118. // Get the path of this bus (like bus:/SFX)
  119. bus->getPath(busName, 512, nullptr);
  120. // Add to buses map
  121. mBuses.emplace(busName, bus);
  122. }
  123. }
  124. }
  125. }
  126. void AudioSystem::UnloadBank(const std::string& name)
  127. {
  128. // Ignore if not loaded
  129. auto iter = mBanks.find(name);
  130. if (iter == mBanks.end())
  131. {
  132. return;
  133. }
  134. // First we need to remove all events from this bank
  135. FMOD::Studio::Bank* bank = iter->second;
  136. int numEvents = 0;
  137. bank->getEventCount(&numEvents);
  138. if (numEvents > 0)
  139. {
  140. // Get event descriptions for this bank
  141. std::vector<FMOD::Studio::EventDescription*> events(numEvents);
  142. // Get list of events
  143. bank->getEventList(events.data(), numEvents, &numEvents);
  144. char eventName[512];
  145. for (int i = 0; i < numEvents; i++)
  146. {
  147. FMOD::Studio::EventDescription* e = events[i];
  148. // Get the path of this event
  149. e->getPath(eventName, 512, nullptr);
  150. // Remove this event
  151. auto eventi = mEvents.find(eventName);
  152. if (eventi != mEvents.end())
  153. {
  154. mEvents.erase(eventi);
  155. }
  156. }
  157. }
  158. // Get the number of buses in this bank
  159. int numBuses = 0;
  160. bank->getBusCount(&numBuses);
  161. if (numBuses > 0)
  162. {
  163. // Get list of buses in this bank
  164. std::vector<FMOD::Studio::Bus*> buses(numBuses);
  165. bank->getBusList(buses.data(), numBuses, &numBuses);
  166. char busName[512];
  167. for (int i = 0; i < numBuses; i++)
  168. {
  169. FMOD::Studio::Bus* bus = buses[i];
  170. // Get the path of this bus (like bus:/SFX)
  171. bus->getPath(busName, 512, nullptr);
  172. // Remove this bus
  173. auto busi = mBuses.find(busName);
  174. if (busi != mBuses.end())
  175. {
  176. mBuses.erase(busi);
  177. }
  178. }
  179. }
  180. // Unload sample data and bank
  181. bank->unloadSampleData();
  182. bank->unload();
  183. // Remove from banks map
  184. mBanks.erase(iter);
  185. }
  186. void AudioSystem::UnloadAllBanks()
  187. {
  188. for (auto& iter : mBanks)
  189. {
  190. // Unload the sample data, then the bank itself
  191. iter.second->unloadSampleData();
  192. iter.second->unload();
  193. }
  194. mBanks.clear();
  195. // No banks means no events
  196. mEvents.clear();
  197. }
  198. SoundEvent AudioSystem::PlayEvent(const std::string& name)
  199. {
  200. unsigned int retID = 0;
  201. auto iter = mEvents.find(name);
  202. if (iter != mEvents.end())
  203. {
  204. // Create instance of event
  205. FMOD::Studio::EventInstance* event = nullptr;
  206. iter->second->createInstance(&event);
  207. if (event)
  208. {
  209. // Start the event instance
  210. event->start();
  211. // Get the next id, and add to map
  212. sNextID++;
  213. retID = sNextID;
  214. mEventInstances.emplace(retID, event);
  215. }
  216. }
  217. return SoundEvent(this, retID);
  218. }
  219. void AudioSystem::Update(float deltaTime)
  220. {
  221. // Find any stopped event instances
  222. std::vector<unsigned int> done;
  223. for (auto& iter : mEventInstances)
  224. {
  225. FMOD::Studio::EventInstance* e = iter.second;
  226. // Get the state of this event
  227. FMOD_STUDIO_PLAYBACK_STATE state;
  228. e->getPlaybackState(&state);
  229. if (state == FMOD_STUDIO_PLAYBACK_STOPPED)
  230. {
  231. // Release the event and add id to done
  232. e->release();
  233. done.emplace_back(iter.first);
  234. }
  235. }
  236. // Remove done event instances from map
  237. for (auto id : done)
  238. {
  239. mEventInstances.erase(id);
  240. }
  241. // Update FMOD
  242. mSystem->update();
  243. }
  244. namespace
  245. {
  246. FMOD_VECTOR VecToFMOD(const Vector3& in)
  247. {
  248. // Convert from our coordinates (+x forward, +y right, +z up)
  249. // to FMOD (+z forward, +x right, +y up)
  250. FMOD_VECTOR v;
  251. v.x = in.y;
  252. v.y = in.z;
  253. v.z = in.x;
  254. return v;
  255. }
  256. }
  257. void AudioSystem::SetListener(const Matrix4& viewMatrix)
  258. {
  259. // Invert the view matrix to get the correct vectors
  260. Matrix4 invView = viewMatrix;
  261. invView.Invert();
  262. FMOD_3D_ATTRIBUTES listener;
  263. // Set position, forward, up
  264. listener.position = VecToFMOD(invView.GetTranslation());
  265. // In the inverted view, third row is forward
  266. listener.forward = VecToFMOD(invView.GetZAxis());
  267. // In the inverted view, second row is up
  268. listener.up = VecToFMOD(invView.GetYAxis());
  269. // Set velocity to zero (fix if using Doppler effect)
  270. listener.velocity = {0.0f, 0.0f, 0.0f};
  271. // Send to FMOD
  272. mSystem->setListenerAttributes(0, &listener);
  273. }
  274. float AudioSystem::GetBusVolume(const std::string& name) const
  275. {
  276. float retVal = 0.0f;
  277. const auto iter = mBuses.find(name);
  278. if (iter != mBuses.end())
  279. {
  280. iter->second->getVolume(&retVal);
  281. }
  282. return retVal;
  283. }
  284. bool AudioSystem::GetBusPaused(const std::string & name) const
  285. {
  286. bool retVal = false;
  287. const auto iter = mBuses.find(name);
  288. if (iter != mBuses.end())
  289. {
  290. iter->second->getPaused(&retVal);
  291. }
  292. return retVal;
  293. }
  294. void AudioSystem::SetBusVolume(const std::string& name, float volume)
  295. {
  296. auto iter = mBuses.find(name);
  297. if (iter != mBuses.end())
  298. {
  299. iter->second->setVolume(volume);
  300. }
  301. }
  302. void AudioSystem::SetBusPaused(const std::string & name, bool pause)
  303. {
  304. auto iter = mBuses.find(name);
  305. if (iter != mBuses.end())
  306. {
  307. iter->second->setPaused(pause);
  308. }
  309. }
  310. FMOD::Studio::EventInstance* AudioSystem::GetEventInstance(unsigned int id)
  311. {
  312. FMOD::Studio::EventInstance* event = nullptr;
  313. auto iter = mEventInstances.find(id);
  314. if (iter != mEventInstances.end())
  315. {
  316. event = iter->second;
  317. }
  318. return event;
  319. }