Explorar o código

Improvements to audout (#58)

* Some audout refactoring and improvements

* More audio improvements

* Change ReadAsciiString to use long for the Size, avoids some casting
gdkchan %!s(int64=8) %!d(string=hai) anos
pai
achega
79a5939734

+ 34 - 4
ChocolArm64/Memory/AMemoryHelper.cs

@@ -1,4 +1,6 @@
+using System;
 using System.IO;
+using System.Runtime.InteropServices;
 using System.Text;
 
 namespace ChocolArm64.Memory
@@ -20,11 +22,11 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
+        public static byte[] ReadBytes(AMemory Memory, long Position, long Size)
         {
             byte[] Data = new byte[Size];
 
-            for (int Offs = 0; Offs < Size; Offs++)
+            for (long Offs = 0; Offs < Size; Offs++)
             {
                 Data[Offs] = (byte)Memory.ReadByte(Position + Offs);
             }
@@ -40,11 +42,39 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1)
+        public unsafe static T Read<T>(AMemory Memory, long Position) where T : struct
+        {
+            long Size = Marshal.SizeOf<T>();
+
+            if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Position));
+            }
+
+            IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
+
+            return Marshal.PtrToStructure<T>(Ptr);
+        }
+
+        public unsafe static void Write<T>(AMemory Memory, long Position, T Value) where T : struct
+        {
+            long Size = Marshal.SizeOf<T>();
+
+            if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Position));
+            }
+
+            IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
+
+            Marshal.StructureToPtr<T>(Value, Ptr, false);
+        }
+
+        public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1)
         {
             using (MemoryStream MS = new MemoryStream())
             {
-                for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
+                for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
                 {
                     byte Value = (byte)Memory.ReadByte(Position + Offs);
 

+ 13 - 0
Ryujinx.Audio/AudioFormat.cs

@@ -0,0 +1,13 @@
+namespace Ryujinx.Audio
+{
+    public enum AudioFormat
+    {
+        Invalid  = 0,
+        PcmInt8  = 1,
+        PcmInt16 = 2,
+        PcmImt24 = 3,
+        PcmImt32 = 4,
+        PcmFloat = 5,
+        Adpcm    = 6
+    }
+}

+ 18 - 0
Ryujinx.Audio/IAalOutput.cs

@@ -0,0 +1,18 @@
+namespace Ryujinx.Audio
+{
+    public interface IAalOutput
+    {
+        int OpenTrack(int SampleRate, int Channels, out AudioFormat Format);
+        void CloseTrack(int Track);
+
+        void AppendBuffer(int Track, long Tag, byte[] Buffer);        
+        bool ContainsBuffer(int Track, long Tag);
+
+        long[] GetReleasedBuffers(int Track);
+
+        void Start(int Track);
+        void Stop(int Track);
+
+        PlaybackState GetState(int Track);
+    }
+}

+ 283 - 0
Ryujinx.Audio/OpenAL/OpenALAudioOut.cs

@@ -0,0 +1,283 @@
+using OpenTK.Audio;
+using OpenTK.Audio.OpenAL;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+
+namespace Ryujinx.Audio.OpenAL
+{
+    public class OpenALAudioOut : IAalOutput
+    {
+        private const int MaxTracks = 256;
+
+        private AudioContext Context;
+
+        private class Track : IDisposable
+        {
+            public int SourceId { get; private set; }
+
+            public int SampleRate { get; private set; }
+            
+            public ALFormat Format { get; private set; }
+
+            public PlaybackState State { get; set; }
+
+            private ConcurrentDictionary<long, int> Buffers;
+
+            private Queue<long> QueuedTagsQueue;
+
+            private bool Disposed;
+
+            public Track(int SampleRate, ALFormat Format)
+            {
+                this.SampleRate = SampleRate;
+                this.Format     = Format;
+
+                State = PlaybackState.Stopped;
+
+                SourceId = AL.GenSource();
+
+                Buffers = new ConcurrentDictionary<long, int>();
+
+                QueuedTagsQueue = new Queue<long>();
+            }
+
+            public int GetBufferId(long Tag)
+            {
+                if (Disposed)
+                {
+                    throw new ObjectDisposedException(nameof(Track));
+                }
+
+                int Id = AL.GenBuffer();
+
+                Buffers.AddOrUpdate(Tag, Id, (Key, OldId) =>
+                {
+                    AL.DeleteBuffer(OldId);
+
+                    return Id;
+                });
+
+                QueuedTagsQueue.Enqueue(Tag);
+
+                return Id;
+            }
+
+            public long[] GetReleasedBuffers()
+            {
+                ClearReleased();
+
+                List<long> Tags = new List<long>();
+
+                foreach (long Tag in Buffers.Keys)
+                {
+                    if (!ContainsBuffer(Tag))
+                    {
+                        Tags.Add(Tag);
+                    }
+                }
+
+                return Tags.ToArray();
+            }
+
+            public void ClearReleased()
+            {
+                SyncQueuedTags();
+
+                AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
+
+                if (ReleasedCount > 0)
+                {
+                    AL.SourceUnqueueBuffers(SourceId, ReleasedCount);
+                }
+            }
+
+            public bool ContainsBuffer(long Tag)
+            {
+                SyncQueuedTags();
+
+                foreach (long QueuedTag in QueuedTagsQueue)
+                {
+                    if (QueuedTag == Tag)
+                    {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+            
+            private void SyncQueuedTags()
+            {
+                AL.GetSource(SourceId, ALGetSourcei.BuffersQueued,    out int QueuedCount);
+                AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
+
+                QueuedCount -= ReleasedCount;
+
+                while (QueuedTagsQueue.Count > QueuedCount)
+                {
+                    QueuedTagsQueue.Dequeue();
+                }
+            }
+
+            public void Dispose()
+            {
+                Dispose(true);
+            }
+
+            protected virtual void Dispose(bool Disposing)
+            {
+                if (Disposing && !Disposed)
+                {
+                    Disposed = true;
+
+                    AL.DeleteSource(SourceId);
+
+                    foreach (int Id in Buffers.Values)
+                    {
+                        AL.DeleteBuffer(Id);
+                    }
+                }
+            }
+        }
+
+        private ConcurrentDictionary<int, Track> Tracks;
+
+        public OpenALAudioOut()
+        {
+            Context = new AudioContext();
+
+            Tracks = new ConcurrentDictionary<int, Track>();
+        }
+
+        public int OpenTrack(int SampleRate, int Channels, out AudioFormat Format)
+        {
+            Format = AudioFormat.PcmInt16;
+
+            Track Td = new Track(SampleRate, GetALFormat(Channels, Format));
+
+            for (int Id = 0; Id < MaxTracks; Id++)
+            {
+                if (Tracks.TryAdd(Id, Td))
+                {
+                    return Id;
+                }
+            }
+
+            return -1;
+        }
+
+        private ALFormat GetALFormat(int Channels, AudioFormat Format)
+        {
+            if (Channels < 1 || Channels > 2)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Channels));
+            }
+
+            if (Channels == 1)
+            {
+                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;
+                }
+            }
+
+            throw new ArgumentException(nameof(Format));
+        }
+
+        public void CloseTrack(int Track)
+        {
+            if (Tracks.TryRemove(Track, out Track Td))
+            {
+                Td.Dispose();
+            }
+        }
+
+        public void AppendBuffer(int Track, long Tag, byte[] Buffer)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                int BufferId = Td.GetBufferId(Tag);
+
+                AL.BufferData(BufferId, Td.Format, Buffer, Buffer.Length, Td.SampleRate);
+
+                AL.SourceQueueBuffer(Td.SourceId, BufferId);
+
+                StartPlaybackIfNeeded(Td);
+            }
+        }
+
+        public bool ContainsBuffer(int Track, long Tag)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                return Td.ContainsBuffer(Tag);
+            }
+            
+            return false;
+        }
+
+        public long[] GetReleasedBuffers(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                return Td.GetReleasedBuffers();
+            }
+            
+            return null;
+        }
+
+        public void Start(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                Td.State = PlaybackState.Playing;
+
+                StartPlaybackIfNeeded(Td);
+            }
+        }
+
+        private void StartPlaybackIfNeeded(Track Td)
+        {
+            AL.GetSource(Td.SourceId, ALGetSourcei.SourceState, out int StateInt);
+
+            ALSourceState State = (ALSourceState)StateInt;
+
+            if (State != ALSourceState.Playing && Td.State == PlaybackState.Playing)
+            {
+                Td.ClearReleased();
+
+                AL.SourcePlay(Td.SourceId);
+            }
+        }
+
+        public void Stop(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                Td.State = PlaybackState.Stopped;
+
+                AL.SourceStop(Td.SourceId);
+            }
+        }
+
+        public PlaybackState GetState(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                return Td.State;
+            }
+
+            return PlaybackState.Stopped;
+        }
+    }
+}

+ 8 - 0
Ryujinx.Audio/PlaybackState.cs

@@ -0,0 +1,8 @@
+namespace Ryujinx.Audio
+{
+    public enum PlaybackState
+    {
+        Playing = 0,
+        Stopped = 1
+    }
+}

+ 11 - 0
Ryujinx.Audio/Ryujinx.Audio.csproj

@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
+  </ItemGroup>
+
+</Project>

+ 1 - 1
Ryujinx.Core/OsHle/Homebrew.cs

@@ -51,7 +51,7 @@ namespace Ryujinx.Core.OsHle
                     long Value0 = Memory.ReadInt64(Position + 0x08);
                     long Value1 = Memory.ReadInt64(Position + 0x10);
 
-                    FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
+                    FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0);
 
                     break;
                 }

+ 2 - 2
Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs

@@ -6,7 +6,7 @@ namespace Ryujinx.Core.OsHle.Ipc
     {
         public long  Position { get; private set; }
         public int   Index    { get; private set; }
-        public short Size     { get; private set; }
+        public long  Size     { get; private set; }
 
         public IpcPtrBuffDesc(BinaryReader Reader)
         {
@@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle.Ipc
             Index  = ((int)Word0 >> 0) & 0x03f;
             Index |= ((int)Word0 >> 3) & 0x1c0;
 
-            Size = (short)(Word0 >> 16);
+            Size = (ushort)(Word0 >> 16);
         }
     }
 }

+ 20 - 2
Ryujinx.Core/OsHle/ServiceMgr.cs

@@ -37,7 +37,25 @@ namespace Ryujinx.Core.OsHle
         {
             lock (Services)
             {
-                if (!Services.TryGetValue(Name, out IIpcService Service))
+                string LookUpName;
+
+                //Same service with different privileges.
+                if (Name.EndsWith(":a")  ||
+                    Name.EndsWith(":m")  ||
+                    Name.EndsWith(":s")  ||
+                    Name.EndsWith(":su") ||
+                    Name.EndsWith(":u")  ||
+                    Name.EndsWith(":u0") ||
+                    Name.EndsWith(":u1"))
+                {
+                    LookUpName = Name.Substring(0, Name.IndexOf(':'));
+                }
+                else
+                {
+                    LookUpName = Name;
+                }
+
+                if (!Services.TryGetValue(LookUpName, out IIpcService Service))
                 {
                     switch (Name)
                     {
@@ -76,7 +94,7 @@ namespace Ryujinx.Core.OsHle
                         throw new NotImplementedException(Name);
                     }
 
-                    Services.Add(Name, Service);
+                    Services.Add(LookUpName, Service);
                 }
 
                 return Service;

+ 14 - 0
Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs

@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Core.OsHle.IpcServices.Aud
+{
+    [StructLayout(LayoutKind.Sequential)]
+    struct AudioOutData
+    {
+        public long NextBufferPtr;
+        public long SampleBufferPtr;
+        public long SampleBufferCapacity;
+        public long SampleBufferSize;
+        public long SampleBufferInnerOffset;
+    }
+}

+ 1 - 1
Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs

@@ -55,7 +55,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
             long Position = Context.Request.SendBuff[0].Position;
             long Size     = Context.Request.SendBuff[0].Size;
 
-            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, (int)Size);
+            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, Size);
 
             return 0;
         }

+ 57 - 118
Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs

@@ -1,11 +1,9 @@
 using ChocolArm64.Memory;
+using Ryujinx.Audio;
 using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
-using OpenTK.Audio;
-using OpenTK.Audio.OpenAL;
 using System;
 using System.Collections.Generic;
-using System.IO;
 
 namespace Ryujinx.Core.OsHle.IpcServices.Aud
 {
@@ -15,124 +13,64 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
 
         public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public IAudioOut()
+        private IAalOutput AudioOut;
+
+        private int Track;
+
+        public IAudioOut(IAalOutput AudioOut, int Track)
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 0, GetAudioOutState             },
-                { 1, StartAudioOut                },
-                { 2, StopAudioOut                 },
-                { 3, AppendAudioOutBuffer         },
-                { 4, RegisterBufferEvent          },
-                { 5, GetReleasedAudioOutBuffer    },
-                { 6, ContainsAudioOutBuffer       },
-                { 7, AppendAudioOutBuffer_ex      },
-                { 8, GetReleasedAudioOutBuffer_ex }
+                { 0, GetAudioOutState            },
+                { 1, StartAudioOut               },
+                { 2, StopAudioOut                },
+                { 3, AppendAudioOutBuffer        },
+                { 4, RegisterBufferEvent         },
+                { 5, GetReleasedAudioOutBuffer   },
+                { 6, ContainsAudioOutBuffer      },
+                { 7, AppendAudioOutBufferEx      },
+                { 8, GetReleasedAudioOutBufferEx }
             };
-        }
-
-        enum AudioOutState
-        {
-            Started,
-            Stopped
-        };
 
-        //IAudioOut
-        private AudioOutState State = AudioOutState.Stopped;
-        private Queue<long> BufferIdQueue = new Queue<long>();
-
-        //OpenAL
-        private bool OpenALInstalled = true;
-        private AudioContext AudioCtx;
-        private int Source;
-        private int Buffer;
+            this.AudioOut = AudioOut;
+            this.Track    = Track;
+        }
 
-        //Return State of IAudioOut
         public long GetAudioOutState(ServiceCtx Context)
         {
-            Context.ResponseData.Write((int)State);
+            Context.ResponseData.Write((int)AudioOut.GetState(Track));
 
             return 0;
         }
 
         public long StartAudioOut(ServiceCtx Context)
         {
-            if (State == AudioOutState.Stopped)
-            {
-                State = AudioOutState.Started;
-
-                try
-                { 
-                    AudioCtx = new AudioContext(); //Create the audio context
-                }
-                catch (Exception)
-                {
-                    Logging.Warn("OpenAL Error! PS: Install OpenAL Core SDK!");
-                    OpenALInstalled = false;
-                }
-
-                if (OpenALInstalled) AL.Listener(ALListenerf.Gain, 8.0f); //Add more gain to it
-            }
+            AudioOut.Start(Track);
 
             return 0;
         }
 
         public long StopAudioOut(ServiceCtx Context)
         {
-            if (State == AudioOutState.Started)
-            {
-                if (OpenALInstalled)
-                { 
-                    if (AudioCtx == null) //Needed to call the instance of AudioContext()
-                        return 0;
-
-                    AL.SourceStop(Source);
-                    AL.DeleteSource(Source);
-                    AL.DeleteBuffers(1, ref Buffer);
-                }
-                State = AudioOutState.Stopped;
-            }
+            AudioOut.Stop(Track);
 
             return 0;
         }
 
         public long AppendAudioOutBuffer(ServiceCtx Context)
         {
-            long BufferId = Context.RequestData.ReadInt64();
+            long Tag = Context.RequestData.ReadInt64();
 
-            byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
+            AudioOutData Data = AMemoryHelper.Read<AudioOutData>(
+                Context.Memory,
+                Context.Request.SendBuff[0].Position);
+            
+            byte[] Buffer = AMemoryHelper.ReadBytes(
+                Context.Memory,
+                Data.SampleBufferPtr,
+                Data.SampleBufferSize);
 
-            using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
-            {
-                BinaryReader Reader = new BinaryReader(MS);
-                long PointerNextBuffer        = Reader.ReadInt64();
-                long PointerSampleBuffer      = Reader.ReadInt64();
-                long CapacitySampleBuffer     = Reader.ReadInt64();
-                long SizeDataInSampleBuffer   = Reader.ReadInt64();
-                long OffsetDataInSampleBuffer = Reader.ReadInt64();
-
-                if (SizeDataInSampleBuffer > 0)
-                {
-                    BufferIdQueue.Enqueue(BufferId);
-
-                    byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer);
-
-                    if (OpenALInstalled)
-                    {
-                        if (AudioCtx == null) //Needed to call the instance of AudioContext()
-                            return 0;
-                        
-                        EnsureAudioFinalized();
-
-                        Source = AL.GenSource();
-                        Buffer = AL.GenBuffer();
-
-                        AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
-                        AL.SourceQueueBuffer(Source, Buffer);
-                        AL.SourcePlay(Source);
-                    }
-                }
-            }
+            AudioOut.AppendBuffer(Track, Tag, Buffer);
 
             return 0;
         }
@@ -148,50 +86,51 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
 
         public long GetReleasedAudioOutBuffer(ServiceCtx Context)
         {
-            int ReleasedBuffersCount = 0;
+            long Position = Context.Request.ReceiveBuff[0].Position;
+            long Size     = Context.Request.ReceiveBuff[0].Size;
+
+            uint Count = (uint)((ulong)Size >> 3);
 
-            for(int i = 0; i < BufferIdQueue.Count; i++)
+            long[] ReleasedBuffers = AudioOut.GetReleasedBuffers(Track);
+
+            for (uint Index = 0; Index < Count; Index++)
             {
-                long BufferId = BufferIdQueue.Dequeue();
+                long Tag = 0;
 
-                AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position + (8 * i), BitConverter.GetBytes(BufferId));
+                if (Index < ReleasedBuffers.Length)
+                {
+                    Tag = ReleasedBuffers[Index];
+                }
 
-                ReleasedBuffersCount++;
+                Context.Memory.WriteInt64(Position + Index * 8, Tag);
             }
 
-            Context.ResponseData.Write(ReleasedBuffersCount);
+            Context.ResponseData.Write(ReleasedBuffers.Length);
 
             return 0;
         }
 
         public long ContainsAudioOutBuffer(ServiceCtx Context)
         {
-            return 0;
-        }
+            long Tag = Context.RequestData.ReadInt64();
+
+            Context.ResponseData.Write(AudioOut.ContainsBuffer(Track, Tag) ? 1 : 0);
 
-        public long AppendAudioOutBuffer_ex(ServiceCtx Context)
-        {
             return 0;
         }
 
-        public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context)
+        public long AppendAudioOutBufferEx(ServiceCtx Context)
         {
+            Logging.Warn("Not implemented!");
+
             return 0;
         }
 
-        private void EnsureAudioFinalized()
+        public long GetReleasedAudioOutBufferEx(ServiceCtx Context)
         {
-            if (Source != 0 ||
-                Buffer != 0)
-            {
-                AL.SourceStop(Source);
-                AL.SourceUnqueueBuffer(Buffer);
-                AL.DeleteSource(Source);
-                AL.DeleteBuffers(1, ref Buffer);
+            Logging.Warn("Not implemented!");
 
-                Source = 0;
-                Buffer = 0;
-            }
+            return 0;
         }
 
         public void Dispose()
@@ -199,11 +138,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
             Dispose(true);
         }
 
-        protected virtual void Dispose(bool disposing)
+        protected virtual void Dispose(bool Disposing)
         {
-            if (disposing)
+            if (Disposing)
             {
-                EnsureAudioFinalized();
+                AudioOut.CloseTrack(Track);
             }
         }
     }

+ 47 - 16
Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs

@@ -1,4 +1,5 @@
 using ChocolArm64.Memory;
+using Ryujinx.Audio;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 using System.Text;
@@ -18,7 +19,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
                 { 0, ListAudioOuts },
-                { 1, OpenAudioOut  },
+                { 1, OpenAudioOut  }
             };
         }
 
@@ -35,21 +36,51 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
 
         public long OpenAudioOut(ServiceCtx Context)
         {
-            MakeObject(Context, new IAudioOut());
-
-            Context.ResponseData.Write(48000); //Sample Rate
-            Context.ResponseData.Write(2); //Channel Count
-            Context.ResponseData.Write(2); //PCM Format
-            /*  
-                0 - Invalid
-                1 - INT8
-                2 - INT16
-                3 - INT24
-                4 - INT32
-                5 - PCM Float
-                6 - ADPCM
-            */
-            Context.ResponseData.Write(0); //Unknown
+            IAalOutput AudioOut = Context.Ns.AudioOut;
+
+            string DeviceName = AMemoryHelper.ReadAsciiString(
+                Context.Memory,
+                Context.Request.SendBuff[0].Position,
+                Context.Request.SendBuff[0].Size);
+
+            if (DeviceName == string.Empty)
+            {
+                DeviceName = "FIXME";
+            }
+
+            long DeviceNamePosition = Context.Request.ReceiveBuff[0].Position;
+            long DeviceNameSize     = Context.Request.ReceiveBuff[0].Size;
+
+            byte[] DeviceNameBuffer = Encoding.ASCII.GetBytes(DeviceName);
+
+            if (DeviceName.Length <= DeviceNameSize)
+            {
+                AMemoryHelper.WriteBytes(Context.Memory, DeviceNamePosition, DeviceNameBuffer);
+            }
+
+            int SampleRate = Context.RequestData.ReadInt32();
+            int Channels   = Context.RequestData.ReadInt32();
+
+            Channels = (ushort)(Channels >> 16);
+
+            if (SampleRate == 0)
+            {
+                SampleRate = 48000;
+            }
+
+            if (Channels < 1 || Channels > 2)
+            {
+                Channels = 2;
+            }
+
+            int Track = AudioOut.OpenTrack(SampleRate, Channels, out AudioFormat Format);
+
+            MakeObject(Context, new IAudioOut(AudioOut, Track));
+
+            Context.ResponseData.Write(SampleRate);
+            Context.ResponseData.Write(Channels);
+            Context.ResponseData.Write((int)Format);
+            Context.ResponseData.Write((int)PlaybackState.Stopped);
 
             return 0;
         }

+ 6 - 6
Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs

@@ -151,7 +151,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
 
             byte[] SentBuffer     = AMemoryHelper.ReadBytes(Context.Memory, 
                                                             Context.Request.SendBuff[0].Position, 
-                                                            (int)Context.Request.SendBuff[0].Size);
+                                                            Context.Request.SendBuff[0].Size);
             int SocketId          = Get32(SentBuffer, 0);
             short RequestedEvents = (short)Get16(SentBuffer, 4);
             short ReturnedEvents  = (short)Get16(SentBuffer, 6);
@@ -197,7 +197,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
             int SocketFlags   = Context.RequestData.ReadInt32();
             byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
                                                         Context.Request.SendBuff[0].Position, 
-                                                        (int)Context.Request.SendBuff[0].Size);
+                                                        Context.Request.SendBuff[0].Size);
 
             try
             {
@@ -224,10 +224,10 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
             int SocketFlags      = Context.RequestData.ReadInt32();
             byte[] SentBuffer    = AMemoryHelper.ReadBytes(Context.Memory, 
                                                            Context.Request.SendBuff[0].Position, 
-                                                           (int)Context.Request.SendBuff[0].Size);
+                                                           Context.Request.SendBuff[0].Size);
             byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
                                                            Context.Request.SendBuff[1].Position, 
-                                                           (int)Context.Request.SendBuff[1].Size);
+                                                           Context.Request.SendBuff[1].Size);
 
             if (!Sockets[SocketId].Handle.Connected)
             {
@@ -333,7 +333,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
 
             byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
                                                            Context.Request.SendBuff[0].Position, 
-                                                           (int)Context.Request.SendBuff[0].Size);
+                                                           Context.Request.SendBuff[0].Size);
 
             try
             {
@@ -358,7 +358,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
 
             byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
                                                            Context.Request.SendBuff[0].Position, 
-                                                           (int)Context.Request.SendBuff[0].Size);
+                                                           Context.Request.SendBuff[0].Size);
 
             try
             {

+ 1 - 1
Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs

@@ -62,7 +62,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
             long Offset = Context.RequestData.ReadInt64();
             long Size   = Context.RequestData.ReadInt64();
 
-            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, (int)Size);
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, Size);
 
             BaseStream.Seek(Offset, SeekOrigin.Begin);
             BaseStream.Write(Data, 0, (int)Size);

+ 1 - 1
Ryujinx.Core/OsHle/Services/Lm/ILogger.cs

@@ -54,7 +54,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Lm
             long BufferPosition = Context.Request.PtrBuff[0].Position;
             long BufferLen      = Context.Request.PtrBuff[0].Size;
 
-            byte[] LogBuffer = AMemoryHelper.ReadBytes(Context.Memory, BufferPosition, (int)BufferLen);
+            byte[] LogBuffer = AMemoryHelper.ReadBytes(Context.Memory, BufferPosition, BufferLen);
 
             MemoryStream LogMessage = new MemoryStream(LogBuffer);
             BinaryReader bReader = new BinaryReader(LogMessage);

+ 3 - 3
Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs

@@ -35,7 +35,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
             long DataPos  = Context.Request.SendBuff[0].Position;
             long DataSize = Context.Request.SendBuff[0].Size;
 
-            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, DataSize);
 
             Data = Parcel.GetParcelData(Data);
 
@@ -66,9 +66,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
             Dispose(true);
         }
 
-        protected virtual void Dispose(bool disposing)
+        protected virtual void Dispose(bool Disposing)
         {
-            if (disposing)
+            if (Disposing)
             {
                 Flinger.Dispose();
             }

+ 3 - 3
Ryujinx.Core/OsHle/Svc/SvcSystem.cs

@@ -118,7 +118,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             Process.Scheduler.Suspend(CurrThread.ProcessorId);
 
-            byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
+            byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
 
             HSession Session = Process.HandleTable.GetData<HSession>(Handle);
 
@@ -136,7 +136,7 @@ namespace Ryujinx.Core.OsHle.Svc
                     CmdPtr,
                     Handle);
 
-                byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
+                byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
 
                 ThreadState.X0 = 0;
             }
@@ -164,7 +164,7 @@ namespace Ryujinx.Core.OsHle.Svc
             long Position = (long)ThreadState.X0;
             long Size     = (long)ThreadState.X1;
 
-            string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
+            string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
 
             Logging.Info($"SvcOutputDebugString: {Str}");
 

+ 1 - 0
Ryujinx.Core/Ryujinx.Core.csproj

@@ -14,6 +14,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
+    <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
     <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
   </ItemGroup>
 

+ 1 - 1
Ryujinx.Core/Settings/SetSys.cs → Ryujinx.Core/Settings/SystemSettings.cs

@@ -1,6 +1,6 @@
 namespace Ryujinx.Core.Settings
 {
-    public class SetSys
+    public class SystemSettings
     {
         public ColorSet ThemeColor;
     }

+ 30 - 11
Ryujinx.Core/Switch.cs

@@ -1,3 +1,4 @@
+using Ryujinx.Audio;
 using Ryujinx.Core.Input;
 using Ryujinx.Core.OsHle;
 using Ryujinx.Core.Settings;
@@ -9,32 +10,50 @@ namespace Ryujinx.Core
 {
     public class Switch : IDisposable
     {
-        internal NsGpu     Gpu { get; private set; }
-        internal Horizon   Os  { get; private set; }
-        internal VirtualFs VFs { get; private set; }
+        internal IAalOutput AudioOut { get; private set; }
+
+        internal NsGpu Gpu { get; private set; }
+
+        internal Horizon Os { get; private set; }
+
+        internal VirtualFileSystem VFs { get; private set; }
+
+        public SystemSettings Settings { get; private set; }
 
-        public Hid    Hid                       { get; private set; }        
-        public SetSys Settings                  { get; private set; }
         public PerformanceStatistics Statistics { get; private set; }
 
+        public Hid Hid { get; private set; }
+
         public event EventHandler Finish;
 
-        public Switch(IGalRenderer Renderer)
+        public Switch(IGalRenderer Renderer, IAalOutput AudioOut)
         {
+            if (Renderer == null)
+            {
+                throw new ArgumentNullException(nameof(Renderer));
+            }
+
+            if (AudioOut == null)
+            {
+                throw new ArgumentNullException(nameof(AudioOut));
+            }
+
+            this.AudioOut = AudioOut;
+
             Gpu = new NsGpu(Renderer);
 
-            VFs = new VirtualFs();
+            Os = new Horizon(this);
 
-            Hid = new Hid();
+            VFs = new VirtualFileSystem();
+
+            Settings = new SystemSettings();
 
             Statistics = new PerformanceStatistics();
 
-            Os = new Horizon(this);
+            Hid = new Hid();
 
             Os.HidSharedMem.MemoryMapped   += Hid.ShMemMap;
             Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap;
-
-            Settings = new SetSys();
         }
 
         public void LoadCart(string ExeFsDir, string RomFsFile = null)

+ 1 - 1
Ryujinx.Core/VirtualFs.cs → Ryujinx.Core/VirtualFileSystem.cs

@@ -3,7 +3,7 @@ using System.IO;
 
 namespace Ryujinx.Core
 {
-    class VirtualFs : IDisposable
+    class VirtualFileSystem : IDisposable
     {
         private const string BasePath   = "RyuFs";
         private const string NandPath   = "nand";

+ 1 - 1
Ryujinx.Graphics/Gpu/NsGpuPGraph.cs

@@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu
 
                 if (Position != -1)
                 {
-                    byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, (int)Size);
+                    byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, Size);
 
                     int Stride = GetRegister(NsGpuRegister._3dVertexArray0Fetch) & 0xfff;
 

+ 1 - 0
Ryujinx/Ryujinx.csproj

@@ -11,6 +11,7 @@
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
+    <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
     <ProjectReference Include="..\Ryujinx.Core\Ryujinx.Core.csproj" />
     <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
   </ItemGroup>

+ 6 - 2
Ryujinx/Ui/Program.cs

@@ -1,4 +1,6 @@
-using Ryujinx.Core;
+using Ryujinx.Audio;
+using Ryujinx.Audio.OpenAL;
+using Ryujinx.Core;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Gal.OpenGL;
 using System;
@@ -18,7 +20,9 @@ namespace Ryujinx
 
             IGalRenderer Renderer = new OpenGLRenderer();
 
-            Switch Ns = new Switch(Renderer);
+            IAalOutput AudioOut = new OpenALAudioOut();
+
+            Switch Ns = new Switch(Renderer, AudioOut);
 
             if (args.Length == 1)
             {