Просмотр исходного кода

Basic audio support

Implement IAudioOut.
Small corrections on AudIAudioRenderer.
Add glitched audio playback support through OpenAL.
AcK77 8 лет назад
Родитель
Сommit
9f2aea4059

+ 3 - 0
README.md

@@ -8,3 +8,6 @@ Contributions are always welcome.
 
 
 To run this emulator, you need the .NET Core 2.0 (or higher) SDK.
 To run this emulator, you need the .NET Core 2.0 (or higher) SDK.
 Run `dotnet run -c Release -- game.nro` inside the Ryujinx solution folder.
 Run `dotnet run -c Release -- game.nro` inside the Ryujinx solution folder.
+
+Audio is partially supported (glitched) on Windows, you need to install the OpenAL Core SDK :
+https://openal.org/downloads/OpenAL11CoreSDK.zip

+ 12 - 0
Ryujinx/OsHle/Ipc/IpcHandler.cs

@@ -127,8 +127,20 @@ namespace Ryujinx.OsHle.Ipc
             //IAudioRenderer
             //IAudioRenderer
             { (typeof(AudIAudioRenderer), 4), AudIAudioRenderer.RequestUpdateAudioRenderer },
             { (typeof(AudIAudioRenderer), 4), AudIAudioRenderer.RequestUpdateAudioRenderer },
             { (typeof(AudIAudioRenderer), 5), AudIAudioRenderer.StartAudioRenderer         },
             { (typeof(AudIAudioRenderer), 5), AudIAudioRenderer.StartAudioRenderer         },
+            { (typeof(AudIAudioRenderer), 6), AudIAudioRenderer.StopAudioRenderer          },
             { (typeof(AudIAudioRenderer), 7), AudIAudioRenderer.QuerySystemEvent           },
             { (typeof(AudIAudioRenderer), 7), AudIAudioRenderer.QuerySystemEvent           },
 
 
+            //IAudioOut
+            { (typeof(AudIAudioOut), 0), AudIAudioOut.GetAudioOutState             },
+            { (typeof(AudIAudioOut), 1), AudIAudioOut.StartAudioOut                },
+            { (typeof(AudIAudioOut), 2), AudIAudioOut.StopAudioOut                 },
+            { (typeof(AudIAudioOut), 3), AudIAudioOut.AppendAudioOutBuffer         },
+            { (typeof(AudIAudioOut), 4), AudIAudioOut.RegisterBufferEvent          },
+            { (typeof(AudIAudioOut), 5), AudIAudioOut.GetReleasedAudioOutBuffer    },
+            { (typeof(AudIAudioOut), 6), AudIAudioOut.ContainsAudioOutBuffer       },
+            { (typeof(AudIAudioOut), 7), AudIAudioOut.AppendAudioOutBuffer_ex      },
+            { (typeof(AudIAudioOut), 8), AudIAudioOut.GetReleasedAudioOutBuffer_ex },
+
             //IFile
             //IFile
             { (typeof(FspSrvIFile), 0), FspSrvIFile.Read  },
             { (typeof(FspSrvIFile), 0), FspSrvIFile.Read  },
             { (typeof(FspSrvIFile), 1), FspSrvIFile.Write },
             { (typeof(FspSrvIFile), 1), FspSrvIFile.Write },

+ 164 - 0
Ryujinx/OsHle/Objects/AudIAudioOut.cs

@@ -0,0 +1,164 @@
+using ChocolArm64.Memory;
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+using System.Collections.Generic;
+using System.IO;
+using static Ryujinx.OsHle.Objects.ObjHelper;
+using OpenTK.Audio;
+using OpenTK.Audio.OpenAL; // https://openal.org/downloads/OpenAL11CoreSDK.zip Needed!
+using System;
+
+namespace Ryujinx.OsHle.Objects
+{
+    class AudIAudioOut
+    {
+        enum AudioOutState
+        {
+            Started,
+            Stopped
+        };
+
+        //IAudioOut
+        private static AudioOutState State = AudioOutState.Stopped;
+        private static List<long> KeysQueue = new List<long>();
+
+        //OpenAL
+        private static bool OpenALInstalled = true;
+        private static AudioContext AudioCtx;
+        private static int Source;
+        private static int Buffer;
+
+        //Return State of IAudioOut
+        public static long GetAudioOutState(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((int)State);
+
+            return 0;
+        }
+
+        public static long StartAudioOut(ServiceCtx Context)
+        {
+            if (State == AudioOutState.Stopped)
+            {
+                State = AudioOutState.Started;
+
+                try
+                { 
+                    AudioCtx = new AudioContext(); //Create the audio context
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("OpenAL Error! PS: Install OpenAL Core SDK!");
+                    OpenALInstalled = false;
+                }
+
+                if(OpenALInstalled) AL.Listener(ALListenerf.Gain, (float)8.0); //Add more gain to it
+            }
+
+            return 0;
+        }
+
+        public static 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);
+                }
+                State = AudioOutState.Stopped;
+            }
+
+            return 0;
+        }
+
+        public static long AppendAudioOutBuffer(ServiceCtx Context)
+        {
+            long BufferId = Context.RequestData.ReadInt64();
+
+            KeysQueue.Insert(0, BufferId);
+
+            byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, 0x28);
+            using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+                long PointerToSampleDataPointer = Reader.ReadInt64();
+                long PointerToSampleData = Reader.ReadInt64();
+                long CapacitySampleBuffer = Reader.ReadInt64();
+                long SizeDataSampleBuffer = Reader.ReadInt64();
+                long Unknown = Reader.ReadInt64();
+
+                byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerToSampleData, (int)SizeDataSampleBuffer);
+
+                if (OpenALInstalled)
+                {
+                    if (AudioCtx == null) //Needed to call the instance of AudioContext()
+                        return 0;
+
+                    Buffer = AL.GenBuffer();
+                    AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
+
+                    Source = AL.GenSource();
+                    AL.SourceQueueBuffer(Source, Buffer);
+                }
+            }
+
+            return 0;
+        }
+
+        public static long RegisterBufferEvent(ServiceCtx Context)
+        {
+            int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
+            return 0;
+        }
+
+        public static long GetReleasedAudioOutBuffer(ServiceCtx Context)
+        {
+            long TempKey = 0;
+
+            if(KeysQueue.Count > 0)
+            {
+                TempKey = KeysQueue[KeysQueue.Count - 1];
+                KeysQueue.Remove(KeysQueue[KeysQueue.Count - 1]);
+            }
+
+            AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, System.BitConverter.GetBytes(TempKey));
+
+            Context.ResponseData.Write((int)TempKey);
+
+            if (OpenALInstalled)
+            {
+                if (AudioCtx == null) //Needed to call the instance of AudioContext()
+                    return 0;
+
+                AL.SourcePlay(Source);
+                int[] FreeBuffers = AL.SourceUnqueueBuffers(Source, 1);
+                AL.DeleteBuffers(FreeBuffers);
+            }
+
+            return 0;
+        }
+
+        public static long ContainsAudioOutBuffer(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        public static long AppendAudioOutBuffer_ex(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        public static long GetReleasedAudioOutBuffer_ex(ServiceCtx Context)
+        {
+            return 0;
+        }
+    }
+}

+ 17 - 3
Ryujinx/OsHle/Objects/AudIAudioRenderer.cs

@@ -1,15 +1,20 @@
+using Ryujinx.OsHle.Handles;
+using Ryujinx.OsHle.Ipc;
+
 namespace Ryujinx.OsHle.Objects
 namespace Ryujinx.OsHle.Objects
 {
 {
     class AudIAudioRenderer
     class AudIAudioRenderer
     {
     {
         public static long RequestUpdateAudioRenderer(ServiceCtx Context)
         public static long RequestUpdateAudioRenderer(ServiceCtx Context)
         {
         {
+            //buffer < unknown, 5, 0 >) -> (buffer < unknown, 6, 0 >, buffer < unknown, 6, 0 >
+
             long Position = Context.Request.ReceiveBuff[0].Position;
             long Position = Context.Request.ReceiveBuff[0].Position;
 
 
             //0x40 bytes header
             //0x40 bytes header
-            Context.Memory.WriteInt32(Position + 0x4,  0xb0); //Behavior Out State Size? (note: this is the last section)
-            Context.Memory.WriteInt32(Position + 0x8,  0x18e0); //Memory Pool Out State Size?
-            Context.Memory.WriteInt32(Position + 0xc,  0x600); //Voice Out State Size?
+            Context.Memory.WriteInt32(Position + 0x4, 0xb0); //Behavior Out State Size? (note: this is the last section)
+            Context.Memory.WriteInt32(Position + 0x8, 0x18e0); //Memory Pool Out State Size?
+            Context.Memory.WriteInt32(Position + 0xc, 0x600); //Voice Out State Size?
             Context.Memory.WriteInt32(Position + 0x14, 0xe0); //Effect Out State Size?
             Context.Memory.WriteInt32(Position + 0x14, 0xe0); //Effect Out State Size?
             Context.Memory.WriteInt32(Position + 0x1c, 0x20); //Sink Out State Size?
             Context.Memory.WriteInt32(Position + 0x1c, 0x20); //Sink Out State Size?
             Context.Memory.WriteInt32(Position + 0x20, 0x10); //Performance Out State Size?
             Context.Memory.WriteInt32(Position + 0x20, 0x10); //Performance Out State Size?
@@ -28,8 +33,17 @@ namespace Ryujinx.OsHle.Objects
             return 0;
             return 0;
         }
         }
 
 
+        public static long StopAudioRenderer(ServiceCtx Context)
+        {
+            return 0;
+        }
+
         public static long QuerySystemEvent(ServiceCtx Context)
         public static long QuerySystemEvent(ServiceCtx Context)
         {
         {
+            int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
             return 0;
             return 0;
         }
         }
     }
     }

+ 15 - 4
Ryujinx/OsHle/Services/ServiceAud.cs

@@ -21,10 +21,21 @@ namespace Ryujinx.OsHle.Services
 
 
         public static long AudOutOpenAudioOut(ServiceCtx Context)
         public static long AudOutOpenAudioOut(ServiceCtx Context)
         {
         {
-            Context.ResponseData.Write(48000);
-            Context.ResponseData.Write(2);
-            Context.ResponseData.Write(2);
-            Context.ResponseData.Write(0);
+            MakeObject(Context, new AudIAudioOut());
+
+            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
 
 
             return 0;
             return 0;
         }
         }