diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Audio/AudioRenderer')
8 files changed, 904 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs new file mode 100644 index 00000000..fdc23604 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs @@ -0,0 +1,98 @@ +using Ryujinx.Audio; +using Ryujinx.Audio.Renderer; +using Ryujinx.Audio.Renderer.Integration; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + public class AalHardwareDevice : HardwareDevice + { + private IAalOutput _output; + private int _trackId; + private int _bufferTag; + private int _nextTag; + private AutoResetEvent _releaseEvent; + + private uint _channelCount; + private uint _sampleRate; + + private short[] _buffer; + + private Queue<long> _releasedTags; + + public AalHardwareDevice(int bufferTag, IAalOutput output, uint channelCount, uint sampleRate) + { + _bufferTag = bufferTag; + _channelCount = channelCount; + _sampleRate = sampleRate; + _output = output; + _releaseEvent = new AutoResetEvent(true); + _trackId = _output.OpenTrack((int)sampleRate, (int)channelCount, AudioCallback); + _releasedTags = new Queue<long>(); + + _buffer = new short[RendererConstants.TargetSampleCount * channelCount]; + + _output.Start(_trackId); + } + + private void AudioCallback() + { + long[] released = _output.GetReleasedBuffers(_trackId, int.MaxValue); + + lock (_releasedTags) + { + foreach (long tag in released) + { + _releasedTags.Enqueue(tag); + } + } + } + + private long GetReleasedTag() + { + lock (_releasedTags) + { + if (_releasedTags.Count > 0) + { + return _releasedTags.Dequeue(); + } + + return (_bufferTag << 16) | (_nextTag++); + } + } + + public void AppendBuffer(ReadOnlySpan<short> data, uint channelCount) + { + data.CopyTo(_buffer.AsSpan()); + + _output.AppendBuffer(_trackId, GetReleasedTag(), _buffer); + } + + public uint GetChannelCount() + { + return _channelCount; + } + + public uint GetSampleRate() + { + return _sampleRate; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _output.Stop(_trackId); + _output.CloseTrack(_trackId); + _releaseEvent.Dispose(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs new file mode 100644 index 00000000..8ba10946 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs @@ -0,0 +1,158 @@ +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 ResultCode GetAudioSystemMasterVolumeSetting(string name, out float systemMasterVolume) + { + if (TryGetDeviceByName(out VirtualDeviceSession result, name, true)) + { + systemMasterVolume = result.Device.MasterVolume; + } + else + { + systemMasterVolume = 0.0f; + } + + return ResultCode.Success; + } + + public string[] ListAudioDeviceName() + { + int deviceCount = _sessions.Length; + + if (!_isUsbDeviceSupported) + { + deviceCount--; + } + + string[] result = new string[deviceCount]; + + for (int i = 0; i < deviceCount; i++) + { + result[i] = _sessions[i].Device.Name; + } + + return result; + } + + public KEvent QueryAudioDeviceInputEvent() + { + return _systemEvent; + } + + public KEvent QueryAudioDeviceOutputEvent() + { + return _systemEvent; + } + + public KEvent QueryAudioDeviceSystemEvent() + { + return _systemEvent; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs new file mode 100644 index 00000000..1cf6741e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -0,0 +1,286 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +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; + } + + [Command(0)] + // ListAudioDeviceName() -> (u32, buffer<bytes, 6>) + public ResultCode ListAudioDeviceName(ServiceCtx context) + { + string[] deviceNames = _impl.ListAudioDeviceName(); + + long position = context.Request.ReceiveBuff[0].Position; + long size = context.Request.ReceiveBuff[0].Size; + + long basePosition = position; + + int count = 0; + + foreach (string name in deviceNames) + { + byte[] buffer = Encoding.ASCII.GetBytes(name); + + if ((position - basePosition) + buffer.Length > size) + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + + break; + } + + context.Memory.Write((ulong)position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length); + + position += AudioDeviceNameSize; + count++; + } + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + [Command(1)] + // SetAudioDeviceOutputVolume(f32 volume, buffer<bytes, 5> name) + public ResultCode SetAudioDeviceOutputVolume(ServiceCtx context) + { + float volume = context.RequestData.ReadSingle(); + + long position = context.Request.SendBuff[0].Position; + long size = context.Request.SendBuff[0].Size; + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + + return _impl.SetAudioDeviceOutputVolume(deviceName, volume); + } + + [Command(2)] + // GetAudioDeviceOutputVolume(buffer<bytes, 5> name) -> f32 volume + public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context) + { + long position = context.Request.SendBuff[0].Position; + long size = context.Request.SendBuff[0].Size; + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + + ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(volume); + } + + return result; + } + + [Command(3)] + // GetActiveAudioDeviceName() -> buffer<bytes, 6> + public ResultCode GetActiveAudioDeviceName(ServiceCtx context) + { + string name = _impl.GetActiveAudioDeviceName(); + + long position = context.Request.ReceiveBuff[0].Position; + long size = context.Request.ReceiveBuff[0].Size; + + byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); + + if ((ulong)deviceNameBuffer.Length <= (ulong)size) + { + context.Memory.Write((ulong)position, deviceNameBuffer); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + } + + return ResultCode.Success; + } + + [Command(4)] + // QueryAudioDeviceSystemEvent() -> handle<copy, event> + public ResultCode QueryAudioDeviceSystemEvent(ServiceCtx context) + { + KEvent deviceSystemEvent = _impl.QueryAudioDeviceSystemEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [Command(5)] + // GetActiveChannelCount() -> u32 + public ResultCode GetActiveChannelCount(ServiceCtx context) + { + context.ResponseData.Write(_impl.GetActiveChannelCount()); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [Command(6)] // 3.0.0+ + // ListAudioDeviceNameAuto() -> (u32, buffer<bytes, 0x22>) + public ResultCode ListAudioDeviceNameAuto(ServiceCtx context) + { + string[] deviceNames = _impl.ListAudioDeviceName(); + + (long position, long size) = context.Request.GetBufferType0x22(); + + long basePosition = position; + + int count = 0; + + foreach (string name in deviceNames) + { + byte[] buffer = Encoding.ASCII.GetBytes(name); + + if ((position - basePosition) + buffer.Length > size) + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + + break; + } + + context.Memory.Write((ulong)position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length); + + position += AudioDeviceNameSize; + count++; + } + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + [Command(7)] // 3.0.0+ + // SetAudioDeviceOutputVolumeAuto(f32 volume, buffer<bytes, 0x21> name) + public ResultCode SetAudioDeviceOutputVolumeAuto(ServiceCtx context) + { + float volume = context.RequestData.ReadSingle(); + + (long position, long size) = context.Request.GetBufferType0x21(); + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + + return _impl.SetAudioDeviceOutputVolume(deviceName, volume); + } + + [Command(8)] // 3.0.0+ + // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32 + public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context) + { + (long position, long size) = context.Request.GetBufferType0x21(); + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + + ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(volume); + } + + return ResultCode.Success; + } + + [Command(10)] // 3.0.0+ + // GetActiveAudioDeviceNameAuto() -> buffer<bytes, 0x22> + public ResultCode GetActiveAudioDeviceNameAuto(ServiceCtx context) + { + string name = _impl.GetActiveAudioDeviceName(); + + (long position, long size) = context.Request.GetBufferType0x22(); + + byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0'); + + if ((ulong)deviceNameBuffer.Length <= (ulong)size) + { + context.Memory.Write((ulong)position, deviceNameBuffer); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); + } + + return ResultCode.Success; + } + + [Command(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) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [Command(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) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [Command(13)] + // GetAudioSystemMasterVolumeSetting(buffer<bytes, 5> name) -> f32 + public ResultCode GetAudioSystemMasterVolumeSetting(ServiceCtx context) + { + long position = context.Request.SendBuff[0].Position; + long size = context.Request.SendBuff[0].Size; + + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + + ResultCode result = _impl.GetAudioSystemMasterVolumeSetting(deviceName, out float systemMasterVolume); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(systemMasterVolume); + } + + return result; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs new file mode 100644 index 00000000..1a132b91 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs @@ -0,0 +1,25 @@ +using Ryujinx.Audio.Renderer.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/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs new file mode 100644 index 00000000..702648dd --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs @@ -0,0 +1,112 @@ +using Ryujinx.Audio.Renderer.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() + { + throw new NotImplementedException(); + } + + 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(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs new file mode 100644 index 00000000..5ff5ddb3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -0,0 +1,188 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; +using System.Buffers; + +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer +{ + class AudioRendererServer : IpcService, IDisposable + { + private IAudioRenderer _impl; + + public AudioRendererServer(IAudioRenderer impl) + { + _impl = impl; + } + + [Command(0)] + // GetSampleRate() -> u32 + public ResultCode GetSampleRate(ServiceCtx context) + { + context.ResponseData.Write(_impl.GetSampleRate()); + + return ResultCode.Success; + } + + [Command(1)] + // GetSampleCount() -> u32 + public ResultCode GetSampleCount(ServiceCtx context) + { + context.ResponseData.Write(_impl.GetSampleCount()); + + return ResultCode.Success; + } + + [Command(2)] + // GetMixBufferCount() -> u32 + public ResultCode GetMixBufferCount(ServiceCtx context) + { + context.ResponseData.Write(_impl.GetMixBufferCount()); + + return ResultCode.Success; + } + + [Command(3)] + // GetState() -> u32 + public ResultCode GetState(ServiceCtx context) + { + context.ResponseData.Write(_impl.GetState()); + + return ResultCode.Success; + } + + [Command(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) + { + long inputPosition = context.Request.SendBuff[0].Position; + long inputSize = context.Request.SendBuff[0].Size; + + long outputPosition = context.Request.ReceiveBuff[0].Position; + long outputSize = context.Request.ReceiveBuff[0].Size; + + long performanceOutputPosition = context.Request.ReceiveBuff[1].Position; + long performanceOutputSize = context.Request.ReceiveBuff[1].Size; + + ReadOnlyMemory<byte> input = context.Memory.GetSpan((ulong)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((ulong)outputPosition, output.Span); + context.Memory.Write((ulong)performanceOutputPosition, performanceOutput.Span); + } + else + { + Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{result}"); + } + + return result; + } + + [Command(5)] + // Start() + public ResultCode Start(ServiceCtx context) + { + return _impl.Start(); + } + + [Command(6)] + // Stop() + public ResultCode Stop(ServiceCtx context) + { + return _impl.Stop(); + } + + [Command(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) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + } + + return result; + } + + [Command(8)] + // SetAudioRendererRenderingTimeLimit(u32 limit) + public ResultCode SetAudioRendererRenderingTimeLimit(ServiceCtx context) + { + uint limit = context.RequestData.ReadUInt32(); + + _impl.SetRenderingTimeLimit(limit); + + return ResultCode.Success; + } + + [Command(9)] + // GetAudioRendererRenderingTimeLimit() -> u32 limit + public ResultCode GetAudioRendererRenderingTimeLimit(ServiceCtx context) + { + uint limit = _impl.GetRenderingTimeLimit(); + + context.ResponseData.Write(limit); + + return ResultCode.Success; + } + + [Command(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) + { + (long inputPosition, long inputSize) = context.Request.GetBufferType0x21(); + (long outputPosition, long outputSize) = context.Request.GetBufferType0x22(0); + (long performanceOutputPosition, long performanceOutputSize) = context.Request.GetBufferType0x22(1); + + ReadOnlyMemory<byte> input = context.Memory.GetSpan((ulong)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((ulong)outputPosition, output.Span); + context.Memory.Write((ulong)performanceOutputPosition, performanceOutput.Span); + } + + return result; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _impl.Dispose(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs new file mode 100644 index 00000000..42ea727f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs @@ -0,0 +1,17 @@ +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(); + ResultCode GetAudioSystemMasterVolumeSetting(string name, out float systemMasterVolume); + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs new file mode 100644 index 00000000..a59c94e9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs @@ -0,0 +1,20 @@ +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(); + } +} |
