diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer')
7 files changed, 896 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs new file mode 100644 index 00000000..724a1e9e --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs @@ -0,0 +1,172 @@ +using Ryujinx.Audio.Renderer.Device; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + class AudioDevice : IAudioDevice + { + private VirtualDeviceSession[] _sessions; + private ulong _appletResourceId; + private int _revision; + private bool _isUsbDeviceSupported; + + private VirtualDeviceSessionRegistry _registry; + private KEvent _systemEvent; + + public AudioDevice(VirtualDeviceSessionRegistry registry, KernelContext context, ulong appletResourceId, int revision) + { + _registry = registry; + _appletResourceId = appletResourceId; + _revision = revision; + + BehaviourContext behaviourContext = new BehaviourContext(); + behaviourContext.SetUserRevision(revision); + + _isUsbDeviceSupported = behaviourContext.IsAudioUsbDeviceOutputSupported(); + _sessions = _registry.GetSessionByAppletResourceId(appletResourceId); + + // TODO: support the 3 different events correctly when we will have hot plugable audio devices. + _systemEvent = new KEvent(context); + _systemEvent.ReadableEvent.Signal(); + } + + private bool TryGetDeviceByName(out VirtualDeviceSession result, string name, bool ignoreRevLimitation = false) + { + result = null; + + foreach (VirtualDeviceSession session in _sessions) + { + if (session.Device.Name.Equals(name)) + { + if (!ignoreRevLimitation && !_isUsbDeviceSupported && session.Device.IsUsbDevice()) + { + return false; + } + + result = session; + + return true; + } + } + + return false; + } + + public string GetActiveAudioDeviceName() + { + VirtualDevice device = _registry.ActiveDevice; + + if (!_isUsbDeviceSupported && device.IsUsbDevice()) + { + device = _registry.DefaultDevice; + } + + return device.Name; + } + + public uint GetActiveChannelCount() + { + VirtualDevice device = _registry.ActiveDevice; + + if (!_isUsbDeviceSupported && device.IsUsbDevice()) + { + device = _registry.DefaultDevice; + } + + return device.ChannelCount; + } + + public ResultCode GetAudioDeviceOutputVolume(string name, out float volume) + { + if (TryGetDeviceByName(out VirtualDeviceSession result, name)) + { + volume = result.Volume; + } + else + { + volume = 0.0f; + } + + return ResultCode.Success; + } + + public ResultCode SetAudioDeviceOutputVolume(string name, float volume) + { + if (TryGetDeviceByName(out VirtualDeviceSession result, name, true)) + { + if (!_isUsbDeviceSupported && result.Device.IsUsbDevice()) + { + result = _sessions[0]; + } + + result.Volume = volume; + } + + return ResultCode.Success; + } + + public string GetActiveAudioOutputDeviceName() + { + return _registry.ActiveDevice.GetOutputDeviceName(); + } + + public string[] ListAudioDeviceName() + { + int deviceCount = _sessions.Length; + + if (!_isUsbDeviceSupported) + { + deviceCount--; + } + + string[] result = new string[deviceCount]; + + int i = 0; + + foreach (VirtualDeviceSession session in _sessions) + { + if (!_isUsbDeviceSupported && session.Device.IsUsbDevice()) + { + continue; + } + + result[i] = session.Device.Name; + + i++; + } + + return result; + } + + public string[] ListAudioOutputDeviceName() + { + int deviceCount = _sessions.Length; + + string[] result = new string[deviceCount]; + + for (int i = 0; i < deviceCount; i++) + { + result[i] = _sessions[i].Device.GetOutputDeviceName(); + } + + return result; + } + + public KEvent QueryAudioDeviceInputEvent() + { + return _systemEvent; + } + + public KEvent QueryAudioDeviceOutputEvent() + { + return _systemEvent; + } + + public KEvent QueryAudioDeviceSystemEvent() + { + return _systemEvent; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs new file mode 100644 index 00000000..e7a75121 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -0,0 +1,320 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + class AudioDeviceServer : IpcService + { + private const int AudioDeviceNameSize = 0x100; + + private IAudioDevice _impl; + + public AudioDeviceServer(IAudioDevice impl) + { + _impl = impl; + } + + [CommandCmif(0)] + // ListAudioDeviceName() -> (u32, buffer<bytes, 6>) + public ResultCode ListAudioDeviceName(ServiceCtx context) + { + string[] deviceNames = _impl.ListAudioDeviceName(); + + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; + + ulong basePosition = position; + + int count = 0; + + foreach (string name in deviceNames) + { + byte[] buffer = Encoding.ASCII.GetBytes(name); + + if ((position - basePosition) + (ulong)buffer.Length > size) + { + break; + } + + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); + + position += AudioDeviceNameSize; + count++; + } + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // SetAudioDeviceOutputVolume(f32 volume, buffer<bytes, 5> name) + public ResultCode SetAudioDeviceOutputVolume(ServiceCtx context) + { + float volume = context.RequestData.ReadSingle(); + + ulong position = context.Request.SendBuff[0].Position; + ulong size = context.Request.SendBuff[0].Size; + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); + + return _impl.SetAudioDeviceOutputVolume(deviceName, volume); + } + + [CommandCmif(2)] + // GetAudioDeviceOutputVolume(buffer<bytes, 5> name) -> f32 volume + public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context) + { + ulong position = context.Request.SendBuff[0].Position; + ulong size = context.Request.SendBuff[0].Size; + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); + + ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(volume); + } + + return result; + } + + [CommandCmif(3)] + // GetActiveAudioDeviceName() -> buffer<bytes, 6> + public ResultCode GetActiveAudioDeviceName(ServiceCtx context) + { + string name = _impl.GetActiveAudioDeviceName(); + + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; + + byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); + + if ((ulong)deviceNameBuffer.Length <= size) + { + context.Memory.Write(position, deviceNameBuffer); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + } + + return ResultCode.Success; + } + + [CommandCmif(4)] + // QueryAudioDeviceSystemEvent() -> handle<copy, event> + public ResultCode QueryAudioDeviceSystemEvent(ServiceCtx context) + { + KEvent deviceSystemEvent = _impl.QueryAudioDeviceSystemEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(5)] + // GetActiveChannelCount() -> u32 + public ResultCode GetActiveChannelCount(ServiceCtx context) + { + context.ResponseData.Write(_impl.GetActiveChannelCount()); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(6)] // 3.0.0+ + // ListAudioDeviceNameAuto() -> (u32, buffer<bytes, 0x22>) + public ResultCode ListAudioDeviceNameAuto(ServiceCtx context) + { + string[] deviceNames = _impl.ListAudioDeviceName(); + + (ulong position, ulong size) = context.Request.GetBufferType0x22(); + + ulong basePosition = position; + + int count = 0; + + foreach (string name in deviceNames) + { + byte[] buffer = Encoding.ASCII.GetBytes(name); + + if ((position - basePosition) + (ulong)buffer.Length > size) + { + break; + } + + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); + + position += AudioDeviceNameSize; + count++; + } + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + [CommandCmif(7)] // 3.0.0+ + // SetAudioDeviceOutputVolumeAuto(f32 volume, buffer<bytes, 0x21> name) + public ResultCode SetAudioDeviceOutputVolumeAuto(ServiceCtx context) + { + float volume = context.RequestData.ReadSingle(); + + (ulong position, ulong size) = context.Request.GetBufferType0x21(); + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); + + return _impl.SetAudioDeviceOutputVolume(deviceName, volume); + } + + [CommandCmif(8)] // 3.0.0+ + // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32 + public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context) + { + (ulong position, ulong size) = context.Request.GetBufferType0x21(); + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); + + ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(volume); + } + + return ResultCode.Success; + } + + [CommandCmif(10)] // 3.0.0+ + // GetActiveAudioDeviceNameAuto() -> buffer<bytes, 0x22> + public ResultCode GetActiveAudioDeviceNameAuto(ServiceCtx context) + { + string name = _impl.GetActiveAudioDeviceName(); + + (ulong position, ulong size) = context.Request.GetBufferType0x22(); + + byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0'); + + if ((ulong)deviceNameBuffer.Length <= size) + { + context.Memory.Write(position, deviceNameBuffer); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + } + + return ResultCode.Success; + } + + [CommandCmif(11)] // 3.0.0+ + // QueryAudioDeviceInputEvent() -> handle<copy, event> + public ResultCode QueryAudioDeviceInputEvent(ServiceCtx context) + { + KEvent deviceInputEvent = _impl.QueryAudioDeviceInputEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceInputEvent.ReadableEvent, out int handle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(12)] // 3.0.0+ + // QueryAudioDeviceOutputEvent() -> handle<copy, event> + public ResultCode QueryAudioDeviceOutputEvent(ServiceCtx context) + { + KEvent deviceOutputEvent = _impl.QueryAudioDeviceOutputEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceOutputEvent.ReadableEvent, out int handle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(13)] // 13.0.0+ + // GetActiveAudioOutputDeviceName() -> buffer<bytes, 6> + public ResultCode GetActiveAudioOutputDeviceName(ServiceCtx context) + { + string name = _impl.GetActiveAudioOutputDeviceName(); + + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; + + byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); + + if ((ulong)deviceNameBuffer.Length <= size) + { + context.Memory.Write(position, deviceNameBuffer); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + } + + return ResultCode.Success; + } + + [CommandCmif(14)] // 13.0.0+ + // ListAudioOutputDeviceName() -> (u32, buffer<bytes, 6>) + public ResultCode ListAudioOutputDeviceName(ServiceCtx context) + { + string[] deviceNames = _impl.ListAudioOutputDeviceName(); + + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; + + ulong basePosition = position; + + int count = 0; + + foreach (string name in deviceNames) + { + byte[] buffer = Encoding.ASCII.GetBytes(name); + + if ((position - basePosition) + (ulong)buffer.Length > size) + { + break; + } + + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); + + position += AudioDeviceNameSize; + count++; + } + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs new file mode 100644 index 00000000..55bf29ae --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs @@ -0,0 +1,25 @@ +using Ryujinx.Audio.Integration; +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + class AudioKernelEvent : IWritableEvent + { + public KEvent Event { get; } + + public AudioKernelEvent(KEvent evnt) + { + Event = evnt; + } + + public void Clear() + { + Event.WritableEvent.Clear(); + } + + public void Signal() + { + Event.WritableEvent.Signal(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs new file mode 100644 index 00000000..5b682bf8 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs @@ -0,0 +1,122 @@ +using Ryujinx.Audio.Integration; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + class AudioRenderer : IAudioRenderer + { + private AudioRenderSystem _impl; + + public AudioRenderer(AudioRenderSystem impl) + { + _impl = impl; + } + + public ResultCode ExecuteAudioRendererRendering() + { + return (ResultCode)_impl.ExecuteAudioRendererRendering(); + } + + public uint GetMixBufferCount() + { + return _impl.GetMixBufferCount(); + } + + public uint GetRenderingTimeLimit() + { + return _impl.GetRenderingTimeLimit(); + } + + public uint GetSampleCount() + { + return _impl.GetSampleCount(); + } + + public uint GetSampleRate() + { + return _impl.GetSampleRate(); + } + + public int GetState() + { + if (_impl.IsActive()) + { + return 0; + } + + return 1; + } + + public ResultCode QuerySystemEvent(out KEvent systemEvent) + { + ResultCode resultCode = (ResultCode)_impl.QuerySystemEvent(out IWritableEvent outEvent); + + if (resultCode == ResultCode.Success) + { + if (outEvent is AudioKernelEvent) + { + systemEvent = ((AudioKernelEvent)outEvent).Event; + } + else + { + throw new NotImplementedException(); + } + } + else + { + systemEvent = null; + } + + return resultCode; + } + + public ResultCode RequestUpdate(Memory<byte> output, Memory<byte> performanceOutput, ReadOnlyMemory<byte> input) + { + return (ResultCode)_impl.Update(output, performanceOutput, input); + } + + public void SetRenderingTimeLimit(uint percent) + { + _impl.SetRenderingTimeLimitPercent(percent); + } + + public ResultCode Start() + { + _impl.Start(); + + return ResultCode.Success; + } + + public ResultCode Stop() + { + _impl.Stop(); + + return ResultCode.Success; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _impl.Dispose(); + } + } + + public void SetVoiceDropParameter(float voiceDropParameter) + { + _impl.SetVoiceDropParameter(voiceDropParameter); + } + + public float GetVoiceDropParameter() + { + return _impl.GetVoiceDropParameter(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs new file mode 100644 index 00000000..a137c413 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -0,0 +1,217 @@ +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<nn::audio::detail::AudioRendererUpdateDataHeader, 5> input) + // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> 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<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); + + using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize)) + using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize)) + { + Memory<byte> output = outputOwner.Memory; + Memory<byte> 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<copy, event> + 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<nn::audio::detail::AudioRendererUpdateDataHeader, 0x21> input) + // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> 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<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); + + Memory<byte> output = new byte[outputSize]; + Memory<byte> 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(); + } + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs new file mode 100644 index 00000000..1918a977 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs @@ -0,0 +1,18 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + interface IAudioDevice + { + string[] ListAudioDeviceName(); + ResultCode SetAudioDeviceOutputVolume(string name, float volume); + ResultCode GetAudioDeviceOutputVolume(string name, out float volume); + string GetActiveAudioDeviceName(); + KEvent QueryAudioDeviceSystemEvent(); + uint GetActiveChannelCount(); + KEvent QueryAudioDeviceInputEvent(); + KEvent QueryAudioDeviceOutputEvent(); + string GetActiveAudioOutputDeviceName(); + string[] ListAudioOutputDeviceName(); + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs new file mode 100644 index 00000000..404bf4c1 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs @@ -0,0 +1,22 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + interface IAudioRenderer : IDisposable + { + uint GetSampleRate(); + uint GetSampleCount(); + uint GetMixBufferCount(); + int GetState(); + ResultCode RequestUpdate(Memory<byte> output, Memory<byte> performanceOutput, ReadOnlyMemory<byte> input); + ResultCode Start(); + ResultCode Stop(); + ResultCode QuerySystemEvent(out KEvent systemEvent); + void SetRenderingTimeLimit(uint percent); + uint GetRenderingTimeLimit(); + ResultCode ExecuteAudioRendererRendering(); + void SetVoiceDropParameter(float voiceDropParameter); + float GetVoiceDropParameter(); + } +} |
