diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Audio')
41 files changed, 885 insertions, 1488 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/AudioRendererManager/IAudioDevice.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs index 1cc263b6..1cf6741e 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -1,41 +1,40 @@ -using Ryujinx.Common.Logging; +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 Ryujinx.HLE.HOS.SystemState; using System; using System.Text; -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager +namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { - class IAudioDevice : IpcService + class AudioDeviceServer : IpcService { - private KEvent _systemEvent; + private const int AudioDeviceNameSize = 0x100; - public IAudioDevice(Horizon system) - { - _systemEvent = new KEvent(system.KernelContext); + private IAudioDevice _impl; - // TODO: We shouldn't be signaling this here. - _systemEvent.ReadableEvent.Signal(); + public AudioDeviceServer(IAudioDevice impl) + { + _impl = impl; } [Command(0)] // ListAudioDeviceName() -> (u32, buffer<bytes, 6>) public ResultCode ListAudioDeviceName(ServiceCtx context) { - string[] deviceNames = SystemStateMgr.AudioOutputs; - - context.ResponseData.Write(deviceNames.Length); + string[] deviceNames = _impl.ListAudioDeviceName(); long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + long size = context.Request.ReceiveBuff[0].Size; long basePosition = position; + int count = 0; + foreach (string name in deviceNames) { - byte[] buffer = Encoding.ASCII.GetBytes(name + "\0"); + byte[] buffer = Encoding.ASCII.GetBytes(name); if ((position - basePosition) + buffer.Length > size) { @@ -45,41 +44,58 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager } context.Memory.Write((ulong)position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length); - position += buffer.Length; + position += AudioDeviceNameSize; + count++; } + context.ResponseData.Write(count); + return ResultCode.Success; } [Command(1)] - // SetAudioDeviceOutputVolume(u32, buffer<bytes, 5>) + // 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; + long size = context.Request.SendBuff[0].Size; - byte[] deviceNameBuffer = new byte[size]; - - context.Memory.Read((ulong)position, deviceNameBuffer); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); - string deviceName = Encoding.ASCII.GetString(deviceNameBuffer); + return _impl.SetAudioDeviceOutputVolume(deviceName, volume); + } - Logger.Stub?.PrintStub(LogClass.ServiceAudio); + [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; - return ResultCode.Success; + 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 = context.Device.System.State.ActiveAudioOutput; + string name = _impl.GetActiveAudioDeviceName(); long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + long size = context.Request.ReceiveBuff[0].Size; byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); @@ -99,7 +115,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager // QueryAudioDeviceSystemEvent() -> handle<copy, event> public ResultCode QueryAudioDeviceSystemEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out int handle) != KernelResult.Success) + KEvent deviceSystemEvent = _impl.QueryAudioDeviceSystemEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -115,28 +133,28 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager // GetActiveChannelCount() -> u32 public ResultCode GetActiveChannelCount(ServiceCtx context) { - context.ResponseData.Write(2); + context.ResponseData.Write(_impl.GetActiveChannelCount()); Logger.Stub?.PrintStub(LogClass.ServiceAudio); return ResultCode.Success; } - [Command(6)] + [Command(6)] // 3.0.0+ // ListAudioDeviceNameAuto() -> (u32, buffer<bytes, 0x22>) public ResultCode ListAudioDeviceNameAuto(ServiceCtx context) { - string[] deviceNames = SystemStateMgr.AudioOutputs; - - context.ResponseData.Write(deviceNames.Length); + 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.UTF8.GetBytes(name + '\0'); + byte[] buffer = Encoding.ASCII.GetBytes(name); if ((position - basePosition) + buffer.Length > size) { @@ -146,48 +164,53 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager } context.Memory.Write((ulong)position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length); - position += buffer.Length; + position += AudioDeviceNameSize; + count++; } + context.ResponseData.Write(count); + return ResultCode.Success; } - [Command(7)] - // SetAudioDeviceOutputVolumeAuto(u32, buffer<bytes, 0x21>) + [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(); - byte[] deviceNameBuffer = new byte[size]; - - context.Memory.Read((ulong)position, deviceNameBuffer); - - string deviceName = Encoding.UTF8.GetString(deviceNameBuffer); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - return ResultCode.Success; + return _impl.SetAudioDeviceOutputVolume(deviceName, volume); } - [Command(8)] - // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21>) -> u32 + [Command(8)] // 3.0.0+ + // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32 public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context) { - context.ResponseData.Write(1f); + (long position, long size) = context.Request.GetBufferType0x21(); - Logger.Stub?.PrintStub(LogClass.ServiceAudio); + 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)] + [Command(10)] // 3.0.0+ // GetActiveAudioDeviceNameAuto() -> buffer<bytes, 0x22> public ResultCode GetActiveAudioDeviceNameAuto(ServiceCtx context) { - string name = context.Device.System.State.ActiveAudioOutput; + string name = _impl.GetActiveAudioDeviceName(); (long position, long size) = context.Request.GetBufferType0x22(); @@ -205,11 +228,13 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager return ResultCode.Success; } - [Command(11)] + [Command(11)] // 3.0.0+ // QueryAudioDeviceInputEvent() -> handle<copy, event> public ResultCode QueryAudioDeviceInputEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out int handle) != KernelResult.Success) + KEvent deviceInputEvent = _impl.QueryAudioDeviceInputEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceInputEvent.ReadableEvent, out int handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -221,11 +246,13 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager return ResultCode.Success; } - [Command(12)] + [Command(12)] // 3.0.0+ // QueryAudioDeviceOutputEvent() -> handle<copy, event> public ResultCode QueryAudioDeviceOutputEvent(ServiceCtx context) { - if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out int handle) != KernelResult.Success) + KEvent deviceOutputEvent = _impl.QueryAudioDeviceOutputEvent(); + + if (context.Process.HandleTable.GenerateHandle(deviceOutputEvent.ReadableEvent, out int handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -236,5 +263,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager 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; + } } -}
\ No newline at end of file +} 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(); + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs new file mode 100644 index 00000000..e12a9919 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs @@ -0,0 +1,50 @@ +using Ryujinx.Audio.Renderer.Device; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; + +using AudioRendererManagerImpl = Ryujinx.Audio.Renderer.Server.AudioRendererManager; + +namespace Ryujinx.HLE.HOS.Services.Audio +{ + class AudioRendererManager : IAudioRendererManager + { + private AudioRendererManagerImpl _impl; + private VirtualDeviceSessionRegistry _registry; + + public AudioRendererManager(AudioRendererManagerImpl impl, VirtualDeviceSessionRegistry registry) + { + _impl = impl; + _registry = registry; + } + + public ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context, out IAudioDevice outObject, int revision, ulong appletResourceUserId) + { + outObject = new AudioDevice(_registry, context.Device.System.KernelContext, appletResourceUserId, revision); + + return ResultCode.Success; + } + + public ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter) + { + return AudioRendererManagerImpl.GetWorkBufferSize(ref parameter); + } + + public ResultCode OpenAudioRenderer(ServiceCtx context, out IAudioRenderer obj, ref AudioRendererConfiguration parameter, ulong workBufferSize, ulong appletResourceUserId, KTransferMemory workBufferTransferMemory, uint processHandle) + { + ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, context.Memory, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle); + + if (result == ResultCode.Success) + { + obj = new AudioRenderer.AudioRenderer(renderer); + } + else + { + obj = null; + } + + return result; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs deleted file mode 100644 index c884b465..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/AudioRendererCommon.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class AudioRendererCommon - { - public static bool CheckValidRevision(AudioRendererParameter parameters) => GetRevisionVersion(parameters.Revision) <= AudioRendererConsts.Revision; - public static bool CheckFeatureSupported(int revision, int supportedRevision) => revision >= supportedRevision; - public static int GetRevisionVersion(int revision) => (revision - AudioRendererConsts.Rev0Magic) >> 24; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs deleted file mode 100644 index 461e4337..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/BehaviorInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - class BehaviorInfo - { - private const int _revision = AudioRendererConsts.Revision; - - private int _userRevision = 0; - - public BehaviorInfo() - { - /* TODO: this class got a size of 0xC0 - 0x00 - uint - Internal Revision - 0x04 - uint - User Revision - 0x08 - ... unknown ... - */ - } - - public bool IsSplitterSupported() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.Splitter); - public bool IsSplitterBugFixed() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.SplitterBugFix); - public bool IsVariadicCommandBufferSizeSupported() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.VariadicCommandBufferSize); - public bool IsElapsedFrameCountSupported() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.ElapsedFrameCount); - - public int GetPerformanceMetricsDataFormat() => AudioRendererCommon.CheckFeatureSupported(_userRevision, SupportTags.PerformanceMetricsDataFormatVersion2) ? 2 : 1; - - public void SetUserLibRevision(int revision) - { - _userRevision = AudioRendererCommon.GetRevisionVersion(revision); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs deleted file mode 100644 index b09b990b..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/CommandGenerator.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class CommandGenerator - { - public static long CalculateCommandBufferSize(AudioRendererParameter parameters) - { - return parameters.EffectCount * 0x840 + - parameters.SubMixCount * 0x5A38 + - parameters.SinkCount * 0x148 + - parameters.SplitterDestinationDataCount * 0x540 + - (parameters.SplitterCount * 0x68 + 0x2E0) * parameters.VoiceCount + - ((parameters.VoiceCount + parameters.SubMixCount + parameters.EffectCount + parameters.SinkCount + 0x65) << 6) + - 0x3F8; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs deleted file mode 100644 index 3f87ae04..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EdgeMatrix.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.Common; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class EdgeMatrix - { - public static int GetWorkBufferSize(int totalMixCount) - { - int size = BitUtils.AlignUp(totalMixCount * totalMixCount, AudioRendererConsts.BufferAlignment); - - if (size < 0) - { - size |= 7; - } - - return size / 8; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EffectContext.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EffectContext.cs deleted file mode 100644 index 88f087ed..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/EffectContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - class EffectContext - { - public EffectOut OutStatus; - } -} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs deleted file mode 100644 index b5802d2d..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/IAudioRenderer.cs +++ /dev/null @@ -1,452 +0,0 @@ -using Ryujinx.Audio; -using Ryujinx.Audio.Adpcm; -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 Ryujinx.HLE.Utilities; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - class IAudioRenderer : IpcService, IDisposable - { - // This is the amount of samples that are going to be appended - // each time that RequestUpdateAudioRenderer is called. Ideally, - // this value shouldn't be neither too small (to avoid the player - // starving due to running out of samples) or too large (to avoid - // high latency). - private const int MixBufferSamplesCount = 960; - - private KEvent _updateEvent; - - private MemoryManager _memory; - - private IAalOutput _audioOut; - - private AudioRendererParameter _params; - - private MemoryPoolContext[] _memoryPools; - - private VoiceContext[] _voices; - - private EffectContext[] _effects; - - private int _track; - - private PlayState _playState; - - private ulong _elapsedFrameCount; - - public IAudioRenderer( - Horizon system, - MemoryManager memory, - IAalOutput audioOut, - AudioRendererParameter rendererParams) - { - _updateEvent = new KEvent(system.KernelContext); - - _memory = memory; - _audioOut = audioOut; - _params = rendererParams; - - _track = audioOut.OpenTrack( - AudioRendererConsts.HostSampleRate, - AudioRendererConsts.HostChannelsCount, - AudioCallback); - - _memoryPools = CreateArray<MemoryPoolContext>(rendererParams.EffectCount + rendererParams.VoiceCount * 4); - - _voices = CreateArray<VoiceContext>(rendererParams.VoiceCount); - - _effects = CreateArray<EffectContext>(rendererParams.EffectCount); - - _elapsedFrameCount = 0; - - InitializeAudioOut(); - - _playState = PlayState.Stopped; - } - - [Command(0)] - // GetSampleRate() -> u32 - public ResultCode GetSampleRate(ServiceCtx context) - { - context.ResponseData.Write(_params.SampleRate); - - return ResultCode.Success; - } - - [Command(1)] - // GetSampleCount() -> u32 - public ResultCode GetSampleCount(ServiceCtx context) - { - context.ResponseData.Write(_params.SampleCount); - - return ResultCode.Success; - } - - [Command(2)] - // GetMixBufferCount() -> u32 - public ResultCode GetMixBufferCount(ServiceCtx context) - { - context.ResponseData.Write(_params.SubMixCount); - - return ResultCode.Success; - } - - [Command(3)] - // GetState() -> u32 - public ResultCode GetState(ServiceCtx context) - { - context.ResponseData.Write((int)_playState); - - Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { State = Enum.GetName(typeof(PlayState), _playState) }); - - return ResultCode.Success; - } - - private void AudioCallback() - { - _updateEvent.ReadableEvent.Signal(); - } - - private static T[] CreateArray<T>(int size) where T : new() - { - T[] output = new T[size]; - - for (int index = 0; index < size; index++) - { - output[index] = new T(); - } - - return output; - } - - private void InitializeAudioOut() - { - AppendMixedBuffer(0); - AppendMixedBuffer(1); - AppendMixedBuffer(2); - - _audioOut.Start(_track); - } - - [Command(4)] - // RequestUpdateAudioRenderer(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 5>) - // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6>, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6>) - public ResultCode RequestUpdateAudioRenderer(ServiceCtx context) - { - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); - - long inputPosition = context.Request.SendBuff[0].Position; - - StructReader reader = new StructReader(context.Memory, inputPosition); - StructWriter writer = new StructWriter(context.Memory, outputPosition); - - UpdateDataHeader inputHeader = reader.Read<UpdateDataHeader>(); - - BehaviorInfo behaviorInfo = new BehaviorInfo(); - - behaviorInfo.SetUserLibRevision(inputHeader.Revision); - - reader.Read<BehaviorIn>(inputHeader.BehaviorSize); - - MemoryPoolIn[] memoryPoolsIn = reader.Read<MemoryPoolIn>(inputHeader.MemoryPoolSize); - - for (int index = 0; index < memoryPoolsIn.Length; index++) - { - MemoryPoolIn memoryPool = memoryPoolsIn[index]; - - if (memoryPool.State == MemoryPoolState.RequestAttach) - { - _memoryPools[index].OutStatus.State = MemoryPoolState.Attached; - } - else if (memoryPool.State == MemoryPoolState.RequestDetach) - { - _memoryPools[index].OutStatus.State = MemoryPoolState.Detached; - } - } - - reader.Read<VoiceChannelResourceIn>(inputHeader.VoiceResourceSize); - - VoiceIn[] voicesIn = reader.Read<VoiceIn>(inputHeader.VoiceSize); - - for (int index = 0; index < voicesIn.Length; index++) - { - VoiceIn voice = voicesIn[index]; - - VoiceContext voiceCtx = _voices[index]; - - voiceCtx.SetAcquireState(voice.Acquired != 0); - - if (voice.Acquired == 0) - { - continue; - } - - if (voice.FirstUpdate != 0) - { - voiceCtx.AdpcmCtx = GetAdpcmDecoderContext( - voice.AdpcmCoeffsPosition, - voice.AdpcmCoeffsSize); - - voiceCtx.SampleFormat = voice.SampleFormat; - voiceCtx.SampleRate = voice.SampleRate; - voiceCtx.ChannelsCount = voice.ChannelsCount; - - voiceCtx.SetBufferIndex(voice.BaseWaveBufferIndex); - } - - voiceCtx.WaveBuffers[0] = voice.WaveBuffer0; - voiceCtx.WaveBuffers[1] = voice.WaveBuffer1; - voiceCtx.WaveBuffers[2] = voice.WaveBuffer2; - voiceCtx.WaveBuffers[3] = voice.WaveBuffer3; - voiceCtx.Volume = voice.Volume; - voiceCtx.PlayState = voice.PlayState; - } - - EffectIn[] effectsIn = reader.Read<EffectIn>(inputHeader.EffectSize); - - for (int index = 0; index < effectsIn.Length; index++) - { - if (effectsIn[index].IsNew != 0) - { - _effects[index].OutStatus.State = EffectState.New; - } - } - - UpdateAudio(); - - UpdateDataHeader outputHeader = new UpdateDataHeader(); - - int updateHeaderSize = Marshal.SizeOf<UpdateDataHeader>(); - - outputHeader.Revision = AudioRendererConsts.RevMagic; - outputHeader.BehaviorSize = 0xb0; - outputHeader.MemoryPoolSize = (_params.EffectCount + _params.VoiceCount * 4) * 0x10; - outputHeader.VoiceSize = _params.VoiceCount * 0x10; - outputHeader.EffectSize = _params.EffectCount * 0x10; - outputHeader.SinkSize = _params.SinkCount * 0x20; - outputHeader.PerformanceManagerSize = 0x10; - - if (behaviorInfo.IsElapsedFrameCountSupported()) - { - outputHeader.ElapsedFrameCountInfoSize = 0x10; - } - - outputHeader.TotalSize = updateHeaderSize + - outputHeader.BehaviorSize + - outputHeader.MemoryPoolSize + - outputHeader.VoiceSize + - outputHeader.EffectSize + - outputHeader.SinkSize + - outputHeader.PerformanceManagerSize + - outputHeader.ElapsedFrameCountInfoSize; - - writer.Write(outputHeader); - - foreach (MemoryPoolContext memoryPool in _memoryPools) - { - writer.Write(memoryPool.OutStatus); - } - - foreach (VoiceContext voice in _voices) - { - writer.Write(voice.OutStatus); - } - - foreach (EffectContext effect in _effects) - { - writer.Write(effect.OutStatus); - } - - writer.SkipBytes(_params.SinkCount * 0x20); - writer.SkipBytes(outputHeader.PerformanceManagerSize); - writer.SkipBytes(outputHeader.BehaviorSize); - - if (behaviorInfo.IsElapsedFrameCountSupported()) - { - writer.Write(new RendererInfoOut - { - ElapsedFrameCount = _elapsedFrameCount - }); - } - - return ResultCode.Success; - } - - [Command(5)] - // Start() - public ResultCode StartAudioRenderer(ServiceCtx context) - { - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - _playState = PlayState.Playing; - - return ResultCode.Success; - } - - [Command(6)] - // Stop() - public ResultCode StopAudioRenderer(ServiceCtx context) - { - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - _playState = PlayState.Stopped; - - return ResultCode.Success; - } - - [Command(7)] - // QuerySystemEvent() -> handle<copy, event> - public ResultCode QuerySystemEvent(ServiceCtx context) - { - if (context.Process.HandleTable.GenerateHandle(_updateEvent.ReadableEvent, out int handle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - return ResultCode.Success; - } - - private AdpcmDecoderContext GetAdpcmDecoderContext(long position, long size) - { - if (size == 0) - { - return null; - } - - AdpcmDecoderContext context = new AdpcmDecoderContext - { - Coefficients = new short[size >> 1] - }; - - for (int offset = 0; offset < size; offset += 2) - { - context.Coefficients[offset >> 1] = _memory.Read<short>((ulong)(position + offset)); - } - - return context; - } - - private void UpdateAudio() - { - long[] released = _audioOut.GetReleasedBuffers(_track, 2); - - for (int index = 0; index < released.Length; index++) - { - AppendMixedBuffer(released[index]); - } - - _elapsedFrameCount++; - } - - private void AppendMixedBuffer(long tag) - { - int[] mixBuffer = new int[MixBufferSamplesCount * AudioRendererConsts.HostChannelsCount]; - - foreach (VoiceContext voice in _voices) - { - if (!voice.Playing || voice.CurrentWaveBuffer.Size == 0) - { - continue; - } - - int outOffset = 0; - int pendingSamples = MixBufferSamplesCount; - - while (pendingSamples > 0) - { - int[] samples = voice.GetBufferData(_memory, pendingSamples, out int returnedSamples); - - if (returnedSamples == 0) - { - break; - } - - pendingSamples -= returnedSamples; - - for (int offset = 0; offset < samples.Length; offset++) - { - mixBuffer[outOffset++] += (int)(samples[offset] * voice.Volume); - } - } - } - - _audioOut.AppendBuffer(_track, tag, GetFinalBuffer(mixBuffer)); - } - - private unsafe static short[] GetFinalBuffer(int[] buffer) - { - short[] output = new short[buffer.Length]; - - int offset = 0; - - // Perform Saturation using SSE2 if supported - if (Sse2.IsSupported) - { - fixed (int* inptr = buffer) - fixed (short* outptr = output) - { - for (; offset + 32 <= buffer.Length; offset += 32) - { - // Unroll the loop a little to ensure the CPU pipeline - // is always full. - Vector128<int> block1A = Sse2.LoadVector128(inptr + offset + 0); - Vector128<int> block1B = Sse2.LoadVector128(inptr + offset + 4); - - Vector128<int> block2A = Sse2.LoadVector128(inptr + offset + 8); - Vector128<int> block2B = Sse2.LoadVector128(inptr + offset + 12); - - Vector128<int> block3A = Sse2.LoadVector128(inptr + offset + 16); - Vector128<int> block3B = Sse2.LoadVector128(inptr + offset + 20); - - Vector128<int> block4A = Sse2.LoadVector128(inptr + offset + 24); - Vector128<int> block4B = Sse2.LoadVector128(inptr + offset + 28); - - Vector128<short> output1 = Sse2.PackSignedSaturate(block1A, block1B); - Vector128<short> output2 = Sse2.PackSignedSaturate(block2A, block2B); - Vector128<short> output3 = Sse2.PackSignedSaturate(block3A, block3B); - Vector128<short> output4 = Sse2.PackSignedSaturate(block4A, block4B); - - Sse2.Store(outptr + offset + 0, output1); - Sse2.Store(outptr + offset + 8, output2); - Sse2.Store(outptr + offset + 16, output3); - Sse2.Store(outptr + offset + 24, output4); - } - } - } - - // Process left overs - for (; offset < buffer.Length; offset++) - { - output[offset] = DspUtils.Saturate(buffer[offset]); - } - - return output; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _audioOut.CloseTrack(_track); - } - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/MemoryPoolContext.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/MemoryPoolContext.cs deleted file mode 100644 index 3f48114c..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/MemoryPoolContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - class MemoryPoolContext - { - public MemoryPoolOut OutStatus; - - public MemoryPoolContext() - { - OutStatus.State = MemoryPoolState.Detached; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs deleted file mode 100644 index 7ae9aeea..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/NodeStates.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.Common; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class NodeStates - { - public static long GetWorkBufferSize(int totalMixCount) - { - int size = BitUtils.AlignUp(totalMixCount, AudioRendererConsts.BufferAlignment); - - if (size < 0) - { - size |= 7; - } - - return 4 * (totalMixCount * totalMixCount) + 12 * totalMixCount + 2 * (size / 8); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs deleted file mode 100644 index a5b3d79f..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/PerformanceManager.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Ryujinx.Common.Logging; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class PerformanceManager - { - public static long GetRequiredBufferSizeForPerformanceMetricsPerFrame(BehaviorInfo behaviorInfo, AudioRendererParameter parameters) - { - int performanceMetricsDataFormat = behaviorInfo.GetPerformanceMetricsDataFormat(); - - if (performanceMetricsDataFormat == 2) - { - return 24 * (parameters.VoiceCount + - parameters.EffectCount + - parameters.SubMixCount + - parameters.SinkCount + 1) + 0x990; - } - - if (performanceMetricsDataFormat != 1) - { - Logger.Warning?.Print(LogClass.ServiceAudio, $"PerformanceMetricsDataFormat: {performanceMetricsDataFormat} is not supported!"); - } - - return (((parameters.VoiceCount + - parameters.EffectCount + - parameters.SubMixCount + - parameters.SinkCount + 1) << 32) >> 0x1C) + 0x658; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Resampler.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Resampler.cs deleted file mode 100644 index 936e7f50..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Resampler.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class Resampler - { -#region "LookUp Tables" - private static short[] _curveLut0 = new short[] - { - 6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239, 19412, 7093, 22, - 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377, 7472, 41, 5773, 19361, 7600, 48, - 5659, 19342, 7728, 55, 5546, 19321, 7857, 62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77, - 5213, 19245, 8249, 84, 5104, 19215, 8381, 92, 4997, 19183, 8513, 101, 4890, 19148, 8646, 109, - 4785, 19112, 8780, 118, 4681, 19073, 8914, 127, 4579, 19031, 9048, 137, 4477, 18988, 9183, 147, - 4377, 18942, 9318, 157, 4277, 18895, 9454, 168, 4179, 18845, 9590, 179, 4083, 18793, 9726, 190, - 3987, 18738, 9863, 202, 3893, 18682, 10000, 215, 3800, 18624, 10137, 228, 3709, 18563, 10274, 241, - 3618, 18500, 10411, 255, 3529, 18436, 10549, 270, 3441, 18369, 10687, 285, 3355, 18300, 10824, 300, - 3269, 18230, 10962, 317, 3186, 18157, 11100, 334, 3103, 18082, 11238, 351, 3022, 18006, 11375, 369, - 2942, 17927, 11513, 388, 2863, 17847, 11650, 408, 2785, 17765, 11788, 428, 2709, 17681, 11925, 449, - 2635, 17595, 12062, 471, 2561, 17507, 12198, 494, 2489, 17418, 12334, 517, 2418, 17327, 12470, 541, - 2348, 17234, 12606, 566, 2280, 17140, 12741, 592, 2213, 17044, 12876, 619, 2147, 16946, 13010, 647, - 2083, 16846, 13144, 675, 2020, 16745, 13277, 704, 1958, 16643, 13409, 735, 1897, 16539, 13541, 766, - 1838, 16434, 13673, 798, 1780, 16327, 13803, 832, 1723, 16218, 13933, 866, 1667, 16109, 14062, 901, - 1613, 15998, 14191, 937, 1560, 15885, 14318, 975, 1508, 15772, 14445, 1013, 1457, 15657, 14571, 1052, - 1407, 15540, 14695, 1093, 1359, 15423, 14819, 1134, 1312, 15304, 14942, 1177, 1266, 15185, 15064, 1221, - 1221, 15064, 15185, 1266, 1177, 14942, 15304, 1312, 1134, 14819, 15423, 1359, 1093, 14695, 15540, 1407, - 1052, 14571, 15657, 1457, 1013, 14445, 15772, 1508, 975, 14318, 15885, 1560, 937, 14191, 15998, 1613, - 901, 14062, 16109, 1667, 866, 13933, 16218, 1723, 832, 13803, 16327, 1780, 798, 13673, 16434, 1838, - 766, 13541, 16539, 1897, 735, 13409, 16643, 1958, 704, 13277, 16745, 2020, 675, 13144, 16846, 2083, - 647, 13010, 16946, 2147, 619, 12876, 17044, 2213, 592, 12741, 17140, 2280, 566, 12606, 17234, 2348, - 541, 12470, 17327, 2418, 517, 12334, 17418, 2489, 494, 12198, 17507, 2561, 471, 12062, 17595, 2635, - 449, 11925, 17681, 2709, 428, 11788, 17765, 2785, 408, 11650, 17847, 2863, 388, 11513, 17927, 2942, - 369, 11375, 18006, 3022, 351, 11238, 18082, 3103, 334, 11100, 18157, 3186, 317, 10962, 18230, 3269, - 300, 10824, 18300, 3355, 285, 10687, 18369, 3441, 270, 10549, 18436, 3529, 255, 10411, 18500, 3618, - 241, 10274, 18563, 3709, 228, 10137, 18624, 3800, 215, 10000, 18682, 3893, 202, 9863, 18738, 3987, - 190, 9726, 18793, 4083, 179, 9590, 18845, 4179, 168, 9454, 18895, 4277, 157, 9318, 18942, 4377, - 147, 9183, 18988, 4477, 137, 9048, 19031, 4579, 127, 8914, 19073, 4681, 118, 8780, 19112, 4785, - 109, 8646, 19148, 4890, 101, 8513, 19183, 4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213, - 77, 8118, 19273, 5323, 69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659, - 48, 7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219, 19403, 6121, - 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424, 6479, 3, 6722, 19426, 6600 - }; - - private static short[] _curveLut1 = new short[] - { - -68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450, 32586, 512, -36, - -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454, 1000, -69, -891, 32393, 1174, -80, - -990, 32323, 1352, -92, -1084, 32244, 1536, -103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128, - -1338, 31956, 2118, -140, -1414, 31844, 2322, -153, -1486, 31723, 2532, -167, -1554, 31593, 2747, -180, - -1617, 31456, 2967, -194, -1676, 31310, 3192, -209, -1732, 31157, 3422, -224, -1783, 30995, 3657, -240, - -1830, 30826, 3897, -256, -1874, 30649, 4143, -272, -1914, 30464, 4393, -289, -1951, 30272, 4648, -307, - -1984, 30072, 4908, -325, -2014, 29866, 5172, -343, -2040, 29652, 5442, -362, -2063, 29431, 5716, -382, - -2083, 29203, 5994, -403, -2100, 28968, 6277, -424, -2114, 28727, 6565, -445, -2125, 28480, 6857, -468, - -2133, 28226, 7153, -490, -2139, 27966, 7453, -514, -2142, 27700, 7758, -538, -2142, 27428, 8066, -563, - -2141, 27151, 8378, -588, -2136, 26867, 8694, -614, -2130, 26579, 9013, -641, -2121, 26285, 9336, -668, - -2111, 25987, 9663, -696, -2098, 25683, 9993, -724, -2084, 25375, 10326, -753, -2067, 25063, 10662, -783, - -2049, 24746, 11000, -813, -2030, 24425, 11342, -844, -2009, 24100, 11686, -875, -1986, 23771, 12033, -907, - -1962, 23438, 12382, -939, -1937, 23103, 12733, -972, -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039, - -1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764, 21027, 14877, -1176, - -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959, 15965, -1282, -1633, 19600, 16329, -1317, - -1599, 19239, 16694, -1353, -1564, 18878, 17058, -1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459, - -1459, 17787, 18151, -1495, -1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599, - -1317, 16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239, 20673, -1732, - -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729, -1825, -1072, 13798, 22077, -1855, - -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911, -972, 12733, 23103, -1937, -939, 12382, 23438, -1962, - -907, 12033, 23771, -1986, -875, 11686, 24100, -2009, -844, 11342, 24425, -2030, -813, 11000, 24746, -2049, - -783, 10662, 25063, -2067, -753, 10326, 25375, -2084, -724, 9993, 25683, -2098, -696, 9663, 25987, -2111, - -668, 9336, 26285, -2121, -641, 9013, 26579, -2130, -614, 8694, 26867, -2136, -588, 8378, 27151, -2141, - -563, 8066, 27428, -2142, -538, 7758, 27700, -2142, -514, 7453, 27966, -2139, -490, 7153, 28226, -2133, - -468, 6857, 28480, -2125, -445, 6565, 28727, -2114, -424, 6277, 28968, -2100, -403, 5994, 29203, -2083, - -382, 5716, 29431, -2063, -362, 5442, 29652, -2040, -343, 5172, 29866, -2014, -325, 4908, 30072, -1984, - -307, 4648, 30272, -1951, -289, 4393, 30464, -1914, -272, 4143, 30649, -1874, -256, 3897, 30826, -1830, - -240, 3657, 30995, -1783, -224, 3422, 31157, -1732, -209, 3192, 31310, -1676, -194, 2967, 31456, -1617, - -180, 2747, 31593, -1554, -167, 2532, 31723, -1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338, - -128, 1919, 32061, -1258, -115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990, - -80, 1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669, 32551, -568, - -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630, -200, -5, 69, 32639, -68 - }; - - private static short[] _curveLut2 = new short[] - { - 3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811, 26253, 3751, -42, - 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169, 4199, -54, 2338, 26130, 4354, -58, - 2227, 26085, 4512, -63, 2120, 26035, 4673, -67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76, - 1813, 25852, 5174, -81, 1716, 25780, 5347, -87, 1622, 25704, 5522, -92, 1531, 25621, 5701, -98, - 1442, 25533, 5882, -103, 1357, 25440, 6066, -109, 1274, 25342, 6253, -115, 1193, 25239, 6442, -121, - 1115, 25131, 6635, -127, 1040, 25018, 6830, -133, 967, 24899, 7027, -140, 897, 24776, 7227, -146, - 829, 24648, 7430, -153, 764, 24516, 7635, -159, 701, 24379, 7842, -166, 641, 24237, 8052, -174, - 583, 24091, 8264, -181, 526, 23940, 8478, -187, 472, 23785, 8695, -194, 420, 23626, 8914, -202, - 371, 23462, 9135, -209, 324, 23295, 9358, -215, 279, 23123, 9583, -222, 236, 22948, 9809, -230, - 194, 22769, 10038, -237, 154, 22586, 10269, -243, 117, 22399, 10501, -250, 81, 22208, 10735, -258, - 47, 22015, 10970, -265, 15, 21818, 11206, -271, -16, 21618, 11444, -277, -44, 21415, 11684, -283, - -71, 21208, 11924, -290, -97, 20999, 12166, -296, -121, 20786, 12409, -302, -143, 20571, 12653, -306, - -163, 20354, 12898, -311, -183, 20134, 13143, -316, -201, 19911, 13389, -321, -218, 19686, 13635, -325, - -234, 19459, 13882, -328, -248, 19230, 14130, -332, -261, 18998, 14377, -335, -273, 18765, 14625, -337, - -284, 18531, 14873, -339, -294, 18295, 15121, -341, -302, 18057, 15369, -341, -310, 17817, 15617, -341, - -317, 17577, 15864, -340, -323, 17335, 16111, -340, -328, 17092, 16357, -338, -332, 16848, 16603, -336, - -336, 16603, 16848, -332, -338, 16357, 17092, -328, -340, 16111, 17335, -323, -340, 15864, 17577, -317, - -341, 15617, 17817, -310, -341, 15369, 18057, -302, -341, 15121, 18295, -294, -339, 14873, 18531, -284, - -337, 14625, 18765, -273, -335, 14377, 18998, -261, -332, 14130, 19230, -248, -328, 13882, 19459, -234, - -325, 13635, 19686, -218, -321, 13389, 19911, -201, -316, 13143, 20134, -183, -311, 12898, 20354, -163, - -306, 12653, 20571, -143, -302, 12409, 20786, -121, -296, 12166, 20999, -97, -290, 11924, 21208, -71, - -283, 11684, 21415, -44, -277, 11444, 21618, -16, -271, 11206, 21818, 15, -265, 10970, 22015, 47, - -258, 10735, 22208, 81, -250, 10501, 22399, 117, -243, 10269, 22586, 154, -237, 10038, 22769, 194, - -230, 9809, 22948, 236, -222, 9583, 23123, 279, -215, 9358, 23295, 324, -209, 9135, 23462, 371, - -202, 8914, 23626, 420, -194, 8695, 23785, 472, -187, 8478, 23940, 526, -181, 8264, 24091, 583, - -174, 8052, 24237, 641, -166, 7842, 24379, 701, -159, 7635, 24516, 764, -153, 7430, 24648, 829, - -146, 7227, 24776, 897, -140, 7027, 24899, 967, -133, 6830, 25018, 1040, -127, 6635, 25131, 1115, - -121, 6442, 25239, 1193, -115, 6253, 25342, 1274, -109, 6066, 25440, 1357, -103, 5882, 25533, 1442, - -98, 5701, 25621, 1531, -92, 5522, 25704, 1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813, - -76, 5004, 25919, 1912, -72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227, - -58, 4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897, 26230, 2688, - -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281, 3064, -32, 3329, 26287, 3195 - }; -#endregion - - public static int[] Resample2Ch( - int[] buffer, - int srcSampleRate, - int dstSampleRate, - int samplesCount, - ref int fracPart) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (srcSampleRate <= 0) - { - throw new ArgumentOutOfRangeException(nameof(srcSampleRate)); - } - - if (dstSampleRate <= 0) - { - throw new ArgumentOutOfRangeException(nameof(dstSampleRate)); - } - - double ratio = (double)srcSampleRate / dstSampleRate; - - int newSamplesCount = (int)(samplesCount / ratio); - - int step = (int)(ratio * 0x8000); - - int[] output = new int[newSamplesCount * 2]; - - short[] lut; - - if (step > 0xaaaa) - { - lut = _curveLut0; - } - else if (step <= 0x8000) - { - lut = _curveLut1; - } - else - { - lut = _curveLut2; - } - - int inOffs = 0; - - for (int outOffs = 0; outOffs < output.Length; outOffs += 2) - { - int lutIndex = (fracPart >> 8) * 4; - - int sample0 = buffer[(inOffs + 0) * 2 + 0] * lut[lutIndex + 0] + - buffer[(inOffs + 1) * 2 + 0] * lut[lutIndex + 1] + - buffer[(inOffs + 2) * 2 + 0] * lut[lutIndex + 2] + - buffer[(inOffs + 3) * 2 + 0] * lut[lutIndex + 3]; - - int sample1 = buffer[(inOffs + 0) * 2 + 1] * lut[lutIndex + 0] + - buffer[(inOffs + 1) * 2 + 1] * lut[lutIndex + 1] + - buffer[(inOffs + 2) * 2 + 1] * lut[lutIndex + 2] + - buffer[(inOffs + 3) * 2 + 1] * lut[lutIndex + 3]; - - int newOffset = fracPart + step; - - inOffs += newOffset >> 15; - - fracPart = newOffset & 0x7fff; - - output[outOffs + 0] = sample0 >> 15; - output[outOffs + 1] = sample1 >> 15; - } - - return output; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs deleted file mode 100644 index e46af443..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/SplitterContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Ryujinx.Common; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - class SplitterContext - { - public static long CalcWorkBufferSize(BehaviorInfo behaviorInfo, AudioRendererParameter parameters) - { - if (!behaviorInfo.IsSplitterSupported()) - { - return 0; - } - - long size = parameters.SplitterDestinationDataCount * 0xE0 + - parameters.SplitterCount * 0x20; - - if (!behaviorInfo.IsSplitterBugFixed()) - { - size += BitUtils.AlignUp(4 * parameters.SplitterDestinationDataCount, 16); - } - - return size; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs deleted file mode 100644 index 864bda0d..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererConsts.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class AudioRendererConsts - { - // Revision Consts - public const int Revision = 8; - public const int Rev0Magic = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('0' << 24); - public const int RevMagic = Rev0Magic + (Revision << 24); - - // Misc Consts - public const int BufferAlignment = 0x40; - - // Host Consts - public const int HostSampleRate = 48000; - public const int HostChannelsCount = 2; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs deleted file mode 100644 index 2cfbc4b7..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/AudioRendererParameter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential)] - struct AudioRendererParameter - { - public int SampleRate; - public int SampleCount; - public int MixBufferCount; - public int SubMixCount; - public int VoiceCount; - public int SinkCount; - public int EffectCount; - public int PerformanceManagerCount; - public int VoiceDropEnable; - public int SplitterCount; - public int SplitterDestinationDataCount; - public int Unknown2C; - public int Revision; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/BehaviorIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/BehaviorIn.cs deleted file mode 100644 index 953b4ce3..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/BehaviorIn.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] - struct BehaviorIn - { - public long Unknown0; - public long Unknown8; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/BiquadFilter.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/BiquadFilter.cs deleted file mode 100644 index d0d8ed9b..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/BiquadFilter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0xc, Pack = 1)] - struct BiquadFilter - { - public byte Enable; - public byte Padding; - public short B0; - public short B1; - public short B2; - public short A1; - public short A2; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectIn.cs deleted file mode 100644 index 03520475..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectIn.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0xc0, Pack = 1)] - unsafe struct EffectIn - { - public byte Unknown0x0; - public byte IsNew; - public fixed byte Unknown[0xbe]; - } -} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectOut.cs deleted file mode 100644 index 5106ad9e..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectOut.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 1)] - unsafe struct EffectOut - { - public EffectState State; - public fixed byte Reserved[15]; - } -} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectState.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectState.cs deleted file mode 100644 index ed676684..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/EffectState.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - enum EffectState : byte - { - None = 0, - New = 1 - } -} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolIn.cs deleted file mode 100644 index 8dc53929..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolIn.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 4)] - struct MemoryPoolIn - { - public long Address; - public long Size; - public MemoryPoolState State; - public int Unknown14; - public long Unknown18; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolOut.cs deleted file mode 100644 index 7581e8a7..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolOut.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] - struct MemoryPoolOut - { - public MemoryPoolState State; - public int Unknown14; - public long Unknown18; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolState.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolState.cs deleted file mode 100644 index a82747b8..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/MemoryPoolState.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - enum MemoryPoolState - { - Invalid = 0, - Unknown = 1, - RequestDetach = 2, - Detached = 3, - RequestAttach = 4, - Attached = 5, - Released = 6 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/PlayState.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/PlayState.cs deleted file mode 100644 index d63df971..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/PlayState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - enum PlayState : byte - { - Playing = 0, - Stopped = 1, - Paused = 2 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/RendererInfoOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/RendererInfoOut.cs deleted file mode 100644 index 0ea89384..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/RendererInfoOut.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] - struct RendererInfoOut - { - public ulong ElapsedFrameCount; - public ulong Reserved; - } -} diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs deleted file mode 100644 index a418d89e..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/SupportTags.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - static class SupportTags - { - public const int Splitter = 2; - public const int SplitterBugFix = 5; - public const int PerformanceMetricsDataFormatVersion2 = 5; - public const int VariadicCommandBufferSize = 5; - public const int ElapsedFrameCount = 5; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs deleted file mode 100644 index 6adb874c..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/UpdateDataHeader.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - struct UpdateDataHeader - { -#pragma warning disable CS0649 - public int Revision; - public int BehaviorSize; - public int MemoryPoolSize; - public int VoiceSize; - public int VoiceResourceSize; - public int EffectSize; - public int MixSize; - public int SinkSize; - public int PerformanceManagerSize; - public int Unknown24; - public int ElapsedFrameCountInfoSize; - public int Unknown2C; - public int Unknown30; - public int Unknown34; - public int Unknown38; - public int TotalSize; -#pragma warning restore CS0649 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceChannelResourceIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceChannelResourceIn.cs deleted file mode 100644 index 4871713e..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceChannelResourceIn.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x70, Pack = 1)] - struct VoiceChannelResourceIn - { - // ??? - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceIn.cs deleted file mode 100644 index dbcd5558..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceIn.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)] - struct VoiceIn - { - public int VoiceSlot; - public int NodeId; - - public byte FirstUpdate; - public byte Acquired; - - public PlayState PlayState; - - public SampleFormat SampleFormat; - - public int SampleRate; - - public int Priority; - - public int Unknown14; - - public int ChannelsCount; - - public float Pitch; - public float Volume; - - public BiquadFilter BiquadFilter0; - public BiquadFilter BiquadFilter1; - - public int AppendedWaveBuffersCount; - - public int BaseWaveBufferIndex; - - public int Unknown44; - - public long AdpcmCoeffsPosition; - public long AdpcmCoeffsSize; - - public int VoiceDestination; - public int Padding; - - public WaveBuffer WaveBuffer0; - public WaveBuffer WaveBuffer1; - public WaveBuffer WaveBuffer2; - public WaveBuffer WaveBuffer3; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceOut.cs deleted file mode 100644 index 3a295971..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/VoiceOut.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] - struct VoiceOut - { - public long PlayedSamplesCount; - public int PlayedWaveBuffersCount; - public int VoiceDropsCount; //? - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/WaveBuffer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/WaveBuffer.cs deleted file mode 100644 index 1c0d5630..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/Types/WaveBuffer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)] - struct WaveBuffer - { - public long Position; - public long Size; - public int FirstSampleOffset; - public int LastSampleOffset; - public byte Looping; - public byte LastBuffer; - public short Unknown1A; - public int Unknown1C; - public long AdpcmLoopContextPosition; - public long AdpcmLoopContextSize; - public long Unknown30; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs deleted file mode 100644 index c14c46b3..00000000 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager/VoiceContext.cs +++ /dev/null @@ -1,201 +0,0 @@ -using Ryujinx.Audio.Adpcm; -using Ryujinx.Cpu; -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager -{ - class VoiceContext - { - private bool _acquired; - private bool _bufferReload; - - private int _resamplerFracPart; - - private int _bufferIndex; - private int _offset; - - public int SampleRate { get; set; } - public int ChannelsCount { get; set; } - - public float Volume { get; set; } - - public PlayState PlayState { get; set; } - - public SampleFormat SampleFormat { get; set; } - - public AdpcmDecoderContext AdpcmCtx { get; set; } - - public WaveBuffer[] WaveBuffers { get; } - - public WaveBuffer CurrentWaveBuffer => WaveBuffers[_bufferIndex]; - - private VoiceOut _outStatus; - - public VoiceOut OutStatus => _outStatus; - - private int[] _samples; - - public bool Playing => _acquired && PlayState == PlayState.Playing; - - public VoiceContext() - { - WaveBuffers = new WaveBuffer[4]; - } - - public void SetAcquireState(bool newState) - { - if (_acquired && !newState) - { - // Release. - Reset(); - } - - _acquired = newState; - } - - private void Reset() - { - _bufferReload = true; - - _bufferIndex = 0; - _offset = 0; - - _outStatus.PlayedSamplesCount = 0; - _outStatus.PlayedWaveBuffersCount = 0; - _outStatus.VoiceDropsCount = 0; - } - - public int[] GetBufferData(MemoryManager memory, int maxSamples, out int samplesCount) - { - if (!Playing) - { - samplesCount = 0; - - return null; - } - - if (_bufferReload) - { - _bufferReload = false; - - UpdateBuffer(memory); - } - - WaveBuffer wb = WaveBuffers[_bufferIndex]; - - int maxSize = _samples.Length - _offset; - - int size = maxSamples * AudioRendererConsts.HostChannelsCount; - - if (size > maxSize) - { - size = maxSize; - } - - int[] output = new int[size]; - - Array.Copy(_samples, _offset, output, 0, size); - - samplesCount = size / AudioRendererConsts.HostChannelsCount; - - _outStatus.PlayedSamplesCount += samplesCount; - - _offset += size; - - if (_offset == _samples.Length) - { - _offset = 0; - - if (wb.Looping == 0) - { - SetBufferIndex(_bufferIndex + 1); - } - - _outStatus.PlayedWaveBuffersCount++; - - if (wb.LastBuffer != 0) - { - PlayState = PlayState.Paused; - } - } - - return output; - } - - private void UpdateBuffer(MemoryManager memory) - { - // TODO: Implement conversion for formats other - // than interleaved stereo (2 channels). - // As of now, it assumes that HostChannelsCount == 2. - WaveBuffer wb = WaveBuffers[_bufferIndex]; - - if (wb.Position == 0) - { - _samples = new int[0]; - - return; - } - - if (SampleFormat == SampleFormat.PcmInt16) - { - int samplesCount = (int)(wb.Size / (sizeof(short) * ChannelsCount)); - - _samples = new int[samplesCount * AudioRendererConsts.HostChannelsCount]; - - if (ChannelsCount == 1) - { - for (int index = 0; index < samplesCount; index++) - { - short sample = memory.Read<short>((ulong)(wb.Position + index * 2)); - - _samples[index * 2 + 0] = sample; - _samples[index * 2 + 1] = sample; - } - } - else - { - for (int index = 0; index < samplesCount * 2; index++) - { - _samples[index] = memory.Read<short>((ulong)(wb.Position + index * 2)); - } - } - } - else if (SampleFormat == SampleFormat.Adpcm) - { - byte[] buffer = new byte[wb.Size]; - - memory.Read((ulong)wb.Position, buffer); - - _samples = AdpcmDecoder.Decode(buffer, AdpcmCtx); - } - else - { - throw new InvalidOperationException(); - } - - if (SampleRate != AudioRendererConsts.HostSampleRate) - { - // TODO: We should keep the frames being discarded (see the 4 below) - // on a buffer and include it on the next samples buffer, to allow - // the resampler to do seamless interpolation between wave buffers. - int samplesCount = _samples.Length / AudioRendererConsts.HostChannelsCount; - - samplesCount = Math.Max(samplesCount - 4, 0); - - _samples = Resampler.Resample2Ch( - _samples, - SampleRate, - AudioRendererConsts.HostSampleRate, - samplesCount, - ref _resamplerFracPart); - } - } - - public void SetBufferIndex(int index) - { - _bufferIndex = index & 3; - - _bufferReload = true; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs new file mode 100644 index 00000000..94972dbf --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs @@ -0,0 +1,105 @@ +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; + +namespace Ryujinx.HLE.HOS.Services.Audio +{ + [Service("audren:u")] + class AudioRendererManagerServer : IpcService + { + private const int InitialRevision = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('1' << 24); + + private IAudioRendererManager _impl; + + public AudioRendererManagerServer(ServiceCtx context) : this(new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { } + + public AudioRendererManagerServer(IAudioRendererManager impl) + { + _impl = impl; + } + + [Command(0)] + // OpenAudioRenderer(nn::audio::detail::AudioRendererParameterInternal parameter, u64 workBufferSize, nn::applet::AppletResourceUserId appletResourceId, pid, handle<copy> workBuffer, handle<copy> processHandle) + // -> object<nn::audio::detail::IAudioRenderer> + public ResultCode OpenAudioRenderer(ServiceCtx context) + { + AudioRendererConfiguration parameter = context.RequestData.ReadStruct<AudioRendererConfiguration>(); + ulong workBufferSize = context.RequestData.ReadUInt64(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + + KTransferMemory workBufferTransferMemory = context.Process.HandleTable.GetObject<KTransferMemory>(context.Request.HandleDesc.ToCopy[0]); + uint processHandle = (uint)context.Request.HandleDesc.ToCopy[1]; + + ResultCode result = _impl.OpenAudioRenderer(context, out IAudioRenderer renderer, ref parameter, workBufferSize, appletResourceUserId, workBufferTransferMemory, processHandle); + + if (result == ResultCode.Success) + { + MakeObject(context, new AudioRendererServer(renderer)); + } + + return result; + } + + [Command(1)] + // GetWorkBufferSize(nn::audio::detail::AudioRendererParameterInternal parameter) -> u64 workBufferSize + public ResultCode GetAudioRendererWorkBufferSize(ServiceCtx context) + { + AudioRendererConfiguration parameter = context.RequestData.ReadStruct<AudioRendererConfiguration>(); + + if (BehaviourContext.CheckValidRevision(parameter.Revision)) + { + ulong size = _impl.GetWorkBufferSize(ref parameter); + + context.ResponseData.Write(size); + + Logger.Debug?.Print(LogClass.ServiceAudio, $"WorkBufferSize is 0x{size:x16}."); + + return ResultCode.Success; + } + else + { + context.ResponseData.Write(0L); + + Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{BehaviourContext.GetRevisionNumber(parameter.Revision)} is not supported!"); + + return ResultCode.UnsupportedRevision; + } + } + + [Command(2)] + // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice> + public ResultCode GetAudioDeviceService(ServiceCtx context) + { + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + + ResultCode result = _impl.GetAudioDeviceServiceWithRevisionInfo(context, out IAudioDevice device, InitialRevision, appletResourceUserId); + + if (result == ResultCode.Success) + { + MakeObject(context, new AudioDeviceServer(device)); + } + + return result; + } + + [Command(4)] // 4.0.0+ + // GetAudioDeviceServiceWithRevisionInfo(s32 revision, nn::applet::AppletResourceUserId appletResourceId) -> object<nn::audio::detail::IAudioDevice> + public ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context) + { + int revision = context.RequestData.ReadInt32(); + ulong appletResourceUserId = context.RequestData.ReadUInt64(); + + ResultCode result = _impl.GetAudioDeviceServiceWithRevisionInfo(context, out IAudioDevice device, revision, appletResourceUserId); + + if (result == ResultCode.Success) + { + MakeObject(context, new AudioDeviceServer(device)); + } + + return result; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs index 7e770a78..642e2525 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs @@ -1,148 +1,19 @@ -using Ryujinx.Audio; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; namespace Ryujinx.HLE.HOS.Services.Audio { - [Service("audren:u")] - class IAudioRendererManager : IpcService + interface IAudioRendererManager { - public IAudioRendererManager(ServiceCtx context) { } + // TODO: Remove ServiceCtx argument + // BODY: This is only needed by the legacy backend. Refactor this when removing the legacy backend. + ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context, out IAudioDevice outObject, int revision, ulong appletResourceUserId); - [Command(0)] - // OpenAudioRenderer(nn::audio::detail::AudioRendererParameterInternal, u64, nn::applet::AppletResourceUserId, pid, handle<copy>, handle<copy>) - // -> object<nn::audio::detail::IAudioRenderer> - public ResultCode OpenAudioRenderer(ServiceCtx context) - { - IAalOutput audioOut = context.Device.AudioOut; + // TODO: Remove ServiceCtx argument + // BODY: This is only needed by the legacy backend. Refactor this when removing the legacy backend. + ResultCode OpenAudioRenderer(ServiceCtx context, out IAudioRenderer obj, ref AudioRendererConfiguration parameter, ulong workBufferSize, ulong appletResourceUserId, KTransferMemory workBufferTransferMemory, uint processHandle); - AudioRendererParameter Params = GetAudioRendererParameter(context); - - MakeObject(context, new IAudioRenderer( - context.Device.System, - context.Memory, - audioOut, - Params)); - - return ResultCode.Success; - } - - [Command(1)] - // GetWorkBufferSize(nn::audio::detail::AudioRendererParameterInternal) -> u64 - public ResultCode GetAudioRendererWorkBufferSize(ServiceCtx context) - { - AudioRendererParameter parameters = GetAudioRendererParameter(context); - - if (AudioRendererCommon.CheckValidRevision(parameters)) - { - BehaviorInfo behaviorInfo = new BehaviorInfo(); - - behaviorInfo.SetUserLibRevision(parameters.Revision); - - long size; - - int totalMixCount = parameters.SubMixCount + 1; - - size = BitUtils.AlignUp(parameters.MixBufferCount * 4, AudioRendererConsts.BufferAlignment) + - parameters.SubMixCount * 0x400 + - totalMixCount * 0x940 + - parameters.VoiceCount * 0x3F0 + - BitUtils.AlignUp(totalMixCount * 8, 16) + - BitUtils.AlignUp(parameters.VoiceCount * 8, 16) + - BitUtils.AlignUp(((parameters.SinkCount + parameters.SubMixCount) * 0x3C0 + parameters.SampleCount * 4) * - (parameters.MixBufferCount + 6), AudioRendererConsts.BufferAlignment) + - (parameters.SinkCount + parameters.SubMixCount) * 0x2C0 + - (parameters.EffectCount + parameters.VoiceCount * 4) * 0x30 + - 0x50; - - if (behaviorInfo.IsSplitterSupported()) - { - size += BitUtils.AlignUp(NodeStates.GetWorkBufferSize(totalMixCount) + EdgeMatrix.GetWorkBufferSize(totalMixCount), 16); - } - - size = parameters.SinkCount * 0x170 + - (parameters.SinkCount + parameters.SubMixCount) * 0x280 + - parameters.EffectCount * 0x4C0 + - ((size + SplitterContext.CalcWorkBufferSize(behaviorInfo, parameters) + 0x30 * parameters.EffectCount + (4 * parameters.VoiceCount) + 0x8F) & ~0x3FL) + - ((parameters.VoiceCount << 8) | 0x40); - - if (parameters.PerformanceManagerCount >= 1) - { - size += (PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(behaviorInfo, parameters) * - (parameters.PerformanceManagerCount + 1) + 0xFF) & ~0x3FL; - } - - if (behaviorInfo.IsVariadicCommandBufferSizeSupported()) - { - size += CommandGenerator.CalculateCommandBufferSize(parameters) + 0x7E; - } - else - { - size += 0x1807E; - } - - size = BitUtils.AlignUp(size, 0x1000); - - context.ResponseData.Write(size); - - Logger.Debug?.Print(LogClass.ServiceAudio, $"WorkBufferSize is 0x{size:x16}."); - - return ResultCode.Success; - } - else - { - context.ResponseData.Write(0L); - - Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{AudioRendererCommon.GetRevisionVersion(parameters.Revision)} is not supported!"); - - return ResultCode.UnsupportedRevision; - } - } - - private AudioRendererParameter GetAudioRendererParameter(ServiceCtx context) - { - AudioRendererParameter Params = new AudioRendererParameter - { - SampleRate = context.RequestData.ReadInt32(), - SampleCount = context.RequestData.ReadInt32(), - MixBufferCount = context.RequestData.ReadInt32(), - SubMixCount = context.RequestData.ReadInt32(), - VoiceCount = context.RequestData.ReadInt32(), - SinkCount = context.RequestData.ReadInt32(), - EffectCount = context.RequestData.ReadInt32(), - PerformanceManagerCount = context.RequestData.ReadInt32(), - VoiceDropEnable = context.RequestData.ReadInt32(), - SplitterCount = context.RequestData.ReadInt32(), - SplitterDestinationDataCount = context.RequestData.ReadInt32(), - Unknown2C = context.RequestData.ReadInt32(), - Revision = context.RequestData.ReadInt32() - }; - - return Params; - } - - [Command(2)] - // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice> - public ResultCode GetAudioDeviceService(ServiceCtx context) - { - long appletResourceUserId = context.RequestData.ReadInt64(); - - MakeObject(context, new IAudioDevice(context.Device.System)); - - return ResultCode.Success; - } - - [Command(4)] // 4.0.0+ - // GetAudioDeviceServiceWithRevisionInfo(u32 revision_info, nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice> - public ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context) - { - int revisionInfo = context.RequestData.ReadInt32(); - long appletResourceUserId = context.RequestData.ReadInt64(); - - Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { appletResourceUserId, revisionInfo }); - - return GetAudioDeviceService(context); - } + ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter); } } |
