AudioSystem.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. #include "AudioSystem.h"
  2. #include "MiniaudioBackend.h"
  3. #include "MusicPlayer.h"
  4. #include "Sound.h"
  5. #include <QDebug>
  6. #include <algorithm>
  7. #include <chrono>
  8. #include <cmath>
  9. #include <cstddef>
  10. #include <memory>
  11. #include <mutex>
  12. #include <qglobal.h>
  13. #include <string>
  14. #include <utility>
  15. #include <vector>
  16. AudioSystem::AudioSystem()
  17. : isRunning(false), masterVolume(AudioConstants::DEFAULT_VOLUME),
  18. soundVolume(AudioConstants::DEFAULT_VOLUME),
  19. musicVolume(AudioConstants::DEFAULT_VOLUME),
  20. voiceVolume(AudioConstants::DEFAULT_VOLUME) {}
  21. AudioSystem::~AudioSystem() { shutdown(); }
  22. auto AudioSystem::getInstance() -> AudioSystem & {
  23. static AudioSystem instance;
  24. return instance;
  25. }
  26. auto AudioSystem::initialize() -> bool {
  27. if (isRunning) {
  28. return true;
  29. }
  30. m_musicPlayer = &Game::Audio::MusicPlayer::getInstance();
  31. if (!m_musicPlayer->initialize()) {
  32. qWarning() << "Failed to initialize MusicPlayer";
  33. return false;
  34. }
  35. isRunning = true;
  36. audioThread = std::thread(&AudioSystem::audioThreadFunc, this);
  37. return true;
  38. }
  39. void AudioSystem::shutdown() {
  40. if (!isRunning) {
  41. return;
  42. }
  43. {
  44. std::lock_guard<std::mutex> const lock(queueMutex);
  45. eventQueue.emplace(AudioEventType::SHUTDOWN);
  46. }
  47. queueCondition.notify_one();
  48. if (audioThread.joinable()) {
  49. audioThread.join();
  50. }
  51. if (m_musicPlayer != nullptr) {
  52. m_musicPlayer->shutdown();
  53. m_musicPlayer = nullptr;
  54. }
  55. sounds.clear();
  56. soundCategories.clear();
  57. activeResources.clear();
  58. {
  59. std::lock_guard<std::mutex> const lock(activeSoundsMutex);
  60. activeSounds.clear();
  61. }
  62. }
  63. void AudioSystem::playSound(const std::string &soundId, float volume, bool loop,
  64. int priority, AudioCategory category) {
  65. std::lock_guard<std::mutex> const lock(queueMutex);
  66. eventQueue.emplace(AudioEventType::PLAY_SOUND, soundId, volume, loop,
  67. priority, category);
  68. queueCondition.notify_one();
  69. }
  70. void AudioSystem::playMusic(const std::string &musicId, float volume, bool) {
  71. std::lock_guard<std::mutex> const lock(queueMutex);
  72. eventQueue.emplace(AudioEventType::PLAY_MUSIC, musicId, volume);
  73. queueCondition.notify_one();
  74. }
  75. void AudioSystem::stopSound(const std::string &soundId) {
  76. std::lock_guard<std::mutex> const lock(queueMutex);
  77. eventQueue.emplace(AudioEventType::STOP_SOUND, soundId);
  78. queueCondition.notify_one();
  79. }
  80. void AudioSystem::stopMusic() {
  81. std::lock_guard<std::mutex> const lock(queueMutex);
  82. eventQueue.emplace(AudioEventType::STOP_MUSIC);
  83. queueCondition.notify_one();
  84. }
  85. void AudioSystem::setMasterVolume(float volume) {
  86. masterVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
  87. AudioConstants::MAX_VOLUME);
  88. std::lock_guard<std::mutex> const lock(resourceMutex);
  89. for (auto &sound : sounds) {
  90. auto it = soundCategories.find(sound.first);
  91. AudioCategory const category =
  92. (it != soundCategories.end()) ? it->second : AudioCategory::SFX;
  93. sound.second->set_volume(
  94. getEffectiveVolume(category, AudioConstants::DEFAULT_VOLUME));
  95. }
  96. if (m_musicPlayer != nullptr) {
  97. m_musicPlayer->setVolume(masterVolume * musicVolume);
  98. }
  99. }
  100. void AudioSystem::setSoundVolume(float volume) {
  101. soundVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
  102. AudioConstants::MAX_VOLUME);
  103. std::lock_guard<std::mutex> const lock(resourceMutex);
  104. for (auto &sound : sounds) {
  105. auto it = soundCategories.find(sound.first);
  106. if (it != soundCategories.end() && it->second == AudioCategory::SFX) {
  107. sound.second->set_volume(getEffectiveVolume(
  108. AudioCategory::SFX, AudioConstants::DEFAULT_VOLUME));
  109. }
  110. }
  111. }
  112. void AudioSystem::setMusicVolume(float volume) {
  113. musicVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
  114. AudioConstants::MAX_VOLUME);
  115. std::lock_guard<std::mutex> const lock(resourceMutex);
  116. if (m_musicPlayer != nullptr) {
  117. m_musicPlayer->setVolume(masterVolume * musicVolume);
  118. }
  119. }
  120. void AudioSystem::setVoiceVolume(float volume) {
  121. voiceVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
  122. AudioConstants::MAX_VOLUME);
  123. std::lock_guard<std::mutex> const lock(resourceMutex);
  124. for (auto &sound : sounds) {
  125. auto it = soundCategories.find(sound.first);
  126. if (it != soundCategories.end() && it->second == AudioCategory::VOICE) {
  127. sound.second->set_volume(getEffectiveVolume(
  128. AudioCategory::VOICE, AudioConstants::DEFAULT_VOLUME));
  129. }
  130. }
  131. }
  132. void AudioSystem::pauseAll() {
  133. std::lock_guard<std::mutex> const lock(queueMutex);
  134. eventQueue.emplace(AudioEventType::PAUSE);
  135. queueCondition.notify_one();
  136. }
  137. void AudioSystem::resumeAll() {
  138. std::lock_guard<std::mutex> const lock(queueMutex);
  139. eventQueue.emplace(AudioEventType::RESUME);
  140. queueCondition.notify_one();
  141. }
  142. auto AudioSystem::loadSound(const std::string &soundId,
  143. const std::string &filePath,
  144. AudioCategory category) -> bool {
  145. std::lock_guard<std::mutex> const lock(resourceMutex);
  146. if (sounds.find(soundId) != sounds.end()) {
  147. return true;
  148. }
  149. MiniaudioBackend *backend =
  150. (m_musicPlayer != nullptr) ? m_musicPlayer->getBackend() : nullptr;
  151. auto sound = std::make_unique<Sound>(filePath, backend);
  152. if (!sound || !sound->is_loaded()) {
  153. return false;
  154. }
  155. sounds[soundId] = std::move(sound);
  156. soundCategories[soundId] = category;
  157. activeResources.insert(soundId);
  158. return true;
  159. }
  160. auto AudioSystem::loadMusic(const std::string &musicId,
  161. const std::string &filePath) -> bool {
  162. std::lock_guard<std::mutex> const lock(resourceMutex);
  163. if (m_musicPlayer == nullptr) {
  164. qWarning() << "MusicPlayer not initialized";
  165. return false;
  166. }
  167. m_musicPlayer->registerTrack(musicId, filePath);
  168. activeResources.insert(musicId);
  169. return true;
  170. }
  171. void AudioSystem::unloadSound(const std::string &soundId) {
  172. std::lock_guard<std::mutex> const lock(queueMutex);
  173. eventQueue.emplace(AudioEventType::UNLOAD_RESOURCE, soundId);
  174. queueCondition.notify_one();
  175. }
  176. void AudioSystem::unloadMusic(const std::string &musicId) {
  177. std::lock_guard<std::mutex> const lock(queueMutex);
  178. eventQueue.emplace(AudioEventType::UNLOAD_RESOURCE, musicId);
  179. queueCondition.notify_one();
  180. }
  181. void AudioSystem::unloadAllSounds() {
  182. std::lock_guard<std::mutex> const lock(queueMutex);
  183. for (const auto &sound : sounds) {
  184. eventQueue.emplace(AudioEventType::UNLOAD_RESOURCE, sound.first);
  185. }
  186. queueCondition.notify_one();
  187. }
  188. void AudioSystem::unloadAllMusic() {
  189. std::lock_guard<std::mutex> const lock(resourceMutex);
  190. if (m_musicPlayer != nullptr) {
  191. m_musicPlayer->stop();
  192. }
  193. std::vector<std::string> music_resources;
  194. for (const auto &res : activeResources) {
  195. if (sounds.find(res) == sounds.end()) {
  196. music_resources.push_back(res);
  197. }
  198. }
  199. for (const auto &res : music_resources) {
  200. activeResources.erase(res);
  201. }
  202. }
  203. void AudioSystem::setMaxChannels(size_t channels) {
  204. maxChannels = std::max(AudioConstants::MIN_CHANNELS, channels);
  205. }
  206. auto AudioSystem::getActiveChannelCount() const -> size_t {
  207. std::lock_guard<std::mutex> const lock(activeSoundsMutex);
  208. return activeSounds.size();
  209. }
  210. void AudioSystem::audioThreadFunc() {
  211. while (isRunning) {
  212. std::unique_lock<std::mutex> lock(queueMutex);
  213. queueCondition.wait(lock, [this] { return !eventQueue.empty(); });
  214. while (!eventQueue.empty()) {
  215. AudioEvent const event = eventQueue.front();
  216. eventQueue.pop();
  217. lock.unlock();
  218. processEvent(event);
  219. if (event.type == AudioEventType::SHUTDOWN) {
  220. isRunning = false;
  221. return;
  222. }
  223. lock.lock();
  224. }
  225. }
  226. }
  227. void AudioSystem::processEvent(const AudioEvent &event) {
  228. switch (event.type) {
  229. case AudioEventType::PLAY_SOUND: {
  230. std::lock_guard<std::mutex> const lock(resourceMutex);
  231. auto it = sounds.find(event.resourceId);
  232. if (it != sounds.end()) {
  233. if (!canPlaySound(event.priority)) {
  234. evictLowestPrioritySoundLocked();
  235. }
  236. float const effective_vol =
  237. getEffectiveVolume(event.category, event.volume);
  238. it->second->play(effective_vol, event.loop);
  239. {
  240. std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
  241. activeSounds.push_back({event.resourceId, event.priority, event.loop,
  242. event.category,
  243. std::chrono::steady_clock::now()});
  244. }
  245. }
  246. break;
  247. }
  248. case AudioEventType::PLAY_MUSIC: {
  249. std::lock_guard<std::mutex> const lock(resourceMutex);
  250. if (m_musicPlayer != nullptr) {
  251. float const effective_volume = masterVolume * musicVolume * event.volume;
  252. m_musicPlayer->play(event.resourceId, effective_volume, event.loop);
  253. }
  254. break;
  255. }
  256. case AudioEventType::STOP_SOUND: {
  257. std::lock_guard<std::mutex> const lock(resourceMutex);
  258. auto it = sounds.find(event.resourceId);
  259. if (it != sounds.end()) {
  260. it->second->stop();
  261. std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
  262. activeSounds.erase(std::remove_if(activeSounds.begin(),
  263. activeSounds.end(),
  264. [&](const ActiveSound &as) {
  265. return as.id == event.resourceId;
  266. }),
  267. activeSounds.end());
  268. }
  269. break;
  270. }
  271. case AudioEventType::STOP_MUSIC: {
  272. std::lock_guard<std::mutex> const lock(resourceMutex);
  273. if (m_musicPlayer != nullptr) {
  274. m_musicPlayer->stop();
  275. }
  276. break;
  277. }
  278. case AudioEventType::PAUSE: {
  279. std::lock_guard<std::mutex> const lock(resourceMutex);
  280. if (m_musicPlayer != nullptr) {
  281. m_musicPlayer->pause();
  282. }
  283. break;
  284. }
  285. case AudioEventType::RESUME: {
  286. std::lock_guard<std::mutex> const lock(resourceMutex);
  287. if (m_musicPlayer != nullptr) {
  288. m_musicPlayer->resume();
  289. }
  290. break;
  291. }
  292. case AudioEventType::UNLOAD_RESOURCE: {
  293. std::lock_guard<std::mutex> const lock(resourceMutex);
  294. auto sound_it = sounds.find(event.resourceId);
  295. if (sound_it != sounds.end()) {
  296. sound_it->second->stop();
  297. std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
  298. activeSounds.erase(std::remove_if(activeSounds.begin(),
  299. activeSounds.end(),
  300. [&](const ActiveSound &as) {
  301. return as.id == event.resourceId;
  302. }),
  303. activeSounds.end());
  304. sounds.erase(sound_it);
  305. soundCategories.erase(event.resourceId);
  306. activeResources.erase(event.resourceId);
  307. }
  308. activeResources.erase(event.resourceId);
  309. break;
  310. }
  311. case AudioEventType::CLEANUP_INACTIVE: {
  312. cleanupInactiveSounds();
  313. break;
  314. }
  315. case AudioEventType::SET_VOLUME:
  316. case AudioEventType::SHUTDOWN:
  317. break;
  318. }
  319. }
  320. auto AudioSystem::canPlaySound(int) -> bool {
  321. std::lock_guard<std::mutex> const lock(activeSoundsMutex);
  322. return activeSounds.size() < maxChannels;
  323. }
  324. void AudioSystem::evictLowestPrioritySound() {
  325. std::string sound_id_to_stop;
  326. {
  327. std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
  328. if (activeSounds.empty()) {
  329. return;
  330. }
  331. auto lowest_it =
  332. std::min_element(activeSounds.begin(), activeSounds.end(),
  333. [](const ActiveSound &a, const ActiveSound &b) {
  334. if (a.priority != b.priority) {
  335. return a.priority < b.priority;
  336. }
  337. return a.startTime < b.startTime;
  338. });
  339. if (lowest_it != activeSounds.end()) {
  340. sound_id_to_stop = lowest_it->id;
  341. activeSounds.erase(lowest_it);
  342. }
  343. }
  344. if (!sound_id_to_stop.empty()) {
  345. std::lock_guard<std::mutex> const resource_lock(resourceMutex);
  346. auto it = sounds.find(sound_id_to_stop);
  347. if (it != sounds.end()) {
  348. it->second->stop();
  349. }
  350. }
  351. }
  352. void AudioSystem::evictLowestPrioritySoundLocked() {
  353. std::string sound_id_to_stop;
  354. {
  355. std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
  356. if (activeSounds.empty()) {
  357. return;
  358. }
  359. auto lowest_it =
  360. std::min_element(activeSounds.begin(), activeSounds.end(),
  361. [](const ActiveSound &a, const ActiveSound &b) {
  362. if (a.priority != b.priority) {
  363. return a.priority < b.priority;
  364. }
  365. return a.startTime < b.startTime;
  366. });
  367. if (lowest_it != activeSounds.end()) {
  368. sound_id_to_stop = lowest_it->id;
  369. activeSounds.erase(lowest_it);
  370. }
  371. }
  372. if (!sound_id_to_stop.empty()) {
  373. auto it = sounds.find(sound_id_to_stop);
  374. if (it != sounds.end()) {
  375. it->second->stop();
  376. }
  377. }
  378. }
  379. void AudioSystem::cleanupInactiveSounds() {
  380. std::lock_guard<std::mutex> const resource_lock(resourceMutex);
  381. std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
  382. activeSounds.erase(std::remove_if(activeSounds.begin(), activeSounds.end(),
  383. [this](const ActiveSound &as) {
  384. if (as.loop) {
  385. return false;
  386. }
  387. auto it = sounds.find(as.id);
  388. return it == sounds.end();
  389. }),
  390. activeSounds.end());
  391. }
  392. auto AudioSystem::getEffectiveVolume(AudioCategory category,
  393. float eventVolume) const -> float {
  394. float category_volume = NAN;
  395. switch (category) {
  396. case AudioCategory::SFX:
  397. category_volume = soundVolume;
  398. break;
  399. case AudioCategory::VOICE:
  400. category_volume = voiceVolume;
  401. break;
  402. case AudioCategory::MUSIC:
  403. category_volume = musicVolume;
  404. break;
  405. default:
  406. category_volume = soundVolume;
  407. break;
  408. }
  409. return masterVolume * category_volume * eventVolume;
  410. }