using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; using System; using System.Buffers; namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { class AudioRendererServer : DisposableIpcService { private IAudioRenderer _impl; public AudioRendererServer(IAudioRenderer impl) { _impl = impl; } [CommandCmif(0)] // GetSampleRate() -> u32 public ResultCode GetSampleRate(ServiceCtx context) { context.ResponseData.Write(_impl.GetSampleRate()); return ResultCode.Success; } [CommandCmif(1)] // GetSampleCount() -> u32 public ResultCode GetSampleCount(ServiceCtx context) { context.ResponseData.Write(_impl.GetSampleCount()); return ResultCode.Success; } [CommandCmif(2)] // GetMixBufferCount() -> u32 public ResultCode GetMixBufferCount(ServiceCtx context) { context.ResponseData.Write(_impl.GetMixBufferCount()); return ResultCode.Success; } [CommandCmif(3)] // GetState() -> u32 public ResultCode GetState(ServiceCtx context) { context.ResponseData.Write(_impl.GetState()); return ResultCode.Success; } [CommandCmif(4)] // RequestUpdate(buffer input) // -> (buffer output, buffer performanceOutput) public ResultCode RequestUpdate(ServiceCtx context) { ulong inputPosition = context.Request.SendBuff[0].Position; ulong inputSize = context.Request.SendBuff[0].Size; ulong outputPosition = context.Request.ReceiveBuff[0].Position; ulong outputSize = context.Request.ReceiveBuff[0].Size; ulong performanceOutputPosition = context.Request.ReceiveBuff[1].Position; ulong performanceOutputSize = context.Request.ReceiveBuff[1].Size; ReadOnlyMemory input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); using (IMemoryOwner outputOwner = ByteMemoryPool.RentCleared(outputSize)) using (IMemoryOwner performanceOutputOwner = ByteMemoryPool.RentCleared(performanceOutputSize)) { Memory output = outputOwner.Memory; Memory performanceOutput = performanceOutputOwner.Memory; using MemoryHandle outputHandle = output.Pin(); using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); if (result == ResultCode.Success) { context.Memory.Write(outputPosition, output.Span); context.Memory.Write(performanceOutputPosition, performanceOutput.Span); } else { Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}"); } return result; } } [CommandCmif(5)] // Start() public ResultCode Start(ServiceCtx context) { return _impl.Start(); } [CommandCmif(6)] // Stop() public ResultCode Stop(ServiceCtx context) { return _impl.Stop(); } [CommandCmif(7)] // QuerySystemEvent() -> handle public ResultCode QuerySystemEvent(ServiceCtx context) { ResultCode result = _impl.QuerySystemEvent(out KEvent systemEvent); if (result == ResultCode.Success) { if (context.Process.HandleTable.GenerateHandle(systemEvent.ReadableEvent, out int handle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); } return result; } [CommandCmif(8)] // SetAudioRendererRenderingTimeLimit(u32 limit) public ResultCode SetAudioRendererRenderingTimeLimit(ServiceCtx context) { uint limit = context.RequestData.ReadUInt32(); _impl.SetRenderingTimeLimit(limit); return ResultCode.Success; } [CommandCmif(9)] // GetAudioRendererRenderingTimeLimit() -> u32 limit public ResultCode GetAudioRendererRenderingTimeLimit(ServiceCtx context) { uint limit = _impl.GetRenderingTimeLimit(); context.ResponseData.Write(limit); return ResultCode.Success; } [CommandCmif(10)] // 3.0.0+ // RequestUpdateAuto(buffer input) // -> (buffer output, buffer performanceOutput) public ResultCode RequestUpdateAuto(ServiceCtx context) { (ulong inputPosition, ulong inputSize) = context.Request.GetBufferType0x21(); (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(0); (ulong performanceOutputPosition, ulong performanceOutputSize) = context.Request.GetBufferType0x22(1); ReadOnlyMemory input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); Memory output = new byte[outputSize]; Memory performanceOutput = new byte[performanceOutputSize]; using MemoryHandle outputHandle = output.Pin(); using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); if (result == ResultCode.Success) { context.Memory.Write(outputPosition, output.Span); context.Memory.Write(performanceOutputPosition, performanceOutput.Span); } return result; } [CommandCmif(11)] // 3.0.0+ // ExecuteAudioRendererRendering() public ResultCode ExecuteAudioRendererRendering(ServiceCtx context) { return _impl.ExecuteAudioRendererRendering(); } [CommandCmif(12)] // 15.0.0+ // SetVoiceDropParameter(f32 voiceDropParameter) public ResultCode SetVoiceDropParameter(ServiceCtx context) { float voiceDropParameter = context.RequestData.ReadSingle(); _impl.SetVoiceDropParameter(voiceDropParameter); return ResultCode.Success; } [CommandCmif(13)] // 15.0.0+ // GetVoiceDropParameter() -> f32 voiceDropParameter public ResultCode GetVoiceDropParameter(ServiceCtx context) { float voiceDropParameter = _impl.GetVoiceDropParameter(); context.ResponseData.Write(voiceDropParameter); return ResultCode.Success; } protected override void Dispose(bool isDisposing) { if (isDisposing) { _impl.Dispose(); } } } }