Browse Source

harden audio system

Adam Djellouli 2 months ago
parent
commit
6aaa2103a6
4 changed files with 41 additions and 6 deletions
  1. 13 1
      game/audio/AudioEventHandler.cpp
  2. 6 0
      game/audio/AudioEventHandler.h
  3. 19 5
      game/audio/Music.cpp
  4. 3 0
      game/audio/Music.h

+ 13 - 1
game/audio/AudioEventHandler.cpp

@@ -89,7 +89,19 @@ void AudioEventHandler::onUnitSelected(
 
   auto it = m_unitVoiceMap.find(unitComponent->unitType);
   if (it != m_unitVoiceMap.end()) {
-    AudioSystem::getInstance().playSound(it->second);
+    // Throttle: only play sound if enough time has passed OR unit type changed
+    auto now = std::chrono::steady_clock::now();
+    auto timeSinceLastSound = std::chrono::duration_cast<std::chrono::milliseconds>(
+        now - m_lastSelectionSoundTime).count();
+    
+    bool shouldPlay = (timeSinceLastSound >= SELECTION_SOUND_COOLDOWN_MS) ||
+                      (unitComponent->unitType != m_lastSelectionUnitType);
+    
+    if (shouldPlay) {
+      AudioSystem::getInstance().playSound(it->second);
+      m_lastSelectionSoundTime = now;
+      m_lastSelectionUnitType = unitComponent->unitType;
+    }
   }
 }
 

+ 6 - 0
game/audio/AudioEventHandler.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "../core/event_manager.h"
+#include <chrono>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -38,6 +39,11 @@ private:
   std::unordered_map<std::string, std::string> m_unitVoiceMap;
   std::unordered_map<Engine::Core::AmbientState, std::string> m_ambientMusicMap;
 
+  // Throttling for unit selection sounds
+  std::chrono::steady_clock::time_point m_lastSelectionSoundTime;
+  std::string m_lastSelectionUnitType;
+  static constexpr int SELECTION_SOUND_COOLDOWN_MS = 300; // 300ms cooldown
+
   Engine::Core::ScopedEventSubscription<Engine::Core::UnitSelectedEvent>
       m_unitSelectedSub;
   Engine::Core::ScopedEventSubscription<Engine::Core::AmbientStateChangedEvent>

+ 19 - 5
game/audio/Music.cpp

@@ -7,7 +7,8 @@
 #include <QUrl>
 
 Music::Music(const std::string &filePath)
-    : filepath(filePath), loaded(false), mainThread(nullptr) {
+    : filepath(filePath), loaded(false), audioOutput(nullptr),
+      mainThread(nullptr), playing(false) {
 
   if (QCoreApplication::instance()) {
     mainThread = QCoreApplication::instance()->thread();
@@ -20,6 +21,10 @@ Music::Music(const std::string &filePath)
   }
 
 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+  // Create audio output once and set it as a child of player
+  audioOutput = new QAudioOutput(player.get());
+  player->setAudioOutput(audioOutput);
+  
   player->setSource(QUrl::fromLocalFile(QString::fromStdString(filePath)));
   loaded = (player->error() == QMediaPlayer::NoError);
 #else
@@ -61,15 +66,23 @@ void Music::play(float volume, bool loop) {
     return;
   }
 
+  // Already playing, just update volume
+  if (playing) {
+    setVolume(volume);
+    return;
+  }
+
+  playing = true;
   QMediaPlayer *p = player.get();
 
   QMetaObject::invokeMethod(
       p,
-      [p, volume, loop]() {
+      [p, volume, loop, this]() {
 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
-        auto audioOutput = new QAudioOutput(p);
-        audioOutput->setVolume(volume);
-        p->setAudioOutput(audioOutput);
+        // Audio output already set in constructor, just update volume and play
+        if (audioOutput) {
+          audioOutput->setVolume(volume);
+        }
         p->setLoops(loop ? QMediaPlayer::Infinite : 1);
 #else
         p->setVolume(static_cast<int>(volume * 100));
@@ -92,6 +105,7 @@ void Music::stop() {
     return;
   }
 
+  playing = false;
   QMediaPlayer *p = player.get();
   QMetaObject::invokeMethod(p, [p]() { p->stop(); }, Qt::QueuedConnection);
 }

+ 3 - 0
game/audio/Music.h

@@ -4,6 +4,7 @@
 #include <string>
 
 class QMediaPlayer;
+class QAudioOutput;
 class QThread;
 
 class Music {
@@ -22,7 +23,9 @@ private:
   void cleanupPlayer();
 
   std::unique_ptr<QMediaPlayer> player;
+  QAudioOutput *audioOutput; // Owned by player as child QObject
   QThread *mainThread;
   std::string filepath;
   bool loaded;
+  bool playing;
 };