diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs b/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs
new file mode 100644
index 0000000000..24455b4187
--- /dev/null
+++ b/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs
@@ -0,0 +1,91 @@
+namespace Ryujinx.Audio.Adpcm
+{
+    public static class AdpcmDecoder
+    {
+        private const int SamplesPerFrame = 14;
+        private const int BytesPerFrame   = 8;
+
+        public static int[] Decode(byte[] Buffer, AdpcmDecoderContext Context)
+        {
+            int Samples = GetSamplesCountFromSize(Buffer.Length);
+
+            int[] Pcm = new int[Samples * 2];
+
+            short History0 = Context.History0;
+            short History1 = Context.History1;
+
+            int InputOffset  = 0;
+            int OutputOffset = 0;
+
+            while (InputOffset < Buffer.Length)
+            {
+                byte Header = Buffer[InputOffset++];
+
+                int Scale = 0x800 << (Header & 0xf);
+
+                int CoeffIndex = (Header >> 4) & 7;
+
+                short Coeff0 = Context.Coefficients[CoeffIndex * 2 + 0];
+                short Coeff1 = Context.Coefficients[CoeffIndex * 2 + 1];
+
+                int FrameSamples = SamplesPerFrame;
+
+                if (FrameSamples > Samples)
+                {
+                    FrameSamples = Samples;
+                }
+
+                int Value = 0;
+
+                for (int SampleIndex = 0; SampleIndex < FrameSamples; SampleIndex++)
+                {
+                    int Sample;
+
+                    if ((SampleIndex & 1) == 0)
+                    {
+                        Value = Buffer[InputOffset++];
+
+                        Sample = (Value << 24) >> 28;
+                    }
+                    else
+                    {
+                        Sample = (Value << 28) >> 28;
+                    }
+
+                    int Prediction = Coeff0 * History0 + Coeff1 * History1;
+
+                    Sample = (Sample * Scale + Prediction + 0x400) >> 11;
+
+                    short SaturatedSample = DspUtils.Saturate(Sample);
+
+                    History1 = History0;
+                    History0 = SaturatedSample;
+
+                    Pcm[OutputOffset++] = SaturatedSample;
+                    Pcm[OutputOffset++] = SaturatedSample;
+                }
+
+                Samples -= FrameSamples;
+            }
+
+            Context.History0 = History0;
+            Context.History1 = History1;
+
+            return Pcm;
+        }
+
+        public static long GetSizeFromSamplesCount(int SamplesCount)
+        {
+            int Frames = SamplesCount / SamplesPerFrame;
+
+            return Frames * BytesPerFrame;
+        }
+
+        public static int GetSamplesCountFromSize(long Size)
+        {
+            int Frames = (int)(Size / BytesPerFrame);
+
+            return Frames * SamplesPerFrame;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs b/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs
new file mode 100644
index 0000000000..91730333c8
--- /dev/null
+++ b/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Audio.Adpcm
+{
+    public class AdpcmDecoderContext
+    {
+        public short[] Coefficients;
+
+        public short History0;
+        public short History1;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/DspUtils.cs b/Ryujinx.Audio/DspUtils.cs
new file mode 100644
index 0000000000..c048161dae
--- /dev/null
+++ b/Ryujinx.Audio/DspUtils.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Audio.Adpcm
+{
+    public static class DspUtils
+    {
+        public static short Saturate(int Value)
+        {
+            if (Value > short.MaxValue)
+                Value = short.MaxValue;
+
+            if (Value < short.MinValue)
+                Value = short.MinValue;
+
+            return (short)Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs
index f9978ee4d9..e903c5c5cc 100644
--- a/Ryujinx.Audio/IAalOutput.cs
+++ b/Ryujinx.Audio/IAalOutput.cs
@@ -2,11 +2,7 @@ namespace Ryujinx.Audio
 {
     public interface IAalOutput
     {
-        int OpenTrack(
-            int             SampleRate,
-            int             Channels,
-            ReleaseCallback Callback,
-            out AudioFormat Format);
+        int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback);
 
         void CloseTrack(int Track);
 
@@ -14,7 +10,7 @@ namespace Ryujinx.Audio
 
         long[] GetReleasedBuffers(int Track, int MaxCount);
 
-        void AppendBuffer(int Track, long Tag, byte[] Buffer);
+        void AppendBuffer<T>(int Track, long Tag, T[] Buffer)  where T : struct;
 
         void Start(int Track);
         void Stop(int Track);
diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
index 2860dc2e2d..1dd63202ba 100644
--- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
+++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
@@ -3,6 +3,7 @@ using OpenTK.Audio.OpenAL;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Runtime.InteropServices;
 using System.Threading;
 
 namespace Ryujinx.Audio.OpenAL
@@ -226,15 +227,9 @@ namespace Ryujinx.Audio.OpenAL
             while (KeepPolling);
         }
 
-        public int OpenTrack(
-            int             SampleRate,
-            int             Channels,
-            ReleaseCallback Callback,
-            out AudioFormat Format)
+        public int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback)
         {
-            Format = AudioFormat.PcmInt16;
-
-            Track Td = new Track(SampleRate, GetALFormat(Channels, Format), Callback);
+            Track Td = new Track(SampleRate, GetALFormat(Channels), Callback);
 
             for (int Id = 0; Id < MaxTracks; Id++)
             {
@@ -247,38 +242,16 @@ namespace Ryujinx.Audio.OpenAL
             return -1;
         }
 
-        private ALFormat GetALFormat(int Channels, AudioFormat Format)
+        private ALFormat GetALFormat(int Channels)
         {
-            if (Channels == 1)
+            switch (Channels)
             {
-                switch (Format)
-                {
-                    case AudioFormat.PcmInt8:  return ALFormat.Mono8;
-                    case AudioFormat.PcmInt16: return ALFormat.Mono16;
-                }
-            }
-            else if (Channels == 2)
-            {
-                switch (Format)
-                {
-                    case AudioFormat.PcmInt8:  return ALFormat.Stereo8;
-                    case AudioFormat.PcmInt16: return ALFormat.Stereo16;
-                }
-            }
-            else if (Channels == 6)
-            {
-                switch (Format)
-                {
-                    case AudioFormat.PcmInt8:  return ALFormat.Multi51Chn8Ext;
-                    case AudioFormat.PcmInt16: return ALFormat.Multi51Chn16Ext;
-                }
-            }
-            else
-            {
-                throw new ArgumentOutOfRangeException(nameof(Channels));
+                case 1: return ALFormat.Mono16;
+                case 2: return ALFormat.Stereo16;
+                case 6: return ALFormat.Multi51Chn16Ext;
             }
 
-            throw new ArgumentException(nameof(Format));
+            throw new ArgumentOutOfRangeException(nameof(Channels));
         }
 
         public void CloseTrack(int Track)
@@ -309,13 +282,15 @@ namespace Ryujinx.Audio.OpenAL
             return null;
         }
 
-        public void AppendBuffer(int Track, long Tag, byte[] Buffer)
+        public void AppendBuffer<T>(int Track, long Tag, T[] Buffer) where T : struct
         {
             if (Tracks.TryGetValue(Track, out Track Td))
             {
                 int BufferId = Td.AppendBuffer(Tag);
 
-                AL.BufferData(BufferId, Td.Format, Buffer, Buffer.Length, Td.SampleRate);
+                int Size = Buffer.Length * Marshal.SizeOf<T>();
+
+                AL.BufferData<T>(BufferId, Td.Format, Buffer, Size, Td.SampleRate);
 
                 AL.SourceQueueBuffer(Td.SourceId, BufferId);
 
@@ -366,7 +341,5 @@ namespace Ryujinx.Audio.OpenAL
 
             return PlaybackState.Stopped;
         }
-
-
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs
index fa201d8cdf..72c3e65eb9 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs
@@ -3,6 +3,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
     static class AudErr
     {
         public const int DeviceNotFound        = 1;
+        public const int UnsupportedRevision   = 2;
         public const int UnsupportedSampleRate = 3;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs
similarity index 86%
rename from Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs
rename to Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs
index 9d68c24ab2..6887a38bed 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs
@@ -1,6 +1,6 @@
 using System.Runtime.InteropServices;
 
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioOut
 {
     [StructLayout(LayoutKind.Sequential)]
     struct AudioOutData
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs
similarity index 98%
rename from Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs
rename to Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs
index 49c87a5616..d89fc293af 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs
@@ -1,12 +1,11 @@
 using ChocolArm64.Memory;
 using Ryujinx.Audio;
-using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Handles;
 using Ryujinx.HLE.OsHle.Ipc;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioOut
 {
     class IAudioOut : IpcService, IDisposable
     {
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs
new file mode 100644
index 0000000000..fed41959d7
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    static class AudioConsts
+    {
+        public const int HostSampleRate    = 48000;
+        public const int HostChannelsCount = 2;
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs
new file mode 100644
index 0000000000..4e33de6214
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
+    struct BehaviorIn
+    {
+        public long Unknown0;
+        public long Unknown8;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs
new file mode 100644
index 0000000000..9fa4cd3303
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs
@@ -0,0 +1,16 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0xc, Pack = 1)]
+    struct BiquadFilter
+    {
+        public byte  Enable;
+        public byte  Padding;
+        public short B0;
+        public short B1;
+        public short B2;
+        public short A1;
+        public short A2;
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs
new file mode 100644
index 0000000000..f91a8da37e
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs
@@ -0,0 +1,316 @@
+using ChocolArm64.Memory;
+using Ryujinx.Audio;
+using Ryujinx.Audio.Adpcm;
+using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.OsHle.Handles;
+using Ryujinx.HLE.OsHle.Ipc;
+using Ryujinx.HLE.OsHle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    class IAudioRenderer : IpcService, IDisposable
+    {
+        //This is the amount of samples that are going to be appended
+        //each time that RequestUpdateAudioRenderer is called. Ideally,
+        //this value shouldn't be neither too small (to avoid the player
+        //starving due to running out of samples) or too large (to avoid
+        //high latency).
+        private const int MixBufferSamplesCount = 960;
+
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent UpdateEvent;
+
+        private AMemory Memory;
+
+        private IAalOutput AudioOut;
+
+        private AudioRendererParameter Params;
+
+        private MemoryPoolContext[] MemoryPools;
+
+        private VoiceContext[] Voices;
+
+        private int Track;
+
+        public IAudioRenderer(AMemory Memory, IAalOutput AudioOut, AudioRendererParameter Params)
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 4, RequestUpdateAudioRenderer },
+                { 5, StartAudioRenderer         },
+                { 6, StopAudioRenderer          },
+                { 7, QuerySystemEvent           }
+            };
+
+            UpdateEvent = new KEvent();
+
+            this.Memory   = Memory;
+            this.AudioOut = AudioOut;
+            this.Params   = Params;
+
+            Track = AudioOut.OpenTrack(
+                AudioConsts.HostSampleRate,
+                AudioConsts.HostChannelsCount,
+                AudioCallback);
+
+            MemoryPools = CreateArray<MemoryPoolContext>(Params.EffectCount + Params.VoiceCount * 4);
+
+            Voices = CreateArray<VoiceContext>(Params.VoiceCount);
+
+            InitializeAudioOut();
+        }
+
+        private void AudioCallback()
+        {
+            UpdateEvent.WaitEvent.Set();
+        }
+
+        private static T[] CreateArray<T>(int Size) where T : new()
+        {
+            T[] Output = new T[Size];
+
+            for (int Index = 0; Index < Size; Index++)
+            {
+                Output[Index] = new T();
+            }
+
+            return Output;
+        }
+
+        private void InitializeAudioOut()
+        {
+            AppendMixedBuffer(0);
+            AppendMixedBuffer(1);
+            AppendMixedBuffer(2);
+
+            AudioOut.Start(Track);
+        }
+
+        public long RequestUpdateAudioRenderer(ServiceCtx Context)
+        {
+            long OutputPosition = Context.Request.ReceiveBuff[0].Position;
+            long OutputSize     = Context.Request.ReceiveBuff[0].Size;
+
+            AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize);
+
+            long InputPosition = Context.Request.SendBuff[0].Position;
+
+            StructReader Reader = new StructReader(Context.Memory, InputPosition);
+            StructWriter Writer = new StructWriter(Context.Memory, OutputPosition);
+
+            UpdateDataHeader InputHeader = Reader.Read<UpdateDataHeader>();
+
+            Reader.Read<BehaviorIn>(InputHeader.BehaviorSize);
+
+            MemoryPoolIn[] MemoryPoolsIn = Reader.Read<MemoryPoolIn>(InputHeader.MemoryPoolSize);
+
+            for (int Index = 0; Index < MemoryPoolsIn.Length; Index++)
+            {
+                MemoryPoolIn MemoryPool = MemoryPoolsIn[Index];
+
+                if (MemoryPool.State == MemoryPoolState.RequestAttach)
+                {
+                    MemoryPools[Index].OutStatus.State = MemoryPoolState.Attached;
+                }
+                else if (MemoryPool.State == MemoryPoolState.RequestDetach)
+                {
+                    MemoryPools[Index].OutStatus.State = MemoryPoolState.Detached;
+                }
+            }
+
+            Reader.Read<VoiceChannelResourceIn>(InputHeader.VoiceResourceSize);
+
+            VoiceIn[] VoicesIn = Reader.Read<VoiceIn>(InputHeader.VoiceSize);
+
+            for (int Index = 0; Index < VoicesIn.Length; Index++)
+            {
+                VoiceIn Voice = VoicesIn[Index];
+
+                VoiceContext VoiceCtx = Voices[Index];
+
+                VoiceCtx.SetAcquireState(Voice.Acquired != 0);
+
+                if (Voice.Acquired == 0)
+                {
+                    continue;
+                }
+
+                if (Voice.FirstUpdate != 0)
+                {
+                    VoiceCtx.AdpcmCtx = GetAdpcmDecoderContext(
+                        Voice.AdpcmCoeffsPosition,
+                        Voice.AdpcmCoeffsSize);
+
+                    VoiceCtx.SampleFormat  = Voice.SampleFormat;
+                    VoiceCtx.SampleRate    = Voice.SampleRate;
+                    VoiceCtx.ChannelsCount = Voice.ChannelsCount;
+
+                    VoiceCtx.SetBufferIndex(Voice.BaseWaveBufferIndex);
+                }
+
+                VoiceCtx.WaveBuffers[0] = Voice.WaveBuffer0;
+                VoiceCtx.WaveBuffers[1] = Voice.WaveBuffer1;
+                VoiceCtx.WaveBuffers[2] = Voice.WaveBuffer2;
+                VoiceCtx.WaveBuffers[3] = Voice.WaveBuffer3;
+                VoiceCtx.Volume         = Voice.Volume;
+                VoiceCtx.PlayState      = Voice.PlayState;
+            }
+
+            UpdateAudio();
+
+            UpdateDataHeader OutputHeader = new UpdateDataHeader();
+
+            int UpdateHeaderSize = Marshal.SizeOf<UpdateDataHeader>();
+
+            OutputHeader.Revision               = IAudioRendererManager.RevMagic;
+            OutputHeader.BehaviorSize           = 0xb0;
+            OutputHeader.MemoryPoolSize         = (Params.EffectCount + Params.VoiceCount * 4) * 0x10;
+            OutputHeader.VoiceSize              = Params.VoiceCount  * 0x10;
+            OutputHeader.EffectSize             = Params.EffectCount * 0x10;
+            OutputHeader.SinkSize               = Params.SinkCount   * 0x20;
+            OutputHeader.PerformanceManagerSize = 0x10;
+            OutputHeader.TotalSize              = UpdateHeaderSize             +
+                                                  OutputHeader.BehaviorSize    +
+                                                  OutputHeader.MemoryPoolSize +
+                                                  OutputHeader.VoiceSize      +
+                                                  OutputHeader.EffectSize     +
+                                                  OutputHeader.SinkSize       +
+                                                  OutputHeader.PerformanceManagerSize;
+
+            Writer.Write(OutputHeader);
+
+            foreach (MemoryPoolContext MemoryPool in MemoryPools)
+            {
+                Writer.Write(MemoryPool.OutStatus);
+            }
+
+            foreach (VoiceContext Voice in Voices)
+            {
+                Writer.Write(Voice.OutStatus);
+            }
+
+            return 0;
+        }
+
+        public long StartAudioRenderer(ServiceCtx Context)
+        {
+            Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
+
+            return 0;
+        }
+
+        public long StopAudioRenderer(ServiceCtx Context)
+        {
+            Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
+
+            return 0;
+        }
+
+        public long QuerySystemEvent(ServiceCtx Context)
+        {
+            int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
+            return 0;
+        }
+
+        private AdpcmDecoderContext GetAdpcmDecoderContext(long Position, long Size)
+        {
+            if (Size == 0)
+            {
+                return null;
+            }
+
+            AdpcmDecoderContext Context = new AdpcmDecoderContext();
+
+            Context.Coefficients = new short[Size >> 1];
+
+            for (int Offset = 0; Offset < Size; Offset += 2)
+            {
+                Context.Coefficients[Offset >> 1] = Memory.ReadInt16(Position + Offset);
+            }
+
+            return Context;
+        }
+
+        private void UpdateAudio()
+        {
+            long[] Released = AudioOut.GetReleasedBuffers(Track, 2);
+
+            for (int Index = 0; Index < Released.Length; Index++)
+            {
+                AppendMixedBuffer(Released[Index]);
+            }
+        }
+
+        private void AppendMixedBuffer(long Tag)
+        {
+            int[] MixBuffer = new int[MixBufferSamplesCount * AudioConsts.HostChannelsCount];
+
+            foreach (VoiceContext Voice in Voices)
+            {
+                if (!Voice.Playing)
+                {
+                    continue;
+                }
+
+                int OutOffset = 0;
+
+                int PendingSamples = MixBufferSamplesCount;
+
+                while (PendingSamples > 0)
+                {
+                    int[] Samples = Voice.GetBufferData(Memory, PendingSamples, out int ReturnedSamples);
+
+                    if (ReturnedSamples == 0)
+                    {
+                        break;
+                    }
+
+                    PendingSamples -= ReturnedSamples;
+
+                    for (int Offset = 0; Offset < Samples.Length; Offset++)
+                    {
+                        int Sample = (int)(Samples[Offset] * Voice.Volume);
+
+                        MixBuffer[OutOffset++] += Sample;
+                    }
+                }
+            }
+
+            AudioOut.AppendBuffer(Track, Tag, GetFinalBuffer(MixBuffer));
+        }
+
+        private static short[] GetFinalBuffer(int[] Buffer)
+        {
+            short[] Output = new short[Buffer.Length];
+
+            for (int Offset = 0; Offset < Buffer.Length; Offset++)
+            {
+                Output[Offset] = DspUtils.Saturate(Buffer[Offset]);
+            }
+
+            return Output;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing)
+            {
+                UpdateEvent.Dispose();
+            }
+        }
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs
new file mode 100644
index 0000000000..b7af1d3f82
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    class MemoryPoolContext
+    {
+        public MemoryPoolOut OutStatus;
+
+        public MemoryPoolContext()
+        {
+            OutStatus.State = MemoryPoolState.Detached;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs
new file mode 100644
index 0000000000..c852b5198a
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs
@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 4)]
+    struct MemoryPoolIn
+    {
+        public long            Address;
+        public long            Size;
+        public MemoryPoolState State;
+        public int             Unknown14;
+        public long            Unknown18;
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs
new file mode 100644
index 0000000000..dd65df8641
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
+    struct MemoryPoolOut
+    {
+        public MemoryPoolState State;
+        public int             Unknown14;
+        public long            Unknown18;
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs
similarity index 80%
rename from Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs
rename to Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs
index 892cde49e3..f96a0c09d7 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
 {
     enum MemoryPoolState : int
     {
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs
new file mode 100644
index 0000000000..e8bcf64f3f
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    enum PlayState : byte
+    {
+        Playing = 0,
+        Stopped = 1,
+        Paused  = 2
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs
new file mode 100644
index 0000000000..31e0ebecd9
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs
@@ -0,0 +1,191 @@
+using System;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    static class Resampler
+    {
+#region "LookUp Tables"
+        private static short[] CurveLut0 = new short[]
+        {
+            6600,  19426, 6722,  3,     6479,  19424, 6845,  9,     6359,  19419, 6968,  15,    6239,  19412, 7093,  22,
+            6121,  19403, 7219,  28,    6004,  19391, 7345,  34,    5888,  19377, 7472,  41,    5773,  19361, 7600,  48,
+            5659,  19342, 7728,  55,    5546,  19321, 7857,  62,    5434,  19298, 7987,  69,    5323,  19273, 8118,  77,
+            5213,  19245, 8249,  84,    5104,  19215, 8381,  92,    4997,  19183, 8513,  101,   4890,  19148, 8646,  109,
+            4785,  19112, 8780,  118,   4681,  19073, 8914,  127,   4579,  19031, 9048,  137,   4477,  18988, 9183,  147,
+            4377,  18942, 9318,  157,   4277,  18895, 9454,  168,   4179,  18845, 9590,  179,   4083,  18793, 9726,  190,
+            3987,  18738, 9863,  202,   3893,  18682, 10000, 215,   3800,  18624, 10137, 228,   3709,  18563, 10274, 241,
+            3618,  18500, 10411, 255,   3529,  18436, 10549, 270,   3441,  18369, 10687, 285,   3355,  18300, 10824, 300,
+            3269,  18230, 10962, 317,   3186,  18157, 11100, 334,   3103,  18082, 11238, 351,   3022,  18006, 11375, 369,
+            2942,  17927, 11513, 388,   2863,  17847, 11650, 408,   2785,  17765, 11788, 428,   2709,  17681, 11925, 449,
+            2635,  17595, 12062, 471,   2561,  17507, 12198, 494,   2489,  17418, 12334, 517,   2418,  17327, 12470, 541,
+            2348,  17234, 12606, 566,   2280,  17140, 12741, 592,   2213,  17044, 12876, 619,   2147,  16946, 13010, 647,
+            2083,  16846, 13144, 675,   2020,  16745, 13277, 704,   1958,  16643, 13409, 735,   1897,  16539, 13541, 766,
+            1838,  16434, 13673, 798,   1780,  16327, 13803, 832,   1723,  16218, 13933, 866,   1667,  16109, 14062, 901,
+            1613,  15998, 14191, 937,   1560,  15885, 14318, 975,   1508,  15772, 14445, 1013,  1457,  15657, 14571, 1052,
+            1407,  15540, 14695, 1093,  1359,  15423, 14819, 1134,  1312,  15304, 14942, 1177,  1266,  15185, 15064, 1221,
+            1221,  15064, 15185, 1266,  1177,  14942, 15304, 1312,  1134,  14819, 15423, 1359,  1093,  14695, 15540, 1407,
+            1052,  14571, 15657, 1457,  1013,  14445, 15772, 1508,  975,   14318, 15885, 1560,  937,   14191, 15998, 1613,
+            901,   14062, 16109, 1667,  866,   13933, 16218, 1723,  832,   13803, 16327, 1780,  798,   13673, 16434, 1838,
+            766,   13541, 16539, 1897,  735,   13409, 16643, 1958,  704,   13277, 16745, 2020,  675,   13144, 16846, 2083,
+            647,   13010, 16946, 2147,  619,   12876, 17044, 2213,  592,   12741, 17140, 2280,  566,   12606, 17234, 2348,
+            541,   12470, 17327, 2418,  517,   12334, 17418, 2489,  494,   12198, 17507, 2561,  471,   12062, 17595, 2635,
+            449,   11925, 17681, 2709,  428,   11788, 17765, 2785,  408,   11650, 17847, 2863,  388,   11513, 17927, 2942,
+            369,   11375, 18006, 3022,  351,   11238, 18082, 3103,  334,   11100, 18157, 3186,  317,   10962, 18230, 3269,
+            300,   10824, 18300, 3355,  285,   10687, 18369, 3441,  270,   10549, 18436, 3529,  255,   10411, 18500, 3618,
+            241,   10274, 18563, 3709,  228,   10137, 18624, 3800,  215,   10000, 18682, 3893,  202,   9863,  18738, 3987,
+            190,   9726,  18793, 4083,  179,   9590,  18845, 4179,  168,   9454,  18895, 4277,  157,   9318,  18942, 4377,
+            147,   9183,  18988, 4477,  137,   9048,  19031, 4579,  127,   8914,  19073, 4681,  118,   8780,  19112, 4785,
+            109,   8646,  19148, 4890,  101,   8513,  19183, 4997,  92,    8381,  19215, 5104,  84,    8249,  19245, 5213,
+            77,    8118,  19273, 5323,  69,    7987,  19298, 5434,  62,    7857,  19321, 5546,  55,    7728,  19342, 5659,
+            48,    7600,  19361, 5773,  41,    7472,  19377, 5888,  34,    7345,  19391, 6004,  28,    7219,  19403, 6121,
+            22,    7093,  19412, 6239,  15,    6968,  19419, 6359,  9,     6845,  19424, 6479,  3,     6722,  19426, 6600
+        };
+
+        private static short[] CurveLut1 = new short[]
+        {
+            -68,   32639, 69,    -5,    -200,  32630, 212,   -15,   -328,  32613, 359,   -26,   -450,  32586, 512,   -36,
+            -568,  32551, 669,   -47,   -680,  32507, 832,   -58,   -788,  32454, 1000,  -69,   -891,  32393, 1174,  -80,
+            -990,  32323, 1352,  -92,   -1084, 32244, 1536,  -103,  -1173, 32157, 1724,  -115,  -1258, 32061, 1919,  -128,
+            -1338, 31956, 2118,  -140,  -1414, 31844, 2322,  -153,  -1486, 31723, 2532,  -167,  -1554, 31593, 2747,  -180,
+            -1617, 31456, 2967,  -194,  -1676, 31310, 3192,  -209,  -1732, 31157, 3422,  -224,  -1783, 30995, 3657,  -240,
+            -1830, 30826, 3897,  -256,  -1874, 30649, 4143,  -272,  -1914, 30464, 4393,  -289,  -1951, 30272, 4648,  -307,
+            -1984, 30072, 4908,  -325,  -2014, 29866, 5172,  -343,  -2040, 29652, 5442,  -362,  -2063, 29431, 5716,  -382,
+            -2083, 29203, 5994,  -403,  -2100, 28968, 6277,  -424,  -2114, 28727, 6565,  -445,  -2125, 28480, 6857,  -468,
+            -2133, 28226, 7153,  -490,  -2139, 27966, 7453,  -514,  -2142, 27700, 7758,  -538,  -2142, 27428, 8066,  -563,
+            -2141, 27151, 8378,  -588,  -2136, 26867, 8694,  -614,  -2130, 26579, 9013,  -641,  -2121, 26285, 9336,  -668,
+            -2111, 25987, 9663,  -696,  -2098, 25683, 9993,  -724,  -2084, 25375, 10326, -753,  -2067, 25063, 10662, -783,
+            -2049, 24746, 11000, -813,  -2030, 24425, 11342, -844,  -2009, 24100, 11686, -875,  -1986, 23771, 12033, -907,
+            -1962, 23438, 12382, -939,  -1937, 23103, 12733, -972,  -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039,
+            -1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764, 21027, 14877, -1176,
+            -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959, 15965, -1282, -1633, 19600, 16329, -1317,
+            -1599, 19239, 16694, -1353, -1564, 18878, 17058, -1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459,
+            -1459, 17787, 18151, -1495, -1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599,
+            -1317, 16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239, 20673, -1732,
+            -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729, -1825, -1072, 13798, 22077, -1855,
+            -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911, -972,  12733, 23103, -1937, -939,  12382, 23438, -1962,
+            -907,  12033, 23771, -1986, -875,  11686, 24100, -2009, -844,  11342, 24425, -2030, -813,  11000, 24746, -2049,
+            -783,  10662, 25063, -2067, -753,  10326, 25375, -2084, -724,  9993,  25683, -2098, -696,  9663,  25987, -2111,
+            -668,  9336,  26285, -2121, -641,  9013,  26579, -2130, -614,  8694,  26867, -2136, -588,  8378,  27151, -2141,
+            -563,  8066,  27428, -2142, -538,  7758,  27700, -2142, -514,  7453,  27966, -2139, -490,  7153,  28226, -2133,
+            -468,  6857,  28480, -2125, -445,  6565,  28727, -2114, -424,  6277,  28968, -2100, -403,  5994,  29203, -2083,
+            -382,  5716,  29431, -2063, -362,  5442,  29652, -2040, -343,  5172,  29866, -2014, -325,  4908,  30072, -1984,
+            -307,  4648,  30272, -1951, -289,  4393,  30464, -1914, -272,  4143,  30649, -1874, -256,  3897,  30826, -1830,
+            -240,  3657,  30995, -1783, -224,  3422,  31157, -1732, -209,  3192,  31310, -1676, -194,  2967,  31456, -1617,
+            -180,  2747,  31593, -1554, -167,  2532,  31723, -1486, -153,  2322,  31844, -1414, -140,  2118,  31956, -1338,
+            -128,  1919,  32061, -1258, -115,  1724,  32157, -1173, -103,  1536,  32244, -1084, -92,   1352,  32323, -990,
+            -80,   1174,  32393, -891,  -69,   1000,  32454, -788,  -58,   832,   32507, -680,  -47,   669,   32551, -568,
+            -36,   512,   32586, -450,  -26,   359,   32613, -328,  -15,   212,   32630, -200,  -5,    69,    32639, -68
+        };
+
+        private static short[] CurveLut2 = new short[]
+        {
+            3195,  26287, 3329,  -32,   3064,  26281, 3467,  -34,   2936,  26270, 3608,  -38,   2811,  26253, 3751,  -42,
+            2688,  26230, 3897,  -46,   2568,  26202, 4046,  -50,   2451,  26169, 4199,  -54,   2338,  26130, 4354,  -58,
+            2227,  26085, 4512,  -63,   2120,  26035, 4673,  -67,   2015,  25980, 4837,  -72,   1912,  25919, 5004,  -76,
+            1813,  25852, 5174,  -81,   1716,  25780, 5347,  -87,   1622,  25704, 5522,  -92,   1531,  25621, 5701,  -98,
+            1442,  25533, 5882,  -103,  1357,  25440, 6066,  -109,  1274,  25342, 6253,  -115,  1193,  25239, 6442,  -121,
+            1115,  25131, 6635,  -127,  1040,  25018, 6830,  -133,  967,   24899, 7027,  -140,  897,   24776, 7227,  -146,
+            829,   24648, 7430,  -153,  764,   24516, 7635,  -159,  701,   24379, 7842,  -166,  641,   24237, 8052,  -174,
+            583,   24091, 8264,  -181,  526,   23940, 8478,  -187,  472,   23785, 8695,  -194,  420,   23626, 8914,  -202,
+            371,   23462, 9135,  -209,  324,   23295, 9358,  -215,  279,   23123, 9583,  -222,  236,   22948, 9809,  -230,
+            194,   22769, 10038, -237,  154,   22586, 10269, -243,  117,   22399, 10501, -250,  81,    22208, 10735, -258,
+            47,    22015, 10970, -265,  15,    21818, 11206, -271,  -16,   21618, 11444, -277,  -44,   21415, 11684, -283,
+            -71,   21208, 11924, -290,  -97,   20999, 12166, -296,  -121,  20786, 12409, -302,  -143,  20571, 12653, -306,
+            -163,  20354, 12898, -311,  -183,  20134, 13143, -316,  -201,  19911, 13389, -321,  -218,  19686, 13635, -325,
+            -234,  19459, 13882, -328,  -248,  19230, 14130, -332,  -261,  18998, 14377, -335,  -273,  18765, 14625, -337,
+            -284,  18531, 14873, -339,  -294,  18295, 15121, -341,  -302,  18057, 15369, -341,  -310,  17817, 15617, -341,
+            -317,  17577, 15864, -340,  -323,  17335, 16111, -340,  -328,  17092, 16357, -338,  -332,  16848, 16603, -336,
+            -336,  16603, 16848, -332,  -338,  16357, 17092, -328,  -340,  16111, 17335, -323,  -340,  15864, 17577, -317,
+            -341,  15617, 17817, -310,  -341,  15369, 18057, -302,  -341,  15121, 18295, -294,  -339,  14873, 18531, -284,
+            -337,  14625, 18765, -273,  -335,  14377, 18998, -261,  -332,  14130, 19230, -248,  -328,  13882, 19459, -234,
+            -325,  13635, 19686, -218,  -321,  13389, 19911, -201,  -316,  13143, 20134, -183,  -311,  12898, 20354, -163,
+            -306,  12653, 20571, -143,  -302,  12409, 20786, -121,  -296,  12166, 20999, -97,   -290,  11924, 21208, -71,
+            -283,  11684, 21415, -44,   -277,  11444, 21618, -16,   -271,  11206, 21818, 15,    -265,  10970, 22015, 47,
+            -258,  10735, 22208, 81,    -250,  10501, 22399, 117,   -243,  10269, 22586, 154,   -237,  10038, 22769, 194,
+            -230,  9809,  22948, 236,   -222,  9583,  23123, 279,   -215,  9358,  23295, 324,   -209,  9135,  23462, 371,
+            -202,  8914,  23626, 420,   -194,  8695,  23785, 472,   -187,  8478,  23940, 526,   -181,  8264,  24091, 583,
+            -174,  8052,  24237, 641,   -166,  7842,  24379, 701,   -159,  7635,  24516, 764,   -153,  7430,  24648, 829,
+            -146,  7227,  24776, 897,   -140,  7027,  24899, 967,   -133,  6830,  25018, 1040,  -127,  6635,  25131, 1115,
+            -121,  6442,  25239, 1193,  -115,  6253,  25342, 1274,  -109,  6066,  25440, 1357,  -103,  5882,  25533, 1442,
+            -98,   5701,  25621, 1531,  -92,   5522,  25704, 1622,  -87,   5347,  25780, 1716,  -81,   5174,  25852, 1813,
+            -76,   5004,  25919, 1912,  -72,   4837,  25980, 2015,  -67,   4673,  26035, 2120,  -63,   4512,  26085, 2227,
+            -58,   4354,  26130, 2338,  -54,   4199,  26169, 2451,  -50,   4046,  26202, 2568,  -46,   3897,  26230, 2688,
+            -42,   3751,  26253, 2811,  -38,   3608,  26270, 2936,  -34,   3467,  26281, 3064,  -32,   3329,  26287, 3195
+        };
+#endregion
+
+        public static int[] Resample2Ch(
+            int[]   Buffer,
+            int     SrcSampleRate,
+            int     DstSampleRate,
+            int     SamplesCount,
+            ref int FracPart)
+        {
+            if (Buffer == null)
+            {
+                throw new ArgumentNullException(nameof(Buffer));
+            }
+
+            if (SrcSampleRate <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(SrcSampleRate));
+            }
+
+            if (DstSampleRate <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(DstSampleRate));
+            }
+
+            double Ratio = (double)SrcSampleRate / DstSampleRate;
+
+            int NewSamplesCount = (int)(SamplesCount / Ratio);
+
+            int Step = (int)(Ratio * 0x8000);
+
+            int[] Output = new int[NewSamplesCount * 2];
+
+            short[] Lut;
+
+            if (Step > 0xaaaa)
+            {
+                Lut = CurveLut0;
+            }
+            else if (Step <= 0x8000)
+            {
+                Lut = CurveLut1;
+            }
+            else
+            {
+                Lut = CurveLut2;
+            }
+
+            int InOffs = 0;
+
+            for (int OutOffs = 0; OutOffs < Output.Length; OutOffs += 2)
+            {
+                int LutIndex = (FracPart >> 8) * 4;
+
+                int Sample0 = Buffer[(InOffs + 0) * 2 + 0] * Lut[LutIndex + 0] +
+                              Buffer[(InOffs + 1) * 2 + 0] * Lut[LutIndex + 1] +
+                              Buffer[(InOffs + 2) * 2 + 0] * Lut[LutIndex + 2] +
+                              Buffer[(InOffs + 3) * 2 + 0] * Lut[LutIndex + 3];
+
+                int Sample1 = Buffer[(InOffs + 0) * 2 + 1] * Lut[LutIndex + 0] +
+                              Buffer[(InOffs + 1) * 2 + 1] * Lut[LutIndex + 1] +
+                              Buffer[(InOffs + 2) * 2 + 1] * Lut[LutIndex + 2] +
+                              Buffer[(InOffs + 3) * 2 + 1] * Lut[LutIndex + 3];
+
+                int NewOffset = FracPart + Step;
+
+                InOffs += NewOffset >> 15;
+
+                FracPart = NewOffset & 0x7fff;
+
+                Output[OutOffs + 0] = Sample0 >> 15;
+                Output[OutOffs + 1] = Sample1 >> 15;
+            }
+
+            return Output;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs
similarity index 65%
rename from Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs
rename to Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs
index f944b302e0..a6dfbc0bce 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs
@@ -1,15 +1,15 @@
-namespace Ryujinx.HLE.OsHle.Services.Aud
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
 {
     struct UpdateDataHeader
     {
         public int Revision;
         public int BehaviorSize;
-        public int MemoryPoolsSize;
-        public int VoicesSize;
+        public int MemoryPoolSize;
+        public int VoiceSize;
         public int VoiceResourceSize;
-        public int EffectsSize;
-        public int MixesSize;
-        public int SinksSize;
+        public int EffectSize;
+        public int MixeSize;
+        public int SinkSize;
         public int PerformanceManagerSize;
         public int Unknown24;
         public int Unknown28;
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs
new file mode 100644
index 0000000000..0916b03e2e
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x70, Pack = 1)]
+    struct VoiceChannelResourceIn
+    {
+        //???
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs
new file mode 100644
index 0000000000..1bf9ed73e8
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs
@@ -0,0 +1,188 @@
+using ChocolArm64.Memory;
+using Ryujinx.Audio.Adpcm;
+using System;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    class VoiceContext
+    {
+        private bool Acquired;
+        private bool BufferReload;
+
+        private int ResamplerFracPart;
+
+        private int BufferIndex;
+        private int Offset;
+
+        public int SampleRate;
+        public int ChannelsCount;
+
+        public float Volume;
+
+        public PlayState PlayState;
+
+        public SampleFormat SampleFormat;
+
+        public AdpcmDecoderContext AdpcmCtx;
+
+        public WaveBuffer[] WaveBuffers;
+
+        public VoiceOut OutStatus;
+
+        private int[] Samples;
+
+        public bool Playing => Acquired && PlayState == PlayState.Playing;
+
+        public VoiceContext()
+        {
+            WaveBuffers = new WaveBuffer[4];
+        }
+
+        public void SetAcquireState(bool NewState)
+        {
+            if (Acquired && !NewState)
+            {
+                //Release.
+                Reset();
+            }
+
+            Acquired = NewState;
+        }
+
+        private void Reset()
+        {
+            BufferReload = true;
+
+            BufferIndex = 0;
+            Offset      = 0;
+
+            OutStatus.PlayedSamplesCount     = 0;
+            OutStatus.PlayedWaveBuffersCount = 0;
+            OutStatus.VoiceDropsCount        = 0;
+        }
+
+        public int[] GetBufferData(AMemory Memory, int MaxSamples, out int SamplesCount)
+        {
+            if (!Playing)
+            {
+                SamplesCount = 0;
+
+                return null;
+            }
+
+            if (BufferReload)
+            {
+                BufferReload = false;
+
+                UpdateBuffer(Memory);
+            }
+
+            WaveBuffer Wb = WaveBuffers[BufferIndex];
+
+            int MaxSize = Samples.Length - Offset;
+
+            int Size = MaxSamples * AudioConsts.HostChannelsCount;
+
+            if (Size > MaxSize)
+            {
+                Size = MaxSize;
+            }
+
+            int[] Output = new int[Size];
+
+            Array.Copy(Samples, Offset, Output, 0, Size);
+
+            SamplesCount = Size / AudioConsts.HostChannelsCount;
+
+            OutStatus.PlayedSamplesCount += SamplesCount;
+
+            Offset += Size;
+
+            if (Offset == Samples.Length)
+            {
+                Offset = 0;
+
+                if (Wb.Looping == 0)
+                {
+                    SetBufferIndex((BufferIndex + 1) & 3);
+                }
+
+                OutStatus.PlayedWaveBuffersCount++;
+
+                if (Wb.LastBuffer != 0)
+                {
+                    PlayState = PlayState.Paused;
+                }
+            }
+
+            return Output;
+        }
+
+        private void UpdateBuffer(AMemory Memory)
+        {
+            //TODO: Implement conversion for formats other
+            //than interleaved stereo (2 channels).
+            //As of now, it assumes that HostChannelsCount == 2.
+            WaveBuffer Wb = WaveBuffers[BufferIndex];
+
+            if (SampleFormat == SampleFormat.PcmInt16)
+            {
+                int SamplesCount = (int)(Wb.Size / (sizeof(short) * ChannelsCount));
+
+                Samples = new int[SamplesCount * AudioConsts.HostChannelsCount];
+
+                if (ChannelsCount == 1)
+                {
+                    for (int Index = 0; Index < SamplesCount; Index++)
+                    {
+                        short Sample = Memory.ReadInt16(Wb.Position + Index * 2);
+
+                        Samples[Index * 2 + 0] = Sample;
+                        Samples[Index * 2 + 1] = Sample;
+                    }
+                }
+                else
+                {
+                    for (int Index = 0; Index < SamplesCount * 2; Index++)
+                    {
+                        Samples[Index] = Memory.ReadInt16(Wb.Position + Index * 2);
+                    }
+                }
+            }
+            else if (SampleFormat == SampleFormat.Adpcm)
+            {
+                byte[] Buffer = Memory.ReadBytes(Wb.Position, Wb.Size);
+
+                Samples = AdpcmDecoder.Decode(Buffer, AdpcmCtx);
+            }
+            else
+            {
+                throw new InvalidOperationException();
+            }
+
+            if (SampleRate != AudioConsts.HostSampleRate)
+            {
+                //TODO: We should keep the frames being discarded (see the 4 below)
+                //on a buffer and include it on the next samples buffer, to allow
+                //the resampler to do seamless interpolation between wave buffers.
+                int SamplesCount = Samples.Length / AudioConsts.HostChannelsCount;
+
+                SamplesCount = Math.Max(SamplesCount - 4, 0);
+
+                Samples = Resampler.Resample2Ch(
+                    Samples,
+                    SampleRate,
+                    AudioConsts.HostSampleRate,
+                    SamplesCount,
+                    ref ResamplerFracPart);
+            }
+        }
+
+        public void SetBufferIndex(int Index)
+        {
+            BufferIndex = Index & 3;
+
+            BufferReload = true;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs
new file mode 100644
index 0000000000..790affb20e
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs
@@ -0,0 +1,49 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)]
+    struct VoiceIn
+    {
+        public int VoiceSlot;
+        public int NodeId;
+
+        public byte FirstUpdate;
+        public byte Acquired;
+
+        public PlayState PlayState;
+
+        public SampleFormat SampleFormat;
+
+        public int SampleRate;
+
+        public int Priority;
+
+        public int Unknown14;
+
+        public int ChannelsCount;
+
+        public float Pitch;
+        public float Volume;
+
+        public BiquadFilter BiquadFilter0;
+        public BiquadFilter BiquadFilter1;
+
+        public int AppendedWaveBuffersCount;
+
+        public int BaseWaveBufferIndex;
+
+        public int Unknown44;
+
+        public long AdpcmCoeffsPosition;
+        public long AdpcmCoeffsSize;
+
+        public int VoiceDestination;
+        public int Padding;
+
+        public WaveBuffer WaveBuffer0;
+        public WaveBuffer WaveBuffer1;
+        public WaveBuffer WaveBuffer2;
+        public WaveBuffer WaveBuffer3;
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs
new file mode 100644
index 0000000000..1fcf929f27
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
+    struct VoiceOut
+    {
+        public long PlayedSamplesCount;
+        public int  PlayedWaveBuffersCount;
+        public int  VoiceDropsCount; //?
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs
new file mode 100644
index 0000000000..6b56b9081a
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs
@@ -0,0 +1,20 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)]
+    struct WaveBuffer
+    {
+        public long  Position;
+        public long  Size;
+        public int   FirstSampleOffset;
+        public int   LastSampleOffset;
+        public byte  Looping;
+        public byte  LastBuffer;
+        public short Unknown1A;
+        public int   Unknown1C;
+        public long  AdpcmLoopContextPosition;
+        public long  AdpcmLoopContextSize;
+        public long  Unknown30;
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs
index 0a0792ec5b..d7e1df01ac 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs
@@ -8,14 +8,14 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
         public int SampleRate;
         public int SampleCount;
         public int Unknown8;
-        public int UnknownC;
+        public int MixCount;
         public int VoiceCount;
         public int SinkCount;
         public int EffectCount;
-        public int Unknown1C;
-        public int Unknown20;
+        public int PerformanceManagerCount;
+        public int VoiceDropEnable;
         public int SplitterCount;
-        public int Unknown28;
+        public int SplitterDestinationDataCount;
         public int Unknown2C;
         public int Revision;
     }
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs
index 8c78d1d493..7a3bc4d4fa 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs
@@ -3,6 +3,7 @@ using Ryujinx.Audio;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Handles;
 using Ryujinx.HLE.OsHle.Ipc;
+using Ryujinx.HLE.OsHle.Services.Aud.AudioOut;
 using System.Collections.Generic;
 using System.Text;
 
@@ -154,13 +155,13 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
 
             IAalOutput AudioOut = Context.Ns.AudioOut;
 
-            int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback, out AudioFormat Format);
+            int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback);
 
             MakeObject(Context, new IAudioOut(AudioOut, ReleaseEvent, Track));
 
             Context.ResponseData.Write(SampleRate);
             Context.ResponseData.Write(Channels);
-            Context.ResponseData.Write((int)Format);
+            Context.ResponseData.Write((int)SampleFormat.PcmInt16);
             Context.ResponseData.Write((int)PlaybackState.Stopped);
 
             return 0;
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs
deleted file mode 100644
index bd9188c341..0000000000
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.HLE.Logging;
-using Ryujinx.HLE.OsHle.Handles;
-using Ryujinx.HLE.OsHle.Ipc;
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.OsHle.Services.Aud
-{
-    class IAudioRenderer : IpcService, IDisposable
-    {
-        private Dictionary<int, ServiceProcessRequest> m_Commands;
-
-        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
-
-        private KEvent UpdateEvent;
-
-        private AudioRendererParameter Params;
-
-        public IAudioRenderer(AudioRendererParameter Params)
-        {
-            m_Commands = new Dictionary<int, ServiceProcessRequest>()
-            {
-                { 4, RequestUpdateAudioRenderer },
-                { 5, StartAudioRenderer         },
-                { 6, StopAudioRenderer          },
-                { 7, QuerySystemEvent           }
-            };
-
-            UpdateEvent = new KEvent();
-
-            this.Params = Params;
-        }
-
-        public long RequestUpdateAudioRenderer(ServiceCtx Context)
-        {
-            long OutputPosition = Context.Request.ReceiveBuff[0].Position;
-            long OutputSize     = Context.Request.ReceiveBuff[0].Size;
-
-            AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize);
-
-            long InputPosition = Context.Request.SendBuff[0].Position;
-
-            UpdateDataHeader InputDataHeader = AMemoryHelper.Read<UpdateDataHeader>(Context.Memory, InputPosition);
-
-            UpdateDataHeader OutputDataHeader = new UpdateDataHeader();
-
-            int UpdateHeaderSize = Marshal.SizeOf<UpdateDataHeader>();
-
-            OutputDataHeader.Revision               = Params.Revision;
-            OutputDataHeader.BehaviorSize           = 0xb0;
-            OutputDataHeader.MemoryPoolsSize        = (Params.EffectCount + Params.VoiceCount * 4) * 0x10;
-            OutputDataHeader.VoicesSize             = Params.VoiceCount  * 0x10;
-            OutputDataHeader.EffectsSize            = Params.EffectCount * 0x10;
-            OutputDataHeader.SinksSize              = Params.SinkCount   * 0x20;
-            OutputDataHeader.PerformanceManagerSize = 0x10;
-            OutputDataHeader.TotalSize              = UpdateHeaderSize                 +
-                                                      OutputDataHeader.BehaviorSize    +
-                                                      OutputDataHeader.MemoryPoolsSize +
-                                                      OutputDataHeader.VoicesSize      +
-                                                      OutputDataHeader.EffectsSize     +
-                                                      OutputDataHeader.SinksSize       +
-                                                      OutputDataHeader.PerformanceManagerSize;
-
-            AMemoryHelper.Write(Context.Memory, OutputPosition, OutputDataHeader);
-
-            int InMemoryPoolOffset = UpdateHeaderSize + InputDataHeader.BehaviorSize;
-
-            int OutMemoryPoolOffset = UpdateHeaderSize;
-
-            for (int Offset = 0; Offset < OutputDataHeader.MemoryPoolsSize; Offset += 0x10, InMemoryPoolOffset += 0x20)
-            {
-                MemoryPoolState PoolState = (MemoryPoolState)Context.Memory.ReadInt32(InputPosition + InMemoryPoolOffset + 0x10);
-
-                //TODO: Figure out what the other values does.
-                if (PoolState == MemoryPoolState.RequestAttach)
-                {
-                    Context.Memory.WriteInt32(OutputPosition + OutMemoryPoolOffset + Offset, (int)MemoryPoolState.Attached);
-                }
-                else if (PoolState == MemoryPoolState.RequestDetach)
-                {
-                    Context.Memory.WriteInt32(OutputPosition + OutMemoryPoolOffset + Offset, (int)MemoryPoolState.Detached);
-                }
-            }
-
-            int OutVoicesOffset = OutMemoryPoolOffset + OutputDataHeader.MemoryPoolsSize;
-
-            for (int Offset = 0; Offset < OutputDataHeader.VoicesSize; Offset += 0x10)
-            {
-                Context.Memory.WriteInt32(OutputPosition + OutVoicesOffset + Offset + 8, (int)VoicePlaybackState.Finished);
-            }
-
-            //TODO: We shouldn't be signaling this here.
-            UpdateEvent.WaitEvent.Set();
-
-            return 0;
-        }
-
-        public long StartAudioRenderer(ServiceCtx Context)
-        {
-            Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
-
-            return 0;
-        }
-
-        public long StopAudioRenderer(ServiceCtx Context)
-        {
-            Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed.");
-
-            return 0;
-        }
-
-        public long QuerySystemEvent(ServiceCtx Context)
-        {
-            int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent);
-
-            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
-
-            return 0;
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing)
-            {
-                UpdateEvent.Dispose();
-            }
-        }
-    }
-}
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs
index a7daeedd58..021c363577 100644
--- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs
@@ -1,7 +1,11 @@
+using Ryujinx.Audio;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Ipc;
+using Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer;
+using Ryujinx.HLE.OsHle.Utilities;
 using System.Collections.Generic;
-using System.Runtime.InteropServices;
+
+using static Ryujinx.HLE.OsHle.ErrorCode;
 
 namespace Ryujinx.HLE.OsHle.Services.Aud
 {
@@ -12,6 +16,10 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
                                       ('V' << 16) |
                                       ('0' << 24);
 
+        private const int Rev = 4;
+
+        public const int RevMagic = Rev0Magic + Rev;
+
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
         public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
@@ -28,76 +36,69 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
 
         public long OpenAudioRenderer(ServiceCtx Context)
         {
-            //Same buffer as GetAudioRendererWorkBufferSize is receive here.
+            IAalOutput AudioOut = Context.Ns.AudioOut;
 
-            AudioRendererParameter Params = new AudioRendererParameter();
+            AudioRendererParameter Params = GetAudioRendererParameter(Context);
 
-            Params.SampleRate    = Context.RequestData.ReadInt32();
-            Params.SampleCount   = Context.RequestData.ReadInt32();
-            Params.Unknown8      = Context.RequestData.ReadInt32();
-            Params.UnknownC      = Context.RequestData.ReadInt32();
-            Params.VoiceCount    = Context.RequestData.ReadInt32();
-            Params.SinkCount     = Context.RequestData.ReadInt32();
-            Params.EffectCount   = Context.RequestData.ReadInt32();
-            Params.Unknown1C     = Context.RequestData.ReadInt32();
-            Params.Unknown20     = Context.RequestData.ReadInt32();
-            Params.SplitterCount = Context.RequestData.ReadInt32();
-            Params.Unknown28     = Context.RequestData.ReadInt32();
-            Params.Unknown2C     = Context.RequestData.ReadInt32();
-            Params.Revision      = Context.RequestData.ReadInt32();
-
-            MakeObject(Context, new IAudioRenderer(Params));
+            MakeObject(Context, new IAudioRenderer(Context.Memory, AudioOut, Params));
 
             return 0;
         }
 
         public long GetAudioRendererWorkBufferSize(ServiceCtx Context)
         {
-            long SampleRate = Context.RequestData.ReadUInt32();
-            long Unknown4   = Context.RequestData.ReadUInt32();
-            long Unknown8   = Context.RequestData.ReadUInt32();
-            long UnknownC   = Context.RequestData.ReadUInt32();
-            long Unknown10  = Context.RequestData.ReadUInt32(); //VoiceCount
-            long Unknown14  = Context.RequestData.ReadUInt32(); //SinkCount
-            long Unknown18  = Context.RequestData.ReadUInt32(); //EffectCount
-            long Unknown1c  = Context.RequestData.ReadUInt32(); //Boolean
-            long Unknown20  = Context.RequestData.ReadUInt32(); //Not used here in FW3.0.1 - Boolean
-            long Unknown24  = Context.RequestData.ReadUInt32();
-            long Unknown28  = Context.RequestData.ReadUInt32(); //SplitterCount
-            long Unknown2c  = Context.RequestData.ReadUInt32(); //Not used here in FW3.0.1
-            int  RevMagic   = Context.RequestData.ReadInt32();
+            AudioRendererParameter Params = GetAudioRendererParameter(Context);
 
-            int Version = (RevMagic - Rev0Magic) >> 24;
+            int Revision = (Params.Revision - Rev0Magic) >> 24;
 
-            if (Version <= 3) //REV3 Max is supported
+            if (Revision <= Rev) //REV3 Max is supported
             {
-                long Size  = RoundUp(Unknown8 * 4, 64);
-                     Size += (UnknownC << 10);
-                     Size += (UnknownC + 1) * 0x940;
-                     Size += Unknown10 * 0x3F0;
-                     Size += RoundUp((UnknownC + 1) * 8, 16);
-                     Size += RoundUp(Unknown10 * 8, 16);
-                     Size += RoundUp((0x3C0 * (Unknown14 + UnknownC) + 4 * Unknown4) * (Unknown8 + 6), 64);
-                     Size += 0x2C0 * (Unknown14 + UnknownC) + 0x30 * (Unknown18 + (4 * Unknown10)) + 0x50;
+                bool IsSplitterSupported = Revision >= 3;
 
-                if (Version >= 3) //IsSplitterSupported
+                long Size;
+
+                Size  = IntUtils.AlignUp(Params.Unknown8 * 4, 64);
+                Size += Params.MixCount * 0x400;
+                Size += (Params.MixCount + 1) * 0x940;
+                Size += Params.VoiceCount * 0x3F0;
+                Size += IntUtils.AlignUp((Params.MixCount + 1) * 8, 16);
+                Size += IntUtils.AlignUp(Params.VoiceCount * 8, 16);
+                Size += IntUtils.AlignUp(
+                    ((Params.SinkCount + Params.MixCount) * 0x3C0 + Params.SampleCount * 4) *
+                    (Params.Unknown8 + 6), 64);
+                Size += (Params.SinkCount + Params.MixCount) * 0x2C0;
+                Size += (Params.EffectCount + 4 * Params.VoiceCount) * 0x30 + 0x50;
+
+                if (IsSplitterSupported)
                 {
-                    Size += RoundUp((NodeStatesGetWorkBufferSize((int)UnknownC + 1) + EdgeMatrixGetWorkBufferSize((int)UnknownC + 1)), 16);
-                    Size += 0xE0 * Unknown28 + 0x20 * Unknown24 + RoundUp(Unknown28 * 4, 16);
+                    Size += IntUtils.AlignUp((
+                        NodeStatesGetWorkBufferSize(Params.MixCount + 1) +
+                        EdgeMatrixGetWorkBufferSize(Params.MixCount + 1)), 16);
+
+                    Size += Params.SplitterDestinationDataCount * 0xE0;
+                    Size += Params.SplitterCount * 0x20;
+                    Size += IntUtils.AlignUp(Params.SplitterDestinationDataCount * 4, 16);
                 }
 
-                Size = 0x4C0 * Unknown18 + RoundUp(Size, 64) + 0x170 * Unknown14 + ((Unknown10 << 8) | 0x40);
+                Size = Params.EffectCount * 0x4C0 +
+                       Params.SinkCount * 0x170 +
+                       Params.VoiceCount * 0x100 +
+                       IntUtils.AlignUp(Size, 64) + 0x40;
 
-                if (Unknown1c >= 1)
+                if (Params.PerformanceManagerCount >= 1)
                 {
-                    Size += ((((Unknown18 + Unknown14 + Unknown10 + UnknownC + 1) * 16) + 0x658) * (Unknown1c + 1) + 0x13F) & ~0x3FL;
+                    Size += (((Params.EffectCount +
+                               Params.SinkCount +
+                               Params.VoiceCount +
+                               Params.MixCount + 1) * 16 + 0x658) *
+                               (Params.PerformanceManagerCount + 1) + 0x13F) & ~0x3FL;
                 }
 
-                long WorkBufferSize = (Size + 0x1907D) & ~0xFFFL;
+                Size = (Size + 0x1907D) & ~0xFFFL;
 
-                Context.ResponseData.Write(WorkBufferSize);
+                Context.ResponseData.Write(Size);
 
-                Context.Ns.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{WorkBufferSize:x16}.");
+                Context.Ns.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}.");
 
                 return 0;
             }
@@ -105,20 +106,36 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
             {
                 Context.ResponseData.Write(0L);
 
-                Context.Ns.Log.PrintError(LogClass.ServiceAudio, $"Library Revision 0x{RevMagic:x8} is not supported!");
+                Context.Ns.Log.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!");
 
-                return 0x499;
+                return MakeError(ErrorModule.Audio, AudErr.UnsupportedRevision);
             }
         }
 
-        private static long RoundUp(long Value, int Size)
+        private AudioRendererParameter GetAudioRendererParameter(ServiceCtx Context)
         {
-            return (Value + (Size - 1)) & ~((long)Size - 1);
+            AudioRendererParameter Params = new AudioRendererParameter();
+
+            Params.SampleRate                   = Context.RequestData.ReadInt32();
+            Params.SampleCount                  = Context.RequestData.ReadInt32();
+            Params.Unknown8                     = Context.RequestData.ReadInt32();
+            Params.MixCount                     = Context.RequestData.ReadInt32();
+            Params.VoiceCount                   = Context.RequestData.ReadInt32();
+            Params.SinkCount                    = Context.RequestData.ReadInt32();
+            Params.EffectCount                  = Context.RequestData.ReadInt32();
+            Params.PerformanceManagerCount      = Context.RequestData.ReadInt32();
+            Params.VoiceDropEnable              = Context.RequestData.ReadInt32();
+            Params.SplitterCount                = Context.RequestData.ReadInt32();
+            Params.SplitterDestinationDataCount = Context.RequestData.ReadInt32();
+            Params.Unknown2C                    = Context.RequestData.ReadInt32();
+            Params.Revision                     = Context.RequestData.ReadInt32();
+
+            return Params;
         }
 
         private static int NodeStatesGetWorkBufferSize(int Value)
         {
-            int Result = (int)RoundUp(Value, 64);
+            int Result = IntUtils.AlignUp(Value, 64);
 
             if (Result < 0)
             {
@@ -130,7 +147,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud
 
         private static int EdgeMatrixGetWorkBufferSize(int Value)
         {
-            int Result = (int)RoundUp(Value * Value, 64);
+            int Result = IntUtils.AlignUp(Value * Value, 64);
 
             if (Result < 0)
             {
diff --git a/Ryujinx.Audio/AudioFormat.cs b/Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs
similarity index 52%
rename from Ryujinx.Audio/AudioFormat.cs
rename to Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs
index 8250d1368e..06ab492996 100644
--- a/Ryujinx.Audio/AudioFormat.cs
+++ b/Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs
@@ -1,12 +1,12 @@
-namespace Ryujinx.Audio
+namespace Ryujinx.HLE.OsHle.Services.Aud
 {
-    public enum AudioFormat
+    enum SampleFormat : byte
     {
         Invalid  = 0,
         PcmInt8  = 1,
         PcmInt16 = 2,
-        PcmImt24 = 3,
-        PcmImt32 = 4,
+        PcmInt24 = 3,
+        PcmInt32 = 4,
         PcmFloat = 5,
         Adpcm    = 6
     }
diff --git a/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs b/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs
deleted file mode 100644
index 8b34332392..0000000000
--- a/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.OsHle.Services.Aud
-{
-    enum VoicePlaybackState : int
-    {
-        Playing  = 0,
-        Finished = 1,
-        Paused   = 2
-    }
-}
diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
index ec10a37588..4c6107f8f8 100644
--- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
+++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
@@ -48,7 +48,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
                 return NvResult.InvalidInput;
             }
 
-            int Size = IntUtils.RoundUp(Args.Size, NvGpuVmm.PageSize);
+            int Size = IntUtils.AlignUp(Args.Size, NvGpuVmm.PageSize);
 
             Args.Handle = AddNvMap(Context, new NvMapHandle(Size));
 
@@ -121,7 +121,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
                 Map.Align =       Args.Align;
                 Map.Kind  = (byte)Args.Kind;
 
-                int Size = IntUtils.RoundUp(Map.Size, NvGpuVmm.PageSize);
+                int Size = IntUtils.AlignUp(Map.Size, NvGpuVmm.PageSize);
 
                 long Address = Args.Address;
 
diff --git a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
index ba0726c317..39cced280a 100644
--- a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
+++ b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
@@ -2,12 +2,12 @@ namespace Ryujinx.HLE.OsHle.Utilities
 {
     static class IntUtils
     {
-        public static int RoundUp(int Value, int Size)
+        public static int AlignUp(int Value, int Size)
         {
             return (Value + (Size - 1)) & ~(Size - 1);
         }
 
-        public static long RoundUp(long Value, int Size)
+        public static long AlignUp(long Value, int Size)
         {
             return (Value + (Size - 1)) & ~((long)Size - 1);
         }
diff --git a/Ryujinx.HLE/OsHle/Utilities/StructReader.cs b/Ryujinx.HLE/OsHle/Utilities/StructReader.cs
new file mode 100644
index 0000000000..e218288b6e
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Utilities/StructReader.cs
@@ -0,0 +1,45 @@
+using ChocolArm64.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Utilities
+{
+    class StructReader
+    {
+        private AMemory Memory;
+
+        public long Position { get; private set; }
+
+        public StructReader(AMemory Memory, long Position)
+        {
+            this.Memory   = Memory;
+            this.Position = Position;
+        }
+
+        public T Read<T>() where T : struct
+        {
+            T Value = AMemoryHelper.Read<T>(Memory, Position);
+
+            Position += Marshal.SizeOf<T>();
+
+            return Value;
+        }
+
+        public T[] Read<T>(int Size) where T : struct
+        {
+            int StructSize = Marshal.SizeOf<T>();
+
+            int Count = Size / StructSize;
+
+            T[] Output = new T[Count];
+
+            for (int Index = 0; Index < Count; Index++)
+            {
+                Output[Index] = AMemoryHelper.Read<T>(Memory, Position);
+
+                Position += StructSize;
+            }
+
+            return Output;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs b/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs
new file mode 100644
index 0000000000..7daa95fb63
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs
@@ -0,0 +1,25 @@
+using ChocolArm64.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.OsHle.Utilities
+{
+    class StructWriter
+    {
+        private AMemory Memory;
+
+        public long Position { get; private set; }
+
+        public StructWriter(AMemory Memory, long Position)
+        {
+            this.Memory   = Memory;
+            this.Position = Position;
+        }
+
+        public void Write<T>(T Value) where T : struct
+        {
+            AMemoryHelper.Write(Memory, Position, Value);
+
+            Position += Marshal.SizeOf<T>();
+        }
+    }
+}