From 9f846d3aa42d7ad269d3d5d0cd1ad68c43b37bb7 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 2 Aug 2018 18:27:22 -0400
Subject: [PATCH 1/6] audio_core: Streams need unique names for CoreTiming.

---
 src/audio_core/audio_out.cpp            | 8 ++++----
 src/audio_core/audio_out.h              | 3 ++-
 src/audio_core/stream.cpp               | 7 ++++---
 src/audio_core/stream.h                 | 4 +++-
 src/core/hle/service/audio/audout_u.cpp | 2 +-
 5 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 3dfdf61f9..5cf665c22 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
     return {};
 }
 
-StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
+StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
                                Stream::ReleaseCallback&& release_callback) {
     if (!sink) {
         const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
         sink = sink_details.factory(Settings::values.audio_device_id);
     }
 
-    return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
-                                    std::move(release_callback),
-                                    sink->AcquireSinkStream(sample_rate, num_channels));
+    return std::make_shared<Stream>(
+        sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
+        sink->AcquireSinkStream(sample_rate, num_channels), std::move(name));
 }
 
 std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index 95e9b53fe..d564ff91a 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "audio_core/buffer.h"
@@ -20,7 +21,7 @@ namespace AudioCore {
 class AudioOut {
 public:
     /// Opens a new audio stream
-    StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
+    StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
                          Stream::ReleaseCallback&& release_callback);
 
     /// Returns a vector of recently released buffers specified by tag for the specified stream
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index a0045b7a1..7ab87c0c9 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -37,12 +37,12 @@ u32 Stream::GetSampleSize() const {
 }
 
 Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
-               SinkStream& sink_stream)
+               SinkStream& sink_stream, std::string&& name_)
     : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
-      sink_stream{sink_stream} {
+      sink_stream{sink_stream}, name{std::move(name_)} {
 
     release_event = CoreTiming::RegisterEvent(
-        "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
+        name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
 }
 
 void Stream::Play() {
@@ -104,6 +104,7 @@ void Stream::PlayNextBuffer() {
 }
 
 void Stream::ReleaseActiveBuffer() {
+    ASSERT(active_buffer);
     released_buffers.push(std::move(active_buffer));
     release_callback();
     PlayNextBuffer();
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 35253920e..00f991733 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -6,6 +6,7 @@
 
 #include <functional>
 #include <memory>
+#include <string>
 #include <vector>
 #include <queue>
 
@@ -33,7 +34,7 @@ public:
     using ReleaseCallback = std::function<void()>;
 
     Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
-           SinkStream& sink_stream);
+           SinkStream& sink_stream, std::string&& name_);
 
     /// Plays the audio stream
     void Play();
@@ -96,6 +97,7 @@ private:
     std::queue<BufferPtr> queued_buffers;   ///< Buffers queued to be played in the stream
     std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
     SinkStream& sink_stream;                ///< Output sink for the stream
+    std::string name;                       ///< Name of the stream, must be unique
 };
 
 using StreamPtr = std::shared_ptr<Stream>;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b317027b6..9f4c7855a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -48,7 +48,7 @@ public:
         buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
 
         stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
-                                       [=]() { buffer_event->Signal(); });
+                                       "IAudioOut", [=]() { buffer_event->Signal(); });
     }
 
 private:

From 34b3f8349814c8c3a7ca5f1f6bd36a98dd32a207 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 2 Aug 2018 18:54:25 -0400
Subject: [PATCH 2/6] audio_core: Sinks need unique names as well.

---
 src/audio_core/audio_out.cpp  |  2 +-
 src/audio_core/cubeb_sink.cpp | 11 ++++++-----
 src/audio_core/cubeb_sink.h   |  3 ++-
 src/audio_core/null_sink.h    |  3 ++-
 src/audio_core/sink.h         |  4 +++-
 5 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 5cf665c22..eb9db755a 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -36,7 +36,7 @@ StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&&
 
     return std::make_shared<Stream>(
         sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
-        sink->AcquireSinkStream(sample_rate, num_channels), std::move(name));
+        sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
 }
 
 std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 34ae5b062..cf4839989 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -13,7 +13,7 @@ namespace AudioCore {
 
 class SinkStreamImpl final : public SinkStream {
 public:
-    SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
+    SinkStreamImpl(cubeb* ctx, cubeb_devid output_device, const std::string& name) : ctx{ctx} {
         cubeb_stream_params params;
         params.rate = 48000;
         params.channels = GetNumChannels();
@@ -25,8 +25,8 @@ public:
             LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
         }
 
-        if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
-                              output_device, &params, std::max(512u, minimum_latency),
+        if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
+                              &params, std::max(512u, minimum_latency),
                               &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
                               this) != CUBEB_OK) {
             LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -129,8 +129,9 @@ CubebSink::~CubebSink() {
     cubeb_destroy(ctx);
 }
 
-SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
-    sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
+SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
+                                         const std::string& name) {
+    sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device, name));
     return *sink_streams.back();
 }
 
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h
index d07113f1f..59cbf05e9 100644
--- a/src/audio_core/cubeb_sink.h
+++ b/src/audio_core/cubeb_sink.h
@@ -18,7 +18,8 @@ public:
     explicit CubebSink(std::string device_id);
     ~CubebSink() override;
 
-    SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
+    SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
+                                  const std::string& name) override;
 
 private:
     cubeb* ctx{};
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index 2e04438f7..66041ea3f 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -13,7 +13,8 @@ public:
     explicit NullSink(std::string){};
     ~NullSink() override = default;
 
-    SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
+    SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
+                                  const std::string& /*name*/) override {
         return null_sink_stream;
     }
 
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index d1bb98c3d..95c7b2b6e 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <memory>
+#include <string>
 
 #include "audio_core/sink_stream.h"
 #include "common/common_types.h"
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
 class Sink {
 public:
     virtual ~Sink() = default;
-    virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
+    virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
+                                          const std::string& name) = 0;
 };
 
 using SinkPtr = std::unique_ptr<Sink>;

From 02fccc09408de59629ea408d825a8978882a6e06 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 3 Aug 2018 14:50:53 -0400
Subject: [PATCH 3/6] cubeb_sink: Support variable sample_rate and
 num_channels.

---
 src/audio_core/cubeb_sink.cpp | 42 ++++++++++++++++++++++-------------
 1 file changed, 26 insertions(+), 16 deletions(-)

diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index cf4839989..0b0e9a053 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -13,14 +13,24 @@ namespace AudioCore {
 
 class SinkStreamImpl final : public SinkStream {
 public:
-    SinkStreamImpl(cubeb* ctx, cubeb_devid output_device, const std::string& name) : ctx{ctx} {
-        cubeb_stream_params params;
-        params.rate = 48000;
-        params.channels = GetNumChannels();
-        params.format = CUBEB_SAMPLE_S16NE;
-        params.layout = CUBEB_LAYOUT_STEREO;
+    SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
+                   const std::string& name)
+        : ctx{ctx}, num_channels{num_channels_} {
 
-        u32 minimum_latency = 0;
+        if (num_channels == 6) {
+            // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
+            // channel for now
+            is_6_channel = true;
+            num_channels = 2;
+        }
+
+        cubeb_stream_params params{};
+        params.rate = sample_rate;
+        params.channels = num_channels;
+        params.format = CUBEB_SAMPLE_S16NE;
+        params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
+
+        u32 minimum_latency{};
         if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
             LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
         }
@@ -58,11 +68,7 @@ public:
 
         queue.reserve(queue.size() + sample_count * GetNumChannels());
 
-        if (num_channels == 2) {
-            // Copy as-is
-            std::copy(samples, samples + sample_count * GetNumChannels(),
-                      std::back_inserter(queue));
-        } else if (num_channels == 6) {
+        if (is_6_channel) {
             // Downsample 6 channels to 2
             const size_t sample_count_copy_size = sample_count * num_channels * 2;
             queue.reserve(sample_count_copy_size);
@@ -71,13 +77,14 @@ public:
                 queue.push_back(samples[i + 1]);
             }
         } else {
-            ASSERT_MSG(false, "Unimplemented");
+            // Copy as-is
+            std::copy(samples, samples + sample_count * GetNumChannels(),
+                      std::back_inserter(queue));
         }
     }
 
     u32 GetNumChannels() const {
-        // Only support 2-channel stereo output for now
-        return 2;
+        return num_channels;
     }
 
 private:
@@ -85,6 +92,8 @@ private:
 
     cubeb* ctx{};
     cubeb_stream* stream_backend{};
+    u32 num_channels{};
+    bool is_6_channel{};
 
     std::vector<s16> queue;
 
@@ -131,7 +140,8 @@ CubebSink::~CubebSink() {
 
 SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
                                          const std::string& name) {
-    sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device, name));
+    sink_streams.push_back(
+        std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
     return *sink_streams.back();
 }
 

From f1cb3903ac358183dcdc562ba19dc469b056e73f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Fri, 3 Aug 2018 15:30:01 -0400
Subject: [PATCH 4/6] audio_core: Port codec code from Citra for ADPCM
 decoding.

---
 src/audio_core/CMakeLists.txt           |  2 +
 src/audio_core/codec.cpp                | 77 +++++++++++++++++++++++++
 src/audio_core/codec.h                  | 44 ++++++++++++++
 src/core/hle/service/audio/audout_u.cpp |  4 +-
 src/core/hle/service/audio/audout_u.h   | 10 ----
 5 files changed, 126 insertions(+), 11 deletions(-)
 create mode 100644 src/audio_core/codec.cpp
 create mode 100644 src/audio_core/codec.h

diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 81121167d..05a61b5cd 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -4,6 +4,8 @@ add_library(audio_core STATIC
     buffer.h
     cubeb_sink.cpp
     cubeb_sink.h
+    codec.cpp
+    codec.h
     null_sink.h
     stream.cpp
     stream.h
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..c3021403f
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,77 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "audio_core/codec.h"
+
+namespace AudioCore::Codec {
+
+std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
+                             ADPCMState& state) {
+    // GC-ADPCM with scale factor and variable coefficients.
+    // Frames are 8 bytes long containing 14 samples each.
+    // Samples are 4 bits (one nibble) long.
+
+    constexpr size_t FRAME_LEN = 8;
+    constexpr size_t SAMPLES_PER_FRAME = 14;
+    constexpr std::array<int, 16> SIGNED_NIBBLES = {
+        {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
+
+    const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
+    const size_t ret_size =
+        sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
+    std::vector<s16> ret(ret_size);
+
+    int yn1 = state.yn1, yn2 = state.yn2;
+
+    const size_t NUM_FRAMES =
+        (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
+    for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
+        const int frame_header = data[framei * FRAME_LEN];
+        const int scale = 1 << (frame_header & 0xF);
+        const int idx = (frame_header >> 4) & 0x7;
+
+        // Coefficients are fixed point with 11 bits fractional part.
+        const int coef1 = coeff[idx * 2 + 0];
+        const int coef2 = coeff[idx * 2 + 1];
+
+        // Decodes an audio sample. One nibble produces one sample.
+        const auto decode_sample = [&](const int nibble) -> s16 {
+            const int xn = nibble * scale;
+            // We first transform everything into 11 bit fixed point, perform the second order
+            // digital filter, then transform back.
+            // 0x400 == 0.5 in 11 bit fixed point.
+            // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
+            int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
+            // Clamp to output range.
+            val = std::clamp<s32>(val, -32768, 32767);
+            // Advance output feedback.
+            yn2 = yn1;
+            yn1 = val;
+            return static_cast<s16>(val);
+        };
+
+        size_t outputi = framei * SAMPLES_PER_FRAME;
+        size_t datai = framei * FRAME_LEN + 1;
+        for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
+            const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
+            ret[outputi] = sample1;
+            outputi++;
+
+            const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
+            ret[outputi] = sample2;
+            outputi++;
+
+            datai++;
+        }
+    }
+
+    state.yn1 = yn1;
+    state.yn2 = yn2;
+
+    return ret;
+}
+
+} // namespace AudioCore::Codec
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..3f845c42c
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,44 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace AudioCore::Codec {
+
+enum class PcmFormat : u32 {
+    Invalid = 0,
+    Int8 = 1,
+    Int16 = 2,
+    Int24 = 3,
+    Int32 = 4,
+    PcmFloat = 5,
+    Adpcm = 6,
+};
+
+/// See: Codec::DecodeADPCM
+struct ADPCMState {
+    // Two historical samples from previous processed buffer,
+    // required for ADPCM decoding
+    s16 yn1; ///< y[n-1]
+    s16 yn2; ///< y[n-2]
+};
+
+using ADPCM_Coeff = std::array<s16, 16>;
+
+/**
+ * @param data Pointer to buffer that contains ADPCM data to decode
+ * @param size Size of buffer in bytes
+ * @param coeff ADPCM coefficients
+ * @param state ADPCM state, this is updated with new state
+ * @return Decoded stereo signed PCM16 data, sample_count in length
+ */
+std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
+                             ADPCMState& state);
+
+}; // namespace AudioCore::Codec
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 9f4c7855a..f4a557634 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -4,6 +4,8 @@
 
 #include <array>
 #include <vector>
+
+#include "audio_core/codec.h"
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/hle/ipc_helpers.h"
@@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
     rb.Push(RESULT_SUCCESS);
     rb.Push<u32>(DefaultSampleRate);
     rb.Push<u32>(params.channel_count);
-    rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
+    rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
     rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
     rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
 }
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index e5c2184d5..fd491f65d 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -38,16 +38,6 @@ private:
 
     void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
     void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
-
-    enum class PcmFormat : u32 {
-        Invalid = 0,
-        Int8 = 1,
-        Int16 = 2,
-        Int24 = 3,
-        Int32 = 4,
-        PcmFloat = 5,
-        Adpcm = 6,
-    };
 };
 
 } // namespace Service::Audio

From 1dee8ceda1e5ecd5ebaee464b1450f323e82305f Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 4 Aug 2018 00:03:12 -0400
Subject: [PATCH 5/6] audio_core: Use s16 where possible for audio samples.

---
 src/audio_core/audio_out.cpp            |  2 +-
 src/audio_core/audio_out.h              |  2 +-
 src/audio_core/buffer.h                 | 13 +++++++++----
 src/audio_core/cubeb_sink.cpp           | 11 +++++------
 src/audio_core/null_sink.h              |  3 +--
 src/audio_core/sink_stream.h            |  4 ++--
 src/audio_core/stream.cpp               | 19 +++++--------------
 src/audio_core/stream.h                 |  3 ---
 src/core/hle/service/audio/audout_u.cpp |  6 +++---
 9 files changed, 27 insertions(+), 36 deletions(-)

diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index eb9db755a..12632a95c 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
     stream->Stop();
 }
 
-bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
+bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
     return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
 }
 
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index d564ff91a..39b7e656b 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -34,7 +34,7 @@ public:
     void StopStream(StreamPtr stream);
 
     /// Queues a buffer into the specified audio stream, returns true on success
-    bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
+    bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
 
 private:
     SinkPtr sink;
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
index 4bf5fd58a..a323b23ec 100644
--- a/src/audio_core/buffer.h
+++ b/src/audio_core/buffer.h
@@ -18,11 +18,16 @@ class Buffer {
 public:
     using Tag = u64;
 
-    Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
+    Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
 
     /// Returns the raw audio data for the buffer
-    const std::vector<u8>& GetData() const {
-        return data;
+    std::vector<s16>& Samples() {
+        return samples;
+    }
+
+    /// Returns the raw audio data for the buffer
+    const std::vector<s16>& GetSamples() const {
+        return samples;
     }
 
     /// Returns the buffer tag, this is provided by the game to the audout service
@@ -32,7 +37,7 @@ public:
 
 private:
     Tag tag;
-    std::vector<u8> data;
+    std::vector<s16> samples;
 };
 
 using BufferPtr = std::shared_ptr<Buffer>;
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 0b0e9a053..1501ef1f4 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -61,25 +61,24 @@ public:
         cubeb_stream_destroy(stream_backend);
     }
 
-    void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
+    void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
         if (!ctx) {
             return;
         }
 
-        queue.reserve(queue.size() + sample_count * GetNumChannels());
+        queue.reserve(queue.size() + samples.size() * GetNumChannels());
 
         if (is_6_channel) {
             // Downsample 6 channels to 2
-            const size_t sample_count_copy_size = sample_count * num_channels * 2;
+            const size_t sample_count_copy_size = samples.size() * 2;
             queue.reserve(sample_count_copy_size);
-            for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
+            for (size_t i = 0; i < samples.size(); i += num_channels) {
                 queue.push_back(samples[i]);
                 queue.push_back(samples[i + 1]);
             }
         } else {
             // Copy as-is
-            std::copy(samples, samples + sample_count * GetNumChannels(),
-                      std::back_inserter(queue));
+            std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
         }
     }
 
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index 66041ea3f..f235d93e5 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -20,8 +20,7 @@ public:
 
 private:
     struct NullSinkStreamImpl final : SinkStream {
-        void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
-                            size_t /*sample_count*/) override {}
+        void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
     } null_sink_stream;
 };
 
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
index e7a3f01b0..41b6736d8 100644
--- a/src/audio_core/sink_stream.h
+++ b/src/audio_core/sink_stream.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <memory>
+#include <vector>
 
 #include "common/common_types.h"
 
@@ -22,9 +23,8 @@ public:
      * Feed stereo samples to sink.
      * @param num_channels Number of channels used.
      * @param samples Samples in interleaved stereo PCM16 format.
-     * @param sample_count Number of samples.
      */
-    virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
+    virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
 };
 
 using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 7ab87c0c9..ad9e2915c 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -32,10 +32,6 @@ u32 Stream::GetNumChannels() const {
     return {};
 }
 
-u32 Stream::GetSampleSize() const {
-    return GetNumChannels() * 2;
-}
-
 Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
                SinkStream& sink_stream, std::string&& name_)
     : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
@@ -55,17 +51,15 @@ void Stream::Stop() {
 }
 
 s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
-    const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
+    const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
     return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
 }
 
-static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
-    std::vector<s16> samples(data.size() / sizeof(s16));
-    std::memcpy(samples.data(), data.data(), data.size());
+static void VolumeAdjustSamples(std::vector<s16>& samples) {
     const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
 
     if (volume == 1.0f) {
-        return samples;
+        return;
     }
 
     // Implementation of a volume slider with a dynamic range of 60 dB
@@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
     for (auto& sample : samples) {
         sample = static_cast<s16>(sample * volume_scale_factor);
     }
-
-    return samples;
 }
 
 void Stream::PlayNextBuffer() {
@@ -96,9 +88,8 @@ void Stream::PlayNextBuffer() {
     active_buffer = queued_buffers.front();
     queued_buffers.pop();
 
-    const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
-    sink_stream.EnqueueSamples(
-        GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
+    VolumeAdjustSamples(active_buffer->Samples());
+    sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
 
     CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
 }
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 00f991733..049b92ca9 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -69,9 +69,6 @@ public:
     /// Gets the number of channels
     u32 GetNumChannels() const;
 
-    /// Gets the sample size in bytes
-    u32 GetSampleSize() const;
-
 private:
     /// Current state of the stream
     enum class State {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index f4a557634..108a7c6eb 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -113,10 +113,10 @@ private:
         std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
         const u64 tag{rp.Pop<u64>()};
 
-        std::vector<u8> data(audio_buffer.buffer_size);
-        Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
+        std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
+        Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
 
-        if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
+        if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
             IPC::ResponseBuilder rb{ctx, 2};
             rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
         }

From b46df98e935552ea48ed86360e8c8b34b294982d Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sat, 4 Aug 2018 17:45:14 -0400
Subject: [PATCH 6/6] audio_core: Implement audren_u audio playback.

---
 src/audio_core/CMakeLists.txt           |   2 +
 src/audio_core/audio_renderer.cpp       | 234 ++++++++++++++++++++++++
 src/audio_core/audio_renderer.h         | 206 +++++++++++++++++++++
 src/core/hle/service/audio/audren_u.cpp | 208 +--------------------
 src/core/hle/service/audio/audren_u.h   |  19 +-
 5 files changed, 451 insertions(+), 218 deletions(-)
 create mode 100644 src/audio_core/audio_renderer.cpp
 create mode 100644 src/audio_core/audio_renderer.h

diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 05a61b5cd..827ab0ac7 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -1,6 +1,8 @@
 add_library(audio_core STATIC
     audio_out.cpp
     audio_out.h
+    audio_renderer.cpp
+    audio_renderer.h
     buffer.h
     cubeb_sink.cpp
     cubeb_sink.h
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
new file mode 100644
index 000000000..282f345c5
--- /dev/null
+++ b/src/audio_core/audio_renderer.cpp
@@ -0,0 +1,234 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/audio_renderer.h"
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/memory.h"
+
+namespace AudioCore {
+
+constexpr u32 STREAM_SAMPLE_RATE{48000};
+constexpr u32 STREAM_NUM_CHANNELS{2};
+
+AudioRenderer::AudioRenderer(AudioRendererParameter params,
+                             Kernel::SharedPtr<Kernel::Event> buffer_event)
+    : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
+
+    audio_core = std::make_unique<AudioCore::AudioOut>();
+    stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
+                                    [=]() { buffer_event->Signal(); });
+    audio_core->StartStream(stream);
+
+    QueueMixedBuffer(0);
+    QueueMixedBuffer(1);
+    QueueMixedBuffer(2);
+}
+
+std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
+    // Copy UpdateDataHeader struct
+    UpdateDataHeader config{};
+    std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
+    u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
+
+    // Copy MemoryPoolInfo structs
+    std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
+    std::memcpy(mem_pool_info.data(),
+                input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
+                memory_pool_count * sizeof(MemoryPoolInfo));
+
+    // Copy VoiceInfo structs
+    size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
+                  config.voice_resource_size};
+    for (auto& voice : voices) {
+        std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
+        offset += sizeof(VoiceInfo);
+    }
+
+    // Update voices
+    for (auto& voice : voices) {
+        voice.UpdateState();
+        if (!voice.GetInfo().is_in_use) {
+            continue;
+        }
+        if (voice.GetInfo().is_new) {
+            voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
+        }
+    }
+
+    // Update memory pool state
+    std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
+    for (size_t index = 0; index < memory_pool.size(); ++index) {
+        if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
+            memory_pool[index].state = MemoryPoolStates::Attached;
+        } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
+            memory_pool[index].state = MemoryPoolStates::Detached;
+        }
+    }
+
+    // Release previous buffers and queue next ones for playback
+    ReleaseAndQueueBuffers();
+
+    // Copy output header
+    UpdateDataHeader response_data{worker_params};
+    std::vector<u8> output_params(response_data.total_size);
+    std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
+
+    // Copy output memory pool entries
+    std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
+                response_data.memory_pools_size);
+
+    // Copy output voice status
+    size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
+    for (const auto& voice : voices) {
+        std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
+                    sizeof(VoiceOutStatus));
+        voice_out_status_offset += sizeof(VoiceOutStatus);
+    }
+
+    return output_params;
+}
+
+void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
+    wave_index = index & 3;
+    is_refresh_pending = true;
+}
+
+std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
+    if (!IsPlaying()) {
+        return {};
+    }
+
+    if (is_refresh_pending) {
+        RefreshBuffer();
+    }
+
+    const size_t max_size{samples.size() - offset};
+    const size_t dequeue_offset{offset};
+    size_t size{sample_count * STREAM_NUM_CHANNELS};
+    if (size > max_size) {
+        size = max_size;
+    }
+
+    out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
+    offset += size;
+
+    const auto& wave_buffer{info.wave_buffer[wave_index]};
+    if (offset == samples.size()) {
+        offset = 0;
+
+        if (!wave_buffer.is_looping) {
+            SetWaveIndex(wave_index + 1);
+        }
+
+        out_status.wave_buffer_consumed++;
+
+        if (wave_buffer.end_of_stream) {
+            info.play_state = PlayState::Paused;
+        }
+    }
+
+    return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
+}
+
+void AudioRenderer::VoiceState::UpdateState() {
+    if (is_in_use && !info.is_in_use) {
+        // No longer in use, reset state
+        is_refresh_pending = true;
+        wave_index = 0;
+        offset = 0;
+        out_status = {};
+    }
+    is_in_use = info.is_in_use;
+}
+
+void AudioRenderer::VoiceState::RefreshBuffer() {
+    std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
+    Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
+                      info.wave_buffer[wave_index].buffer_sz);
+
+    switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
+    case Codec::PcmFormat::Int16: {
+        // PCM16 is played as-is
+        break;
+    }
+    case Codec::PcmFormat::Adpcm: {
+        // Decode ADPCM to PCM16
+        Codec::ADPCM_Coeff coeffs;
+        Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
+        new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
+                                         new_samples.size() * sizeof(s16), coeffs, adpcm_state);
+        break;
+    }
+    default:
+        LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
+        UNREACHABLE();
+        break;
+    }
+
+    switch (info.channel_count) {
+    case 1:
+        // 1 channel is upsampled to 2 channel
+        samples.resize(new_samples.size() * 2);
+        for (size_t index = 0; index < new_samples.size(); ++index) {
+            samples[index * 2] = new_samples[index];
+            samples[index * 2 + 1] = new_samples[index];
+        }
+        break;
+    case 2: {
+        // 2 channel is played as is
+        samples = std::move(new_samples);
+        break;
+    }
+    default:
+        LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
+        UNREACHABLE();
+        break;
+    }
+
+    is_refresh_pending = false;
+}
+
+static constexpr s16 ClampToS16(s32 value) {
+    return static_cast<s16>(std::clamp(value, -32768, 32767));
+}
+
+void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
+    constexpr size_t BUFFER_SIZE{512};
+    std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
+
+    for (auto& voice : voices) {
+        if (!voice.IsPlaying()) {
+            continue;
+        }
+
+        size_t offset{};
+        s64 samples_remaining{BUFFER_SIZE};
+        while (samples_remaining > 0) {
+            const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
+
+            if (samples.empty()) {
+                break;
+            }
+
+            samples_remaining -= samples.size();
+
+            for (const auto& sample : samples) {
+                const s32 buffer_sample{buffer[offset]};
+                buffer[offset++] =
+                    ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
+            }
+        }
+    }
+    audio_core->QueueBuffer(stream, tag, std::move(buffer));
+}
+
+void AudioRenderer::ReleaseAndQueueBuffers() {
+    const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
+    for (const auto& tag : released_buffers) {
+        QueueMixedBuffer(tag);
+    }
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
new file mode 100644
index 000000000..6950a4681
--- /dev/null
+++ b/src/audio_core/audio_renderer.h
@@ -0,0 +1,206 @@
+// Copyright 2018 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "audio_core/audio_out.h"
+#include "audio_core/codec.h"
+#include "audio_core/stream.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/kernel/event.h"
+
+namespace AudioCore {
+
+enum class PlayState : u8 {
+    Started = 0,
+    Stopped = 1,
+    Paused = 2,
+};
+
+struct AudioRendererParameter {
+    u32_le sample_rate;
+    u32_le sample_count;
+    u32_le unknown_8;
+    u32_le unknown_c;
+    u32_le voice_count;
+    u32_le sink_count;
+    u32_le effect_count;
+    u32_le unknown_1c;
+    u8 unknown_20;
+    INSERT_PADDING_BYTES(3);
+    u32_le splitter_count;
+    u32_le unknown_2c;
+    INSERT_PADDING_WORDS(1);
+    u32_le revision;
+};
+static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
+
+enum class MemoryPoolStates : u32 { // Should be LE
+    Invalid = 0x0,
+    Unknown = 0x1,
+    RequestDetach = 0x2,
+    Detached = 0x3,
+    RequestAttach = 0x4,
+    Attached = 0x5,
+    Released = 0x6,
+};
+
+struct MemoryPoolEntry {
+    MemoryPoolStates state;
+    u32_le unknown_4;
+    u32_le unknown_8;
+    u32_le unknown_c;
+};
+static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
+
+struct MemoryPoolInfo {
+    u64_le pool_address;
+    u64_le pool_size;
+    MemoryPoolStates pool_state;
+    INSERT_PADDING_WORDS(3); // Unknown
+};
+static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
+struct BiquadFilter {
+    u8 enable;
+    INSERT_PADDING_BYTES(1);
+    std::array<s16_le, 3> numerator;
+    std::array<s16_le, 2> denominator;
+};
+static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
+
+struct WaveBuffer {
+    u64_le buffer_addr;
+    u64_le buffer_sz;
+    s32_le start_sample_offset;
+    s32_le end_sample_offset;
+    u8 is_looping;
+    u8 end_of_stream;
+    u8 sent_to_server;
+    INSERT_PADDING_BYTES(5);
+    u64 context_addr;
+    u64 context_sz;
+    INSERT_PADDING_BYTES(8);
+};
+static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
+
+struct VoiceInfo {
+    u32_le id;
+    u32_le node_id;
+    u8 is_new;
+    u8 is_in_use;
+    PlayState play_state;
+    u8 sample_format;
+    u32_le sample_rate;
+    u32_le priority;
+    u32_le sorting_order;
+    u32_le channel_count;
+    float_le pitch;
+    float_le volume;
+    std::array<BiquadFilter, 2> biquad_filter;
+    u32_le wave_buffer_count;
+    u32_le wave_buffer_head;
+    INSERT_PADDING_WORDS(1);
+    u64_le additional_params_addr;
+    u64_le additional_params_sz;
+    u32_le mix_id;
+    u32_le splitter_info_id;
+    std::array<WaveBuffer, 4> wave_buffer;
+    std::array<u32_le, 6> voice_channel_resource_ids;
+    INSERT_PADDING_BYTES(24);
+};
+static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
+
+struct VoiceOutStatus {
+    u64_le played_sample_count;
+    u32_le wave_buffer_consumed;
+    u32_le voice_drops_count;
+};
+static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
+
+struct UpdateDataHeader {
+    UpdateDataHeader() {}
+
+    explicit UpdateDataHeader(const AudioRendererParameter& config) {
+        revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
+        behavior_size = 0xb0;
+        memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
+        voices_size = config.voice_count * 0x10;
+        voice_resource_size = 0x0;
+        effects_size = config.effect_count * 0x10;
+        mixes_size = 0x0;
+        sinks_size = config.sink_count * 0x20;
+        performance_manager_size = 0x10;
+        total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
+                     effects_size + sinks_size + performance_manager_size;
+    }
+
+    u32_le revision;
+    u32_le behavior_size;
+    u32_le memory_pools_size;
+    u32_le voices_size;
+    u32_le voice_resource_size;
+    u32_le effects_size;
+    u32_le mixes_size;
+    u32_le sinks_size;
+    u32_le performance_manager_size;
+    INSERT_PADDING_WORDS(6);
+    u32_le total_size;
+};
+static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
+
+class AudioRenderer {
+public:
+    AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
+    std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
+    void QueueMixedBuffer(Buffer::Tag tag);
+    void ReleaseAndQueueBuffers();
+
+private:
+    class VoiceState {
+    public:
+        bool IsPlaying() const {
+            return is_in_use && info.play_state == PlayState::Started;
+        }
+
+        const VoiceOutStatus& GetOutStatus() const {
+            return out_status;
+        }
+
+        const VoiceInfo& GetInfo() const {
+            return info;
+        }
+
+        VoiceInfo& Info() {
+            return info;
+        }
+
+        void SetWaveIndex(size_t index);
+        std::vector<s16> DequeueSamples(size_t sample_count);
+        void UpdateState();
+        void RefreshBuffer();
+
+    private:
+        bool is_in_use{};
+        bool is_refresh_pending{};
+        size_t wave_index{};
+        size_t offset{};
+        Codec::ADPCMState adpcm_state{};
+        std::vector<s16> samples;
+        VoiceOutStatus out_status{};
+        VoiceInfo info{};
+    };
+
+    AudioRendererParameter worker_params;
+    Kernel::SharedPtr<Kernel::Event> buffer_event;
+    std::vector<VoiceState> voices;
+    std::unique_ptr<AudioCore::AudioOut> audio_core;
+    AudioCore::StreamPtr stream;
+};
+
+} // namespace AudioCore
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6aed9e2fa..f99304de5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -15,13 +15,10 @@
 
 namespace Service::Audio {
 
-/// TODO(bunnei): Find a proper value for the audio_ticks
-constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
-
 class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
 public:
-    explicit IAudioRenderer(AudioRendererParameter audren_params)
-        : ServiceFramework("IAudioRenderer"), worker_params(audren_params) {
+    explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
+        : ServiceFramework("IAudioRenderer") {
         static const FunctionInfo functions[] = {
             {0, nullptr, "GetAudioRendererSampleRate"},
             {1, nullptr, "GetAudioRendererSampleCount"},
@@ -39,21 +36,8 @@ public:
         RegisterHandlers(functions);
 
         system_event =
-            Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent");
-
-        // Register event callback to update the Audio Buffer
-        audio_event = CoreTiming::RegisterEvent(
-            "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
-                UpdateAudioCallback();
-                CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
-            });
-
-        // Start the audio event
-        CoreTiming::ScheduleEvent(audio_ticks, audio_event);
-        voice_status_list.resize(worker_params.voice_count);
-    }
-    ~IAudioRenderer() {
-        CoreTiming::UnscheduleEvent(audio_event, 0);
+            Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
+        renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
     }
 
 private:
@@ -62,60 +46,9 @@ private:
     }
 
     void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
-        UpdateDataHeader config{};
-        auto buf = ctx.ReadBuffer();
-        std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
-        u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
-
-        std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
-        std::memcpy(mem_pool_info.data(),
-                    buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
-                    memory_pool_count * sizeof(MemoryPoolInfo));
-
-        std::vector<VoiceInfo> voice_info(worker_params.voice_count);
-        std::memcpy(voice_info.data(),
-                    buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
-                        config.memory_pools_size + config.voice_resource_size,
-                    worker_params.voice_count * sizeof(VoiceInfo));
-
-        UpdateDataHeader response_data{worker_params};
-
-        ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
-
-        std::vector<u8> output(response_data.total_size);
-        std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
-        std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
-        for (unsigned i = 0; i < memory_pool.size(); i++) {
-            if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
-                memory_pool[i].state = MemoryPoolStates::Attached;
-            else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
-                memory_pool[i].state = MemoryPoolStates::Detached;
-        }
-        std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
-                    response_data.memory_pools_size);
-
-        for (unsigned i = 0; i < voice_info.size(); i++) {
-            if (voice_info[i].is_new) {
-                voice_status_list[i].played_sample_count = 0;
-                voice_status_list[i].wave_buffer_consumed = 0;
-            } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
-                for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
-                    voice_status_list[i].played_sample_count +=
-                        (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
-                         voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
-                        2;
-                    voice_status_list[i].wave_buffer_consumed++;
-                }
-            }
-        }
-        std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
-                    voice_status_list.data(), response_data.voices_size);
-
-        ctx.WriteBuffer(output);
-
+        ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_SUCCESS);
-
         LOG_WARNING(Service_Audio, "(STUBBED) called");
     }
 
@@ -136,8 +69,6 @@ private:
     }
 
     void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
-        // system_event->Signal();
-
         IPC::ResponseBuilder rb{ctx, 2, 1};
         rb.Push(RESULT_SUCCESS);
         rb.PushCopyObjects(system_event);
@@ -145,131 +76,8 @@ private:
         LOG_WARNING(Service_Audio, "(STUBBED) called");
     }
 
-    enum class MemoryPoolStates : u32 { // Should be LE
-        Invalid = 0x0,
-        Unknown = 0x1,
-        RequestDetach = 0x2,
-        Detached = 0x3,
-        RequestAttach = 0x4,
-        Attached = 0x5,
-        Released = 0x6,
-    };
-
-    enum class PlayStates : u8 {
-        Started = 0,
-        Stopped = 1,
-    };
-
-    struct MemoryPoolEntry {
-        MemoryPoolStates state;
-        u32_le unknown_4;
-        u32_le unknown_8;
-        u32_le unknown_c;
-    };
-    static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
-
-    struct MemoryPoolInfo {
-        u64_le pool_address;
-        u64_le pool_size;
-        MemoryPoolStates pool_state;
-        INSERT_PADDING_WORDS(3); // Unknown
-    };
-    static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
-
-    struct UpdateDataHeader {
-        UpdateDataHeader() {}
-
-        explicit UpdateDataHeader(const AudioRendererParameter& config) {
-            revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
-            behavior_size = 0xb0;
-            memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
-            voices_size = config.voice_count * 0x10;
-            voice_resource_size = 0x0;
-            effects_size = config.effect_count * 0x10;
-            mixes_size = 0x0;
-            sinks_size = config.sink_count * 0x20;
-            performance_manager_size = 0x10;
-            total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
-                         voices_size + effects_size + sinks_size + performance_manager_size;
-        }
-
-        u32_le revision;
-        u32_le behavior_size;
-        u32_le memory_pools_size;
-        u32_le voices_size;
-        u32_le voice_resource_size;
-        u32_le effects_size;
-        u32_le mixes_size;
-        u32_le sinks_size;
-        u32_le performance_manager_size;
-        INSERT_PADDING_WORDS(6);
-        u32_le total_size;
-    };
-    static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
-
-    struct BiquadFilter {
-        u8 enable;
-        INSERT_PADDING_BYTES(1);
-        s16_le numerator[3];
-        s16_le denominator[2];
-    };
-    static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
-
-    struct WaveBuffer {
-        u64_le buffer_addr;
-        u64_le buffer_sz;
-        s32_le start_sample_offset;
-        s32_le end_sample_offset;
-        u8 loop;
-        u8 end_of_stream;
-        u8 sent_to_server;
-        INSERT_PADDING_BYTES(5);
-        u64 context_addr;
-        u64 context_sz;
-        INSERT_PADDING_BYTES(8);
-    };
-    static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
-
-    struct VoiceInfo {
-        u32_le id;
-        u32_le node_id;
-        u8 is_new;
-        u8 is_in_use;
-        u8 play_state;
-        u8 sample_format;
-        u32_le sample_rate;
-        u32_le priority;
-        u32_le sorting_order;
-        u32_le channel_count;
-        float_le pitch;
-        float_le volume;
-        BiquadFilter biquad_filter[2];
-        u32_le wave_buffer_count;
-        u16_le wave_buffer_head;
-        INSERT_PADDING_BYTES(6);
-        u64_le additional_params_addr;
-        u64_le additional_params_sz;
-        u32_le mix_id;
-        u32_le splitter_info_id;
-        WaveBuffer wave_buffer[4];
-        u32_le voice_channel_resource_ids[6];
-        INSERT_PADDING_BYTES(24);
-    };
-    static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
-
-    struct VoiceOutStatus {
-        u64_le played_sample_count;
-        u32_le wave_buffer_consumed;
-        INSERT_PADDING_WORDS(1);
-    };
-    static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
-
-    /// This is used to trigger the audio event callback.
-    CoreTiming::EventType* audio_event;
-
     Kernel::SharedPtr<Kernel::Event> system_event;
-    AudioRendererParameter worker_params;
-    std::vector<VoiceOutStatus> voice_status_list;
+    std::unique_ptr<AudioCore::AudioRenderer> renderer;
 };
 
 class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
 
 void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    auto params = rp.PopRaw<AudioRendererParameter>();
+    auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
 
     rb.Push(RESULT_SUCCESS);
@@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
 
 void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    auto params = rp.PopRaw<AudioRendererParameter>();
+    auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
 
     u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
     buffer_sz += params.unknown_c * 1024;
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index b9b81db4f..14907f8ae 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include "audio_core/audio_renderer.h"
 #include "core/hle/service/service.h"
 
 namespace Kernel {
@@ -12,24 +13,6 @@ class HLERequestContext;
 
 namespace Service::Audio {
 
-struct AudioRendererParameter {
-    u32_le sample_rate;
-    u32_le sample_count;
-    u32_le unknown_8;
-    u32_le unknown_c;
-    u32_le voice_count;
-    u32_le sink_count;
-    u32_le effect_count;
-    u32_le unknown_1c;
-    u8 unknown_20;
-    INSERT_PADDING_BYTES(3);
-    u32_le splitter_count;
-    u32_le unknown_2c;
-    INSERT_PADDING_WORDS(1);
-    u32_le revision;
-};
-static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
-
 class AudRenU final : public ServiceFramework<AudRenU> {
 public:
     explicit AudRenU();