From 957ddab6796cb6f644c60993c3035d8bd9c0a398 Mon Sep 17 00:00:00 2001
From: MerryMage <MerryMage@users.noreply.github.com>
Date: Wed, 12 Sep 2018 18:07:16 +0100
Subject: [PATCH] audio_core: Flush stream when not playing anything

---
 src/audio_core/cubeb_sink.cpp   | 11 +++++++++++
 src/audio_core/null_sink.h      |  2 ++
 src/audio_core/sink_stream.h    |  2 ++
 src/audio_core/stream.cpp       |  2 ++
 src/audio_core/time_stretch.cpp |  4 ++++
 src/audio_core/time_stretch.h   |  2 ++
 6 files changed, 23 insertions(+)

diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 067dc98d2..79155a7a0 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <algorithm>
+#include <atomic>
 #include <cstring>
 #include "audio_core/cubeb_sink.h"
 #include "audio_core/stream.h"
@@ -81,6 +82,10 @@ public:
         return queue.Size() / num_channels;
     }
 
+    void Flush() override {
+        should_flush = true;
+    }
+
     u32 GetNumChannels() const {
         return num_channels;
     }
@@ -94,6 +99,7 @@ private:
 
     Common::RingBuffer<s16, 0x10000> queue;
     std::array<s16, 2> last_frame;
+    std::atomic<bool> should_flush{};
     TimeStretcher time_stretch;
 
     static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
@@ -163,6 +169,11 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
         s16* const out{reinterpret_cast<s16*>(buffer)};
         const size_t out_frames = impl->time_stretch.Process(in.data(), num_in, out, num_frames);
         samples_written = out_frames * num_channels;
+
+        if (impl->should_flush) {
+            impl->time_stretch.Flush();
+            impl->should_flush = false;
+        }
     } else {
         samples_written = impl->queue.Pop(buffer, samples_to_write);
     }
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index fbb1bc225..2ed0c83b6 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -25,6 +25,8 @@ private:
         size_t SamplesInQueue(u32 /*num_channels*/) const override {
             return 0;
         }
+
+        void Flush() override {}
     } null_sink_stream;
 };
 
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
index 743a743a3..4309ad094 100644
--- a/src/audio_core/sink_stream.h
+++ b/src/audio_core/sink_stream.h
@@ -27,6 +27,8 @@ public:
     virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
 
     virtual std::size_t SamplesInQueue(u32 num_channels) const = 0;
+
+    virtual void Flush() = 0;
 };
 
 using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 49c6efc85..84dcdd98d 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -73,6 +73,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
 void Stream::PlayNextBuffer() {
     if (!IsPlaying()) {
         // Ensure we are in playing state before playing the next buffer
+        sink_stream.Flush();
         return;
     }
 
@@ -83,6 +84,7 @@ void Stream::PlayNextBuffer() {
 
     if (queued_buffers.empty()) {
         // No queued buffers - we are effectively paused
+        sink_stream.Flush();
         return;
     }
 
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index d2e3391c1..da094c46b 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -22,6 +22,10 @@ void TimeStretcher::Clear() {
     m_sound_touch.clear();
 }
 
+void TimeStretcher::Flush() {
+    m_sound_touch.flush();
+}
+
 size_t TimeStretcher::Process(const s16* in, size_t num_in, s16* out, size_t num_out) {
     const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
 
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
index 0322b8b78..7e39e695e 100644
--- a/src/audio_core/time_stretch.h
+++ b/src/audio_core/time_stretch.h
@@ -24,6 +24,8 @@ public:
 
     void Clear();
 
+    void Flush();
+
 private:
     u32 m_sample_rate;
     u32 m_channel_count;