diff options
author | Mary <me@thog.eu> | 2020-08-18 03:49:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-17 22:49:37 -0300 |
commit | a389dd59bd881cf2cff09a1f67f5c30de61123e6 (patch) | |
tree | 90e53ccbc18534b7c40323324d9a10675eb1a879 /Ryujinx.HLE/HOS/Services | |
parent | 2a314f3c28bb000b796f784a0eb791b63eae22c2 (diff) |
Amadeus: Final Act (#1481)
* Amadeus: Final Act
This is my requiem, I present to you Amadeus, a complete reimplementation of the Audio Renderer!
This reimplementation is based on my reversing of every version of the audio system module that I carried for the past 10 months.
This supports every revision (at the time of writing REV1 to REV8 included) and all features proposed by the Audio Renderer on real hardware.
Because this component could be used outside an emulation context, and to avoid possible "inspirations" not crediting the project, I decided to license the Ryujinx.Audio.Renderer project under LGPLv3.
- FE3H voices in videos and chapter intro are not present.
- Games that use two audio renderer **at the same time** are probably going to have issues right now **until we rewrite the audio output interface** (Crash Team Racing is the only known game to use two renderer at the same time).
- Persona 5 Scrambler now goes ingame but audio is garbage. This is caused by the fact that the game engine is syncing audio and video in a really aggressive way. This will disappears the day this game run at full speed.
* Make timing more precise when sleeping on Windows
Improve precision to a 1ms resolution on Windows NT based OS.
This is used to avoid having totally erratic timings and unify all
Windows users to the same resolution.
NOTE: This is only active when emulation is running.
Diffstat (limited to 'Ryujinx.HLE/HOS/Services')
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); } } |