2018-03-02 04:06:05 +00:00
|
|
|
using ChocolArm64.Memory;
|
2018-02-20 20:09:23 +00:00
|
|
|
using Ryujinx.Core.OsHle.Handles;
|
|
|
|
using Ryujinx.Core.OsHle.Ipc;
|
2018-02-10 00:14:55 +00:00
|
|
|
using OpenTK.Audio;
|
|
|
|
using OpenTK.Audio.OpenAL;
|
2018-02-08 17:20:07 +00:00
|
|
|
using System;
|
2018-02-08 16:52:02 +00:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
2018-02-08 17:20:07 +00:00
|
|
|
|
2018-02-25 04:34:16 +00:00
|
|
|
namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
2018-03-04 04:41:35 +00:00
|
|
|
class IAudioOut : IIpcService, IDisposable
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
2018-02-10 00:14:55 +00:00
|
|
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
|
|
|
|
|
|
|
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
|
|
|
|
|
|
|
public IAudioOut()
|
|
|
|
{
|
|
|
|
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 }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-02-08 16:52:02 +00:00
|
|
|
enum AudioOutState
|
|
|
|
{
|
|
|
|
Started,
|
|
|
|
Stopped
|
|
|
|
};
|
|
|
|
|
|
|
|
//IAudioOut
|
2018-02-10 00:14:55 +00:00
|
|
|
private AudioOutState State = AudioOutState.Stopped;
|
2018-03-02 04:06:05 +00:00
|
|
|
private Queue<long> BufferIdQueue = new Queue<long>();
|
2018-02-08 16:52:02 +00:00
|
|
|
|
|
|
|
//OpenAL
|
2018-02-10 00:14:55 +00:00
|
|
|
private bool OpenALInstalled = true;
|
|
|
|
private AudioContext AudioCtx;
|
|
|
|
private int Source;
|
|
|
|
private int Buffer;
|
2018-02-08 16:52:02 +00:00
|
|
|
|
|
|
|
//Return State of IAudioOut
|
2018-02-10 00:14:55 +00:00
|
|
|
public long GetAudioOutState(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
Context.ResponseData.Write((int)State);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long StartAudioOut(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
if (State == AudioOutState.Stopped)
|
|
|
|
{
|
|
|
|
State = AudioOutState.Started;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
AudioCtx = new AudioContext(); //Create the audio context
|
|
|
|
}
|
2018-02-09 00:43:22 +00:00
|
|
|
catch (Exception)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
2018-02-09 00:51:13 +00:00
|
|
|
Logging.Warn("OpenAL Error! PS: Install OpenAL Core SDK!");
|
2018-02-08 16:52:02 +00:00
|
|
|
OpenALInstalled = false;
|
|
|
|
}
|
|
|
|
|
2018-03-02 04:06:05 +00:00
|
|
|
if (OpenALInstalled) AL.Listener(ALListenerf.Gain, 8.0f); //Add more gain to it
|
2018-02-08 16:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long StopAudioOut(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
if (State == AudioOutState.Started)
|
|
|
|
{
|
|
|
|
if (OpenALInstalled)
|
|
|
|
{
|
|
|
|
if (AudioCtx == null) //Needed to call the instance of AudioContext()
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
AL.SourceStop(Source);
|
|
|
|
AL.DeleteSource(Source);
|
2018-03-02 04:06:05 +00:00
|
|
|
AL.DeleteBuffers(1, ref Buffer);
|
2018-02-08 16:52:02 +00:00
|
|
|
}
|
|
|
|
State = AudioOutState.Stopped;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long AppendAudioOutBuffer(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
long BufferId = Context.RequestData.ReadInt64();
|
|
|
|
|
2018-02-16 02:19:29 +00:00
|
|
|
byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
|
2018-03-02 04:06:05 +00:00
|
|
|
|
2018-02-08 16:52:02 +00:00
|
|
|
using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
|
|
|
|
{
|
|
|
|
BinaryReader Reader = new BinaryReader(MS);
|
2018-02-16 02:19:29 +00:00
|
|
|
long PointerNextBuffer = Reader.ReadInt64();
|
|
|
|
long PointerSampleBuffer = Reader.ReadInt64();
|
|
|
|
long CapacitySampleBuffer = Reader.ReadInt64();
|
|
|
|
long SizeDataInSampleBuffer = Reader.ReadInt64();
|
|
|
|
long OffsetDataInSampleBuffer = Reader.ReadInt64();
|
2018-02-08 16:52:02 +00:00
|
|
|
|
2018-03-02 04:06:05 +00:00
|
|
|
if (SizeDataInSampleBuffer > 0)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
2018-03-02 04:06:05 +00:00
|
|
|
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;
|
2018-03-04 04:41:35 +00:00
|
|
|
|
|
|
|
EnsureAudioFinalized();
|
2018-03-02 04:06:05 +00:00
|
|
|
|
|
|
|
Source = AL.GenSource();
|
|
|
|
Buffer = AL.GenBuffer();
|
2018-02-08 16:52:02 +00:00
|
|
|
|
2018-03-02 04:06:05 +00:00
|
|
|
AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
|
|
|
|
AL.SourceQueueBuffer(Source, Buffer);
|
|
|
|
AL.SourcePlay(Source);
|
|
|
|
}
|
2018-02-08 16:52:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long RegisterBufferEvent(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
|
|
|
|
|
|
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long GetReleasedAudioOutBuffer(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
2018-03-02 04:06:05 +00:00
|
|
|
int ReleasedBuffersCount = 0;
|
2018-02-08 16:52:02 +00:00
|
|
|
|
2018-03-02 04:06:05 +00:00
|
|
|
for(int i = 0; i < BufferIdQueue.Count; i++)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
2018-03-02 04:06:05 +00:00
|
|
|
long BufferId = BufferIdQueue.Dequeue();
|
|
|
|
|
|
|
|
AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position + (8 * i), BitConverter.GetBytes(BufferId));
|
2018-02-08 16:52:02 +00:00
|
|
|
|
2018-03-02 04:06:05 +00:00
|
|
|
ReleasedBuffersCount++;
|
2018-02-08 16:52:02 +00:00
|
|
|
}
|
|
|
|
|
2018-03-02 04:06:05 +00:00
|
|
|
Context.ResponseData.Write(ReleasedBuffersCount);
|
|
|
|
|
2018-02-08 16:52:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long ContainsAudioOutBuffer(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long AppendAudioOutBuffer_ex(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 00:14:55 +00:00
|
|
|
public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context)
|
2018-02-08 16:52:02 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2018-03-04 04:41:35 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (disposing)
|
|
|
|
{
|
|
|
|
EnsureAudioFinalized();
|
|
|
|
}
|
|
|
|
}
|
2018-02-08 16:52:02 +00:00
|
|
|
}
|
|
|
|
}
|