forked from Mirror/Ryujinx
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.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace ChocolArm64.Memory
|
||||
|
@ -20,11 +22,11 @@ namespace ChocolArm64.Memory
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
|
||||
public static byte[] ReadBytes(AMemory Memory, long Position, long Size)
|
||||
{
|
||||
byte[] Data = new byte[Size];
|
||||
|
||||
for (int Offs = 0; Offs < Size; Offs++)
|
||||
for (long Offs = 0; Offs < Size; Offs++)
|
||||
{
|
||||
Data[Offs] = (byte)Memory.ReadByte(Position + Offs);
|
||||
}
|
||||
|
@ -40,11 +42,39 @@ namespace ChocolArm64.Memory
|
|||
}
|
||||
}
|
||||
|
||||
public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1)
|
||||
public unsafe static T Read<T>(AMemory Memory, long Position) where T : struct
|
||||
{
|
||||
long Size = Marshal.SizeOf<T>();
|
||||
|
||||
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Position));
|
||||
}
|
||||
|
||||
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
|
||||
|
||||
return Marshal.PtrToStructure<T>(Ptr);
|
||||
}
|
||||
|
||||
public unsafe static void Write<T>(AMemory Memory, long Position, T Value) where T : struct
|
||||
{
|
||||
long Size = Marshal.SizeOf<T>();
|
||||
|
||||
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(Position));
|
||||
}
|
||||
|
||||
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
|
||||
|
||||
Marshal.StructureToPtr<T>(Value, Ptr, false);
|
||||
}
|
||||
|
||||
public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1)
|
||||
{
|
||||
using (MemoryStream MS = new MemoryStream())
|
||||
{
|
||||
for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
|
||||
for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
|
||||
{
|
||||
byte Value = (byte)Memory.ReadByte(Position + Offs);
|
||||
|
||||
|
|
13
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 Value1 = Memory.ReadInt64(Position + 0x10);
|
||||
|
||||
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
|
||||
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,25 @@ namespace Ryujinx.Core.OsHle
|
|||
{
|
||||
lock (Services)
|
||||
{
|
||||
if (!Services.TryGetValue(Name, out IIpcService Service))
|
||||
string LookUpName;
|
||||
|
||||
//Same service with different privileges.
|
||||
if (Name.EndsWith(":a") ||
|
||||
Name.EndsWith(":m") ||
|
||||
Name.EndsWith(":s") ||
|
||||
Name.EndsWith(":su") ||
|
||||
Name.EndsWith(":u") ||
|
||||
Name.EndsWith(":u0") ||
|
||||
Name.EndsWith(":u1"))
|
||||
{
|
||||
LookUpName = Name.Substring(0, Name.IndexOf(':'));
|
||||
}
|
||||
else
|
||||
{
|
||||
LookUpName = Name;
|
||||
}
|
||||
|
||||
if (!Services.TryGetValue(LookUpName, out IIpcService Service))
|
||||
{
|
||||
switch (Name)
|
||||
{
|
||||
|
@ -76,7 +94,7 @@ namespace Ryujinx.Core.OsHle
|
|||
throw new NotImplementedException(Name);
|
||||
}
|
||||
|
||||
Services.Add(Name, Service);
|
||||
Services.Add(LookUpName, Service);
|
||||
}
|
||||
|
||||
return Service;
|
||||
|
|
14
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 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;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Core.OsHle.Handles;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using OpenTK.Audio;
|
||||
using OpenTK.Audio.OpenAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
||||
{
|
||||
|
@ -15,124 +13,64 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
|||
|
||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IAudioOut()
|
||||
private IAalOutput AudioOut;
|
||||
|
||||
private int Track;
|
||||
|
||||
public IAudioOut(IAalOutput AudioOut, int Track)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetAudioOutState },
|
||||
{ 1, StartAudioOut },
|
||||
{ 2, StopAudioOut },
|
||||
{ 3, AppendAudioOutBuffer },
|
||||
{ 4, RegisterBufferEvent },
|
||||
{ 5, GetReleasedAudioOutBuffer },
|
||||
{ 6, ContainsAudioOutBuffer },
|
||||
{ 7, AppendAudioOutBuffer_ex },
|
||||
{ 8, GetReleasedAudioOutBuffer_ex }
|
||||
{ 0, GetAudioOutState },
|
||||
{ 1, StartAudioOut },
|
||||
{ 2, StopAudioOut },
|
||||
{ 3, AppendAudioOutBuffer },
|
||||
{ 4, RegisterBufferEvent },
|
||||
{ 5, GetReleasedAudioOutBuffer },
|
||||
{ 6, ContainsAudioOutBuffer },
|
||||
{ 7, AppendAudioOutBufferEx },
|
||||
{ 8, GetReleasedAudioOutBufferEx }
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
Context.ResponseData.Write((int)State);
|
||||
Context.ResponseData.Write((int)AudioOut.GetState(Track));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long StartAudioOut(ServiceCtx Context)
|
||||
{
|
||||
if (State == AudioOutState.Stopped)
|
||||
{
|
||||
State = AudioOutState.Started;
|
||||
|
||||
try
|
||||
{
|
||||
AudioCtx = new AudioContext(); //Create the audio context
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logging.Warn("OpenAL Error! PS: Install OpenAL Core SDK!");
|
||||
OpenALInstalled = false;
|
||||
}
|
||||
|
||||
if (OpenALInstalled) AL.Listener(ALListenerf.Gain, 8.0f); //Add more gain to it
|
||||
}
|
||||
AudioOut.Start(Track);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long StopAudioOut(ServiceCtx Context)
|
||||
{
|
||||
if (State == AudioOutState.Started)
|
||||
{
|
||||
if (OpenALInstalled)
|
||||
{
|
||||
if (AudioCtx == null) //Needed to call the instance of AudioContext()
|
||||
return 0;
|
||||
|
||||
AL.SourceStop(Source);
|
||||
AL.DeleteSource(Source);
|
||||
AL.DeleteBuffers(1, ref Buffer);
|
||||
}
|
||||
State = AudioOutState.Stopped;
|
||||
}
|
||||
AudioOut.Stop(Track);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long AppendAudioOutBuffer(ServiceCtx Context)
|
||||
{
|
||||
long BufferId = Context.RequestData.ReadInt64();
|
||||
long Tag = Context.RequestData.ReadInt64();
|
||||
|
||||
byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
|
||||
AudioOutData Data = AMemoryHelper.Read<AudioOutData>(
|
||||
Context.Memory,
|
||||
Context.Request.SendBuff[0].Position);
|
||||
|
||||
byte[] Buffer = AMemoryHelper.ReadBytes(
|
||||
Context.Memory,
|
||||
Data.SampleBufferPtr,
|
||||
Data.SampleBufferSize);
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
|
||||
{
|
||||
BinaryReader Reader = new BinaryReader(MS);
|
||||
long PointerNextBuffer = Reader.ReadInt64();
|
||||
long PointerSampleBuffer = Reader.ReadInt64();
|
||||
long CapacitySampleBuffer = Reader.ReadInt64();
|
||||
long SizeDataInSampleBuffer = Reader.ReadInt64();
|
||||
long OffsetDataInSampleBuffer = Reader.ReadInt64();
|
||||
|
||||
if (SizeDataInSampleBuffer > 0)
|
||||
{
|
||||
BufferIdQueue.Enqueue(BufferId);
|
||||
|
||||
byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer);
|
||||
|
||||
if (OpenALInstalled)
|
||||
{
|
||||
if (AudioCtx == null) //Needed to call the instance of AudioContext()
|
||||
return 0;
|
||||
|
||||
EnsureAudioFinalized();
|
||||
|
||||
Source = AL.GenSource();
|
||||
Buffer = AL.GenBuffer();
|
||||
|
||||
AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
|
||||
AL.SourceQueueBuffer(Source, Buffer);
|
||||
AL.SourcePlay(Source);
|
||||
}
|
||||
}
|
||||
}
|
||||
AudioOut.AppendBuffer(Track, Tag, Buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -148,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
@ -18,7 +19,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
|||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, ListAudioOuts },
|
||||
{ 1, OpenAudioOut },
|
||||
{ 1, OpenAudioOut }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -35,21 +36,51 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
|||
|
||||
public long OpenAudioOut(ServiceCtx Context)
|
||||
{
|
||||
MakeObject(Context, new IAudioOut());
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
|||
|
||||
Process.Scheduler.Suspend(CurrThread.ProcessorId);
|
||||
|
||||
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
||||
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
|
||||
|
||||
HSession Session = Process.HandleTable.GetData<HSession>(Handle);
|
||||
|
||||
|
@ -136,7 +136,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
|||
CmdPtr,
|
||||
Handle);
|
||||
|
||||
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
||||
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
|||
long Position = (long)ThreadState.X0;
|
||||
long Size = (long)ThreadState.X1;
|
||||
|
||||
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
|
||||
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
|
||||
|
||||
Logging.Info($"SvcOutputDebugString: {Str}");
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace Ryujinx.Core.Settings
|
||||
{
|
||||
public class SetSys
|
||||
public class SystemSettings
|
||||
{
|
||||
public ColorSet ThemeColor;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Core\Ryujinx.Core.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue