Fixing audio runtimes.

Race condition when completing in the audio completed callback thread!

Want to add a test here but ran out of time!

Diffs=
8ecc99130 Fixing audio runtimes. (#7007)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head
index ed40a47..406e3a5 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-0bc446fad2a568a7088fdaa122af0de2de1dc940
+8ecc99130db737d49ce0889501c421bd53b58f9e
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 67369e6..98a5693 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -51,7 +51,7 @@
 
     template <typename SMType, typename InstType>
     InstType* getNamedInput(const std::string& name) const;
-    void notifyEventListeners(std::vector<EventReport> events, NestedArtboard* source);
+    void notifyEventListeners(const std::vector<EventReport>& events, NestedArtboard* source);
     void sortHitComponents();
 
 public:
@@ -120,6 +120,7 @@
 
     /// Gets a reported event at an index < reportedEventCount().
     const EventReport reportedEventAt(std::size_t index) const;
+    bool playsAudio() override { return true; }
 
 private:
     std::vector<EventReport> m_reportedEvents;
diff --git a/include/rive/audio/audio_engine.hpp b/include/rive/audio/audio_engine.hpp
index e23f539..a49f669 100644
--- a/include/rive/audio/audio_engine.hpp
+++ b/include/rive/audio/audio_engine.hpp
@@ -7,6 +7,7 @@
 #include <vector>
 #include <stdio.h>
 #include <cstdint>
+#include <mutex>
 
 typedef struct ma_engine ma_engine;
 typedef struct ma_sound ma_sound;
@@ -60,6 +61,9 @@
     AudioEngine(ma_engine* engine);
     ma_device* m_device;
     ma_engine* m_engine;
+    std::mutex m_mutex;
+
+    void soundCompleted(rcp<AudioSound> sound);
 
     std::vector<rcp<AudioSound>> m_completedSounds;
     rcp<AudioSound> m_playingSoundsHead;
diff --git a/include/rive/audio_event.hpp b/include/rive/audio_event.hpp
index cd67c6c..2844e9c 100644
--- a/include/rive/audio_event.hpp
+++ b/include/rive/audio_event.hpp
@@ -13,6 +13,7 @@
     void setAsset(FileAsset* asset) override;
     uint32_t assetId() override;
     void trigger(const CallbackData& value) override;
+    void play();
 
 #ifdef TESTING
     AudioAsset* asset() const { return (AudioAsset*)m_fileAsset; }
diff --git a/include/rive/core/field_types/core_callback_type.hpp b/include/rive/core/field_types/core_callback_type.hpp
index 1c8ba62..a6bb93f 100644
--- a/include/rive/core/field_types/core_callback_type.hpp
+++ b/include/rive/core/field_types/core_callback_type.hpp
@@ -9,6 +9,7 @@
 public:
     virtual ~CallbackContext() {}
     virtual void reportEvent(Event* event, float secondsDelay = 0.0f) {}
+    virtual bool playsAudio() { return false; }
 };
 
 class CallbackData
diff --git a/include/rive/relative_local_asset_loader.hpp b/include/rive/relative_local_asset_loader.hpp
index 6056c9c..c4dc317 100644
--- a/include/rive/relative_local_asset_loader.hpp
+++ b/include/rive/relative_local_asset_loader.hpp
@@ -36,6 +36,11 @@
     {
         std::string filename = m_Path + asset.uniqueFilename();
         FILE* fp = fopen(filename.c_str(), "rb");
+        if (fp == nullptr)
+        {
+            fprintf(stderr, "Failed to find file at %s\n", filename.c_str());
+            return false;
+        }
 
         fseek(fp, 0, SEEK_END);
         const size_t length = ftell(fp);
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index 0348c6e..b75642a 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -24,6 +24,7 @@
 #include "rive/nested_artboard.hpp"
 #include "rive/shapes/shape.hpp"
 #include "rive/math/math_types.hpp"
+#include "rive/audio_event.hpp"
 #include <unordered_map>
 
 using namespace rive;
@@ -782,7 +783,7 @@
     return m_reportedEvents[index];
 }
 
-void StateMachineInstance::notifyEventListeners(std::vector<EventReport> events,
+void StateMachineInstance::notifyEventListeners(const std::vector<EventReport>& events,
                                                 NestedArtboard* source)
 {
     if (events.size() > 0)
@@ -826,5 +827,14 @@
         {
             m_parentStateMachineInstance->notifyEventListeners(events, m_parentNestedArtboard);
         }
+
+        for (auto report : events)
+        {
+            auto event = report.event();
+            if (event->is<AudioEvent>())
+            {
+                event->as<AudioEvent>()->play();
+            }
+        }
     }
 }
diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp
index 268e22e..c63bb69 100644
--- a/src/audio/audio_engine.cpp
+++ b/src/audio/audio_engine.cpp
@@ -25,9 +25,17 @@
 void AudioEngine::SoundCompleted(void* pUserData, ma_sound* pSound)
 {
     AudioSound* audioSound = (AudioSound*)pUserData;
+    auto engine = audioSound->m_engine;
+    engine->soundCompleted(ref_rcp(audioSound));
+}
 
-    auto next = audioSound->m_nextPlaying;
-    auto prev = audioSound->m_prevPlaying;
+void AudioEngine::soundCompleted(rcp<AudioSound> sound)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+    m_completedSounds.push_back(sound);
+
+    auto next = sound->m_nextPlaying;
+    auto prev = sound->m_prevPlaying;
     if (next != nullptr)
     {
         next->m_prevPlaying = prev;
@@ -37,16 +45,13 @@
         prev->m_nextPlaying = next;
     }
 
-    auto engine = audioSound->m_engine;
-    if (engine->m_playingSoundsHead.get() == audioSound)
+    if (m_playingSoundsHead == sound)
     {
-        engine->m_playingSoundsHead = next;
+        m_playingSoundsHead = next;
     }
 
-    // Unlink audio sound.
-    engine->m_completedSounds.push_back(ref_rcp(audioSound));
-    audioSound->m_nextPlaying = nullptr;
-    audioSound->m_prevPlaying = nullptr;
+    sound->m_nextPlaying = nullptr;
+    sound->m_prevPlaying = nullptr;
 }
 
 #ifdef WITH_RIVE_AUDIO_TOOLS
@@ -182,6 +187,7 @@
                                   uint64_t endTime,
                                   uint64_t soundStartTime)
 {
+    std::unique_lock<std::mutex> lock(m_mutex);
     // We have to dispose completed sounds out of the completed callback. So we
     // do it on next play or at destruct.
     for (auto sound : m_completedSounds)
diff --git a/src/audio_event.cpp b/src/audio_event.cpp
index 1ba130b..d0d8098 100644
--- a/src/audio_event.cpp
+++ b/src/audio_event.cpp
@@ -6,10 +6,8 @@
 
 using namespace rive;
 
-void AudioEvent::trigger(const CallbackData& value)
+void AudioEvent::play()
 {
-    Super::trigger(value);
-
 #ifdef WITH_RIVE_AUDIO
     auto audioAsset = (AudioAsset*)m_fileAsset;
     if (audioAsset == nullptr)
@@ -36,6 +34,16 @@
 #endif
 }
 
+void AudioEvent::trigger(const CallbackData& value)
+{
+    Super::trigger(value);
+    if (!value.context()->playsAudio())
+    {
+        // Context won't play audio, we'll do it ourselves.
+        play();
+    }
+}
+
 StatusCode AudioEvent::import(ImportStack& importStack)
 {
     auto result = registerReferencer(importStack);