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:
parent
92f47d535e
commit
79a5939734
25 changed files with 567 additions and 177 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
13
Ryujinx.Audio/AudioFormat.cs
Normal file
13
Ryujinx.Audio/AudioFormat.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Audio/IAalOutput.cs
Normal file
18
Ryujinx.Audio/IAalOutput.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
283
Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
Normal file
283
Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Audio/PlaybackState.cs
Normal file
8
Ryujinx.Audio/PlaybackState.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Audio
|
||||||
|
{
|
||||||
|
public enum PlaybackState
|
||||||
|
{
|
||||||
|
Playing = 0,
|
||||||
|
Stopped = 1
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Audio/Ryujinx.Audio.csproj
Normal file
11
Ryujinx.Audio/Ryujinx.Audio.csproj
Normal 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>
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
14
Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs
Normal file
14
Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}");
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.Core.Settings
|
namespace Ryujinx.Core.Settings
|
||||||
{
|
{
|
||||||
public class SetSys
|
public class SystemSettings
|
||||||
{
|
{
|
||||||
public ColorSet ThemeColor;
|
public ColorSet ThemeColor;
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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";
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue