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
25 changed files with 567 additions and 177 deletions

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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