From fb562c8077c9e14bde11e673352e83756e081f69 Mon Sep 17 00:00:00 2001
From: Emmanuel Hansen <emmausssss@gmail.com>
Date: Sat, 29 Jul 2023 12:43:02 +0000
Subject: [PATCH] safely close audio on game exit

---
 .../Audio/Oboe/OboeHardwareDeviceDriver.cs    |   7 +-
 .../Audio/Oboe/OboeHardwareDeviceSession.cs   |  22 ++-
 src/LibRyujinx/Android/JniExportedMethods.cs  |   3 +-
 src/RyujinxAndroid/app/src/main/cpp/oboe.cpp  | 176 +++++++++---------
 4 files changed, 117 insertions(+), 91 deletions(-)

diff --git a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs b/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs
index c56d0fe38..7377b171e 100644
--- a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs
+++ b/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceDriver.cs
@@ -62,11 +62,6 @@ namespace LibRyujinx.Shared.Audio.Oboe
             return session;
         }
 
-        internal bool Unregister(OboeHardwareDeviceSession session)
-        {
-            return _sessions.TryRemove(session, out _);
-        }
-
         public void Dispose()
         {
             Dispose(true);
@@ -82,6 +77,8 @@ namespace LibRyujinx.Shared.Audio.Oboe
                 }
 
                 _pauseEvent.Dispose();
+
+                _sessions.Clear();
             }
         }
 
diff --git a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs b/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs
index b23426d30..76b41d802 100644
--- a/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs
+++ b/src/LibRyujinx/Android/Audio/Oboe/OboeHardwareDeviceSession.cs
@@ -10,6 +10,7 @@ namespace LibRyujinx.Shared.Audio.Oboe
     internal class OboeHardwareDeviceSession : HardwareDeviceSessionOutputBase
     {
         private OboeHardwareDeviceDriver _driver;
+        private bool _isClosed;
         private bool _isWorkerActive;
         private Queue<OboeAudioBuffer> _queuedBuffers;
         private bool _isActive;
@@ -59,6 +60,9 @@ namespace LibRyujinx.Shared.Audio.Oboe
                 {
                     StartIfNotPlaying();
 
+                    if (_isClosed)
+                        break;
+
                     fixed(byte* ptr = buffer.Data)
                         OboeInterop.WriteToSession(_session, (ulong)ptr, buffer.SampleCount);
 
@@ -90,18 +94,31 @@ namespace LibRyujinx.Shared.Audio.Oboe
 
         public override void Dispose()
         {
+            if (_session == 0)
+                return;
+
+            PrepareToClose();
+
             OboeInterop.CloseSession(_session);
+
+            _session = 0;
         }
+
         public override void PrepareToClose()
         {
+            _isClosed = true;
             _isWorkerActive = false;
-            _workerThread.Join();
+            _workerThread?.Join();
+            Stop();
         }
 
         private void StartIfNotPlaying()
         {
             lock (_trackLock)
             {
+                if (_isClosed)
+                    return;
+
                 if (OboeInterop.IsPlaying(_session) == 0)
                 {
                     Start();
@@ -145,6 +162,9 @@ namespace LibRyujinx.Shared.Audio.Oboe
 
         public override void Start()
         {
+            if (_isClosed)
+                return;
+
             OboeInterop.StartSession(_session);
         }
 
diff --git a/src/LibRyujinx/Android/JniExportedMethods.cs b/src/LibRyujinx/Android/JniExportedMethods.cs
index f7f04a732..013b9bbec 100644
--- a/src/LibRyujinx/Android/JniExportedMethods.cs
+++ b/src/LibRyujinx/Android/JniExportedMethods.cs
@@ -72,8 +72,6 @@ namespace LibRyujinx
 
             var init = Initialize(path, enableDebugLogs);
 
-            // AudioDriver = new OboeHardwareDeviceDriver();
-
             _surfaceEvent?.Set();
 
             _surfaceEvent = new ManualResetEvent(false);
@@ -104,6 +102,7 @@ namespace LibRyujinx
                                                          JStringLocalRef timeZone,
                                                          JBoolean ignoreMissingServices)
         {
+            AudioDriver = new OboeHardwareDeviceDriver();
             return InitializeDevice(isHostMapped,
                                     useNce,
                                     (SystemLanguage)(int)systemLanguage,
diff --git a/src/RyujinxAndroid/app/src/main/cpp/oboe.cpp b/src/RyujinxAndroid/app/src/main/cpp/oboe.cpp
index 61b605ab5..f643a4fbe 100644
--- a/src/RyujinxAndroid/app/src/main/cpp/oboe.cpp
+++ b/src/RyujinxAndroid/app/src/main/cpp/oboe.cpp
@@ -10,8 +10,11 @@ void AudioSession::initialize() {
 }
 
 void AudioSession::destroy() {
+    if(stream == nullptr)
+        return;
     stream->close();
-    delete stream;
+
+    stream = nullptr;
 }
 
 void AudioSession::start() {
@@ -37,101 +40,108 @@ extern "C"
 JNIEXPORT void JNICALL
 Java_org_ryujinx_android_NativeHelpers_setDeviceId(
         JNIEnv *env,
-jobject instance,
-    jint device_id){
+        jobject instance,
+        jint device_id) {
     s_device_id = device_id;
 }
 
-    AudioSession* create_session(int sample_format,
-                         uint sample_rate,
-                         uint channel_count)
-    {
-        using namespace oboe;
+AudioSession *create_session(int sample_format,
+                             uint sample_rate,
+                             uint channel_count) {
+    using namespace oboe;
 
-        AudioStreamBuilder builder;
+    AudioStreamBuilder builder;
 
-        AudioFormat format;
+    AudioFormat format;
 
-        switch (sample_format) {
-            case 0:
-                format = AudioFormat::Invalid;
-                break;
-            case 1:
-            case 2:
-                format = AudioFormat::I16;
-                break;
-            case 3:
-                format = AudioFormat::I24;
-                break;
-            case 4:
-                format = AudioFormat::I32;
-                break;
-            case 5:
-                format = AudioFormat::Float;
-                break;
-            default:
-                std::ostringstream string;
-                string << "Invalid Format" << sample_format;
+    switch (sample_format) {
+        case 0:
+            format = AudioFormat::Invalid;
+            break;
+        case 1:
+        case 2:
+            format = AudioFormat::I16;
+            break;
+        case 3:
+            format = AudioFormat::I24;
+            break;
+        case 4:
+            format = AudioFormat::I32;
+            break;
+        case 5:
+            format = AudioFormat::Float;
+            break;
+        default:
+            std::ostringstream string;
+            string << "Invalid Format" << sample_format;
 
-                throw std::runtime_error(string.str());
-        }
+            throw std::runtime_error(string.str());
+    }
 
-        auto session = new AudioSession();
-        session->initialize();
+    auto session = new AudioSession();
+    session->initialize();
 
-        session->format = format;
-        session->channelCount = channel_count;
+    session->format = format;
+    session->channelCount = channel_count;
 
-        builder.setDirection(Direction::Output)
+    builder.setDirection(Direction::Output)
             ->setPerformanceMode(PerformanceMode::LowLatency)
-                ->setSharingMode(SharingMode::Shared)
-                ->setFormat(format)
-                ->setChannelCount(channel_count)
-                ->setSampleRate(sample_rate);
-        AudioStream* stream;
-        if(builder.openStream(&stream) != oboe::Result::OK)
-        {
-            delete session;
-            return nullptr;
-        }
-        session->stream = stream;
-
-        return session;
-    }
-
-    void start_session(AudioSession* session)
-    {
-        session->start();
-    }
-
-    void stop_session(AudioSession* session)
-    {
-        session->stop();
-    }
-
-    void set_session_volume(AudioSession* session, float volume)
-    {
-        session->volume = volume;
-    }
-
-    float get_session_volume(AudioSession* session)
-    {
-        return session->volume;
-    }
-
-    void close_session(AudioSession* session)
-    {
-        session->destroy();
-
+            ->setSharingMode(SharingMode::Shared)
+            ->setFormat(format)
+            ->setChannelCount(channel_count)
+            ->setSampleRate(sample_rate);
+    AudioStream *stream;
+    if (builder.openStream(&stream) != oboe::Result::OK) {
         delete session;
+        session = nullptr;
+        return nullptr;
     }
+    session->stream = stream;
 
-    bool is_playing(AudioSession* session) {
-        return  session->isStarted;
-    }
+    return session;
+}
 
-    void write_to_session(AudioSession* session, uint64_t data, uint64_t samples)
-    {
-        session->read(data, samples);
-    }
+void start_session(AudioSession *session) {
+    if (session == nullptr)
+        return;
+    session->start();
+}
+
+void stop_session(AudioSession *session) {
+    if (session == nullptr)
+        return;
+    session->stop();
+}
+
+void set_session_volume(AudioSession *session, float volume) {
+    if (session == nullptr)
+        return;
+    session->volume = volume;
+}
+
+float get_session_volume(AudioSession *session) {
+    if (session == nullptr)
+        return 0;
+    return session->volume;
+}
+
+void close_session(AudioSession *session) {
+    if (session == nullptr)
+        return;
+    session->destroy();
+
+    delete session;
+}
+
+bool is_playing(AudioSession *session) {
+    if (session == nullptr)
+        return false;
+    return session->isStarted;
+}
+
+void write_to_session(AudioSession *session, uint64_t data, uint64_t samples) {
+    if (session == nullptr)
+        return;
+    session->read(data, samples);
+}
 }
\ No newline at end of file