Improvements to audout (#58)

* Some audout refactoring and improvements

* More audio improvements

* Change ReadAsciiString to use long for the Size, avoids some casting
This commit is contained in:
gdkchan 2018-03-15 21:06:24 -03:00 committed by GitHub
parent 92f47d535e
commit 79a5939734
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 567 additions and 177 deletions

View File

@ -1,4 +1,6 @@
using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace ChocolArm64.Memory 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]; 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); 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()) 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); byte Value = (byte)Memory.ReadByte(Position + Offs);

View File

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

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

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

View File

@ -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>

View File

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

View File

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

View File

@ -37,7 +37,25 @@ namespace Ryujinx.Core.OsHle
{ {
lock (Services) 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) switch (Name)
{ {
@ -76,7 +94,7 @@ namespace Ryujinx.Core.OsHle
throw new NotImplementedException(Name); throw new NotImplementedException(Name);
} }
Services.Add(Name, Service); Services.Add(LookUpName, Service);
} }
return Service; return Service;

View File

@ -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;
}
}

View File

@ -55,7 +55,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
long Position = Context.Request.SendBuff[0].Position; long Position = Context.Request.SendBuff[0].Position;
long Size = Context.Request.SendBuff[0].Size; 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; return 0;
} }

View File

@ -1,11 +1,9 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Audio;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Core.OsHle.IpcServices.Aud namespace Ryujinx.Core.OsHle.IpcServices.Aud
{ {
@ -15,124 +13,64 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; 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>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, GetAudioOutState }, { 0, GetAudioOutState },
{ 1, StartAudioOut }, { 1, StartAudioOut },
{ 2, StopAudioOut }, { 2, StopAudioOut },
{ 3, AppendAudioOutBuffer }, { 3, AppendAudioOutBuffer },
{ 4, RegisterBufferEvent }, { 4, RegisterBufferEvent },
{ 5, GetReleasedAudioOutBuffer }, { 5, GetReleasedAudioOutBuffer },
{ 6, ContainsAudioOutBuffer }, { 6, ContainsAudioOutBuffer },
{ 7, AppendAudioOutBuffer_ex }, { 7, AppendAudioOutBufferEx },
{ 8, GetReleasedAudioOutBuffer_ex } { 8, GetReleasedAudioOutBufferEx }
}; };
this.AudioOut = AudioOut;
this.Track = Track;
} }
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;
//Return State of IAudioOut
public long GetAudioOutState(ServiceCtx Context) public long GetAudioOutState(ServiceCtx Context)
{ {
Context.ResponseData.Write((int)State); Context.ResponseData.Write((int)AudioOut.GetState(Track));
return 0; return 0;
} }
public long StartAudioOut(ServiceCtx Context) public long StartAudioOut(ServiceCtx Context)
{ {
if (State == AudioOutState.Stopped) AudioOut.Start(Track);
{
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
}
return 0; return 0;
} }
public long StopAudioOut(ServiceCtx Context) public long StopAudioOut(ServiceCtx Context)
{ {
if (State == AudioOutState.Started) AudioOut.Stop(Track);
{
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;
}
return 0; return 0;
} }
public long AppendAudioOutBuffer(ServiceCtx Context) 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)) AudioOut.AppendBuffer(Track, Tag, Buffer);
{
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);
}
}
}
return 0; return 0;
} }
@ -148,62 +86,63 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
public long GetReleasedAudioOutBuffer(ServiceCtx Context) 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; return 0;
} }
public long ContainsAudioOutBuffer(ServiceCtx Context) public long ContainsAudioOutBuffer(ServiceCtx Context)
{ {
long Tag = Context.RequestData.ReadInt64();
Context.ResponseData.Write(AudioOut.ContainsBuffer(Track, Tag) ? 1 : 0);
return 0; return 0;
} }
public long AppendAudioOutBuffer_ex(ServiceCtx Context) public long AppendAudioOutBufferEx(ServiceCtx Context)
{ {
Logging.Warn("Not implemented!");
return 0; return 0;
} }
public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context) public long GetReleasedAudioOutBufferEx(ServiceCtx Context)
{ {
Logging.Warn("Not implemented!");
return 0; 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() public void Dispose()
{ {
Dispose(true); Dispose(true);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool Disposing)
{ {
if (disposing) if (Disposing)
{ {
EnsureAudioFinalized(); AudioOut.CloseTrack(Track);
} }
} }
} }

View File

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

View File

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

View File

@ -62,7 +62,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
long Offset = Context.RequestData.ReadInt64(); long Offset = Context.RequestData.ReadInt64();
long Size = 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.Seek(Offset, SeekOrigin.Begin);
BaseStream.Write(Data, 0, (int)Size); BaseStream.Write(Data, 0, (int)Size);

View File

@ -54,7 +54,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Lm
long BufferPosition = Context.Request.PtrBuff[0].Position; long BufferPosition = Context.Request.PtrBuff[0].Position;
long BufferLen = Context.Request.PtrBuff[0].Size; 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); MemoryStream LogMessage = new MemoryStream(LogBuffer);
BinaryReader bReader = new BinaryReader(LogMessage); BinaryReader bReader = new BinaryReader(LogMessage);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
using Ryujinx.Graphics.Gal.OpenGL; using Ryujinx.Graphics.Gal.OpenGL;
using System; using System;
@ -18,7 +20,9 @@ namespace Ryujinx
IGalRenderer Renderer = new OpenGLRenderer(); IGalRenderer Renderer = new OpenGLRenderer();
Switch Ns = new Switch(Renderer); IAalOutput AudioOut = new OpenALAudioOut();
Switch Ns = new Switch(Renderer, AudioOut);
if (args.Length == 1) if (args.Length == 1)
{ {