diff --git a/ChocolArm64/Memory/AMemoryHelper.cs b/ChocolArm64/Memory/AMemoryHelper.cs index 219aeebf9..1e3462985 100644 --- a/ChocolArm64/Memory/AMemoryHelper.cs +++ b/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(AMemory Memory, long Position) where T : struct + { + long Size = Marshal.SizeOf(); + + if ((ulong)(Position + Size) > AMemoryMgr.AddrSize) + { + throw new ArgumentOutOfRangeException(nameof(Position)); + } + + IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position); + + return Marshal.PtrToStructure(Ptr); + } + + public unsafe static void Write(AMemory Memory, long Position, T Value) where T : struct + { + long Size = Marshal.SizeOf(); + + if ((ulong)(Position + Size) > AMemoryMgr.AddrSize) + { + throw new ArgumentOutOfRangeException(nameof(Position)); + } + + IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position); + + Marshal.StructureToPtr(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); diff --git a/Ryujinx.Audio/AudioFormat.cs b/Ryujinx.Audio/AudioFormat.cs new file mode 100644 index 000000000..8250d1368 --- /dev/null +++ b/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 + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs new file mode 100644 index 000000000..7ed0e0b6b --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs new file mode 100644 index 000000000..7cf30c182 --- /dev/null +++ b/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 Buffers; + + private Queue 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(); + + QueuedTagsQueue = new Queue(); + } + + 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 Tags = new List(); + + 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 Tracks; + + public OpenALAudioOut() + { + Context = new AudioContext(); + + Tracks = new ConcurrentDictionary(); + } + + 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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/PlaybackState.cs b/Ryujinx.Audio/PlaybackState.cs new file mode 100644 index 000000000..8b53128aa --- /dev/null +++ b/Ryujinx.Audio/PlaybackState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Audio +{ + public enum PlaybackState + { + Playing = 0, + Stopped = 1 + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj new file mode 100644 index 000000000..2e7c86fa0 --- /dev/null +++ b/Ryujinx.Audio/Ryujinx.Audio.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.0 + + + + + + + diff --git a/Ryujinx.Core/OsHle/Homebrew.cs b/Ryujinx.Core/OsHle/Homebrew.cs index 2a717ca73..873dda025 100644 --- a/Ryujinx.Core/OsHle/Homebrew.cs +++ b/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; } diff --git a/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs index d39f78db8..4eb15b9d7 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs +++ b/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); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/ServiceMgr.cs b/Ryujinx.Core/OsHle/ServiceMgr.cs index f59647afe..39f623685 100644 --- a/Ryujinx.Core/OsHle/ServiceMgr.cs +++ b/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; diff --git a/Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs b/Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs new file mode 100644 index 000000000..6b27f5291 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs index 9ebf140a2..863c9a27f 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs +++ b/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; } diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs index 2312920f6..d0528a6d3 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs +++ b/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 Commands => m_Commands; - public IAudioOut() + private IAalOutput AudioOut; + + private int Track; + + public IAudioOut(IAalOutput AudioOut, int Track) { m_Commands = new Dictionary() { - { 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 } }; + + this.AudioOut = AudioOut; + this.Track = Track; } - enum AudioOutState - { - Started, - Stopped - }; - - //IAudioOut - private AudioOutState State = AudioOutState.Stopped; - private Queue BufferIdQueue = new Queue(); - - //OpenAL - private bool OpenALInstalled = true; - private AudioContext AudioCtx; - private int Source; - private int Buffer; - - //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( + 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,62 +86,63 @@ 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; - for(int i = 0; i < BufferIdQueue.Count; i++) + uint Count = (uint)((ulong)Size >> 3); + + 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) { + long Tag = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(AudioOut.ContainsBuffer(Track, Tag) ? 1 : 0); + return 0; } - public long AppendAudioOutBuffer_ex(ServiceCtx Context) + public long AppendAudioOutBufferEx(ServiceCtx Context) { + Logging.Warn("Not implemented!"); + return 0; } - public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context) + public long GetReleasedAudioOutBufferEx(ServiceCtx Context) { + Logging.Warn("Not implemented!"); + return 0; } - private void EnsureAudioFinalized() - { - if (Source != 0 || - Buffer != 0) - { - AL.SourceStop(Source); - AL.SourceUnqueueBuffer(Buffer); - AL.DeleteSource(Source); - AL.DeleteBuffers(1, ref Buffer); - - Source = 0; - Buffer = 0; - } - } - public void Dispose() { Dispose(true); } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool Disposing) { - if (disposing) + if (Disposing) { - EnsureAudioFinalized(); + AudioOut.CloseTrack(Track); } } } diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs index eb923562b..19f23d1c7 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs +++ b/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() { { 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()); + IAalOutput AudioOut = Context.Ns.AudioOut; - 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 + 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; } diff --git a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs index da1e51e17..680e8405c 100644 --- a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs +++ b/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 { diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs index ac2100f29..b99730612 100644 --- a/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs +++ b/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); diff --git a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs index 5ee097b6f..8ef9f3c6d 100644 --- a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs +++ b/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); diff --git a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs index b24a773bf..beccbe7d3 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs +++ b/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(); } diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 671a32d36..9417473cf 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/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(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}"); diff --git a/Ryujinx.Core/Ryujinx.Core.csproj b/Ryujinx.Core/Ryujinx.Core.csproj index 7d5ad7185..b9374af1d 100644 --- a/Ryujinx.Core/Ryujinx.Core.csproj +++ b/Ryujinx.Core/Ryujinx.Core.csproj @@ -14,6 +14,7 @@ + diff --git a/Ryujinx.Core/Settings/SetSys.cs b/Ryujinx.Core/Settings/SystemSettings.cs similarity index 72% rename from Ryujinx.Core/Settings/SetSys.cs rename to Ryujinx.Core/Settings/SystemSettings.cs index d8b6eb6ef..0f56ef3ae 100644 --- a/Ryujinx.Core/Settings/SetSys.cs +++ b/Ryujinx.Core/Settings/SystemSettings.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Core.Settings { - public class SetSys + public class SystemSettings { public ColorSet ThemeColor; } diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index 487f3bdb9..92d78f453 100644 --- a/Ryujinx.Core/Switch.cs +++ b/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(); - - Hid = new Hid(); - - Statistics = new PerformanceStatistics(); - Os = new Horizon(this); + VFs = new VirtualFileSystem(); + + Settings = new SystemSettings(); + + Statistics = new PerformanceStatistics(); + + Hid = new Hid(); + Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; - - Settings = new SetSys(); } public void LoadCart(string ExeFsDir, string RomFsFile = null) diff --git a/Ryujinx.Core/VirtualFs.cs b/Ryujinx.Core/VirtualFileSystem.cs similarity index 98% rename from Ryujinx.Core/VirtualFs.cs rename to Ryujinx.Core/VirtualFileSystem.cs index c0858e0ee..1c717b2ca 100644 --- a/Ryujinx.Core/VirtualFs.cs +++ b/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"; diff --git a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs index eb893f74c..6543b1d1d 100644 --- a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs +++ b/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; diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index bc5dbe042..f2d9cafe9 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -11,6 +11,7 @@ + diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index b67e52bdc..062e8bade 100644 --- a/Ryujinx/Ui/Program.cs +++ b/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) {