From d4d0a48bfe89d6e8e12ce16829bb2c440b56007c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 22 Feb 2024 16:58:33 -0300 Subject: [PATCH] Migrate Audio service to new IPC (#6285) * Migrate audren to new IPC * Migrate audout * Migrate audin * Migrate hwopus * Bye bye old audio service * Switch volume control to IHardwareDeviceDriver * Somewhat unrelated changes * Remove Concentus reference from HLE * Implement OpenAudioRendererForManualExecution * Remove SetVolume/GetVolume methods that are not necessary * Remove SetVolume/GetVolume methods that are not necessary (2) * Fix incorrect volume update * PR feedback * PR feedback * Stub audrec * Init outParameter * Make FinalOutputRecorderParameter/Internal readonly * Make FinalOutputRecorder IDisposable * Fix HardwareOpusDecoderManager parameter buffers * Opus work buffer size and error handling improvements * Add AudioInProtocolName enum * Fix potential divisions by zero --- .../OpenALHardwareDeviceDriver.cs | 25 +- .../OpenALHardwareDeviceSession.cs | 22 +- .../SDL2HardwareDeviceDriver.cs | 8 +- .../SDL2HardwareDeviceSession.cs | 6 +- .../SoundIoHardwareDeviceDriver.cs | 25 +- .../SoundIoHardwareDeviceSession.cs | 17 +- .../CompatLayerHardwareDeviceDriver.cs | 12 +- .../Dummy/DummyHardwareDeviceDriver.cs | 8 +- .../Dummy/DummyHardwareDeviceSessionOutput.cs | 4 +- src/Ryujinx.Audio/Input/AudioInputManager.cs | 8 +- .../Integration/HardwareDeviceImpl.cs | 4 +- .../Integration/IHardwareDeviceDriver.cs | 4 +- .../Output/AudioOutputManager.cs | 47 +-- .../Renderer/Dsp/AudioProcessor.cs | 34 +- .../Renderer/Server/AudioRendererManager.cs | 28 +- src/Ryujinx.HLE/HOS/Horizon.cs | 95 +---- .../Kernel/SupervisorCall/ExternalEvent.cs | 25 ++ .../HOS/Kernel/SupervisorCall/Syscall.cs | 32 ++ .../HOS/Services/Audio/AudioIn/AudioIn.cs | 108 ----- .../Services/Audio/AudioIn/AudioInServer.cs | 200 --------- .../HOS/Services/Audio/AudioIn/IAudioIn.cs | 34 -- .../HOS/Services/Audio/AudioInManager.cs | 40 -- .../Services/Audio/AudioInManagerServer.cs | 243 ----------- .../HOS/Services/Audio/AudioOut/AudioOut.cs | 108 ----- .../Services/Audio/AudioOut/AudioOutServer.cs | 181 -------- .../HOS/Services/Audio/AudioOut/IAudioOut.cs | 33 -- .../HOS/Services/Audio/AudioOutManager.cs | 40 -- .../Services/Audio/AudioOutManagerServer.cs | 166 -------- .../Audio/AudioRenderer/AudioDevice.cs | 174 -------- .../Audio/AudioRenderer/AudioDeviceServer.cs | 320 --------------- .../Audio/AudioRenderer/AudioKernelEvent.cs | 25 -- .../Audio/AudioRenderer/AudioRenderer.cs | 122 ------ .../AudioRenderer/AudioRendererServer.cs | 215 ---------- .../Audio/AudioRenderer/IAudioDevice.cs | 18 - .../Audio/AudioRenderer/IAudioRenderer.cs | 22 - .../Services/Audio/AudioRendererManager.cs | 67 --- .../Audio/AudioRendererManagerServer.cs | 116 ------ .../HardwareOpusDecoderManager/Decoder.cs | 27 -- .../DecoderCommon.cs | 92 ----- .../HardwareOpusDecoderManager/IDecoder.cs | 11 - .../IHardwareOpusDecoder.cs | 116 ------ .../MultiSampleDecoder.cs | 28 -- .../HOS/Services/Audio/IAudioController.cs | 8 - .../HOS/Services/Audio/IAudioInManager.cs | 12 - .../Audio/IAudioInManagerForApplet.cs | 8 - .../Audio/IAudioInManagerForDebugger.cs | 8 - .../HOS/Services/Audio/IAudioOutManager.cs | 12 - .../Audio/IAudioOutManagerForApplet.cs | 8 - .../Audio/IAudioOutManagerForDebugger.cs | 8 - .../Services/Audio/IAudioRendererManager.cs | 19 - .../Audio/IAudioRendererManagerForApplet.cs | 8 - .../Audio/IAudioRendererManagerForDebugger.cs | 8 - .../HOS/Services/Audio/IAudioSnoopManager.cs | 8 - .../Audio/IFinalOutputRecorderManager.cs | 8 - .../IFinalOutputRecorderManagerForApplet.cs | 8 - .../IFinalOutputRecorderManagerForDebugger.cs | 8 - .../Audio/IHardwareOpusDecoderManager.cs | 227 ---------- .../HOS/Services/Audio/ResultCode.cs | 21 - .../Services/Audio/Types/OpusPacketHeader.cs | 23 -- .../Services/Audio/Types/OpusParametersEx.cs | 15 - src/Ryujinx.HLE/Ryujinx.HLE.csproj | 6 - src/Ryujinx.HLE/Switch.cs | 6 +- src/Ryujinx.Horizon.Common/IExternalEvent.cs | 10 + src/Ryujinx.Horizon.Common/ISyscallApi.cs | 5 + src/Ryujinx.Horizon.Common/Result.cs | 5 + .../Hipc/HipcGenerator.cs | 4 +- src/Ryujinx.Horizon/Arp/ArpIpcServer.cs | 1 + src/Ryujinx.Horizon/Audio/AudioMain.cs | 17 + src/Ryujinx.Horizon/Audio/AudioManagers.cs | 78 ++++ .../Audio/AudioUserIpcServer.cs | 55 +++ src/Ryujinx.Horizon/Audio/HwopusIpcServer.cs | 46 +++ src/Ryujinx.Horizon/Audio/HwopusMain.cs | 17 + src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs | 1 + .../Friends/FriendsIpcServer.cs | 1 + src/Ryujinx.Horizon/HorizonOptions.cs | 14 +- src/Ryujinx.Horizon/Hshl/HshlIpcServer.cs | 1 + src/Ryujinx.Horizon/Ins/InsIpcServer.cs | 1 + src/Ryujinx.Horizon/Lbl/LblIpcServer.cs | 1 + src/Ryujinx.Horizon/LogManager/LmIpcServer.cs | 1 + src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs | 1 + src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs | 2 +- src/Ryujinx.Horizon/Ovln/OvlnIpcServer.cs | 1 + src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs | 1 + src/Ryujinx.Horizon/Psc/PscIpcServer.cs | 1 + src/Ryujinx.Horizon/Ryujinx.Horizon.csproj | 7 + src/Ryujinx.Horizon/Sdk/Account/Uid.cs | 2 +- src/Ryujinx.Horizon/Sdk/Applet/AppletId.cs | 71 ++++ .../Sdk/Applet/AppletResourceUserId.cs | 15 + src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs | 50 +++ src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs | 12 + .../Sdk/Audio/Detail/AudioDevice.cs | 252 ++++++++++++ .../Sdk/Audio/Detail/AudioIn.cs | 171 ++++++++ .../Sdk/Audio/Detail/AudioInManager.cs | 130 ++++++ .../Sdk/Audio/Detail/AudioInProtocol.cs | 23 ++ .../Sdk/Audio/Detail/AudioInProtocolName.cs | 8 + .../Sdk/Audio/Detail/AudioOut.cs | 154 +++++++ .../Sdk/Audio/Detail/AudioOutManager.cs | 93 +++++ .../Sdk/Audio/Detail/AudioRenderer.cs | 187 +++++++++ .../Sdk/Audio/Detail/AudioRendererManager.cs | 132 ++++++ .../Detail/AudioRendererParameterInternal.cs | 14 + .../Sdk/Audio/Detail/AudioSnoopManager.cs | 30 ++ .../Sdk/Audio/Detail/DeviceName.cs | 30 ++ .../Sdk/Audio/Detail/FinalOutputRecorder.cs | 147 +++++++ .../Detail/FinalOutputRecorderManager.cs | 23 ++ .../Detail/FinalOutputRecorderParameter.cs | 17 + .../FinalOutputRecorderParameterInternal.cs | 21 + .../Sdk/Audio/Detail/IAudioDevice.cs | 24 ++ .../Sdk/Audio/Detail/IAudioIn.cs | 26 ++ .../Sdk/Audio/Detail/IAudioInManager.cs | 43 ++ .../Sdk/Audio/Detail/IAudioOut.cs | 25 ++ .../Sdk/Audio/Detail/IAudioOutManager.cs | 32 ++ .../Sdk/Audio/Detail/IAudioRenderer.cs | 24 ++ .../Sdk/Audio/Detail/IAudioRendererManager.cs | 29 ++ .../Sdk/Audio/Detail/IAudioSnoopManager.cs | 12 + .../Sdk/Audio/Detail/IFinalOutputRecorder.cs | 22 + .../Detail/IFinalOutputRecorderManager.cs | 16 + src/Ryujinx.Horizon/Sdk/Codec/CodecResult.cs | 16 + .../Sdk/Codec/Detail/HardwareOpusDecoder.cs | 336 +++++++++++++++ .../Detail/HardwareOpusDecoderManager.cs | 386 ++++++++++++++++++ .../HardwareOpusDecoderParameterInternal.cs | 11 + .../HardwareOpusDecoderParameterInternalEx.cs | 13 + ...pusMultiStreamDecoderParameterInternal.cs} | 6 +- ...sMultiStreamDecoderParameterInternalEx.cs} | 10 +- .../Sdk/Codec/Detail/IHardwareOpusDecoder.cs | 20 + .../Detail/IHardwareOpusDecoderManager.cs | 19 + .../Sdk/Codec/Detail}/OpusDecoderFlags.cs | 2 +- src/Ryujinx.Horizon/ServiceTable.cs | 3 + src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs | 1 + src/Ryujinx.Horizon/Usb/UsbIpcServer.cs | 1 + src/Ryujinx.Horizon/Wlan/WlanIpcServer.cs | 1 + 130 files changed, 3096 insertions(+), 3174 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ExternalEvent.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs delete mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs create mode 100644 src/Ryujinx.Horizon.Common/IExternalEvent.cs create mode 100644 src/Ryujinx.Horizon/Audio/AudioMain.cs create mode 100644 src/Ryujinx.Horizon/Audio/AudioManagers.cs create mode 100644 src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs create mode 100644 src/Ryujinx.Horizon/Audio/HwopusIpcServer.cs create mode 100644 src/Ryujinx.Horizon/Audio/HwopusMain.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Applet/AppletId.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Applet/AppletResourceUserId.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocolName.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererParameterInternal.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameter.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameterInternal.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRendererManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorderManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/CodecResult.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderManager.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternal.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternalEx.cs rename src/{Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs => Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternal.cs} (65%) rename src/{Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs => Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternalEx.cs} (64%) create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoder.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoderManager.cs rename src/{Ryujinx.HLE/HOS/Services/Audio/Types => Ryujinx.Horizon/Sdk/Codec/Detail}/OpusDecoderFlags.cs (72%) diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs index 744a4bc56..01286992f 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs @@ -20,6 +20,25 @@ namespace Ryujinx.Audio.Backends.OpenAL private bool _stillRunning; private readonly Thread _updaterThread; + private float _volume; + + public float Volume + { + get + { + return _volume; + } + set + { + _volume = value; + + foreach (OpenALHardwareDeviceSession session in _sessions.Keys) + { + session.UpdateMasterVolume(value); + } + } + } + public OpenALHardwareDeviceDriver() { _device = ALC.OpenDevice(""); @@ -34,6 +53,8 @@ namespace Ryujinx.Audio.Backends.OpenAL Name = "HardwareDeviceDriver.OpenAL", }; + _volume = 1f; + _updaterThread.Start(); } @@ -52,7 +73,7 @@ namespace Ryujinx.Audio.Backends.OpenAL } } - public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume) + public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) { if (channelCount == 0) { @@ -73,7 +94,7 @@ namespace Ryujinx.Audio.Backends.OpenAL throw new ArgumentException($"{channelCount}"); } - OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + OpenALHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount); _sessions.TryAdd(session, 0); diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs index a52821616..3b9129130 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -16,10 +16,11 @@ namespace Ryujinx.Audio.Backends.OpenAL private bool _isActive; private readonly Queue _queuedBuffers; private ulong _playedSampleCount; + private float _volume; private readonly object _lock = new(); - public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) + public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { _driver = driver; _queuedBuffers = new Queue(); @@ -27,7 +28,7 @@ namespace Ryujinx.Audio.Backends.OpenAL _targetFormat = GetALFormat(); _isActive = false; _playedSampleCount = 0; - SetVolume(requestedVolume); + SetVolume(1f); } private ALFormat GetALFormat() @@ -85,17 +86,22 @@ namespace Ryujinx.Audio.Backends.OpenAL public override void SetVolume(float volume) { - lock (_lock) - { - AL.Source(_sourceId, ALSourcef.Gain, volume); - } + _volume = volume; + + UpdateMasterVolume(_driver.Volume); } public override float GetVolume() { - AL.GetSource(_sourceId, ALSourcef.Gain, out float volume); + return _volume; + } - return volume; + public void UpdateMasterVolume(float newVolume) + { + lock (_lock) + { + AL.Source(_sourceId, ALSourcef.Gain, newVolume * _volume); + } } public override void Start() diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index b83e63dbc..e39bfe549 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -20,6 +20,8 @@ namespace Ryujinx.Audio.Backends.SDL2 private readonly bool _supportSurroundConfiguration; + public float Volume { get; set; } + // TODO: Add this to SDL2-CS // NOTE: We use a DllImport here because of marshaling issue for spec. #pragma warning disable SYSLIB1054 @@ -48,6 +50,8 @@ namespace Ryujinx.Audio.Backends.SDL2 { _supportSurroundConfiguration = spec.channels >= 6; } + + Volume = 1f; } public static bool IsSupported => IsSupportedInternal(); @@ -74,7 +78,7 @@ namespace Ryujinx.Audio.Backends.SDL2 return _pauseEvent; } - public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume) + public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) { if (channelCount == 0) { @@ -91,7 +95,7 @@ namespace Ryujinx.Audio.Backends.SDL2 throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!"); } - SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + SDL2HardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount); _sessions.TryAdd(session, 0); diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs index 7a683f4ed..00188ba58 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Backends.SDL2 private float _volume; private readonly ushort _nativeSampleFormat; - public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) + public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { _driver = driver; _updateRequiredEvent = _driver.GetUpdateRequiredEvent(); @@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Backends.SDL2 _nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat); _sampleCount = uint.MaxValue; _started = false; - _volume = requestedVolume; + _volume = 1f; } private void EnsureAudioStreamSetup(AudioBuffer buffer) @@ -99,7 +99,7 @@ namespace Ryujinx.Audio.Backends.SDL2 streamSpan.Clear(); // Apply volume to written data - SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME)); + SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_driver.Volume * _volume * SDL_MIX_MAXVOLUME)); } ulong sampleCount = GetSampleCount(samples.Length); diff --git a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs index ff0392882..e3e5d2913 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs @@ -19,6 +19,25 @@ namespace Ryujinx.Audio.Backends.SoundIo private readonly ConcurrentDictionary _sessions; private int _disposeState; + private float _volume = 1f; + + public float Volume + { + get + { + return _volume; + } + set + { + _volume = value; + + foreach (SoundIoHardwareDeviceSession session in _sessions.Keys) + { + session.UpdateMasterVolume(value); + } + } + } + public SoundIoHardwareDeviceDriver() { _audioContext = SoundIoContext.Create(); @@ -122,7 +141,7 @@ namespace Ryujinx.Audio.Backends.SoundIo return _pauseEvent; } - public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume) + public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) { if (channelCount == 0) { @@ -134,14 +153,12 @@ namespace Ryujinx.Audio.Backends.SoundIo sampleRate = Constants.TargetSampleRate; } - volume = Math.Clamp(volume, 0, 1); - if (direction != Direction.Output) { throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!"); } - SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + SoundIoHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount); _sessions.TryAdd(session, 0); diff --git a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs index 123cfd27a..f60982e30 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs @@ -18,16 +18,18 @@ namespace Ryujinx.Audio.Backends.SoundIo private readonly DynamicRingBuffer _ringBuffer; private ulong _playedSampleCount; private readonly ManualResetEvent _updateRequiredEvent; + private float _volume; private int _disposeState; - public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) + public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { _driver = driver; _updateRequiredEvent = _driver.GetUpdateRequiredEvent(); _queuedBuffers = new ConcurrentQueue(); _ringBuffer = new DynamicRingBuffer(); + _volume = 1f; - SetupOutputStream(requestedVolume); + SetupOutputStream(driver.Volume); } private void SetupOutputStream(float requestedVolume) @@ -47,7 +49,7 @@ namespace Ryujinx.Audio.Backends.SoundIo public override float GetVolume() { - return _outputStream.Volume; + return _volume; } public override void PrepareToClose() { } @@ -63,7 +65,14 @@ namespace Ryujinx.Audio.Backends.SoundIo public override void SetVolume(float volume) { - _outputStream.SetVolume(volume); + _volume = volume; + + _outputStream.SetVolume(_driver.Volume * volume); + } + + public void UpdateMasterVolume(float newVolume) + { + _outputStream.SetVolume(newVolume * _volume); } public override void Start() diff --git a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs index 3f3806c3e..a2c2cdcd0 100644 --- a/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs @@ -16,6 +16,12 @@ namespace Ryujinx.Audio.Backends.CompatLayer public static bool IsSupported => true; + public float Volume + { + get => _realDriver.Volume; + set => _realDriver.Volume = value; + } + public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice) { _realDriver = realDevice; @@ -90,7 +96,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer throw new ArgumentException("No valid sample format configuration found!"); } - public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume) + public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) { if (channelCount == 0) { @@ -102,8 +108,6 @@ namespace Ryujinx.Audio.Backends.CompatLayer sampleRate = Constants.TargetSampleRate; } - volume = Math.Clamp(volume, 0, 1); - if (!_realDriver.SupportsDirection(direction)) { if (direction == Direction.Input) @@ -119,7 +123,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat); uint hardwareChannelCount = SelectHardwareChannelCount(channelCount); - IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount, volume); + IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount); if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat) { diff --git a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs index bac21c448..3a3c1d1b1 100644 --- a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs @@ -14,13 +14,17 @@ namespace Ryujinx.Audio.Backends.Dummy public static bool IsSupported => true; + public float Volume { get; set; } + public DummyHardwareDeviceDriver() { _updateRequiredEvent = new ManualResetEvent(false); _pauseEvent = new ManualResetEvent(true); + + Volume = 1f; } - public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume) + public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) { if (sampleRate == 0) { @@ -34,7 +38,7 @@ namespace Ryujinx.Audio.Backends.Dummy if (direction == Direction.Output) { - return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount, volume); + return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount); } return new DummyHardwareDeviceSessionInput(this, memoryManager); diff --git a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs index 1c248faaa..34cf653c2 100644 --- a/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs +++ b/src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Audio.Backends.Dummy private ulong _playedSampleCount; - public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) + public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) { - _volume = requestedVolume; + _volume = 1f; _manager = manager; } diff --git a/src/Ryujinx.Audio/Input/AudioInputManager.cs b/src/Ryujinx.Audio/Input/AudioInputManager.cs index 4d1796c96..d56997e9c 100644 --- a/src/Ryujinx.Audio/Input/AudioInputManager.cs +++ b/src/Ryujinx.Audio/Input/AudioInputManager.cs @@ -166,7 +166,6 @@ namespace Ryujinx.Audio.Input /// /// If true, filter disconnected devices /// The list of all audio inputs name -#pragma warning disable CA1822 // Mark member as static public string[] ListAudioIns(bool filtered) { if (filtered) @@ -176,7 +175,6 @@ namespace Ryujinx.Audio.Input return new[] { Constants.DefaultDeviceInputName }; } -#pragma warning restore CA1822 /// /// Open a new . @@ -188,8 +186,6 @@ namespace Ryujinx.Audio.Input /// The input device name wanted by the user /// The sample format to use /// The user configuration - /// The applet resource user id of the application - /// The process handle of the application /// A reporting an error or a success public ResultCode OpenAudioIn(out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, @@ -197,9 +193,7 @@ namespace Ryujinx.Audio.Input IVirtualMemoryManager memoryManager, string inputDeviceName, SampleFormat sampleFormat, - ref AudioInputConfiguration parameter, - ulong appletResourceUserId, - uint processHandle) + ref AudioInputConfiguration parameter) { int sessionId = AcquireSessionId(); diff --git a/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs index 576954b96..1369f953a 100644 --- a/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs +++ b/src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Audio.Integration private readonly byte[] _buffer; - public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate, float volume) + public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate) { - _session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount, volume); + _session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount); _channelCount = channelCount; _sampleRate = sampleRate; _currentBufferTag = 0; diff --git a/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs b/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs index 9c812fb9a..95b0e4e5e 100644 --- a/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs @@ -16,7 +16,9 @@ namespace Ryujinx.Audio.Integration Output, } - IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume = 1f); + float Volume { get; set; } + + IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount); ManualResetEvent GetUpdateRequiredEvent(); ManualResetEvent GetPauseEvent(); diff --git a/src/Ryujinx.Audio/Output/AudioOutputManager.cs b/src/Ryujinx.Audio/Output/AudioOutputManager.cs index 5232357bb..308cd1564 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputManager.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputManager.cs @@ -165,12 +165,10 @@ namespace Ryujinx.Audio.Output /// Get the list of all audio outputs name. /// /// The list of all audio outputs name -#pragma warning disable CA1822 // Mark member as static public string[] ListAudioOuts() { return new[] { Constants.DefaultDeviceOutputName }; } -#pragma warning restore CA1822 /// /// Open a new . @@ -182,9 +180,6 @@ namespace Ryujinx.Audio.Output /// The input device name wanted by the user /// The sample format to use /// The user configuration - /// The applet resource user id of the application - /// The process handle of the application - /// The volume level to request /// A reporting an error or a success public ResultCode OpenAudioOut(out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, @@ -192,16 +187,13 @@ namespace Ryujinx.Audio.Output IVirtualMemoryManager memoryManager, string inputDeviceName, SampleFormat sampleFormat, - ref AudioInputConfiguration parameter, - ulong appletResourceUserId, - uint processHandle, - float volume) + ref AudioInputConfiguration parameter) { int sessionId = AcquireSessionId(); _sessionsBufferEvents[sessionId].Clear(); - IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount, volume); + IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount); AudioOutputSystem audioOut = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); @@ -234,41 +226,6 @@ namespace Ryujinx.Audio.Output return result; } - /// - /// Sets the volume for all output devices. - /// - /// The volume to set. - public void SetVolume(float volume) - { - if (_sessions != null) - { - foreach (AudioOutputSystem session in _sessions) - { - session?.SetVolume(volume); - } - } - } - - /// - /// Gets the volume for all output devices. - /// - /// A float indicating the volume level. - public float GetVolume() - { - if (_sessions != null) - { - foreach (AudioOutputSystem session in _sessions) - { - if (session != null) - { - return session.GetVolume(); - } - } - } - - return 0.0f; - } - public void Dispose() { GC.SuppressFinalize(this); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs index 9c885b2cf..3e11df056 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs @@ -45,7 +45,6 @@ namespace Ryujinx.Audio.Renderer.Dsp _event = new ManualResetEvent(false); } -#pragma warning disable IDE0051 // Remove unused private member private static uint GetHardwareChannelCount(IHardwareDeviceDriver deviceDriver) { // Get the real device driver (In case the compat layer is on top of it). @@ -59,9 +58,8 @@ namespace Ryujinx.Audio.Renderer.Dsp // NOTE: We default to stereo as this will get downmixed to mono by the compat layer if it's not compatible. return 2; } -#pragma warning restore IDE0051 - public void Start(IHardwareDeviceDriver deviceDriver, float volume) + public void Start(IHardwareDeviceDriver deviceDriver) { OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax]; @@ -70,7 +68,7 @@ namespace Ryujinx.Audio.Renderer.Dsp for (int i = 0; i < OutputDevices.Length; i++) { // TODO: Don't hardcode sample rate. - OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate, volume); + OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate); } _mailbox = new Mailbox(); @@ -231,33 +229,6 @@ namespace Ryujinx.Audio.Renderer.Dsp _mailbox.SendResponse(MailboxMessage.Stop); } - public float GetVolume() - { - if (OutputDevices != null) - { - foreach (IHardwareDevice outputDevice in OutputDevices) - { - if (outputDevice != null) - { - return outputDevice.GetVolume(); - } - } - } - - return 0f; - } - - public void SetVolume(float volume) - { - if (OutputDevices != null) - { - foreach (IHardwareDevice outputDevice in OutputDevices) - { - outputDevice?.SetVolume(volume); - } - } - } - public void Dispose() { GC.SuppressFinalize(this); @@ -269,6 +240,7 @@ namespace Ryujinx.Audio.Renderer.Dsp if (disposing) { _event.Dispose(); + _mailbox?.Dispose(); } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs index 0dbbd26c8..e334a89f6 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs @@ -177,12 +177,12 @@ namespace Ryujinx.Audio.Renderer.Server /// /// Start the and worker thread. /// - private void StartLocked(float volume) + private void StartLocked() { _isRunning = true; // TODO: virtual device mapping (IAudioDevice) - Processor.Start(_deviceDriver, volume); + Processor.Start(_deviceDriver); _workerThread = new Thread(SendCommands) { @@ -254,7 +254,7 @@ namespace Ryujinx.Audio.Renderer.Server /// Register a new . /// /// The to register. - private void Register(AudioRenderSystem renderer, float volume) + private void Register(AudioRenderSystem renderer) { lock (_sessionLock) { @@ -265,7 +265,7 @@ namespace Ryujinx.Audio.Renderer.Server { if (!_isRunning) { - StartLocked(volume); + StartLocked(); } } } @@ -312,8 +312,7 @@ namespace Ryujinx.Audio.Renderer.Server ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, - uint processHandle, - float volume) + uint processHandle) { int sessionId = AcquireSessionId(); @@ -338,7 +337,7 @@ namespace Ryujinx.Audio.Renderer.Server { renderer = audioRenderer; - Register(renderer, volume); + Register(renderer); } else { @@ -350,21 +349,6 @@ namespace Ryujinx.Audio.Renderer.Server return result; } - public float GetVolume() - { - if (Processor != null) - { - return Processor.GetVolume(); - } - - return 0f; - } - - public void SetVolume(float volume) - { - Processor?.SetVolume(volume); - } - public void Dispose() { GC.SuppressFinalize(this); diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index cd3365ce2..64b08e309 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -4,12 +4,6 @@ using LibHac.Fs; using LibHac.Fs.Shim; using LibHac.FsSystem; using LibHac.Tools.FsSystem; -using Ryujinx.Audio; -using Ryujinx.Audio.Input; -using Ryujinx.Audio.Integration; -using Ryujinx.Audio.Output; -using Ryujinx.Audio.Renderer.Device; -using Ryujinx.Audio.Renderer.Server; using Ryujinx.Cpu; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Kernel; @@ -20,7 +14,6 @@ using Ryujinx.HLE.HOS.Services; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; using Ryujinx.HLE.HOS.Services.Apm; -using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; @@ -61,11 +54,6 @@ namespace Ryujinx.HLE.HOS internal ITickSource TickSource { get; } internal SurfaceFlinger SurfaceFlinger { get; private set; } - internal AudioManager AudioManager { get; private set; } - internal AudioOutputManager AudioOutputManager { get; private set; } - internal AudioInputManager AudioInputManager { get; private set; } - internal AudioRendererManager AudioRendererManager { get; private set; } - internal VirtualDeviceSessionRegistry AudioDeviceSessionRegistry { get; private set; } public SystemStateMgr State { get; private set; } @@ -79,8 +67,6 @@ namespace Ryujinx.HLE.HOS internal ServerBase SmServer { get; private set; } internal ServerBase BsdServer { get; private set; } - internal ServerBase AudRenServer { get; private set; } - internal ServerBase AudOutServer { get; private set; } internal ServerBase FsServer { get; private set; } internal ServerBase HidServer { get; private set; } internal ServerBase NvDrvServer { get; private set; } @@ -248,56 +234,6 @@ namespace Ryujinx.HLE.HOS HostSyncpoint = new NvHostSyncpt(device); SurfaceFlinger = new SurfaceFlinger(device); - - InitializeAudioRenderer(TickSource); - } - - private void InitializeAudioRenderer(ITickSource tickSource) - { - AudioManager = new AudioManager(); - AudioOutputManager = new AudioOutputManager(); - AudioInputManager = new AudioInputManager(); - AudioRendererManager = new AudioRendererManager(tickSource); - AudioRendererManager.SetVolume(Device.Configuration.AudioVolume); - AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(Device.AudioDeviceDriver); - - IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax]; - - for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++) - { - KEvent registerBufferEvent = new(KernelContext); - - audioOutputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent); - } - - AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents); - AudioOutputManager.SetVolume(Device.Configuration.AudioVolume); - - IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax]; - - for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++) - { - KEvent registerBufferEvent = new(KernelContext); - - audioInputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent); - } - - AudioInputManager.Initialize(Device.AudioDeviceDriver, audioInputRegisterBufferEvents); - - IWritableEvent[] systemEvents = new IWritableEvent[Constants.AudioRendererSessionCountMax]; - - for (int i = 0; i < systemEvents.Length; i++) - { - KEvent systemEvent = new(KernelContext); - - systemEvents[i] = new AudioKernelEvent(systemEvent); - } - - AudioManager.Initialize(Device.AudioDeviceDriver.GetUpdateRequiredEvent(), AudioOutputManager.Update, AudioInputManager.Update); - - AudioRendererManager.Initialize(systemEvents, Device.AudioDeviceDriver); - - AudioManager.Start(); } public void InitializeServices() @@ -310,8 +246,6 @@ namespace Ryujinx.HLE.HOS SmServer.InitDone.WaitOne(); BsdServer = new ServerBase(KernelContext, "BsdServer"); - AudRenServer = new ServerBase(KernelContext, "AudioRendererServer"); - AudOutServer = new ServerBase(KernelContext, "AudioOutServer"); FsServer = new ServerBase(KernelContext, "FsServer"); HidServer = new ServerBase(KernelContext, "HidServer"); NvDrvServer = new ServerBase(KernelContext, "NvservicesServer"); @@ -329,7 +263,13 @@ namespace Ryujinx.HLE.HOS HorizonFsClient fsClient = new(this); ServiceTable = new ServiceTable(); - var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient, fsClient, AccountManager)); + var services = ServiceTable.GetServices(new HorizonOptions + (Device.Configuration.IgnoreMissingServices, + LibHacHorizonManager.BcatClient, + fsClient, + AccountManager, + Device.AudioDeviceDriver, + TickSource)); foreach (var service in services) { @@ -384,17 +324,6 @@ namespace Ryujinx.HLE.HOS } } - public void SetVolume(float volume) - { - AudioOutputManager.SetVolume(volume); - AudioRendererManager.SetVolume(volume); - } - - public float GetVolume() - { - return AudioOutputManager.GetVolume() == 0 ? AudioRendererManager.GetVolume() : AudioOutputManager.GetVolume(); - } - public void ReturnFocus() { AppletState.SetFocus(true); @@ -458,11 +387,7 @@ namespace Ryujinx.HLE.HOS // "Soft" stops AudioRenderer and AudioManager to avoid some sound between resume and stop. if (IsPaused) { - AudioManager.StopUpdates(); - TogglePauseEmulation(false); - - AudioRendererManager.StopSendingCommands(); } KProcess terminationProcess = new(KernelContext); @@ -513,12 +438,6 @@ namespace Ryujinx.HLE.HOS // This is safe as KThread that are likely to call ioctls are going to be terminated by the post handler hook on the SVC facade. INvDrvServices.Destroy(); - AudioManager.Dispose(); - AudioOutputManager.Dispose(); - AudioInputManager.Dispose(); - - AudioRendererManager.Dispose(); - if (LibHacHorizonManager.ApplicationClient != null) { LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ExternalEvent.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ExternalEvent.cs new file mode 100644 index 000000000..738d6b64a --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ExternalEvent.cs @@ -0,0 +1,25 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; + +namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall +{ + readonly struct ExternalEvent : IExternalEvent + { + private readonly KWritableEvent _writableEvent; + + public ExternalEvent(KWritableEvent writableEvent) + { + _writableEvent = writableEvent; + } + + public readonly void Signal() + { + _writableEvent.Signal(); + } + + public readonly void Clear() + { + _writableEvent.Clear(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index b07f5194e..6595ecef2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -8,6 +8,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon.Common; +using Ryujinx.Memory; using System; using System.Buffers; using System.Threading; @@ -3142,6 +3143,37 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } #pragma warning restore CA1822 + // Not actual syscalls, used by HLE services and such. + + public IExternalEvent GetExternalEvent(int handle) + { + KWritableEvent writableEvent = KernelStatic.GetCurrentProcess().HandleTable.GetObject(handle); + + if (writableEvent == null) + { + return null; + } + + return new ExternalEvent(writableEvent); + } + + public IVirtualMemoryManager GetMemoryManagerByProcessHandle(int handle) + { + return KernelStatic.GetCurrentProcess().HandleTable.GetKProcess(handle).CpuMemory; + } + + public ulong GetTransferMemoryAddress(int handle) + { + KTransferMemory transferMemory = KernelStatic.GetCurrentProcess().HandleTable.GetObject(handle); + + if (transferMemory == null) + { + return 0; + } + + return transferMemory.Address; + } + private static bool IsPointingInsideKernel(ulong address) { return (address + 0x1000000000) < 0xffffff000; diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs deleted file mode 100644 index acf83f488..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Audio.Input; -using Ryujinx.Audio.Integration; -using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn -{ - class AudioIn : IAudioIn - { - private readonly AudioInputSystem _system; - private readonly uint _processHandle; - private readonly KernelContext _kernelContext; - - public AudioIn(AudioInputSystem system, KernelContext kernelContext, uint processHandle) - { - _system = system; - _kernelContext = kernelContext; - _processHandle = processHandle; - } - - public ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer) - { - return (ResultCode)_system.AppendBuffer(bufferTag, ref buffer); - } - - public ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer buffer, uint handle) - { - return (ResultCode)_system.AppendUacBuffer(bufferTag, ref buffer, handle); - } - - public bool ContainsBuffer(ulong bufferTag) - { - return _system.ContainsBuffer(bufferTag); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _system.Dispose(); - - _kernelContext.Syscall.CloseHandle((int)_processHandle); - } - } - - public bool FlushBuffers() - { - return _system.FlushBuffers(); - } - - public uint GetBufferCount() - { - return _system.GetBufferCount(); - } - - public ResultCode GetReleasedBuffers(Span releasedBuffers, out uint releasedCount) - { - return (ResultCode)_system.GetReleasedBuffers(releasedBuffers, out releasedCount); - } - - public AudioDeviceState GetState() - { - return _system.GetState(); - } - - public float GetVolume() - { - return _system.GetVolume(); - } - - public KEvent RegisterBufferEvent() - { - IWritableEvent outEvent = _system.RegisterBufferEvent(); - - if (outEvent is AudioKernelEvent kernelEvent) - { - return kernelEvent.Event; - } - else - { - throw new NotImplementedException(); - } - } - - public void SetVolume(float volume) - { - _system.SetVolume(volume); - } - - public ResultCode Start() - { - return (ResultCode)_system.Start(); - } - - public ResultCode Stop() - { - return (ResultCode)_system.Stop(); - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs deleted file mode 100644 index 3f138021c..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs +++ /dev/null @@ -1,200 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.Horizon.Common; -using Ryujinx.Memory; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn -{ - class AudioInServer : DisposableIpcService - { - private readonly IAudioIn _impl; - - public AudioInServer(IAudioIn impl) - { - _impl = impl; - } - - [CommandCmif(0)] - // GetAudioInState() -> u32 state - public ResultCode GetAudioInState(ServiceCtx context) - { - context.ResponseData.Write((uint)_impl.GetState()); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // Start() - public ResultCode Start(ServiceCtx context) - { - return _impl.Start(); - } - - [CommandCmif(2)] - // Stop() - public ResultCode StopAudioIn(ServiceCtx context) - { - return _impl.Stop(); - } - - [CommandCmif(3)] - // AppendAudioInBuffer(u64 tag, buffer) - public ResultCode AppendAudioInBuffer(ServiceCtx context) - { - ulong position = context.Request.SendBuff[0].Position; - - ulong bufferTag = context.RequestData.ReadUInt64(); - - AudioUserBuffer data = MemoryHelper.Read(context.Memory, position); - - return _impl.AppendBuffer(bufferTag, ref data); - } - - [CommandCmif(4)] - // RegisterBufferEvent() -> handle - public ResultCode RegisterBufferEvent(ServiceCtx context) - { - KEvent bufferEvent = _impl.RegisterBufferEvent(); - - if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - return ResultCode.Success; - } - - [CommandCmif(5)] - // GetReleasedAudioInBuffers() -> (u32 count, buffer tags) - public ResultCode GetReleasedAudioInBuffers(ServiceCtx context) - { - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - using WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size); - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast(outputRegion.Memory.Span), out uint releasedCount); - - context.ResponseData.Write(releasedCount); - - return result; - } - - [CommandCmif(6)] - // ContainsAudioInBuffer(u64 tag) -> b8 - public ResultCode ContainsAudioInBuffer(ServiceCtx context) - { - ulong bufferTag = context.RequestData.ReadUInt64(); - - context.ResponseData.Write(_impl.ContainsBuffer(bufferTag)); - - return ResultCode.Success; - } - - [CommandCmif(7)] // 3.0.0+ - // AppendUacInBuffer(u64 tag, handle, buffer) - public ResultCode AppendUacInBuffer(ServiceCtx context) - { - ulong position = context.Request.SendBuff[0].Position; - - ulong bufferTag = context.RequestData.ReadUInt64(); - uint handle = (uint)context.Request.HandleDesc.ToCopy[0]; - - AudioUserBuffer data = MemoryHelper.Read(context.Memory, position); - - return _impl.AppendUacBuffer(bufferTag, ref data, handle); - } - - [CommandCmif(8)] // 3.0.0+ - // AppendAudioInBufferAuto(u64 tag, buffer) - public ResultCode AppendAudioInBufferAuto(ServiceCtx context) - { - (ulong position, _) = context.Request.GetBufferType0x21(); - - ulong bufferTag = context.RequestData.ReadUInt64(); - - AudioUserBuffer data = MemoryHelper.Read(context.Memory, position); - - return _impl.AppendBuffer(bufferTag, ref data); - } - - [CommandCmif(9)] // 3.0.0+ - // GetReleasedAudioInBuffersAuto() -> (u32 count, buffer tags) - public ResultCode GetReleasedAudioInBuffersAuto(ServiceCtx context) - { - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - using WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size); - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast(outputRegion.Memory.Span), out uint releasedCount); - - context.ResponseData.Write(releasedCount); - - return result; - } - - [CommandCmif(10)] // 3.0.0+ - // AppendUacInBufferAuto(u64 tag, handle, buffer) - public ResultCode AppendUacInBufferAuto(ServiceCtx context) - { - (ulong position, _) = context.Request.GetBufferType0x21(); - - ulong bufferTag = context.RequestData.ReadUInt64(); - uint handle = (uint)context.Request.HandleDesc.ToCopy[0]; - - AudioUserBuffer data = MemoryHelper.Read(context.Memory, position); - - return _impl.AppendUacBuffer(bufferTag, ref data, handle); - } - - [CommandCmif(11)] // 4.0.0+ - // GetAudioInBufferCount() -> u32 - public ResultCode GetAudioInBufferCount(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetBufferCount()); - - return ResultCode.Success; - } - - [CommandCmif(12)] // 4.0.0+ - // SetAudioInVolume(s32) - public ResultCode SetAudioInVolume(ServiceCtx context) - { - float volume = context.RequestData.ReadSingle(); - - _impl.SetVolume(volume); - - return ResultCode.Success; - } - - [CommandCmif(13)] // 4.0.0+ - // GetAudioInVolume() -> s32 - public ResultCode GetAudioInVolume(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetVolume()); - - return ResultCode.Success; - } - - [CommandCmif(14)] // 6.0.0+ - // FlushAudioInBuffers() -> b8 - public ResultCode FlushAudioInBuffers(ServiceCtx context) - { - context.ResponseData.Write(_impl.FlushBuffers()); - - return ResultCode.Success; - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _impl.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs deleted file mode 100644 index 4e67303df..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.HLE.HOS.Kernel.Threading; -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn -{ - interface IAudioIn : IDisposable - { - AudioDeviceState GetState(); - - ResultCode Start(); - - ResultCode Stop(); - - ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer); - - // NOTE: This is broken by design... not quite sure what it's used for (if anything in production). - ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer buffer, uint handle); - - KEvent RegisterBufferEvent(); - - ResultCode GetReleasedBuffers(Span releasedBuffers, out uint releasedCount); - - bool ContainsBuffer(ulong bufferTag); - - uint GetBufferCount(); - - bool FlushBuffers(); - - void SetVolume(float volume); - - float GetVolume(); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs deleted file mode 100644 index 1e759e0ca..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Audio.Input; -using Ryujinx.HLE.HOS.Services.Audio.AudioIn; -using AudioInManagerImpl = Ryujinx.Audio.Input.AudioInputManager; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - class AudioInManager : IAudioInManager - { - private readonly AudioInManagerImpl _impl; - - public AudioInManager(AudioInManagerImpl impl) - { - _impl = impl; - } - - public string[] ListAudioIns(bool filtered) - { - return _impl.ListAudioIns(filtered); - } - - public ResultCode OpenAudioIn(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle) - { - var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory; - - ResultCode result = (ResultCode)_impl.OpenAudioIn(out outputDeviceName, out outputConfiguration, out AudioInputSystem inSystem, memoryManager, inputDeviceName, SampleFormat.PcmInt16, ref parameter, appletResourceUserId, processHandle); - - if (result == ResultCode.Success) - { - obj = new AudioIn.AudioIn(inSystem, context.Device.System.KernelContext, processHandle); - } - else - { - obj = null; - } - - return result; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs deleted file mode 100644 index 1b35a62d8..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs +++ /dev/null @@ -1,243 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Services.Audio.AudioIn; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audin:u")] - class AudioInManagerServer : IpcService - { - private const int AudioInNameSize = 0x100; - - private readonly IAudioInManager _impl; - - public AudioInManagerServer(ServiceCtx context) : this(context, new AudioInManager(context.Device.System.AudioInputManager)) { } - - public AudioInManagerServer(ServiceCtx context, IAudioInManager impl) : base(context.Device.System.AudOutServer) - { - _impl = impl; - } - - [CommandCmif(0)] - // ListAudioIns() -> (u32, buffer) - public ResultCode ListAudioIns(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioIns(false); - - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length); - - position += AudioInNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // OpenAudioIn(AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle, buffer name) - // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object, buffer name) - public ResultCode OpenAudioIn(ServiceCtx context) - { - AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - - ulong deviceNameInputPosition = context.Request.SendBuff[0].Position; - ulong deviceNameInputSize = context.Request.SendBuff[0].Size; - - ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; -#pragma warning disable IDE0059 // Remove unnecessary value assignment - ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; -#pragma warning restore IDE0059 - - uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); - - ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); - - if (resultCode == ResultCode.Success) - { - context.ResponseData.WriteStruct(outputConfiguration); - - byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - - context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); - - MakeObject(context, new AudioInServer(obj)); - } - - return resultCode; - } - - [CommandCmif(2)] // 3.0.0+ - // ListAudioInsAuto() -> (u32, buffer) - public ResultCode ListAudioInsAuto(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioIns(false); - - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length); - - position += AudioInNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(3)] // 3.0.0+ - // OpenAudioInAuto(AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle, buffer) - // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object, buffer name) - public ResultCode OpenAudioInAuto(ServiceCtx context) - { - AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - - (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21(); -#pragma warning disable IDE0059 // Remove unnecessary value assignment - (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22(); -#pragma warning restore IDE0059 - - uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); - - ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); - - if (resultCode == ResultCode.Success) - { - context.ResponseData.WriteStruct(outputConfiguration); - - byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - - context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); - - MakeObject(context, new AudioInServer(obj)); - } - - return resultCode; - } - - [CommandCmif(4)] // 3.0.0+ - // ListAudioInsAutoFiltered() -> (u32, buffer) - public ResultCode ListAudioInsAutoFiltered(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioIns(true); - - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length); - - position += AudioInNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(5)] // 5.0.0+ - // OpenAudioInProtocolSpecified(b64 protocol_specified_related, AudioInInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle, buffer name) - // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object, buffer name) - public ResultCode OpenAudioInProtocolSpecified(ServiceCtx context) - { - // NOTE: We always assume that only the default device will be plugged (we never report any USB Audio Class type devices). -#pragma warning disable IDE0059 // Remove unnecessary value assignment - bool protocolSpecifiedRelated = context.RequestData.ReadUInt64() == 1; -#pragma warning restore IDE0059 - - AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - - ulong deviceNameInputPosition = context.Request.SendBuff[0].Position; - ulong deviceNameInputSize = context.Request.SendBuff[0].Size; - - ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; -#pragma warning disable IDE0051, IDE0059 // Remove unused private member - ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; -#pragma warning restore IDE0051, IDE0059 - - uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); - - ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); - - if (resultCode == ResultCode.Success) - { - context.ResponseData.WriteStruct(outputConfiguration); - - byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - - context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); - - MakeObject(context, new AudioInServer(obj)); - } - - return resultCode; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs deleted file mode 100644 index 2ccf0657f..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Audio.Integration; -using Ryujinx.Audio.Output; -using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut -{ - class AudioOut : IAudioOut - { - private readonly AudioOutputSystem _system; - private readonly uint _processHandle; - private readonly KernelContext _kernelContext; - - public AudioOut(AudioOutputSystem system, KernelContext kernelContext, uint processHandle) - { - _system = system; - _kernelContext = kernelContext; - _processHandle = processHandle; - } - - public ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer) - { - return (ResultCode)_system.AppendBuffer(bufferTag, ref buffer); - } - - public bool ContainsBuffer(ulong bufferTag) - { - return _system.ContainsBuffer(bufferTag); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _system.Dispose(); - - _kernelContext.Syscall.CloseHandle((int)_processHandle); - } - } - - public bool FlushBuffers() - { - return _system.FlushBuffers(); - } - - public uint GetBufferCount() - { - return _system.GetBufferCount(); - } - - public ulong GetPlayedSampleCount() - { - return _system.GetPlayedSampleCount(); - } - - public ResultCode GetReleasedBuffers(Span releasedBuffers, out uint releasedCount) - { - return (ResultCode)_system.GetReleasedBuffer(releasedBuffers, out releasedCount); - } - - public AudioDeviceState GetState() - { - return _system.GetState(); - } - - public float GetVolume() - { - return _system.GetVolume(); - } - - public KEvent RegisterBufferEvent() - { - IWritableEvent outEvent = _system.RegisterBufferEvent(); - - if (outEvent is AudioKernelEvent kernelEvent) - { - return kernelEvent.Event; - } - else - { - throw new NotImplementedException(); - } - } - - public void SetVolume(float volume) - { - _system.SetVolume(volume); - } - - public ResultCode Start() - { - return (ResultCode)_system.Start(); - } - - public ResultCode Stop() - { - return (ResultCode)_system.Stop(); - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs deleted file mode 100644 index e1b252631..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs +++ /dev/null @@ -1,181 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.Horizon.Common; -using Ryujinx.Memory; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut -{ - class AudioOutServer : DisposableIpcService - { - private readonly IAudioOut _impl; - - public AudioOutServer(IAudioOut impl) - { - _impl = impl; - } - - [CommandCmif(0)] - // GetAudioOutState() -> u32 state - public ResultCode GetAudioOutState(ServiceCtx context) - { - context.ResponseData.Write((uint)_impl.GetState()); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // Start() - public ResultCode Start(ServiceCtx context) - { - return _impl.Start(); - } - - [CommandCmif(2)] - // Stop() - public ResultCode Stop(ServiceCtx context) - { - return _impl.Stop(); - } - - [CommandCmif(3)] - // AppendAudioOutBuffer(u64 bufferTag, buffer buffer) - public ResultCode AppendAudioOutBuffer(ServiceCtx context) - { - ulong position = context.Request.SendBuff[0].Position; - - ulong bufferTag = context.RequestData.ReadUInt64(); - - AudioUserBuffer data = MemoryHelper.Read(context.Memory, position); - - return _impl.AppendBuffer(bufferTag, ref data); - } - - [CommandCmif(4)] - // RegisterBufferEvent() -> handle - public ResultCode RegisterBufferEvent(ServiceCtx context) - { - KEvent bufferEvent = _impl.RegisterBufferEvent(); - - if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - return ResultCode.Success; - } - - [CommandCmif(5)] - // GetReleasedAudioOutBuffers() -> (u32 count, buffer tags) - public ResultCode GetReleasedAudioOutBuffers(ServiceCtx context) - { - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - using WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size); - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast(outputRegion.Memory.Span), out uint releasedCount); - - context.ResponseData.Write(releasedCount); - - return result; - } - - [CommandCmif(6)] - // ContainsAudioOutBuffer(u64 tag) -> b8 - public ResultCode ContainsAudioOutBuffer(ServiceCtx context) - { - ulong bufferTag = context.RequestData.ReadUInt64(); - - context.ResponseData.Write(_impl.ContainsBuffer(bufferTag)); - - return ResultCode.Success; - } - - [CommandCmif(7)] // 3.0.0+ - // AppendAudioOutBufferAuto(u64 tag, buffer) - public ResultCode AppendAudioOutBufferAuto(ServiceCtx context) - { - (ulong position, _) = context.Request.GetBufferType0x21(); - - ulong bufferTag = context.RequestData.ReadUInt64(); - - AudioUserBuffer data = MemoryHelper.Read(context.Memory, position); - - return _impl.AppendBuffer(bufferTag, ref data); - } - - [CommandCmif(8)] // 3.0.0+ - // GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer tags) - public ResultCode GetReleasedAudioOutBuffersAuto(ServiceCtx context) - { - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - using WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size); - ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast(outputRegion.Memory.Span), out uint releasedCount); - - context.ResponseData.Write(releasedCount); - - return result; - } - - [CommandCmif(9)] // 4.0.0+ - // GetAudioOutBufferCount() -> u32 - public ResultCode GetAudioOutBufferCount(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetBufferCount()); - - return ResultCode.Success; - } - - [CommandCmif(10)] // 4.0.0+ - // GetAudioOutPlayedSampleCount() -> u64 - public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetPlayedSampleCount()); - - return ResultCode.Success; - } - - [CommandCmif(11)] // 4.0.0+ - // FlushAudioOutBuffers() -> b8 - public ResultCode FlushAudioOutBuffers(ServiceCtx context) - { - context.ResponseData.Write(_impl.FlushBuffers()); - - return ResultCode.Success; - } - - [CommandCmif(12)] // 6.0.0+ - // SetAudioOutVolume(s32) - public ResultCode SetAudioOutVolume(ServiceCtx context) - { - float volume = context.RequestData.ReadSingle(); - - _impl.SetVolume(volume); - - return ResultCode.Success; - } - - [CommandCmif(13)] // 6.0.0+ - // GetAudioOutVolume() -> s32 - public ResultCode GetAudioOutVolume(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetVolume()); - - return ResultCode.Success; - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _impl.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs deleted file mode 100644 index 8c8c68629..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.HLE.HOS.Kernel.Threading; -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut -{ - interface IAudioOut : IDisposable - { - AudioDeviceState GetState(); - - ResultCode Start(); - - ResultCode Stop(); - - ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer buffer); - - KEvent RegisterBufferEvent(); - - ResultCode GetReleasedBuffers(Span releasedBuffers, out uint releasedCount); - - bool ContainsBuffer(ulong bufferTag); - - uint GetBufferCount(); - - ulong GetPlayedSampleCount(); - - bool FlushBuffers(); - - void SetVolume(float volume); - - float GetVolume(); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs deleted file mode 100644 index c45a485bc..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Audio.Output; -using Ryujinx.HLE.HOS.Services.Audio.AudioOut; -using AudioOutManagerImpl = Ryujinx.Audio.Output.AudioOutputManager; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - class AudioOutManager : IAudioOutManager - { - private readonly AudioOutManagerImpl _impl; - - public AudioOutManager(AudioOutManagerImpl impl) - { - _impl = impl; - } - - public string[] ListAudioOuts() - { - return _impl.ListAudioOuts(); - } - - public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle, float volume) - { - var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory; - - ResultCode result = (ResultCode)_impl.OpenAudioOut(out outputDeviceName, out outputConfiguration, out AudioOutputSystem outSystem, memoryManager, inputDeviceName, SampleFormat.PcmInt16, ref parameter, appletResourceUserId, processHandle, volume); - - if (result == ResultCode.Success) - { - obj = new AudioOut.AudioOut(outSystem, context.Device.System.KernelContext, processHandle); - } - else - { - obj = null; - } - - return result; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs deleted file mode 100644 index 79ae6a141..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Services.Audio.AudioOut; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audout:u")] - class AudioOutManagerServer : IpcService - { - private const int AudioOutNameSize = 0x100; - - private readonly IAudioOutManager _impl; - - public AudioOutManagerServer(ServiceCtx context) : this(context, new AudioOutManager(context.Device.System.AudioOutputManager)) { } - - public AudioOutManagerServer(ServiceCtx context, IAudioOutManager impl) : base(context.Device.System.AudOutServer) - { - _impl = impl; - } - - [CommandCmif(0)] - // ListAudioOuts() -> (u32, buffer) - public ResultCode ListAudioOuts(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioOuts(); - - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioOutNameSize - buffer.Length); - - position += AudioOutNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // OpenAudioOut(AudioOutInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle process_handle, buffer name_in) - // -> (AudioOutInputConfiguration output_config, object, buffer name_out) - public ResultCode OpenAudioOut(ServiceCtx context) - { - AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - - ulong deviceNameInputPosition = context.Request.SendBuff[0].Position; - ulong deviceNameInputSize = context.Request.SendBuff[0].Size; - - ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; -#pragma warning disable IDE0059 // Remove unnecessary value assignment - ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; -#pragma warning restore IDE0059 - - uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); - - ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle, context.Device.Configuration.AudioVolume); - - if (resultCode == ResultCode.Success) - { - context.ResponseData.WriteStruct(outputConfiguration); - - byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - - context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length); - - MakeObject(context, new AudioOutServer(obj)); - } - - return resultCode; - } - - [CommandCmif(2)] // 3.0.0+ - // ListAudioOutsAuto() -> (u32, buffer) - public ResultCode ListAudioOutsAuto(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioOuts(); - - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioOutNameSize - buffer.Length); - - position += AudioOutNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(3)] // 3.0.0+ - // OpenAudioOut(AudioOutInputConfiguration input_config, nn::applet::AppletResourceUserId, pid, handle process_handle, buffer name_in) - // -> (AudioOutInputConfiguration output_config, object, buffer name_out) - public ResultCode OpenAudioOutAuto(ServiceCtx context) - { - AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - - (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21(); -#pragma warning disable IDE0059 // Remove unnecessary value assignment - (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22(); -#pragma warning restore IDE0059 - - uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); - - ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle, context.Device.Configuration.AudioVolume); - - if (resultCode == ResultCode.Success) - { - context.ResponseData.WriteStruct(outputConfiguration); - - byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - - context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length); - - MakeObject(context, new AudioOutServer(obj)); - } - - return resultCode; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs deleted file mode 100644 index 6497a3b84..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs +++ /dev/null @@ -1,174 +0,0 @@ -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 readonly VirtualDeviceSession[] _sessions; -#pragma warning disable IDE0052 // Remove unread private member - private readonly ulong _appletResourceId; - private readonly int _revision; -#pragma warning restore IDE0052 - private readonly bool _isUsbDeviceSupported; - - private readonly VirtualDeviceSessionRegistry _registry; - private readonly KEvent _systemEvent; - - public AudioDevice(VirtualDeviceSessionRegistry registry, KernelContext context, ulong appletResourceId, int revision) - { - _registry = registry; - _appletResourceId = appletResourceId; - _revision = revision; - - BehaviourContext behaviourContext = new(); - behaviourContext.SetUserRevision(revision); - - _isUsbDeviceSupported = behaviourContext.IsAudioUsbDeviceOutputSupported(); - _sessions = _registry.GetSessionByAppletResourceId(appletResourceId); - - // TODO: support the 3 different events correctly when we will have hot plugable audio devices. - _systemEvent = new KEvent(context); - _systemEvent.ReadableEvent.Signal(); - } - - private bool TryGetDeviceByName(out VirtualDeviceSession result, string name, bool ignoreRevLimitation = false) - { - result = null; - - foreach (VirtualDeviceSession session in _sessions) - { - if (session.Device.Name.Equals(name)) - { - if (!ignoreRevLimitation && !_isUsbDeviceSupported && session.Device.IsUsbDevice()) - { - return false; - } - - result = session; - - return true; - } - } - - return false; - } - - public string GetActiveAudioDeviceName() - { - VirtualDevice device = _registry.ActiveDevice; - - if (!_isUsbDeviceSupported && device.IsUsbDevice()) - { - device = _registry.DefaultDevice; - } - - return device.Name; - } - - public uint GetActiveChannelCount() - { - VirtualDevice device = _registry.ActiveDevice; - - if (!_isUsbDeviceSupported && device.IsUsbDevice()) - { - device = _registry.DefaultDevice; - } - - return device.ChannelCount; - } - - public ResultCode GetAudioDeviceOutputVolume(string name, out float volume) - { - if (TryGetDeviceByName(out VirtualDeviceSession result, name)) - { - volume = result.Volume; - } - else - { - volume = 0.0f; - } - - return ResultCode.Success; - } - - public ResultCode SetAudioDeviceOutputVolume(string name, float volume) - { - if (TryGetDeviceByName(out VirtualDeviceSession result, name, true)) - { - if (!_isUsbDeviceSupported && result.Device.IsUsbDevice()) - { - result = _sessions[0]; - } - - result.Volume = volume; - } - - return ResultCode.Success; - } - - public string GetActiveAudioOutputDeviceName() - { - return _registry.ActiveDevice.GetOutputDeviceName(); - } - - public string[] ListAudioDeviceName() - { - int deviceCount = _sessions.Length; - - if (!_isUsbDeviceSupported) - { - deviceCount--; - } - - string[] result = new string[deviceCount]; - - int i = 0; - - foreach (VirtualDeviceSession session in _sessions) - { - if (!_isUsbDeviceSupported && session.Device.IsUsbDevice()) - { - continue; - } - - result[i] = session.Device.Name; - - i++; - } - - return result; - } - - public string[] ListAudioOutputDeviceName() - { - int deviceCount = _sessions.Length; - - string[] result = new string[deviceCount]; - - for (int i = 0; i < deviceCount; i++) - { - result[i] = _sessions[i].Device.GetOutputDeviceName(); - } - - return result; - } - - public KEvent QueryAudioDeviceInputEvent() - { - return _systemEvent; - } - - public KEvent QueryAudioDeviceOutputEvent() - { - return _systemEvent; - } - - public KEvent QueryAudioDeviceSystemEvent() - { - return _systemEvent; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs deleted file mode 100644 index 6206215d5..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs +++ /dev/null @@ -1,320 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.Horizon.Common; -using System; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer -{ - class AudioDeviceServer : IpcService - { - private const int AudioDeviceNameSize = 0x100; - - private readonly IAudioDevice _impl; - - public AudioDeviceServer(IAudioDevice impl) - { - _impl = impl; - } - - [CommandCmif(0)] - // ListAudioDeviceName() -> (u32, buffer) - public ResultCode ListAudioDeviceName(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioDeviceName(); - - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); - - position += AudioDeviceNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // SetAudioDeviceOutputVolume(f32 volume, buffer name) - public ResultCode SetAudioDeviceOutputVolume(ServiceCtx context) - { - float volume = context.RequestData.ReadSingle(); - - ulong position = context.Request.SendBuff[0].Position; - ulong size = context.Request.SendBuff[0].Size; - - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); - - return _impl.SetAudioDeviceOutputVolume(deviceName, volume); - } - - [CommandCmif(2)] - // GetAudioDeviceOutputVolume(buffer name) -> f32 volume - public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context) - { - ulong position = context.Request.SendBuff[0].Position; - ulong size = context.Request.SendBuff[0].Size; - - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); - - ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); - - if (result == ResultCode.Success) - { - context.ResponseData.Write(volume); - } - - return result; - } - - [CommandCmif(3)] - // GetActiveAudioDeviceName() -> buffer - public ResultCode GetActiveAudioDeviceName(ServiceCtx context) - { - string name = _impl.GetActiveAudioDeviceName(); - - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); - - if ((ulong)deviceNameBuffer.Length <= size) - { - context.Memory.Write(position, deviceNameBuffer); - } - else - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - } - - return ResultCode.Success; - } - - [CommandCmif(4)] - // QueryAudioDeviceSystemEvent() -> handle - public ResultCode QueryAudioDeviceSystemEvent(ServiceCtx context) - { - KEvent deviceSystemEvent = _impl.QueryAudioDeviceSystemEvent(); - - if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - return ResultCode.Success; - } - - [CommandCmif(5)] - // GetActiveChannelCount() -> u32 - public ResultCode GetActiveChannelCount(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetActiveChannelCount()); - - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - return ResultCode.Success; - } - - [CommandCmif(6)] // 3.0.0+ - // ListAudioDeviceNameAuto() -> (u32, buffer) - public ResultCode ListAudioDeviceNameAuto(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioDeviceName(); - - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); - - position += AudioDeviceNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - - [CommandCmif(7)] // 3.0.0+ - // SetAudioDeviceOutputVolumeAuto(f32 volume, buffer name) - public ResultCode SetAudioDeviceOutputVolumeAuto(ServiceCtx context) - { - float volume = context.RequestData.ReadSingle(); - - (ulong position, ulong size) = context.Request.GetBufferType0x21(); - - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); - - return _impl.SetAudioDeviceOutputVolume(deviceName, volume); - } - - [CommandCmif(8)] // 3.0.0+ - // GetAudioDeviceOutputVolumeAuto(buffer name) -> f32 - public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context) - { - (ulong position, ulong size) = context.Request.GetBufferType0x21(); - - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); - - ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); - - if (result == ResultCode.Success) - { - context.ResponseData.Write(volume); - } - - return ResultCode.Success; - } - - [CommandCmif(10)] // 3.0.0+ - // GetActiveAudioDeviceNameAuto() -> buffer - public ResultCode GetActiveAudioDeviceNameAuto(ServiceCtx context) - { - string name = _impl.GetActiveAudioDeviceName(); - - (ulong position, ulong size) = context.Request.GetBufferType0x22(); - - byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0'); - - if ((ulong)deviceNameBuffer.Length <= size) - { - context.Memory.Write(position, deviceNameBuffer); - } - else - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - } - - return ResultCode.Success; - } - - [CommandCmif(11)] // 3.0.0+ - // QueryAudioDeviceInputEvent() -> handle - public ResultCode QueryAudioDeviceInputEvent(ServiceCtx context) - { - KEvent deviceInputEvent = _impl.QueryAudioDeviceInputEvent(); - - if (context.Process.HandleTable.GenerateHandle(deviceInputEvent.ReadableEvent, out int handle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - return ResultCode.Success; - } - - [CommandCmif(12)] // 3.0.0+ - // QueryAudioDeviceOutputEvent() -> handle - public ResultCode QueryAudioDeviceOutputEvent(ServiceCtx context) - { - KEvent deviceOutputEvent = _impl.QueryAudioDeviceOutputEvent(); - - if (context.Process.HandleTable.GenerateHandle(deviceOutputEvent.ReadableEvent, out int handle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - - Logger.Stub?.PrintStub(LogClass.ServiceAudio); - - return ResultCode.Success; - } - - [CommandCmif(13)] // 13.0.0+ - // GetActiveAudioOutputDeviceName() -> buffer - public ResultCode GetActiveAudioOutputDeviceName(ServiceCtx context) - { - string name = _impl.GetActiveAudioOutputDeviceName(); - - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); - - if ((ulong)deviceNameBuffer.Length <= size) - { - context.Memory.Write(position, deviceNameBuffer); - } - else - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); - } - - return ResultCode.Success; - } - - [CommandCmif(14)] // 13.0.0+ - // ListAudioOutputDeviceName() -> (u32, buffer) - public ResultCode ListAudioOutputDeviceName(ServiceCtx context) - { - string[] deviceNames = _impl.ListAudioOutputDeviceName(); - - ulong position = context.Request.ReceiveBuff[0].Position; - ulong size = context.Request.ReceiveBuff[0].Size; - - ulong basePosition = position; - - int count = 0; - - foreach (string name in deviceNames) - { - byte[] buffer = Encoding.ASCII.GetBytes(name); - - if ((position - basePosition) + (ulong)buffer.Length > size) - { - break; - } - - context.Memory.Write(position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); - - position += AudioDeviceNameSize; - count++; - } - - context.ResponseData.Write(count); - - return ResultCode.Success; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs deleted file mode 100644 index 414c70a43..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Ryujinx.Audio.Integration; -using Ryujinx.HLE.HOS.Kernel.Threading; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer -{ - class AudioKernelEvent : IWritableEvent - { - public KEvent Event { get; } - - public AudioKernelEvent(KEvent evnt) - { - Event = evnt; - } - - public void Clear() - { - Event.WritableEvent.Clear(); - } - - public void Signal() - { - Event.WritableEvent.Signal(); - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs deleted file mode 100644 index 88456be3e..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Ryujinx.Audio.Integration; -using Ryujinx.Audio.Renderer.Server; -using Ryujinx.HLE.HOS.Kernel.Threading; -using System; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer -{ - class AudioRenderer : IAudioRenderer - { - private readonly AudioRenderSystem _impl; - - public AudioRenderer(AudioRenderSystem impl) - { - _impl = impl; - } - - public ResultCode ExecuteAudioRendererRendering() - { - return (ResultCode)_impl.ExecuteAudioRendererRendering(); - } - - public uint GetMixBufferCount() - { - return _impl.GetMixBufferCount(); - } - - public uint GetRenderingTimeLimit() - { - return _impl.GetRenderingTimeLimit(); - } - - public uint GetSampleCount() - { - return _impl.GetSampleCount(); - } - - public uint GetSampleRate() - { - return _impl.GetSampleRate(); - } - - public int GetState() - { - if (_impl.IsActive()) - { - return 0; - } - - return 1; - } - - public ResultCode QuerySystemEvent(out KEvent systemEvent) - { - ResultCode resultCode = (ResultCode)_impl.QuerySystemEvent(out IWritableEvent outEvent); - - if (resultCode == ResultCode.Success) - { - if (outEvent is AudioKernelEvent kernelEvent) - { - systemEvent = kernelEvent.Event; - } - else - { - throw new NotImplementedException(); - } - } - else - { - systemEvent = null; - } - - return resultCode; - } - - public ResultCode RequestUpdate(Memory output, Memory performanceOutput, ReadOnlyMemory input) - { - return (ResultCode)_impl.Update(output, performanceOutput, input); - } - - public void SetRenderingTimeLimit(uint percent) - { - _impl.SetRenderingTimeLimitPercent(percent); - } - - public ResultCode Start() - { - _impl.Start(); - - return ResultCode.Success; - } - - public ResultCode Stop() - { - _impl.Stop(); - - return ResultCode.Success; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _impl.Dispose(); - } - } - - public void SetVoiceDropParameter(float voiceDropParameter) - { - _impl.SetVoiceDropParameter(voiceDropParameter); - } - - public float GetVoiceDropParameter() - { - return _impl.GetVoiceDropParameter(); - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs deleted file mode 100644 index baea01072..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ /dev/null @@ -1,215 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.Horizon.Common; -using System; -using System.Buffers; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer -{ - class AudioRendererServer : DisposableIpcService - { - private readonly IAudioRenderer _impl; - - public AudioRendererServer(IAudioRenderer impl) - { - _impl = impl; - } - - [CommandCmif(0)] - // GetSampleRate() -> u32 - public ResultCode GetSampleRate(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetSampleRate()); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // GetSampleCount() -> u32 - public ResultCode GetSampleCount(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetSampleCount()); - - return ResultCode.Success; - } - - [CommandCmif(2)] - // GetMixBufferCount() -> u32 - public ResultCode GetMixBufferCount(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetMixBufferCount()); - - return ResultCode.Success; - } - - [CommandCmif(3)] - // GetState() -> u32 - public ResultCode GetState(ServiceCtx context) - { - context.ResponseData.Write(_impl.GetState()); - - return ResultCode.Success; - } - - [CommandCmif(4)] - // RequestUpdate(buffer input) - // -> (buffer output, buffer performanceOutput) - public ResultCode RequestUpdate(ServiceCtx context) - { - ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; - - ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; - - ulong performanceOutputPosition = context.Request.ReceiveBuff[1].Position; - ulong performanceOutputSize = context.Request.ReceiveBuff[1].Size; - - ReadOnlyMemory input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); - - using IMemoryOwner outputOwner = ByteMemoryPool.RentCleared(outputSize); - using IMemoryOwner performanceOutputOwner = ByteMemoryPool.RentCleared(performanceOutputSize); - Memory output = outputOwner.Memory; - Memory performanceOutput = performanceOutputOwner.Memory; - - using MemoryHandle outputHandle = output.Pin(); - using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); - - ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); - - if (result == ResultCode.Success) - { - context.Memory.Write(outputPosition, output.Span); - context.Memory.Write(performanceOutputPosition, performanceOutput.Span); - } - else - { - Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}"); - } - - return result; - } - - [CommandCmif(5)] - // Start() - public ResultCode Start(ServiceCtx context) - { - return _impl.Start(); - } - - [CommandCmif(6)] - // Stop() - public ResultCode Stop(ServiceCtx context) - { - return _impl.Stop(); - } - - [CommandCmif(7)] - // QuerySystemEvent() -> handle - public ResultCode QuerySystemEvent(ServiceCtx context) - { - ResultCode result = _impl.QuerySystemEvent(out KEvent systemEvent); - - if (result == ResultCode.Success) - { - if (context.Process.HandleTable.GenerateHandle(systemEvent.ReadableEvent, out int handle) != Result.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - } - - return result; - } - - [CommandCmif(8)] - // SetAudioRendererRenderingTimeLimit(u32 limit) - public ResultCode SetAudioRendererRenderingTimeLimit(ServiceCtx context) - { - uint limit = context.RequestData.ReadUInt32(); - - _impl.SetRenderingTimeLimit(limit); - - return ResultCode.Success; - } - - [CommandCmif(9)] - // GetAudioRendererRenderingTimeLimit() -> u32 limit - public ResultCode GetAudioRendererRenderingTimeLimit(ServiceCtx context) - { - uint limit = _impl.GetRenderingTimeLimit(); - - context.ResponseData.Write(limit); - - return ResultCode.Success; - } - - [CommandCmif(10)] // 3.0.0+ - // RequestUpdateAuto(buffer input) - // -> (buffer output, buffer performanceOutput) - public ResultCode RequestUpdateAuto(ServiceCtx context) - { - (ulong inputPosition, ulong inputSize) = context.Request.GetBufferType0x21(); - (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(0); - (ulong performanceOutputPosition, ulong performanceOutputSize) = context.Request.GetBufferType0x22(1); - - ReadOnlyMemory input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); - - Memory output = new byte[outputSize]; - Memory performanceOutput = new byte[performanceOutputSize]; - - using MemoryHandle outputHandle = output.Pin(); - using MemoryHandle performanceOutputHandle = performanceOutput.Pin(); - - ResultCode result = _impl.RequestUpdate(output, performanceOutput, input); - - if (result == ResultCode.Success) - { - context.Memory.Write(outputPosition, output.Span); - context.Memory.Write(performanceOutputPosition, performanceOutput.Span); - } - - return result; - } - - [CommandCmif(11)] // 3.0.0+ - // ExecuteAudioRendererRendering() - public ResultCode ExecuteAudioRendererRendering(ServiceCtx context) - { - return _impl.ExecuteAudioRendererRendering(); - } - - [CommandCmif(12)] // 15.0.0+ - // SetVoiceDropParameter(f32 voiceDropParameter) - public ResultCode SetVoiceDropParameter(ServiceCtx context) - { - float voiceDropParameter = context.RequestData.ReadSingle(); - - _impl.SetVoiceDropParameter(voiceDropParameter); - - return ResultCode.Success; - } - - [CommandCmif(13)] // 15.0.0+ - // GetVoiceDropParameter() -> f32 voiceDropParameter - public ResultCode GetVoiceDropParameter(ServiceCtx context) - { - float voiceDropParameter = _impl.GetVoiceDropParameter(); - - context.ResponseData.Write(voiceDropParameter); - - return ResultCode.Success; - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - _impl.Dispose(); - } - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs deleted file mode 100644 index 0f181a477..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Threading; - -namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer -{ - interface IAudioDevice - { - string[] ListAudioDeviceName(); - ResultCode SetAudioDeviceOutputVolume(string name, float volume); - ResultCode GetAudioDeviceOutputVolume(string name, out float volume); - string GetActiveAudioDeviceName(); - KEvent QueryAudioDeviceSystemEvent(); - uint GetActiveChannelCount(); - KEvent QueryAudioDeviceInputEvent(); - KEvent QueryAudioDeviceOutputEvent(); - string GetActiveAudioOutputDeviceName(); - string[] ListAudioOutputDeviceName(); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs deleted file mode 100644 index 6bb4a5dec..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs +++ /dev/null @@ -1,22 +0,0 @@ -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 output, Memory performanceOutput, ReadOnlyMemory input); - ResultCode Start(); - ResultCode Stop(); - ResultCode QuerySystemEvent(out KEvent systemEvent); - void SetRenderingTimeLimit(uint percent); - uint GetRenderingTimeLimit(); - ResultCode ExecuteAudioRendererRendering(); - void SetVoiceDropParameter(float voiceDropParameter); - float GetVoiceDropParameter(); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs deleted file mode 100644 index 87d0001e3..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs +++ /dev/null @@ -1,67 +0,0 @@ -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 readonly AudioRendererManagerImpl _impl; - private readonly 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) - { - var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory; - - ResultCode result = (ResultCode)_impl.OpenAudioRenderer( - out AudioRenderSystem renderer, - memoryManager, - ref parameter, - appletResourceUserId, - workBufferTransferMemory.Address, - workBufferTransferMemory.Size, - processHandle, - context.Device.Configuration.AudioVolume); - - if (result == ResultCode.Success) - { - obj = new AudioRenderer.AudioRenderer(renderer); - } - else - { - obj = null; - } - - return result; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs deleted file mode 100644 index 38a841d82..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs +++ /dev/null @@ -1,116 +0,0 @@ -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 readonly IAudioRendererManager _impl; - - public AudioRendererManagerServer(ServiceCtx context) : this(context, new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { } - - public AudioRendererManagerServer(ServiceCtx context, IAudioRendererManager impl) : base(context.Device.System.AudRenServer) - { - _impl = impl; - } - - [CommandCmif(0)] - // OpenAudioRenderer(nn::audio::detail::AudioRendererParameterInternal parameter, u64 workBufferSize, nn::applet::AppletResourceUserId appletResourceId, pid, handle workBuffer, handle processHandle) - // -> object - public ResultCode OpenAudioRenderer(ServiceCtx context) - { - AudioRendererConfiguration parameter = context.RequestData.ReadStruct(); - ulong workBufferSize = context.RequestData.ReadUInt64(); - ulong appletResourceUserId = context.RequestData.ReadUInt64(); - - int transferMemoryHandle = context.Request.HandleDesc.ToCopy[0]; - KTransferMemory workBufferTransferMemory = context.Process.HandleTable.GetObject(transferMemoryHandle); - 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)); - } - - context.Device.System.KernelContext.Syscall.CloseHandle(transferMemoryHandle); - context.Device.System.KernelContext.Syscall.CloseHandle((int)processHandle); - - return result; - } - - [CommandCmif(1)] - // GetWorkBufferSize(nn::audio::detail::AudioRendererParameterInternal parameter) -> u64 workBufferSize - public ResultCode GetAudioRendererWorkBufferSize(ServiceCtx context) - { - AudioRendererConfiguration parameter = context.RequestData.ReadStruct(); - - 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; - } - } - - [CommandCmif(2)] - // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object - 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; - } - - [CommandCmif(4)] // 4.0.0+ - // GetAudioDeviceServiceWithRevisionInfo(s32 revision, nn::applet::AppletResourceUserId appletResourceId) -> object - 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/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs deleted file mode 100644 index c5dd2f00d..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Concentus.Structs; - -namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager -{ - class Decoder : IDecoder - { - private readonly OpusDecoder _decoder; - - public int SampleRate => _decoder.SampleRate; - public int ChannelsCount => _decoder.NumChannels; - - public Decoder(int sampleRate, int channelsCount) - { - _decoder = new OpusDecoder(sampleRate, channelsCount); - } - - public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) - { - return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize); - } - - public void ResetState() - { - _decoder.ResetState(); - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs deleted file mode 100644 index 9ff511a50..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Concentus; -using Concentus.Enums; -using Concentus.Structs; -using Ryujinx.HLE.HOS.Services.Audio.Types; -using System; -using System.Runtime.CompilerServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager -{ - static class DecoderCommon - { - private static ResultCode GetPacketNumSamples(this IDecoder decoder, out int numSamples, byte[] packet) - { - int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate); - - numSamples = result; - - if (result == OpusError.OPUS_INVALID_PACKET) - { - return ResultCode.OpusInvalidInput; - } - else if (result == OpusError.OPUS_BAD_ARG) - { - return ResultCode.OpusInvalidInput; - } - - return ResultCode.Success; - } - - public static ResultCode DecodeInterleaved( - this IDecoder decoder, - bool reset, - ReadOnlySpan input, - out short[] outPcmData, - ulong outputSize, - out uint outConsumed, - out int outSamples) - { - outPcmData = null; - outConsumed = 0; - outSamples = 0; - - int streamSize = input.Length; - - if (streamSize < Unsafe.SizeOf()) - { - return ResultCode.OpusInvalidInput; - } - - OpusPacketHeader header = OpusPacketHeader.FromSpan(input); - int headerSize = Unsafe.SizeOf(); - uint totalSize = header.length + (uint)headerSize; - - if (totalSize > streamSize) - { - return ResultCode.OpusInvalidInput; - } - - byte[] opusData = input.Slice(headerSize, (int)header.length).ToArray(); - - ResultCode result = decoder.GetPacketNumSamples(out int numSamples, opusData); - - if (result == ResultCode.Success) - { - if ((uint)numSamples * (uint)decoder.ChannelsCount * sizeof(short) > outputSize) - { - return ResultCode.OpusInvalidInput; - } - - outPcmData = new short[numSamples * decoder.ChannelsCount]; - - if (reset) - { - decoder.ResetState(); - } - - try - { - outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount); - outConsumed = totalSize; - } - catch (OpusException) - { - // TODO: as OpusException doesn't provide us the exact error code, this is kind of inaccurate in some cases... - return ResultCode.OpusInvalidInput; - } - } - - return ResultCode.Success; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs deleted file mode 100644 index cc83be5e3..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager -{ - interface IDecoder - { - int SampleRate { get; } - int ChannelsCount { get; } - - int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize); - void ResetState(); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs deleted file mode 100644 index 3d5d2839f..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Ryujinx.HLE.HOS.Services.Audio.Types; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager -{ - class IHardwareOpusDecoder : IpcService - { - private readonly IDecoder _decoder; - private readonly OpusDecoderFlags _flags; - - public IHardwareOpusDecoder(int sampleRate, int channelsCount, OpusDecoderFlags flags) - { - _decoder = new Decoder(sampleRate, channelsCount); - _flags = flags; - } - - public IHardwareOpusDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, OpusDecoderFlags flags, byte[] mapping) - { - _decoder = new MultiSampleDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); - _flags = flags; - } - - [CommandCmif(0)] - // DecodeInterleavedOld(buffer) -> (u32, u32, buffer) - public ResultCode DecodeInterleavedOld(ServiceCtx context) - { - return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false); - } - - [CommandCmif(2)] - // DecodeInterleavedForMultiStreamOld(buffer) -> (u32, u32, buffer) - public ResultCode DecodeInterleavedForMultiStreamOld(ServiceCtx context) - { - return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false); - } - - [CommandCmif(4)] // 6.0.0+ - // DecodeInterleavedWithPerfOld(buffer) -> (u32, u32, u64, buffer) - public ResultCode DecodeInterleavedWithPerfOld(ServiceCtx context) - { - return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true); - } - - [CommandCmif(5)] // 6.0.0+ - // DecodeInterleavedForMultiStreamWithPerfOld(buffer) -> (u32, u32, u64, buffer) - public ResultCode DecodeInterleavedForMultiStreamWithPerfOld(ServiceCtx context) - { - return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true); - } - - [CommandCmif(6)] // 6.0.0+ - // DecodeInterleavedWithPerfAndResetOld(bool reset, buffer) -> (u32, u32, u64, buffer) - public ResultCode DecodeInterleavedWithPerfAndResetOld(ServiceCtx context) - { - bool reset = context.RequestData.ReadBoolean(); - - return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset, withPerf: true); - } - - [CommandCmif(7)] // 6.0.0+ - // DecodeInterleavedForMultiStreamWithPerfAndResetOld(bool reset, buffer) -> (u32, u32, u64, buffer) - public ResultCode DecodeInterleavedForMultiStreamWithPerfAndResetOld(ServiceCtx context) - { - bool reset = context.RequestData.ReadBoolean(); - - return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset, withPerf: true); - } - - [CommandCmif(8)] // 7.0.0+ - // DecodeInterleaved(bool reset, buffer) -> (u32, u32, u64, buffer) - public ResultCode DecodeInterleaved(ServiceCtx context) - { - bool reset = context.RequestData.ReadBoolean(); - - return DecodeInterleavedInternal(context, _flags, reset, withPerf: true); - } - - [CommandCmif(9)] // 7.0.0+ - // DecodeInterleavedForMultiStream(bool reset, buffer) -> (u32, u32, u64, buffer) - public ResultCode DecodeInterleavedForMultiStream(ServiceCtx context) - { - bool reset = context.RequestData.ReadBoolean(); - - return DecodeInterleavedInternal(context, _flags, reset, withPerf: true); - } - - private ResultCode DecodeInterleavedInternal(ServiceCtx context, OpusDecoderFlags flags, bool reset, bool withPerf) - { - ulong inPosition = context.Request.SendBuff[0].Position; - ulong inSize = context.Request.SendBuff[0].Size; - ulong outputPosition = context.Request.ReceiveBuff[0].Position; - ulong outputSize = context.Request.ReceiveBuff[0].Size; - - ReadOnlySpan input = context.Memory.GetSpan(inPosition, (int)inSize); - - ResultCode result = _decoder.DecodeInterleaved(reset, input, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples); - - if (result == ResultCode.Success) - { - context.Memory.Write(outputPosition, MemoryMarshal.Cast(outPcmData.AsSpan())); - - context.ResponseData.Write(outConsumed); - context.ResponseData.Write(outSamples); - - if (withPerf) - { - // This is the time the DSP took to process the request, TODO: fill this. - context.ResponseData.Write(0UL); - } - } - - return result; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs deleted file mode 100644 index 910bb23ee..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Concentus.Structs; - -namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager -{ - class MultiSampleDecoder : IDecoder - { - private readonly OpusMSDecoder _decoder; - - public int SampleRate => _decoder.SampleRate; - public int ChannelsCount { get; } - - public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping) - { - ChannelsCount = channelsCount; - _decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); - } - - public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) - { - return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0); - } - - public void ResetState() - { - _decoder.ResetState(); - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs deleted file mode 100644 index a250ec799..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audctl")] - class IAudioController : IpcService - { - public IAudioController(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs deleted file mode 100644 index 861e9f2dc..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.HLE.HOS.Services.Audio.AudioIn; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - interface IAudioInManager - { - public string[] ListAudioIns(bool filtered); - - public ResultCode OpenAudioIn(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs deleted file mode 100644 index d0c385b56..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audin:a")] - class IAudioInManagerForApplet : IpcService - { - public IAudioInManagerForApplet(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs deleted file mode 100644 index 120136158..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audin:d")] - class IAudioInManagerForDebugger : IpcService - { - public IAudioInManagerForDebugger(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs deleted file mode 100644 index cd7cbe41c..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ryujinx.Audio.Common; -using Ryujinx.HLE.HOS.Services.Audio.AudioOut; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - interface IAudioOutManager - { - public string[] ListAudioOuts(); - - public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle, float volume); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs deleted file mode 100644 index 9925777e2..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audout:a")] - class IAudioOutManagerForApplet : IpcService - { - public IAudioOutManagerForApplet(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs deleted file mode 100644 index c41767a01..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audout:d")] - class IAudioOutManagerForDebugger : IpcService - { - public IAudioOutManagerForDebugger(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs deleted file mode 100644 index 112e246c0..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.Audio.Renderer.Parameter; -using Ryujinx.HLE.HOS.Kernel.Memory; -using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - interface IAudioRendererManager - { - // 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); - - // 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); - - ulong GetWorkBufferSize(ref AudioRendererConfiguration parameter); - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs deleted file mode 100644 index dd767993d..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audren:a")] - class IAudioRendererManagerForApplet : IpcService - { - public IAudioRendererManagerForApplet(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs deleted file mode 100644 index cd2af09b2..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audren:d")] - class IAudioRendererManagerForDebugger : IpcService - { - public IAudioRendererManagerForDebugger(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs deleted file mode 100644 index aa9789ac5..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("auddev")] // 6.0.0+ - class IAudioSnoopManager : IpcService - { - public IAudioSnoopManager(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs deleted file mode 100644 index 9b58213e9..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audrec:u")] - class IFinalOutputRecorderManager : IpcService - { - public IFinalOutputRecorderManager(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs deleted file mode 100644 index e2d62eee3..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audrec:a")] - class IFinalOutputRecorderManagerForApplet : IpcService - { - public IFinalOutputRecorderManagerForApplet(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs deleted file mode 100644 index 7ded79435..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("audrec:d")] - class IFinalOutputRecorderManagerForDebugger : IpcService - { - public IFinalOutputRecorderManagerForDebugger(ServiceCtx context) { } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs deleted file mode 100644 index 514b51a51..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager; -using Ryujinx.HLE.HOS.Services.Audio.Types; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio -{ - [Service("hwopus")] - class IHardwareOpusDecoderManager : IpcService - { - public IHardwareOpusDecoderManager(ServiceCtx context) { } - - [CommandCmif(0)] - // Initialize(bytes<8, 4>, u32, handle) -> object - public ResultCode Initialize(ServiceCtx context) - { - int sampleRate = context.RequestData.ReadInt32(); - int channelsCount = context.RequestData.ReadInt32(); - - MakeObject(context, new IHardwareOpusDecoder(sampleRate, channelsCount, OpusDecoderFlags.None)); - - // Close transfer memory immediately as we don't use it. - context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); - - return ResultCode.Success; - } - - [CommandCmif(1)] - // GetWorkBufferSize(bytes<8, 4>) -> u32 - public ResultCode GetWorkBufferSize(ServiceCtx context) - { - int sampleRate = context.RequestData.ReadInt32(); - int channelsCount = context.RequestData.ReadInt32(); - - int opusDecoderSize = GetOpusDecoderSize(channelsCount); - - int frameSize = BitUtils.AlignUp(channelsCount * 1920 / (48000 / sampleRate), 64); - int totalSize = opusDecoderSize + 1536 + frameSize; - - context.ResponseData.Write(totalSize); - - return ResultCode.Success; - } - - [CommandCmif(2)] // 3.0.0+ - // InitializeForMultiStream(u32, handle, buffer, 0x19>) -> object - public ResultCode InitializeForMultiStream(ServiceCtx context) - { - ulong parametersAddress = context.Request.PtrBuff[0].Position; - - OpusMultiStreamParameters parameters = context.Memory.Read(parametersAddress); - - MakeObject(context, new IHardwareOpusDecoder(parameters.SampleRate, parameters.ChannelsCount, OpusDecoderFlags.None)); - - // Close transfer memory immediately as we don't use it. - context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); - - return ResultCode.Success; - } - - [CommandCmif(3)] // 3.0.0+ - // GetWorkBufferSizeForMultiStream(buffer, 0x19>) -> u32 - public ResultCode GetWorkBufferSizeForMultiStream(ServiceCtx context) - { - ulong parametersAddress = context.Request.PtrBuff[0].Position; - - OpusMultiStreamParameters parameters = context.Memory.Read(parametersAddress); - - int opusDecoderSize = GetOpusMultistreamDecoderSize(parameters.NumberOfStreams, parameters.NumberOfStereoStreams); - - int streamSize = BitUtils.AlignUp(parameters.NumberOfStreams * 1500, 64); - int frameSize = BitUtils.AlignUp(parameters.ChannelsCount * 1920 / (48000 / parameters.SampleRate), 64); - int totalSize = opusDecoderSize + streamSize + frameSize; - - context.ResponseData.Write(totalSize); - - return ResultCode.Success; - } - - [CommandCmif(4)] // 12.0.0+ - // InitializeEx(OpusParametersEx, u32, handle) -> object - public ResultCode InitializeEx(ServiceCtx context) - { - OpusParametersEx parameters = context.RequestData.ReadStruct(); - - // UseLargeFrameSize can be ignored due to not relying on fixed size buffers for storing the decoded result. - MakeObject(context, new IHardwareOpusDecoder(parameters.SampleRate, parameters.ChannelsCount, parameters.Flags)); - - // Close transfer memory immediately as we don't use it. - context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); - - return ResultCode.Success; - } - - [CommandCmif(5)] // 12.0.0+ - // GetWorkBufferSizeEx(OpusParametersEx) -> u32 - public ResultCode GetWorkBufferSizeEx(ServiceCtx context) - { - OpusParametersEx parameters = context.RequestData.ReadStruct(); - - int opusDecoderSize = GetOpusDecoderSize(parameters.ChannelsCount); - - int frameSizeMono48KHz = parameters.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920; - int frameSize = BitUtils.AlignUp(parameters.ChannelsCount * frameSizeMono48KHz / (48000 / parameters.SampleRate), 64); - int totalSize = opusDecoderSize + 1536 + frameSize; - - context.ResponseData.Write(totalSize); - - return ResultCode.Success; - } - - [CommandCmif(6)] // 12.0.0+ - // InitializeForMultiStreamEx(u32, handle, buffer, 0x19>) -> object - public ResultCode InitializeForMultiStreamEx(ServiceCtx context) - { - ulong parametersAddress = context.Request.PtrBuff[0].Position; - - OpusMultiStreamParametersEx parameters = context.Memory.Read(parametersAddress); - - byte[] mappings = MemoryMarshal.Cast(parameters.ChannelMappings.AsSpan()).ToArray(); - - // UseLargeFrameSize can be ignored due to not relying on fixed size buffers for storing the decoded result. - MakeObject(context, new IHardwareOpusDecoder( - parameters.SampleRate, - parameters.ChannelsCount, - parameters.NumberOfStreams, - parameters.NumberOfStereoStreams, - parameters.Flags, - mappings)); - - // Close transfer memory immediately as we don't use it. - context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); - - return ResultCode.Success; - } - - [CommandCmif(7)] // 12.0.0+ - // GetWorkBufferSizeForMultiStreamEx(buffer, 0x19>) -> u32 - public ResultCode GetWorkBufferSizeForMultiStreamEx(ServiceCtx context) - { - ulong parametersAddress = context.Request.PtrBuff[0].Position; - - OpusMultiStreamParametersEx parameters = context.Memory.Read(parametersAddress); - - int opusDecoderSize = GetOpusMultistreamDecoderSize(parameters.NumberOfStreams, parameters.NumberOfStereoStreams); - - int frameSizeMono48KHz = parameters.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920; - int streamSize = BitUtils.AlignUp(parameters.NumberOfStreams * 1500, 64); - int frameSize = BitUtils.AlignUp(parameters.ChannelsCount * frameSizeMono48KHz / (48000 / parameters.SampleRate), 64); - int totalSize = opusDecoderSize + streamSize + frameSize; - - context.ResponseData.Write(totalSize); - - return ResultCode.Success; - } - - [CommandCmif(8)] // 16.0.0+ - // GetWorkBufferSizeExEx(OpusParametersEx) -> u32 - public ResultCode GetWorkBufferSizeExEx(ServiceCtx context) - { - // NOTE: GetWorkBufferSizeEx use hardcoded values to compute the returned size. - // GetWorkBufferSizeExEx fixes that by using dynamic values. - // Since we're already doing that, it's fine to call it directly. - - return GetWorkBufferSizeEx(context); - } - - [CommandCmif(9)] // 16.0.0+ - // GetWorkBufferSizeForMultiStreamExEx(buffer, 0x19>) -> u32 - public ResultCode GetWorkBufferSizeForMultiStreamExEx(ServiceCtx context) - { - // NOTE: GetWorkBufferSizeForMultiStreamEx use hardcoded values to compute the returned size. - // GetWorkBufferSizeForMultiStreamExEx fixes that by using dynamic values. - // Since we're already doing that, it's fine to call it directly. - - return GetWorkBufferSizeForMultiStreamEx(context); - } - - private static int GetOpusMultistreamDecoderSize(int streams, int coupledStreams) - { - if (streams < 1 || coupledStreams > streams || coupledStreams < 0) - { - return 0; - } - - int coupledSize = GetOpusDecoderSize(2); - int monoSize = GetOpusDecoderSize(1); - - return Align4(monoSize - GetOpusDecoderAllocSize(1)) * (streams - coupledStreams) + - Align4(coupledSize - GetOpusDecoderAllocSize(2)) * coupledStreams + 0xb90c; - } - - private static int Align4(int value) - { - return BitUtils.AlignUp(value, 4); - } - - private static int GetOpusDecoderSize(int channelsCount) - { - const int SilkDecoderSize = 0x2160; - - if (channelsCount < 1 || channelsCount > 2) - { - return 0; - } - - int celtDecoderSize = GetCeltDecoderSize(channelsCount); - int opusDecoderSize = GetOpusDecoderAllocSize(channelsCount) | 0x4c; - - return opusDecoderSize + SilkDecoderSize + celtDecoderSize; - } - - private static int GetOpusDecoderAllocSize(int channelsCount) - { - return (channelsCount * 0x800 + 0x4803) & -0x800; - } - - private static int GetCeltDecoderSize(int channelsCount) - { - const int DecodeBufferSize = 0x2030; - const int Overlap = 120; - const int EBandsCount = 21; - - return (DecodeBufferSize + Overlap * 4) * channelsCount + EBandsCount * 16 + 0x50; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs deleted file mode 100644 index c1d49109c..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Audio -{ - enum ResultCode - { - ModuleId = 153, - ErrorCodeShift = 9, - - Success = 0, - - DeviceNotFound = (1 << ErrorCodeShift) | ModuleId, - UnsupportedRevision = (2 << ErrorCodeShift) | ModuleId, - UnsupportedSampleRate = (3 << ErrorCodeShift) | ModuleId, - BufferSizeTooSmall = (4 << ErrorCodeShift) | ModuleId, - OpusInvalidInput = (6 << ErrorCodeShift) | ModuleId, - TooManyBuffersInUse = (8 << ErrorCodeShift) | ModuleId, - InvalidChannelCount = (10 << ErrorCodeShift) | ModuleId, - InvalidOperation = (513 << ErrorCodeShift) | ModuleId, - InvalidHandle = (1536 << ErrorCodeShift) | ModuleId, - OutputAlreadyStarted = (1540 << ErrorCodeShift) | ModuleId, - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs deleted file mode 100644 index 099769b3a..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.Types -{ - [StructLayout(LayoutKind.Sequential)] - struct OpusPacketHeader - { - public uint length; - public uint finalRange; - - public static OpusPacketHeader FromSpan(ReadOnlySpan data) - { - OpusPacketHeader header = MemoryMarshal.Cast(data)[0]; - - header.length = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.length) : header.length; - header.finalRange = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(header.finalRange) : header.finalRange; - - return header; - } - } -} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs b/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs deleted file mode 100644 index 4d1e0c824..000000000 --- a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Ryujinx.Common.Memory; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Audio.Types -{ - [StructLayout(LayoutKind.Sequential, Size = 0x10)] - struct OpusParametersEx - { - public int SampleRate; - public int ChannelsCount; - public OpusDecoderFlags Flags; - - Array4 Padding1; - } -} diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj index dbcb82212..0fcf9e4b5 100644 --- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -21,7 +21,6 @@ - @@ -30,11 +29,6 @@ - - - NU1605 - - diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 912a39b04..81c3ab473 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -117,12 +117,12 @@ namespace Ryujinx.HLE public void SetVolume(float volume) { - System.SetVolume(Math.Clamp(volume, 0, 1)); + AudioDeviceDriver.Volume = Math.Clamp(volume, 0f, 1f); } public float GetVolume() { - return System.GetVolume(); + return AudioDeviceDriver.Volume; } public void EnableCheats() @@ -132,7 +132,7 @@ namespace Ryujinx.HLE public bool IsAudioMuted() { - return System.GetVolume() == 0; + return AudioDeviceDriver.Volume == 0; } public void DisposeGpu() diff --git a/src/Ryujinx.Horizon.Common/IExternalEvent.cs b/src/Ryujinx.Horizon.Common/IExternalEvent.cs new file mode 100644 index 000000000..dedf4c72a --- /dev/null +++ b/src/Ryujinx.Horizon.Common/IExternalEvent.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public interface IExternalEvent + { + void Signal(); + void Clear(); + } +} diff --git a/src/Ryujinx.Horizon.Common/ISyscallApi.cs b/src/Ryujinx.Horizon.Common/ISyscallApi.cs index 20277f344..3d6da0416 100644 --- a/src/Ryujinx.Horizon.Common/ISyscallApi.cs +++ b/src/Ryujinx.Horizon.Common/ISyscallApi.cs @@ -1,3 +1,4 @@ +using Ryujinx.Memory; using System; namespace Ryujinx.Horizon.Common @@ -29,5 +30,9 @@ namespace Ryujinx.Horizon.Common Result CreatePort(out int serverPortHandle, out int clientPortHandle, int maxSessions, bool isLight, string name); Result ManageNamedPort(out int handle, string name, int maxSessions); Result ConnectToPort(out int clientSessionHandle, int clientPortHandle); + + IExternalEvent GetExternalEvent(int handle); + IVirtualMemoryManager GetMemoryManagerByProcessHandle(int handle); + ulong GetTransferMemoryAddress(int handle); } } diff --git a/src/Ryujinx.Horizon.Common/Result.cs b/src/Ryujinx.Horizon.Common/Result.cs index d313554e3..4b120b847 100644 --- a/src/Ryujinx.Horizon.Common/Result.cs +++ b/src/Ryujinx.Horizon.Common/Result.cs @@ -36,6 +36,11 @@ namespace Ryujinx.Horizon.Common ErrorCode = module | (description << ModuleBits); } + public Result(int errorCode) + { + ErrorCode = errorCode; + } + public readonly override bool Equals(object obj) { return obj is Result result && result.Equals(this); diff --git a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs index a65ec3abd..19667290f 100644 --- a/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs +++ b/src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs @@ -286,13 +286,13 @@ namespace Ryujinx.Horizon.Generators.Hipc { if (IsNonSpanOutBuffer(compilation, parameter)) { - generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({outArgIndex++}));"); + generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}));"); argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}"; } else { - outParameters.Add(new OutParameter(argName, canonicalTypeName, index, argType)); + outParameters.Add(new OutParameter(argName, canonicalTypeName, outArgIndex++, argType)); argName = $"out {canonicalTypeName} {argName}"; } diff --git a/src/Ryujinx.Horizon/Arp/ArpIpcServer.cs b/src/Ryujinx.Horizon/Arp/ArpIpcServer.cs index 6080b4750..a6017b8a6 100644 --- a/src/Ryujinx.Horizon/Arp/ArpIpcServer.cs +++ b/src/Ryujinx.Horizon/Arp/ArpIpcServer.cs @@ -56,6 +56,7 @@ namespace Ryujinx.Horizon.Arp { _applicationInstanceManager.Dispose(); _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Audio/AudioMain.cs b/src/Ryujinx.Horizon/Audio/AudioMain.cs new file mode 100644 index 000000000..92c9e804f --- /dev/null +++ b/src/Ryujinx.Horizon/Audio/AudioMain.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.Audio +{ + class AudioMain : IService + { + public static void Main(ServiceTable serviceTable) + { + AudioUserIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} diff --git a/src/Ryujinx.Horizon/Audio/AudioManagers.cs b/src/Ryujinx.Horizon/Audio/AudioManagers.cs new file mode 100644 index 000000000..493a6f9b5 --- /dev/null +++ b/src/Ryujinx.Horizon/Audio/AudioManagers.cs @@ -0,0 +1,78 @@ +using Ryujinx.Audio; +using Ryujinx.Audio.Input; +using Ryujinx.Audio.Integration; +using Ryujinx.Audio.Output; +using Ryujinx.Audio.Renderer.Device; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Cpu; +using Ryujinx.Horizon.Sdk.Audio; +using System; + +namespace Ryujinx.Horizon.Audio +{ + class AudioManagers : IDisposable + { + public AudioManager AudioManager { get; } + public AudioOutputManager AudioOutputManager { get; } + public AudioInputManager AudioInputManager { get; } + public AudioRendererManager AudioRendererManager { get; } + public VirtualDeviceSessionRegistry AudioDeviceSessionRegistry { get; } + + public AudioManagers(IHardwareDeviceDriver audioDeviceDriver, ITickSource tickSource) + { + AudioManager = new AudioManager(); + AudioOutputManager = new AudioOutputManager(); + AudioInputManager = new AudioInputManager(); + AudioRendererManager = new AudioRendererManager(tickSource); + AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(audioDeviceDriver); + + IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax]; + + for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++) + { + audioOutputRegisterBufferEvents[i] = new AudioEvent(); + } + + AudioOutputManager.Initialize(audioDeviceDriver, audioOutputRegisterBufferEvents); + + IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax]; + + for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++) + { + audioInputRegisterBufferEvents[i] = new AudioEvent(); + } + + AudioInputManager.Initialize(audioDeviceDriver, audioInputRegisterBufferEvents); + + IWritableEvent[] systemEvents = new IWritableEvent[Constants.AudioRendererSessionCountMax]; + + for (int i = 0; i < systemEvents.Length; i++) + { + systemEvents[i] = new AudioEvent(); + } + + AudioManager.Initialize(audioDeviceDriver.GetUpdateRequiredEvent(), AudioOutputManager.Update, AudioInputManager.Update); + + AudioRendererManager.Initialize(systemEvents, audioDeviceDriver); + + AudioManager.Start(); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + AudioManager.Dispose(); + AudioOutputManager.Dispose(); + AudioInputManager.Dispose(); + AudioRendererManager.Dispose(); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs b/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs new file mode 100644 index 000000000..20c824e1e --- /dev/null +++ b/src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs @@ -0,0 +1,55 @@ +using Ryujinx.Horizon.Sdk.Audio.Detail; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Audio +{ + class AudioUserIpcServer + { + private const int MaxSessionsCount = 30; + + private const int PointerBufferSize = 0xB40; + private const int MaxDomains = 0; + private const int MaxDomainObjects = 0; + private const int MaxPortsCount = 1; + + private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + private SmApi _sm; + private ServerManager _serverManager; + private AudioManagers _managers; + + public void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, MaxSessionsCount); + _managers = new AudioManagers(HorizonStatic.Options.AudioDeviceDriver, HorizonStatic.Options.TickSource); + + AudioRendererManager audioRendererManager = new(_managers.AudioRendererManager, _managers.AudioDeviceSessionRegistry); + AudioOutManager audioOutManager = new(_managers.AudioOutputManager); + AudioInManager audioInManager = new(_managers.AudioInputManager); + FinalOutputRecorderManager finalOutputRecorderManager = new(); + + _serverManager.RegisterObjectForServer(audioRendererManager, ServiceName.Encode("audren:u"), MaxSessionsCount); + _serverManager.RegisterObjectForServer(audioOutManager, ServiceName.Encode("audout:u"), MaxSessionsCount); + _serverManager.RegisterObjectForServer(audioInManager, ServiceName.Encode("audin:u"), MaxSessionsCount); + _serverManager.RegisterObjectForServer(finalOutputRecorderManager, ServiceName.Encode("audrec:u"), MaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + _managers.Dispose(); + _sm.Dispose(); + } + } +} diff --git a/src/Ryujinx.Horizon/Audio/HwopusIpcServer.cs b/src/Ryujinx.Horizon/Audio/HwopusIpcServer.cs new file mode 100644 index 000000000..e60e033cc --- /dev/null +++ b/src/Ryujinx.Horizon/Audio/HwopusIpcServer.cs @@ -0,0 +1,46 @@ +using Ryujinx.Horizon.Sdk.Codec.Detail; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using Ryujinx.Horizon.Sdk.Sm; + +namespace Ryujinx.Horizon.Audio +{ + class HwopusIpcServer + { + private const int MaxSessionsCount = 24; + + private const int PointerBufferSize = 0x1000; + private const int MaxDomains = 8; + private const int MaxDomainObjects = 256; + private const int MaxPortsCount = 1; + + private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false); + + private SmApi _sm; + private ServerManager _serverManager; + + public void Initialize() + { + HeapAllocator allocator = new(); + + _sm = new SmApi(); + _sm.Initialize().AbortOnFailure(); + + _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, MaxSessionsCount); + + HardwareOpusDecoderManager hardwareOpusDecoderManager = new(); + + _serverManager.RegisterObjectForServer(hardwareOpusDecoderManager, ServiceName.Encode("hwopus"), MaxSessionsCount); + } + + public void ServiceRequests() + { + _serverManager.ServiceRequests(); + } + + public void Shutdown() + { + _serverManager.Dispose(); + _sm.Dispose(); + } + } +} diff --git a/src/Ryujinx.Horizon/Audio/HwopusMain.cs b/src/Ryujinx.Horizon/Audio/HwopusMain.cs new file mode 100644 index 000000000..04eee3fad --- /dev/null +++ b/src/Ryujinx.Horizon/Audio/HwopusMain.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Horizon.Audio +{ + class HwopusMain : IService + { + public static void Main(ServiceTable serviceTable) + { + HwopusIpcServer ipcServer = new(); + + ipcServer.Initialize(); + + serviceTable.SignalServiceReady(); + + ipcServer.ServiceRequests(); + ipcServer.Shutdown(); + } + } +} diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs index dd4e5b53a..8da3971cf 100644 --- a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs +++ b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Horizon.Bcat public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs b/src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs index 523c617a8..a12c0cae8 100644 --- a/src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs +++ b/src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Horizon.Friends public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/HorizonOptions.cs b/src/Ryujinx.Horizon/HorizonOptions.cs index 794620569..a24ce7f61 100644 --- a/src/Ryujinx.Horizon/HorizonOptions.cs +++ b/src/Ryujinx.Horizon/HorizonOptions.cs @@ -1,4 +1,6 @@ using LibHac; +using Ryujinx.Audio.Integration; +using Ryujinx.Cpu; using Ryujinx.Horizon.Sdk.Account; using Ryujinx.Horizon.Sdk.Fs; @@ -12,14 +14,24 @@ namespace Ryujinx.Horizon public HorizonClient BcatClient { get; } public IFsClient FsClient { get; } public IEmulatorAccountManager AccountManager { get; } + public IHardwareDeviceDriver AudioDeviceDriver { get; } + public ITickSource TickSource { get; } - public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient, IFsClient fsClient, IEmulatorAccountManager accountManager) + public HorizonOptions( + bool ignoreMissingServices, + HorizonClient bcatClient, + IFsClient fsClient, + IEmulatorAccountManager accountManager, + IHardwareDeviceDriver audioDeviceDriver, + ITickSource tickSource) { IgnoreMissingServices = ignoreMissingServices; ThrowOnInvalidCommandIds = true; BcatClient = bcatClient; FsClient = fsClient; AccountManager = accountManager; + AudioDeviceDriver = audioDeviceDriver; + TickSource = tickSource; } } } diff --git a/src/Ryujinx.Horizon/Hshl/HshlIpcServer.cs b/src/Ryujinx.Horizon/Hshl/HshlIpcServer.cs index d7d89e24b..b1cc7259d 100644 --- a/src/Ryujinx.Horizon/Hshl/HshlIpcServer.cs +++ b/src/Ryujinx.Horizon/Hshl/HshlIpcServer.cs @@ -42,6 +42,7 @@ namespace Ryujinx.Horizon.Hshl public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Ins/InsIpcServer.cs b/src/Ryujinx.Horizon/Ins/InsIpcServer.cs index bb2749d5f..4e06dcadd 100644 --- a/src/Ryujinx.Horizon/Ins/InsIpcServer.cs +++ b/src/Ryujinx.Horizon/Ins/InsIpcServer.cs @@ -42,6 +42,7 @@ namespace Ryujinx.Horizon.Ins public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs b/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs index 6b5421653..f25fc54b1 100644 --- a/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs +++ b/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Horizon.Lbl public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs index d023ff927..6bb4e11c7 100644 --- a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs +++ b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Horizon.LogManager public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs b/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs index c52a294f5..b3ce81182 100644 --- a/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs +++ b/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Horizon.MmNv public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs b/src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs index b2a74fb22..ec73f96ae 100644 --- a/src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs +++ b/src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs @@ -3,7 +3,6 @@ using Ryujinx.Horizon.Sdk.Fs; using Ryujinx.Horizon.Sdk.Ngc.Detail; using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sm; -using System; namespace Ryujinx.Horizon.Ngc { @@ -46,6 +45,7 @@ namespace Ryujinx.Horizon.Ngc { _serverManager.Dispose(); _profanityFilter.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Ovln/OvlnIpcServer.cs b/src/Ryujinx.Horizon/Ovln/OvlnIpcServer.cs index c4580a861..d4257be8d 100644 --- a/src/Ryujinx.Horizon/Ovln/OvlnIpcServer.cs +++ b/src/Ryujinx.Horizon/Ovln/OvlnIpcServer.cs @@ -43,6 +43,7 @@ namespace Ryujinx.Horizon.Ovln public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs index 1902cde23..669a64594 100644 --- a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs +++ b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs @@ -51,6 +51,7 @@ namespace Ryujinx.Horizon.Prepo { _arp.Dispose(); _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Psc/PscIpcServer.cs b/src/Ryujinx.Horizon/Psc/PscIpcServer.cs index d6ac65685..8e574ddda 100644 --- a/src/Ryujinx.Horizon/Psc/PscIpcServer.cs +++ b/src/Ryujinx.Horizon/Psc/PscIpcServer.cs @@ -45,6 +45,7 @@ namespace Ryujinx.Horizon.Psc public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj index ae40f7b5e..d1f572d5c 100644 --- a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj +++ b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -5,6 +5,7 @@ + @@ -12,7 +13,13 @@ + + + + NU1605 + + diff --git a/src/Ryujinx.Horizon/Sdk/Account/Uid.cs b/src/Ryujinx.Horizon/Sdk/Account/Uid.cs index ada2c02ba..d612f4792 100644 --- a/src/Ryujinx.Horizon/Sdk/Account/Uid.cs +++ b/src/Ryujinx.Horizon/Sdk/Account/Uid.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.Horizon.Sdk.Account { - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x8)] public readonly record struct Uid { public readonly ulong High; diff --git a/src/Ryujinx.Horizon/Sdk/Applet/AppletId.cs b/src/Ryujinx.Horizon/Sdk/Applet/AppletId.cs new file mode 100644 index 000000000..2b81fbf6f --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Applet/AppletId.cs @@ -0,0 +1,71 @@ +namespace Ryujinx.Horizon.Sdk.Applet +{ + enum AppletId : uint + { + None = 0x00, + Application = 0x01, + OverlayApplet = 0x02, + SystemAppletMenu = 0x03, + SystemApplication = 0x04, + LibraryAppletAuth = 0x0A, + LibraryAppletCabinet = 0x0B, + LibraryAppletController = 0x0C, + LibraryAppletDataErase = 0x0D, + LibraryAppletError = 0x0E, + LibraryAppletNetConnect = 0x0F, + LibraryAppletPlayerSelect = 0x10, + LibraryAppletSwkbd = 0x11, + LibraryAppletMiiEdit = 0x12, + LibraryAppletWeb = 0x13, + LibraryAppletShop = 0x14, + LibraryAppletPhotoViewer = 0x15, + LibraryAppletSet = 0x16, + LibraryAppletOfflineWeb = 0x17, + LibraryAppletLoginShare = 0x18, + LibraryAppletWifiWebAuth = 0x19, + LibraryAppletMyPage = 0x1A, + LibraryAppletGift = 0x1B, + LibraryAppletUserMigration = 0x1C, + LibraryAppletPreomiaSys = 0x1D, + LibraryAppletStory = 0x1E, + LibraryAppletPreomiaUsr = 0x1F, + LibraryAppletPreomiaUsrDummy = 0x20, + LibraryAppletSample = 0x21, + LibraryAppletPromoteQualification = 0x22, + LibraryAppletOfflineWebFw17 = 0x32, + LibraryAppletOfflineWeb2Fw17 = 0x33, + LibraryAppletLoginShareFw17 = 0x35, + LibraryAppletLoginShare2Fw17 = 0x36, + LibraryAppletLoginShare3Fw17 = 0x37, + Unknown38 = 0x38, + DevlopmentTool = 0x3E8, + CombinationLA = 0x3F1, + AeSystemApplet = 0x3F2, + AeOverlayApplet = 0x3F3, + AeStarter = 0x3F4, + AeLibraryAppletAlone = 0x3F5, + AeLibraryApplet1 = 0x3F6, + AeLibraryApplet2 = 0x3F7, + AeLibraryApplet3 = 0x3F8, + AeLibraryApplet4 = 0x3F9, + AppletISA = 0x3FA, + AppletIOA = 0x3FB, + AppletISTA = 0x3FC, + AppletILA1 = 0x3FD, + AppletILA2 = 0x3FE, + CombinationLAFw17 = 0x700000DC, + AeSystemAppletFw17 = 0x700000E6, + AeOverlayAppletFw17 = 0x700000E7, + AeStarterFw17 = 0x700000E8, + AeLibraryAppletAloneFw17 = 0x700000E9, + AeLibraryApplet1Fw17 = 0x700000EA, + AeLibraryApplet2Fw17 = 0x700000EB, + AeLibraryApplet3Fw17 = 0x700000EC, + AeLibraryApplet4Fw17 = 0x700000ED, + AppletISAFw17 = 0x700000F0, + AppletIOAFw17 = 0x700000F1, + AppletISTAFw17 = 0x700000F2, + AppletILA1Fw17 = 0x700000F3, + AppletILA2Fw17 = 0x700000F4, + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Applet/AppletResourceUserId.cs b/src/Ryujinx.Horizon/Sdk/Applet/AppletResourceUserId.cs new file mode 100644 index 000000000..00e2ad368 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Applet/AppletResourceUserId.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Applet +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x8)] + readonly struct AppletResourceUserId + { + public readonly ulong Id; + + public AppletResourceUserId(ulong id) + { + Id = id; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs b/src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs new file mode 100644 index 000000000..efa8d5bc1 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs @@ -0,0 +1,50 @@ +using Ryujinx.Audio.Integration; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio +{ + class AudioEvent : IWritableEvent, IDisposable + { + private SystemEventType _systemEvent; + private readonly IExternalEvent _externalEvent; + + public AudioEvent() + { + Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, interProcess: true); + + // We need to do this because the event will be signalled from a different thread. + _externalEvent = HorizonStatic.Syscall.GetExternalEvent(Os.GetWritableHandleOfSystemEvent(ref _systemEvent)); + } + + public void Signal() + { + _externalEvent.Signal(); + } + + public void Clear() + { + _externalEvent.Clear(); + } + + public int GetReadableHandle() + { + return Os.GetReadableHandleOfSystemEvent(ref _systemEvent); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Os.DestroySystemEvent(ref _systemEvent); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs b/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs new file mode 100644 index 000000000..c18bfee9f --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.Audio +{ + static class AudioResult + { + private const int ModuleId = 153; + + public static Result DeviceNotFound => new(ModuleId, 1); + public static Result UnsupportedRevision => new(ModuleId, 2); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs new file mode 100644 index 000000000..f67ea7298 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs @@ -0,0 +1,252 @@ +using Ryujinx.Audio.Renderer.Device; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioDevice : IAudioDevice, IDisposable + { + private readonly VirtualDeviceSessionRegistry _registry; + private readonly VirtualDeviceSession[] _sessions; + private readonly bool _isUsbDeviceSupported; + + private SystemEventType _audioEvent; + private SystemEventType _audioInputEvent; + private SystemEventType _audioOutputEvent; + + public AudioDevice(VirtualDeviceSessionRegistry registry, AppletResourceUserId appletResourceId, uint revision) + { + _registry = registry; + + BehaviourContext behaviourContext = new(); + behaviourContext.SetUserRevision((int)revision); + + _isUsbDeviceSupported = behaviourContext.IsAudioUsbDeviceOutputSupported(); + _sessions = registry.GetSessionByAppletResourceId(appletResourceId.Id); + + Os.CreateSystemEvent(out _audioEvent, EventClearMode.AutoClear, interProcess: true); + Os.CreateSystemEvent(out _audioInputEvent, EventClearMode.AutoClear, interProcess: true); + Os.CreateSystemEvent(out _audioOutputEvent, EventClearMode.AutoClear, interProcess: true); + } + + 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; + } + + [CmifCommand(0)] + public Result ListAudioDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span names, out int nameCount) + { + int count = 0; + + foreach (VirtualDeviceSession session in _sessions) + { + if (!_isUsbDeviceSupported && session.Device.IsUsbDevice()) + { + continue; + } + + if (count >= names.Length) + { + break; + } + + names[count] = new DeviceName(session.Device.Name); + + count++; + } + + nameCount = count; + + return Result.Success; + } + + [CmifCommand(1)] + public Result SetAudioDeviceOutputVolume([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan name, float volume) + { + if (name.Length > 0 && TryGetDeviceByName(out VirtualDeviceSession result, name[0].ToString(), ignoreRevLimitation: true)) + { + if (!_isUsbDeviceSupported && result.Device.IsUsbDevice()) + { + result = _sessions[0]; + } + + result.Volume = volume; + } + + return Result.Success; + } + + [CmifCommand(2)] + public Result GetAudioDeviceOutputVolume([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan name, out float volume) + { + if (name.Length > 0 && TryGetDeviceByName(out VirtualDeviceSession result, name[0].ToString())) + { + volume = result.Volume; + } + else + { + volume = 0f; + } + + return Result.Success; + } + + [CmifCommand(3)] + public Result GetActiveAudioDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span name) + { + VirtualDevice device = _registry.ActiveDevice; + + if (!_isUsbDeviceSupported && device.IsUsbDevice()) + { + device = _registry.DefaultDevice; + } + + if (name.Length > 0) + { + name[0] = new DeviceName(device.Name); + } + + return Result.Success; + } + + [CmifCommand(4)] + public Result QueryAudioDeviceSystemEvent([CopyHandle] out int eventHandle) + { + eventHandle = Os.GetReadableHandleOfSystemEvent(ref _audioEvent); + + return Result.Success; + } + + [CmifCommand(5)] + public Result GetActiveChannelCount(out int channelCount) + { + VirtualDevice device = _registry.ActiveDevice; + + if (!_isUsbDeviceSupported && device.IsUsbDevice()) + { + device = _registry.DefaultDevice; + } + + channelCount = (int)device.ChannelCount; + + return Result.Success; + } + + [CmifCommand(6)] // 3.0.0+ + public Result ListAudioDeviceNameAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span names, out int nameCount) + { + return ListAudioDeviceName(names, out nameCount); + } + + [CmifCommand(7)] // 3.0.0+ + public Result SetAudioDeviceOutputVolumeAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan name, float volume) + { + return SetAudioDeviceOutputVolume(name, volume); + } + + [CmifCommand(8)] // 3.0.0+ + public Result GetAudioDeviceOutputVolumeAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan name, out float volume) + { + return GetAudioDeviceOutputVolume(name, out volume); + } + + [CmifCommand(10)] // 3.0.0+ + public Result GetActiveAudioDeviceNameAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span name) + { + return GetActiveAudioDeviceName(name); + } + + [CmifCommand(11)] // 3.0.0+ + public Result QueryAudioDeviceInputEvent([CopyHandle] out int eventHandle) + { + eventHandle = Os.GetReadableHandleOfSystemEvent(ref _audioInputEvent); + + return Result.Success; + } + + [CmifCommand(12)] // 3.0.0+ + public Result QueryAudioDeviceOutputEvent([CopyHandle] out int eventHandle) + { + eventHandle = Os.GetReadableHandleOfSystemEvent(ref _audioOutputEvent); + + return Result.Success; + } + + [CmifCommand(13)] // 13.0.0+ + public Result GetActiveAudioOutputDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span name) + { + if (name.Length > 0) + { + name[0] = new DeviceName(_registry.ActiveDevice.GetOutputDeviceName()); + } + + return Result.Success; + } + + [CmifCommand(14)] // 13.0.0+ + public Result ListAudioOutputDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span names, out int nameCount) + { + int count = 0; + + foreach (VirtualDeviceSession session in _sessions) + { + if (!_isUsbDeviceSupported && session.Device.IsUsbDevice()) + { + continue; + } + + if (count >= names.Length) + { + break; + } + + names[count] = new DeviceName(session.Device.GetOutputDeviceName()); + + count++; + } + + nameCount = count; + + return Result.Success; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Os.DestroySystemEvent(ref _audioEvent); + Os.DestroySystemEvent(ref _audioInputEvent); + Os.DestroySystemEvent(ref _audioOutputEvent); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs new file mode 100644 index 000000000..464ede581 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs @@ -0,0 +1,171 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Audio.Input; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioIn : IAudioIn, IDisposable + { + private readonly AudioInputSystem _impl; + private int _processHandle; + + public AudioIn(AudioInputSystem impl, int processHandle) + { + _impl = impl; + _processHandle = processHandle; + } + + [CmifCommand(0)] + public Result GetAudioInState(out AudioDeviceState state) + { + state = _impl.GetState(); + + return Result.Success; + } + + [CmifCommand(1)] + public Result Start() + { + return new Result((int)_impl.Start()); + } + + [CmifCommand(2)] + public Result Stop() + { + return new Result((int)_impl.Stop()); + } + + [CmifCommand(3)] + public Result AppendAudioInBuffer(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan buffer) + { + AudioUserBuffer userBuffer = default; + + if (buffer.Length > 0) + { + userBuffer = buffer[0]; + } + + return new Result((int)_impl.AppendBuffer(bufferTag, ref userBuffer)); + } + + [CmifCommand(4)] + public Result RegisterBufferEvent([CopyHandle] out int eventHandle) + { + eventHandle = 0; + + if (_impl.RegisterBufferEvent() is AudioEvent audioEvent) + { + eventHandle = audioEvent.GetReadableHandle(); + } + + return Result.Success; + } + + [CmifCommand(5)] + public Result GetReleasedAudioInBuffers(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span bufferTags) + { + return new Result((int)_impl.GetReleasedBuffers(bufferTags, out count)); + } + + [CmifCommand(6)] + public Result ContainsAudioInBuffer(out bool contains, ulong bufferTag) + { + contains = _impl.ContainsBuffer(bufferTag); + + return Result.Success; + } + + [CmifCommand(7)] // 3.0.0+ + public Result AppendUacInBuffer( + ulong bufferTag, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan buffer, + [CopyHandle] int eventHandle) + { + AudioUserBuffer userBuffer = default; + + if (buffer.Length > 0) + { + userBuffer = buffer[0]; + } + + return new Result((int)_impl.AppendUacBuffer(bufferTag, ref userBuffer, (uint)eventHandle)); + } + + [CmifCommand(8)] // 3.0.0+ + public Result AppendAudioInBufferAuto(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan buffer) + { + return AppendAudioInBuffer(bufferTag, buffer); + } + + [CmifCommand(9)] // 3.0.0+ + public Result GetReleasedAudioInBuffersAuto(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span bufferTags) + { + return GetReleasedAudioInBuffers(out count, bufferTags); + } + + [CmifCommand(10)] // 3.0.0+ + public Result AppendUacInBufferAuto( + ulong bufferTag, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan buffer, + [CopyHandle] int eventHandle) + { + return AppendUacInBuffer(bufferTag, buffer, eventHandle); + } + + [CmifCommand(11)] // 4.0.0+ + public Result GetAudioInBufferCount(out uint bufferCount) + { + bufferCount = _impl.GetBufferCount(); + + return Result.Success; + } + + [CmifCommand(12)] // 4.0.0+ + public Result SetDeviceGain(float gain) + { + _impl.SetVolume(gain); + + return Result.Success; + } + + [CmifCommand(13)] // 4.0.0+ + public Result GetDeviceGain(out float gain) + { + gain = _impl.GetVolume(); + + return Result.Success; + } + + [CmifCommand(14)] // 6.0.0+ + public Result FlushAudioInBuffers(out bool pending) + { + pending = _impl.FlushBuffers(); + + return Result.Success; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _impl.Dispose(); + + if (_processHandle != 0) + { + HorizonStatic.Syscall.CloseHandle(_processHandle); + + _processHandle = 0; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs new file mode 100644 index 000000000..d5d047201 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs @@ -0,0 +1,130 @@ +using Ryujinx.Audio; +using Ryujinx.Audio.Common; +using Ryujinx.Audio.Input; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioInManager : IAudioInManager + { + private readonly AudioInputManager _impl; + + public AudioInManager(AudioInputManager impl) + { + _impl = impl; + } + + [CmifCommand(0)] + public Result ListAudioIns(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span names) + { + string[] deviceNames = _impl.ListAudioIns(filtered: false); + + count = 0; + + foreach (string deviceName in deviceNames) + { + if (count >= names.Length) + { + break; + } + + names[count++] = new DeviceName(deviceName); + } + + return Result.Success; + } + + [CmifCommand(1)] + public Result OpenAudioIn( + out AudioOutputConfiguration outputConfiguration, + out IAudioIn audioIn, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span outName, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + [CopyHandle] int processHandle, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan name, + [ClientProcessId] ulong pid) + { + var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle); + + ResultCode rc = _impl.OpenAudioIn( + out string outputDeviceName, + out outputConfiguration, + out AudioInputSystem inSystem, + clientMemoryManager, + name.Length > 0 ? name[0].ToString() : string.Empty, + SampleFormat.PcmInt16, + ref parameter); + + if (rc == ResultCode.Success && outName.Length > 0) + { + outName[0] = new DeviceName(outputDeviceName); + } + + audioIn = new AudioIn(inSystem, processHandle); + + return new Result((int)rc); + } + + [CmifCommand(2)] // 3.0.0+ + public Result ListAudioInsAuto(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span names) + { + return ListAudioIns(out count, names); + } + + [CmifCommand(3)] // 3.0.0+ + public Result OpenAudioInAuto( + out AudioOutputConfiguration outputConfig, + out IAudioIn audioIn, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span outName, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + [CopyHandle] int processHandle, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan name, + [ClientProcessId] ulong pid) + { + return OpenAudioIn(out outputConfig, out audioIn, outName, parameter, appletResourceId, processHandle, name, pid); + } + + [CmifCommand(4)] // 3.0.0+ + public Result ListAudioInsAutoFiltered(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span names) + { + string[] deviceNames = _impl.ListAudioIns(filtered: true); + + count = 0; + + foreach (string deviceName in deviceNames) + { + if (count >= names.Length) + { + break; + } + + names[count++] = new DeviceName(deviceName); + } + + return Result.Success; + } + + [CmifCommand(5)] // 5.0.0+ + public Result OpenAudioInProtocolSpecified( + out AudioOutputConfiguration outputConfig, + out IAudioIn audioIn, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span outName, + AudioInProtocol protocol, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + [CopyHandle] int processHandle, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan name, + [ClientProcessId] ulong pid) + { + // NOTE: We always assume that only the default device will be plugged (we never report any USB Audio Class type devices). + + return OpenAudioIn(out outputConfig, out audioIn, outName, parameter, appletResourceId, processHandle, name, pid); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs new file mode 100644 index 000000000..48785f1c0 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs @@ -0,0 +1,23 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x1)] + struct AudioInProtocol + { + public AudioInProtocolName Name; + public Array7 Padding; + + public AudioInProtocol(AudioInProtocolName name) + { + Name = name; + Padding = new(); + } + + public override readonly string ToString() + { + return Name.ToString(); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocolName.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocolName.cs new file mode 100644 index 000000000..68d283cc5 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocolName.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + enum AudioInProtocolName : byte + { + DeviceIn = 0, + UacIn = 1, + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs new file mode 100644 index 000000000..7607e2643 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs @@ -0,0 +1,154 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Audio.Output; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioOut : IAudioOut, IDisposable + { + private readonly AudioOutputSystem _impl; + private int _processHandle; + + public AudioOut(AudioOutputSystem impl, int processHandle) + { + _impl = impl; + _processHandle = processHandle; + } + + [CmifCommand(0)] + public Result GetAudioOutState(out AudioDeviceState state) + { + state = _impl.GetState(); + + return Result.Success; + } + + [CmifCommand(1)] + public Result Start() + { + return new Result((int)_impl.Start()); + } + + [CmifCommand(2)] + public Result Stop() + { + return new Result((int)_impl.Stop()); + } + + [CmifCommand(3)] + public Result AppendAudioOutBuffer(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan buffer) + { + AudioUserBuffer userBuffer = default; + + if (buffer.Length > 0) + { + userBuffer = buffer[0]; + } + + return new Result((int)_impl.AppendBuffer(bufferTag, ref userBuffer)); + } + + [CmifCommand(4)] + public Result RegisterBufferEvent([CopyHandle] out int eventHandle) + { + eventHandle = 0; + + if (_impl.RegisterBufferEvent() is AudioEvent audioEvent) + { + eventHandle = audioEvent.GetReadableHandle(); + } + + return Result.Success; + } + + [CmifCommand(5)] + public Result GetReleasedAudioOutBuffers(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span bufferTags) + { + return new Result((int)_impl.GetReleasedBuffer(bufferTags, out count)); + } + + [CmifCommand(6)] + public Result ContainsAudioOutBuffer(out bool contains, ulong bufferTag) + { + contains = _impl.ContainsBuffer(bufferTag); + + return Result.Success; + } + + [CmifCommand(7)] // 3.0.0+ + public Result AppendAudioOutBufferAuto(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan buffer) + { + return AppendAudioOutBuffer(bufferTag, buffer); + } + + [CmifCommand(8)] // 3.0.0+ + public Result GetReleasedAudioOutBuffersAuto(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span bufferTags) + { + return GetReleasedAudioOutBuffers(out count, bufferTags); + } + + [CmifCommand(9)] // 4.0.0+ + public Result GetAudioOutBufferCount(out uint bufferCount) + { + bufferCount = _impl.GetBufferCount(); + + return Result.Success; + } + + [CmifCommand(10)] // 4.0.0+ + public Result GetAudioOutPlayedSampleCount(out ulong sampleCount) + { + sampleCount = _impl.GetPlayedSampleCount(); + + return Result.Success; + } + + [CmifCommand(11)] // 4.0.0+ + public Result FlushAudioOutBuffers(out bool pending) + { + pending = _impl.FlushBuffers(); + + return Result.Success; + } + + [CmifCommand(12)] // 6.0.0+ + public Result SetAudioOutVolume(float volume) + { + _impl.SetVolume(volume); + + return Result.Success; + } + + [CmifCommand(13)] // 6.0.0+ + public Result GetAudioOutVolume(out float volume) + { + volume = _impl.GetVolume(); + + return Result.Success; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _impl.Dispose(); + + if (_processHandle != 0) + { + HorizonStatic.Syscall.CloseHandle(_processHandle); + + _processHandle = 0; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs new file mode 100644 index 000000000..3d129470c --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs @@ -0,0 +1,93 @@ +using Ryujinx.Audio; +using Ryujinx.Audio.Common; +using Ryujinx.Audio.Output; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioOutManager : IAudioOutManager + { + private readonly AudioOutputManager _impl; + + public AudioOutManager(AudioOutputManager impl) + { + _impl = impl; + } + + [CmifCommand(0)] + public Result ListAudioOuts(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span names) + { + string[] deviceNames = _impl.ListAudioOuts(); + + count = 0; + + foreach (string deviceName in deviceNames) + { + if (count >= names.Length) + { + break; + } + + names[count++] = new DeviceName(deviceName); + } + + return Result.Success; + } + + [CmifCommand(1)] + public Result OpenAudioOut( + out AudioOutputConfiguration outputConfig, + out IAudioOut audioOut, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span outName, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + [CopyHandle] int processHandle, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan name, + [ClientProcessId] ulong pid) + { + var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle); + + ResultCode rc = _impl.OpenAudioOut( + out string outputDeviceName, + out outputConfig, + out AudioOutputSystem outSystem, + clientMemoryManager, + name.Length > 0 ? name[0].ToString() : string.Empty, + SampleFormat.PcmInt16, + ref parameter); + + if (rc == ResultCode.Success && outName.Length > 0) + { + outName[0] = new DeviceName(outputDeviceName); + } + + audioOut = new AudioOut(outSystem, processHandle); + + return new Result((int)rc); + } + + [CmifCommand(2)] // 3.0.0+ + public Result ListAudioOutsAuto(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span names) + { + return ListAudioOuts(out count, names); + } + + [CmifCommand(3)] // 3.0.0+ + public Result OpenAudioOutAuto( + out AudioOutputConfiguration outputConfig, + out IAudioOut audioOut, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span outName, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + [CopyHandle] int processHandle, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan name, + [ClientProcessId] ulong pid) + { + return OpenAudioOut(out outputConfig, out audioOut, outName, parameter, appletResourceId, processHandle, name, pid); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs new file mode 100644 index 000000000..776df641a --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs @@ -0,0 +1,187 @@ +using Ryujinx.Audio; +using Ryujinx.Audio.Integration; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Common.Memory; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Buffers; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioRenderer : IAudioRenderer, IDisposable + { + private readonly AudioRenderSystem _renderSystem; + private int _workBufferHandle; + private int _processHandle; + + public AudioRenderer(AudioRenderSystem renderSystem, int workBufferHandle, int processHandle) + { + _renderSystem = renderSystem; + _workBufferHandle = workBufferHandle; + _processHandle = processHandle; + } + + [CmifCommand(0)] + public Result GetSampleRate(out int sampleRate) + { + sampleRate = (int)_renderSystem.GetSampleRate(); + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetSampleCount(out int sampleCount) + { + sampleCount = (int)_renderSystem.GetSampleCount(); + + return Result.Success; + } + + [CmifCommand(2)] + public Result GetMixBufferCount(out int mixBufferCount) + { + mixBufferCount = (int)_renderSystem.GetMixBufferCount(); + + return Result.Success; + } + + [CmifCommand(3)] + public Result GetState(out int state) + { + state = _renderSystem.IsActive() ? 0 : 1; + + return Result.Success; + } + + [CmifCommand(4)] + public Result RequestUpdate( + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span output, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span performanceOutput, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input) + { + using IMemoryOwner outputOwner = ByteMemoryPool.Rent(output.Length); + using IMemoryOwner performanceOutputOwner = ByteMemoryPool.Rent(performanceOutput.Length); + + Memory outputMemory = outputOwner.Memory; + Memory performanceOutputMemory = performanceOutputOwner.Memory; + + using MemoryHandle outputHandle = outputMemory.Pin(); + using MemoryHandle performanceOutputHandle = performanceOutputMemory.Pin(); + + Result result = new Result((int)_renderSystem.Update(outputMemory, performanceOutputMemory, input.ToArray())); + + outputMemory.Span.CopyTo(output); + performanceOutputMemory.Span.CopyTo(performanceOutput); + + return result; + } + + [CmifCommand(5)] + public Result Start() + { + _renderSystem.Start(); + + return Result.Success; + } + + [CmifCommand(6)] + public Result Stop() + { + _renderSystem.Stop(); + + return Result.Success; + } + + [CmifCommand(7)] + public Result QuerySystemEvent([CopyHandle] out int eventHandle) + { + ResultCode rc = _renderSystem.QuerySystemEvent(out IWritableEvent systemEvent); + + eventHandle = 0; + + if (rc == ResultCode.Success && systemEvent is AudioEvent audioEvent) + { + eventHandle = audioEvent.GetReadableHandle(); + } + + return new Result((int)rc); + } + + [CmifCommand(8)] + public Result SetRenderingTimeLimit(int percent) + { + _renderSystem.SetRenderingTimeLimitPercent((uint)percent); + + return Result.Success; + } + + [CmifCommand(9)] + public Result GetRenderingTimeLimit(out int percent) + { + percent = (int)_renderSystem.GetRenderingTimeLimit(); + + return Result.Success; + } + + [CmifCommand(10)] // 3.0.0+ + public Result RequestUpdateAuto( + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span output, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span performanceOutput, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan input) + { + return RequestUpdate(output, performanceOutput, input); + } + + [CmifCommand(11)] // 3.0.0+ + public Result ExecuteAudioRendererRendering() + { + return new Result((int)_renderSystem.ExecuteAudioRendererRendering()); + } + + [CmifCommand(12)] // 15.0.0+ + public Result SetVoiceDropParameter(float voiceDropParameter) + { + _renderSystem.SetVoiceDropParameter(voiceDropParameter); + + return Result.Success; + } + + [CmifCommand(13)] // 15.0.0+ + public Result GetVoiceDropParameter(out float voiceDropParameter) + { + voiceDropParameter = _renderSystem.GetVoiceDropParameter(); + + return Result.Success; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _renderSystem.Dispose(); + + if (_workBufferHandle != 0) + { + HorizonStatic.Syscall.CloseHandle(_workBufferHandle); + + _workBufferHandle = 0; + } + + if (_processHandle != 0) + { + HorizonStatic.Syscall.CloseHandle(_processHandle); + + _processHandle = 0; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs new file mode 100644 index 000000000..7138d27ce --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs @@ -0,0 +1,132 @@ +using Ryujinx.Audio.Renderer.Device; +using Ryujinx.Audio.Renderer.Server; +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioRendererManager : IAudioRendererManager + { + private const uint InitialRevision = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('1' << 24); + + private readonly Ryujinx.Audio.Renderer.Server.AudioRendererManager _impl; + private readonly VirtualDeviceSessionRegistry _registry; + + public AudioRendererManager(Ryujinx.Audio.Renderer.Server.AudioRendererManager impl, VirtualDeviceSessionRegistry registry) + { + _impl = impl; + _registry = registry; + } + + [CmifCommand(0)] + public Result OpenAudioRenderer( + out IAudioRenderer renderer, + AudioRendererParameterInternal parameter, + [CopyHandle] int workBufferHandle, + [CopyHandle] int processHandle, + ulong workBufferSize, + AppletResourceUserId appletResourceId, + [ClientProcessId] ulong pid) + { + var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle); + ulong workBufferAddress = HorizonStatic.Syscall.GetTransferMemoryAddress(workBufferHandle); + + Result result = new Result((int)_impl.OpenAudioRenderer( + out var renderSystem, + clientMemoryManager, + ref parameter.Configuration, + appletResourceId.Id, + workBufferAddress, + workBufferSize, + (uint)processHandle)); + + if (result.IsSuccess) + { + renderer = new AudioRenderer(renderSystem, workBufferHandle, processHandle); + } + else + { + renderer = null; + + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + HorizonStatic.Syscall.CloseHandle(processHandle); + } + + return result; + } + + [CmifCommand(1)] + public Result GetWorkBufferSize(out long workBufferSize, AudioRendererParameterInternal parameter) + { + if (BehaviourContext.CheckValidRevision(parameter.Configuration.Revision)) + { + workBufferSize = (long)Ryujinx.Audio.Renderer.Server.AudioRendererManager.GetWorkBufferSize(ref parameter.Configuration); + + Logger.Debug?.Print(LogClass.ServiceAudio, $"WorkBufferSize is 0x{workBufferSize:x16}."); + + return Result.Success; + } + else + { + workBufferSize = 0; + + Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{BehaviourContext.GetRevisionNumber(parameter.Configuration.Revision)} is not supported!"); + + return AudioResult.UnsupportedRevision; + } + } + + [CmifCommand(2)] + public Result GetAudioDeviceService(out IAudioDevice audioDevice, AppletResourceUserId appletResourceId) + { + audioDevice = new AudioDevice(_registry, appletResourceId, InitialRevision); + + return Result.Success; + } + + [CmifCommand(3)] // 3.0.0+ + public Result OpenAudioRendererForManualExecution( + out IAudioRenderer renderer, + AudioRendererParameterInternal parameter, + ulong workBufferAddress, + [CopyHandle] int processHandle, + ulong workBufferSize, + AppletResourceUserId appletResourceId, + [ClientProcessId] ulong pid) + { + var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle); + + Result result = new Result((int)_impl.OpenAudioRenderer( + out var renderSystem, + clientMemoryManager, + ref parameter.Configuration, + appletResourceId.Id, + workBufferAddress, + workBufferSize, + (uint)processHandle)); + + if (result.IsSuccess) + { + renderer = new AudioRenderer(renderSystem, 0, processHandle); + } + else + { + renderer = null; + + HorizonStatic.Syscall.CloseHandle(processHandle); + } + + return result; + } + + [CmifCommand(4)] // 4.0.0+ + public Result GetAudioDeviceServiceWithRevisionInfo(out IAudioDevice audioDevice, AppletResourceUserId appletResourceId, uint revision) + { + audioDevice = new AudioDevice(_registry, appletResourceId, revision); + + return Result.Success; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererParameterInternal.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererParameterInternal.cs new file mode 100644 index 000000000..e5fcf7b3b --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererParameterInternal.cs @@ -0,0 +1,14 @@ +using Ryujinx.Audio.Renderer.Parameter; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + struct AudioRendererParameterInternal + { + public AudioRendererConfiguration Configuration; + + public AudioRendererParameterInternal(AudioRendererConfiguration configuration) + { + Configuration = configuration; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs new file mode 100644 index 000000000..cf1fe3d1d --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs @@ -0,0 +1,30 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class AudioSnoopManager : IAudioSnoopManager + { + // Note: The interface changed completely on firmware 17.0.0, this implementation is for older firmware. + + [CmifCommand(0)] + public Result EnableDspUsageMeasurement() + { + return Result.Success; + } + + [CmifCommand(1)] + public Result DisableDspUsageMeasurement() + { + return Result.Success; + } + + [CmifCommand(6)] + public Result GetDspUsage(out uint usage) + { + usage = 0; + + return Result.Success; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs new file mode 100644 index 000000000..b77e2f402 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs @@ -0,0 +1,30 @@ +using Ryujinx.Common.Memory; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + [StructLayout(LayoutKind.Sequential, Size = 0x100, Pack = 1)] + struct DeviceName + { + public Array256 Name; + + public DeviceName(string name) + { + Name = new(); + Encoding.ASCII.GetBytes(name, Name.AsSpan()); + } + + public override string ToString() + { + int length = Name.AsSpan().IndexOf((byte)0); + if (length < 0) + { + length = 0x100; + } + + return Encoding.ASCII.GetString(Name.AsSpan()[..length]); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs new file mode 100644 index 000000000..393914371 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs @@ -0,0 +1,147 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class FinalOutputRecorder : IFinalOutputRecorder, IDisposable + { + private int _processHandle; + private SystemEventType _event; + + public FinalOutputRecorder(int processHandle) + { + _processHandle = processHandle; + Os.CreateSystemEvent(out _event, EventClearMode.ManualClear, interProcess: true); + } + + [CmifCommand(0)] + public Result GetFinalOutputRecorderState(out uint state) + { + state = 0; + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(1)] + public Result Start() + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(2)] + public Result Stop() + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(3)] + public Result AppendFinalOutputRecorderBuffer([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan buffer, ulong bufferClientPtr) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { bufferClientPtr }); + + return Result.Success; + } + + [CmifCommand(4)] + public Result RegisterBufferEvent([CopyHandle] out int eventHandle) + { + eventHandle = Os.GetReadableHandleOfSystemEvent(ref _event); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(5)] + public Result GetReleasedFinalOutputRecorderBuffers([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span buffer, out uint count, out ulong released) + { + count = 0; + released = 0; + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(6)] + public Result ContainsFinalOutputRecorderBuffer(ulong bufferPointer, out bool contains) + { + contains = false; + + Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { bufferPointer }); + + return Result.Success; + } + + [CmifCommand(7)] + public Result GetFinalOutputRecorderBufferEndTime(ulong bufferPointer, out ulong released) + { + released = 0; + + Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { bufferPointer }); + + return Result.Success; + } + + [CmifCommand(8)] // 3.0.0+ + public Result AppendFinalOutputRecorderBufferAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan buffer, ulong bufferClientPtr) + { + return AppendFinalOutputRecorderBuffer(buffer, bufferClientPtr); + } + + [CmifCommand(9)] // 3.0.0+ + public Result GetReleasedFinalOutputRecorderBuffersAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span buffer, out uint count, out ulong released) + { + return GetReleasedFinalOutputRecorderBuffers(buffer, out count, out released); + } + + [CmifCommand(10)] // 6.0.0+ + public Result FlushFinalOutputRecorderBuffers(out bool pending) + { + pending = false; + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(11)] // 9.0.0+ + public Result AttachWorkBuffer(FinalOutputRecorderParameterInternal parameter) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { parameter }); + + return Result.Success; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Os.DestroySystemEvent(ref _event); + + if (_processHandle != 0) + { + HorizonStatic.Syscall.CloseHandle(_processHandle); + + _processHandle = 0; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderManager.cs new file mode 100644 index 000000000..76491bb79 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderManager.cs @@ -0,0 +1,23 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + partial class FinalOutputRecorderManager : IFinalOutputRecorderManager + { + [CmifCommand(0)] + public Result OpenFinalOutputRecorder( + out IFinalOutputRecorder recorder, + FinalOutputRecorderParameter parameter, + [CopyHandle] int processHandle, + out FinalOutputRecorderParameterInternal outParameter, + AppletResourceUserId appletResourceId) + { + recorder = new FinalOutputRecorder(processHandle); + outParameter = new(parameter.SampleRate, 2, 0); + + return Result.Success; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameter.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameter.cs new file mode 100644 index 000000000..afa060fca --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameter.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x4)] + readonly struct FinalOutputRecorderParameter + { + public readonly uint SampleRate; + public readonly uint Padding; + + public FinalOutputRecorderParameter(uint sampleRate) + { + SampleRate = sampleRate; + Padding = 0; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameterInternal.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameterInternal.cs new file mode 100644 index 000000000..e88398eba --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameterInternal.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)] + readonly struct FinalOutputRecorderParameterInternal + { + public readonly uint SampleRate; + public readonly uint ChannelCount; + public readonly uint UseLargeFrameSize; + public readonly uint Padding; + + public FinalOutputRecorderParameterInternal(uint sampleRate, uint channelCount, uint useLargeFrameSize) + { + SampleRate = sampleRate; + ChannelCount = channelCount; + UseLargeFrameSize = useLargeFrameSize; + Padding = 0; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs new file mode 100644 index 000000000..3df1fe227 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs @@ -0,0 +1,24 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioDevice : IServiceObject + { + Result ListAudioDeviceName(Span names, out int nameCount); + Result SetAudioDeviceOutputVolume(ReadOnlySpan name, float volume); + Result GetAudioDeviceOutputVolume(ReadOnlySpan name, out float volume); + Result GetActiveAudioDeviceName(Span name); + Result QueryAudioDeviceSystemEvent(out int eventHandle); + Result GetActiveChannelCount(out int channelCount); + Result ListAudioDeviceNameAuto(Span names, out int nameCount); + Result SetAudioDeviceOutputVolumeAuto(ReadOnlySpan name, float volume); + Result GetAudioDeviceOutputVolumeAuto(ReadOnlySpan name, out float volume); + Result GetActiveAudioDeviceNameAuto(Span name); + Result QueryAudioDeviceInputEvent(out int eventHandle); + Result QueryAudioDeviceOutputEvent(out int eventHandle); + Result GetActiveAudioOutputDeviceName(Span name); + Result ListAudioOutputDeviceName(Span names, out int nameCount); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs new file mode 100644 index 000000000..bdc3bcf62 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs @@ -0,0 +1,26 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioIn : IServiceObject + { + Result GetAudioInState(out AudioDeviceState state); + Result Start(); + Result Stop(); + Result AppendAudioInBuffer(ulong bufferTag, ReadOnlySpan buffer); + Result RegisterBufferEvent(out int eventHandle); + Result GetReleasedAudioInBuffers(out uint count, Span bufferTags); + Result ContainsAudioInBuffer(out bool contains, ulong bufferTag); + Result AppendUacInBuffer(ulong bufferTag, ReadOnlySpan buffer, int eventHandle); + Result AppendAudioInBufferAuto(ulong bufferTag, ReadOnlySpan buffer); + Result GetReleasedAudioInBuffersAuto(out uint count, Span bufferTags); + Result AppendUacInBufferAuto(ulong bufferTag, ReadOnlySpan buffer, int eventHandle); + Result GetAudioInBufferCount(out uint bufferCount); + Result SetDeviceGain(float gain); + Result GetDeviceGain(out float gain); + Result FlushAudioInBuffers(out bool pending); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs new file mode 100644 index 000000000..e7f32fbd2 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs @@ -0,0 +1,43 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioInManager : IServiceObject + { + Result ListAudioIns(out int count, Span names); + Result OpenAudioIn( + out AudioOutputConfiguration outputConfig, + out IAudioIn audioIn, + Span outName, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + int processHandle, + ReadOnlySpan name, + ulong pid); + Result ListAudioInsAuto(out int count, Span names); + Result OpenAudioInAuto( + out AudioOutputConfiguration outputConfig, + out IAudioIn audioIn, + Span outName, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + int processHandle, + ReadOnlySpan name, + ulong pid); + Result ListAudioInsAutoFiltered(out int count, Span names); + Result OpenAudioInProtocolSpecified( + out AudioOutputConfiguration outputConfig, + out IAudioIn audioIn, + Span outName, + AudioInProtocol protocol, + AudioInputConfiguration parameter, + AppletResourceUserId appletResourceId, + int processHandle, + ReadOnlySpan name, + ulong pid); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs new file mode 100644 index 000000000..1b2009260 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs @@ -0,0 +1,25 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioOut : IServiceObject + { + Result GetAudioOutState(out AudioDeviceState state); + Result Start(); + Result Stop(); + Result AppendAudioOutBuffer(ulong bufferTag, ReadOnlySpan buffer); + Result RegisterBufferEvent(out int eventHandle); + Result GetReleasedAudioOutBuffers(out uint count, Span bufferTags); + Result ContainsAudioOutBuffer(out bool contains, ulong bufferTag); + Result AppendAudioOutBufferAuto(ulong bufferTag, ReadOnlySpan buffer); + Result GetReleasedAudioOutBuffersAuto(out uint count, Span bufferTags); + Result GetAudioOutBufferCount(out uint bufferCount); + Result GetAudioOutPlayedSampleCount(out ulong sampleCount); + Result FlushAudioOutBuffers(out bool pending); + Result SetAudioOutVolume(float volume); + Result GetAudioOutVolume(out float volume); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs new file mode 100644 index 000000000..40d62836b --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs @@ -0,0 +1,32 @@ +using Ryujinx.Audio.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioOutManager : IServiceObject + { + Result ListAudioOuts(out int count, Span names); + Result OpenAudioOut( + out AudioOutputConfiguration outputConfig, + out IAudioOut audioOut, + Span outName, + AudioInputConfiguration inputConfig, + AppletResourceUserId appletResourceId, + int processHandle, + ReadOnlySpan name, + ulong pid); + Result ListAudioOutsAuto(out int count, Span names); + Result OpenAudioOutAuto( + out AudioOutputConfiguration outputConfig, + out IAudioOut audioOut, + Span outName, + AudioInputConfiguration inputConfig, + AppletResourceUserId appletResourceId, + int processHandle, + ReadOnlySpan name, + ulong pid); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs new file mode 100644 index 000000000..e4ca2e8ec --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs @@ -0,0 +1,24 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioRenderer : IServiceObject + { + Result GetSampleRate(out int sampleRate); + Result GetSampleCount(out int sampleCount); + Result GetMixBufferCount(out int mixBufferCount); + Result GetState(out int state); + Result RequestUpdate(Span output, Span performanceOutput, ReadOnlySpan input); + Result Start(); + Result Stop(); + Result QuerySystemEvent(out int eventHandle); + Result SetRenderingTimeLimit(int percent); + Result GetRenderingTimeLimit(out int percent); + Result RequestUpdateAuto(Span output, Span performanceOutput, ReadOnlySpan input); + Result ExecuteAudioRendererRendering(); + Result SetVoiceDropParameter(float voiceDropParameter); + Result GetVoiceDropParameter(out float voiceDropParameter); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRendererManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRendererManager.cs new file mode 100644 index 000000000..fe95a2084 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRendererManager.cs @@ -0,0 +1,29 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioRendererManager : IServiceObject + { + Result OpenAudioRenderer( + out IAudioRenderer renderer, + AudioRendererParameterInternal parameter, + int processHandle, + int workBufferHandle, + ulong workBufferSize, + AppletResourceUserId appletUserId, + ulong pid); + Result GetWorkBufferSize(out long workBufferSize, AudioRendererParameterInternal parameter); + Result GetAudioDeviceService(out IAudioDevice audioDevice, AppletResourceUserId appletUserId); + Result OpenAudioRendererForManualExecution( + out IAudioRenderer renderer, + AudioRendererParameterInternal parameter, + ulong workBufferAddress, + int processHandle, + ulong workBufferSize, + AppletResourceUserId appletUserId, + ulong pid); + Result GetAudioDeviceServiceWithRevisionInfo(out IAudioDevice audioDevice, AppletResourceUserId appletUserId, uint revision); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs new file mode 100644 index 000000000..72853886a --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IAudioSnoopManager : IServiceObject + { + Result EnableDspUsageMeasurement(); + Result DisableDspUsageMeasurement(); + Result GetDspUsage(out uint usage); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs new file mode 100644 index 000000000..be21c38b7 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs @@ -0,0 +1,22 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IFinalOutputRecorder : IServiceObject + { + Result GetFinalOutputRecorderState(out uint state); + Result Start(); + Result Stop(); + Result AppendFinalOutputRecorderBuffer(ReadOnlySpan buffer, ulong bufferClientPtr); + Result RegisterBufferEvent(out int eventHandle); + Result GetReleasedFinalOutputRecorderBuffers(Span buffer, out uint count, out ulong released); + Result ContainsFinalOutputRecorderBuffer(ulong bufferPointer, out bool contains); + Result GetFinalOutputRecorderBufferEndTime(ulong bufferPointer, out ulong released); + Result AppendFinalOutputRecorderBufferAuto(ReadOnlySpan buffer, ulong bufferClientPtr); + Result GetReleasedFinalOutputRecorderBuffersAuto(Span buffer, out uint count, out ulong released); + Result FlushFinalOutputRecorderBuffers(out bool pending); + Result AttachWorkBuffer(FinalOutputRecorderParameterInternal parameter); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorderManager.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorderManager.cs new file mode 100644 index 000000000..bac41ca91 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorderManager.cs @@ -0,0 +1,16 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Applet; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Audio.Detail +{ + interface IFinalOutputRecorderManager : IServiceObject + { + Result OpenFinalOutputRecorder( + out IFinalOutputRecorder recorder, + FinalOutputRecorderParameter parameter, + int processHandle, + out FinalOutputRecorderParameterInternal outParameter, + AppletResourceUserId appletResourceId); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Codec/CodecResult.cs b/src/Ryujinx.Horizon/Sdk/Codec/CodecResult.cs new file mode 100644 index 000000000..21508b7f1 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/CodecResult.cs @@ -0,0 +1,16 @@ +using Ryujinx.Horizon.Common; + +namespace Ryujinx.Horizon.Sdk.Codec +{ + static class CodecResult + { + private const int ModuleId = 111; + + public static Result InvalidLength => new(ModuleId, 3); + public static Result OpusBadArg => new(ModuleId, 130); + public static Result OpusInvalidPacket => new(ModuleId, 133); + public static Result InvalidNumberOfStreams => new(ModuleId, 1000); + public static Result InvalidSampleRate => new(ModuleId, 1001); + public static Result InvalidChannelCount => new(ModuleId, 1002); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs new file mode 100644 index 000000000..5d2798582 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs @@ -0,0 +1,336 @@ +using Concentus; +using Concentus.Enums; +using Concentus.Structs; +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Codec.Detail +{ + partial class HardwareOpusDecoder : IHardwareOpusDecoder, IDisposable + { + [StructLayout(LayoutKind.Sequential)] + private struct OpusPacketHeader + { + public uint Length; + public uint FinalRange; + + public static OpusPacketHeader FromSpan(ReadOnlySpan data) + { + return new() + { + Length = BinaryPrimitives.ReadUInt32BigEndian(data), + FinalRange = BinaryPrimitives.ReadUInt32BigEndian(data[sizeof(uint)..]), + }; + } + } + + private interface IDecoder + { + int SampleRate { get; } + int ChannelsCount { get; } + + int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize); + void ResetState(); + } + + private class Decoder : IDecoder + { + private readonly OpusDecoder _decoder; + + public int SampleRate => _decoder.SampleRate; + public int ChannelsCount => _decoder.NumChannels; + + public Decoder(int sampleRate, int channelsCount) + { + _decoder = new OpusDecoder(sampleRate, channelsCount); + } + + public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) + { + return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize); + } + + public void ResetState() + { + _decoder.ResetState(); + } + } + + private class MultiSampleDecoder : IDecoder + { + private readonly OpusMSDecoder _decoder; + + public int SampleRate => _decoder.SampleRate; + public int ChannelsCount { get; } + + public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping) + { + ChannelsCount = channelsCount; + _decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); + } + + public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) + { + return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0); + } + + public void ResetState() + { + _decoder.ResetState(); + } + } + + private readonly IDecoder _decoder; + private int _workBufferHandle; + + private HardwareOpusDecoder(int workBufferHandle) + { + _workBufferHandle = workBufferHandle; + } + + public HardwareOpusDecoder(int sampleRate, int channelsCount, int workBufferHandle) : this(workBufferHandle) + { + _decoder = new Decoder(sampleRate, channelsCount); + } + + public HardwareOpusDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping, int workBufferHandle) : this(workBufferHandle) + { + _decoder = new MultiSampleDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); + } + + [CmifCommand(0)] + public Result DecodeInterleavedOld( + out int outConsumed, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out _, output, input, reset: false, withPerf: false); + } + + [CmifCommand(1)] + public Result SetContext(ReadOnlySpan context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(2)] // 3.0.0+ + public Result DecodeInterleavedForMultiStreamOld( + out int outConsumed, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out _, output, input, reset: false, withPerf: false); + } + + [CmifCommand(3)] // 3.0.0+ + public Result SetContextForMultiStream(ReadOnlySpan arg0) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return Result.Success; + } + + [CmifCommand(4)] // 4.0.0+ + public Result DecodeInterleavedWithPerfOld( + out int outConsumed, + out long timeTaken, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset: false, withPerf: true); + } + + [CmifCommand(5)] // 4.0.0+ + public Result DecodeInterleavedForMultiStreamWithPerfOld( + out int outConsumed, + out long timeTaken, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset: false, withPerf: true); + } + + [CmifCommand(6)] // 6.0.0+ + public Result DecodeInterleavedWithPerfAndResetOld( + out int outConsumed, + out long timeTaken, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input, + bool reset) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true); + } + + [CmifCommand(7)] // 6.0.0+ + public Result DecodeInterleavedForMultiStreamWithPerfAndResetOld( + out int outConsumed, + out long timeTaken, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan input, + bool reset) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true); + } + + [CmifCommand(8)] // 7.0.0+ + public Result DecodeInterleaved( + out int outConsumed, + out long timeTaken, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] ReadOnlySpan input, + bool reset) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true); + } + + [CmifCommand(9)] // 7.0.0+ + public Result DecodeInterleavedForMultiStream( + out int outConsumed, + out long timeTaken, + out int outSamples, + [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] Span output, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] ReadOnlySpan input, + bool reset) + { + return DecodeInterleavedInternal(out outConsumed, out outSamples, out timeTaken, output, input, reset, withPerf: true); + } + + private Result DecodeInterleavedInternal( + out int outConsumed, + out int outSamples, + out long timeTaken, + Span output, + ReadOnlySpan input, + bool reset, + bool withPerf) + { + timeTaken = 0; + + Result result = DecodeInterleaved(_decoder, reset, input, out short[] outPcmData, output.Length, out outConsumed, out outSamples); + + if (withPerf) + { + // This is the time the DSP took to process the request, TODO: fill this. + timeTaken = 0; + } + + MemoryMarshal.Cast(outPcmData).CopyTo(output[..(outPcmData.Length * sizeof(short))]); + + return result; + } + + private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, byte[] packet) + { + int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate); + + numSamples = result; + + if (result == OpusError.OPUS_INVALID_PACKET) + { + return CodecResult.OpusInvalidPacket; + } + else if (result == OpusError.OPUS_BAD_ARG) + { + return CodecResult.OpusBadArg; + } + + return Result.Success; + } + + private static Result DecodeInterleaved( + IDecoder decoder, + bool reset, + ReadOnlySpan input, + out short[] outPcmData, + int outputSize, + out int outConsumed, + out int outSamples) + { + outPcmData = null; + outConsumed = 0; + outSamples = 0; + + int streamSize = input.Length; + + if (streamSize < Unsafe.SizeOf()) + { + return CodecResult.InvalidLength; + } + + OpusPacketHeader header = OpusPacketHeader.FromSpan(input); + int headerSize = Unsafe.SizeOf(); + uint totalSize = header.Length + (uint)headerSize; + + if (totalSize > streamSize) + { + return CodecResult.InvalidLength; + } + + byte[] opusData = input.Slice(headerSize, (int)header.Length).ToArray(); + + Result result = GetPacketNumSamples(decoder, out int numSamples, opusData); + + if (result.IsSuccess) + { + if ((uint)numSamples * (uint)decoder.ChannelsCount * sizeof(short) > outputSize) + { + return CodecResult.InvalidLength; + } + + outPcmData = new short[numSamples * decoder.ChannelsCount]; + + if (reset) + { + decoder.ResetState(); + } + + try + { + outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount); + outConsumed = (int)totalSize; + } + catch (OpusException) + { + // TODO: As OpusException doesn't return the exact error code, this is inaccurate in some cases... + return CodecResult.InvalidLength; + } + } + + return Result.Success; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_workBufferHandle != 0) + { + HorizonStatic.Syscall.CloseHandle(_workBufferHandle); + + _workBufferHandle = 0; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderManager.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderManager.cs new file mode 100644 index 000000000..acec66e82 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderManager.cs @@ -0,0 +1,386 @@ +using Ryujinx.Common; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using Ryujinx.Horizon.Sdk.Sf.Hipc; +using System; + +namespace Ryujinx.Horizon.Sdk.Codec.Detail +{ + partial class HardwareOpusDecoderManager : IHardwareOpusDecoderManager + { + [CmifCommand(0)] + public Result OpenHardwareOpusDecoder( + out IHardwareOpusDecoder decoder, + HardwareOpusDecoderParameterInternal parameter, + [CopyHandle] int workBufferHandle, + int workBufferSize) + { + decoder = null; + + if (!IsValidSampleRate(parameter.SampleRate)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidSampleRate; + } + + if (!IsValidChannelCount(parameter.ChannelsCount)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidChannelCount; + } + + decoder = new HardwareOpusDecoder(parameter.SampleRate, parameter.ChannelsCount, workBufferHandle); + + return Result.Success; + } + + [CmifCommand(1)] + public Result GetWorkBufferSize(out int size, HardwareOpusDecoderParameterInternal parameter) + { + size = 0; + + if (!IsValidChannelCount(parameter.ChannelsCount)) + { + return CodecResult.InvalidChannelCount; + } + + if (!IsValidSampleRate(parameter.SampleRate)) + { + return CodecResult.InvalidSampleRate; + } + + int opusDecoderSize = GetOpusDecoderSize(parameter.ChannelsCount); + + int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0; + int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * 1920 / sampleRateRatio : 0, 64); + size = opusDecoderSize + 1536 + frameSize; + + return Result.Success; + } + + [CmifCommand(2)] // 3.0.0+ + public Result OpenHardwareOpusDecoderForMultiStream( + out IHardwareOpusDecoder decoder, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x110)] in HardwareOpusMultiStreamDecoderParameterInternal parameter, + [CopyHandle] int workBufferHandle, + int workBufferSize) + { + decoder = null; + + if (!IsValidSampleRate(parameter.SampleRate)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidSampleRate; + } + + if (!IsValidMultiChannelCount(parameter.ChannelsCount)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidChannelCount; + } + + if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidNumberOfStreams; + } + + decoder = new HardwareOpusDecoder( + parameter.SampleRate, + parameter.ChannelsCount, + parameter.NumberOfStreams, + parameter.NumberOfStereoStreams, + parameter.ChannelMappings.AsSpan().ToArray(), + workBufferHandle); + + return Result.Success; + } + + [CmifCommand(3)] // 3.0.0+ + public Result GetWorkBufferSizeForMultiStream( + out int size, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x110)] in HardwareOpusMultiStreamDecoderParameterInternal parameter) + { + size = 0; + + if (!IsValidMultiChannelCount(parameter.ChannelsCount)) + { + return CodecResult.InvalidChannelCount; + } + + if (!IsValidSampleRate(parameter.SampleRate)) + { + return CodecResult.InvalidSampleRate; + } + + if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount)) + { + return CodecResult.InvalidSampleRate; + } + + int opusDecoderSize = GetOpusMultistreamDecoderSize(parameter.NumberOfStreams, parameter.NumberOfStereoStreams); + + int streamSize = BitUtils.AlignUp(parameter.NumberOfStreams * 1500, 64); + int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0; + int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * 1920 / sampleRateRatio : 0, 64); + size = opusDecoderSize + streamSize + frameSize; + + return Result.Success; + } + + [CmifCommand(4)] // 12.0.0+ + public Result OpenHardwareOpusDecoderEx( + out IHardwareOpusDecoder decoder, + HardwareOpusDecoderParameterInternalEx parameter, + [CopyHandle] int workBufferHandle, + int workBufferSize) + { + decoder = null; + + if (!IsValidChannelCount(parameter.ChannelsCount)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidChannelCount; + } + + if (!IsValidSampleRate(parameter.SampleRate)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidSampleRate; + } + + decoder = new HardwareOpusDecoder(parameter.SampleRate, parameter.ChannelsCount, workBufferHandle); + + return Result.Success; + } + + [CmifCommand(5)] // 12.0.0+ + public Result GetWorkBufferSizeEx(out int size, HardwareOpusDecoderParameterInternalEx parameter) + { + return GetWorkBufferSizeExImpl(out size, in parameter, fromDsp: false); + } + + [CmifCommand(6)] // 12.0.0+ + public Result OpenHardwareOpusDecoderForMultiStreamEx( + out IHardwareOpusDecoder decoder, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x118)] in HardwareOpusMultiStreamDecoderParameterInternalEx parameter, + [CopyHandle] int workBufferHandle, + int workBufferSize) + { + decoder = null; + + if (!IsValidSampleRate(parameter.SampleRate)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidSampleRate; + } + + if (!IsValidMultiChannelCount(parameter.ChannelsCount)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidChannelCount; + } + + if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount)) + { + HorizonStatic.Syscall.CloseHandle(workBufferHandle); + + return CodecResult.InvalidNumberOfStreams; + } + + decoder = new HardwareOpusDecoder( + parameter.SampleRate, + parameter.ChannelsCount, + parameter.NumberOfStreams, + parameter.NumberOfStereoStreams, + parameter.ChannelMappings.AsSpan().ToArray(), + workBufferHandle); + + return Result.Success; + } + + [CmifCommand(7)] // 12.0.0+ + public Result GetWorkBufferSizeForMultiStreamEx( + out int size, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x118)] in HardwareOpusMultiStreamDecoderParameterInternalEx parameter) + { + return GetWorkBufferSizeForMultiStreamExImpl(out size, in parameter, fromDsp: false); + } + + [CmifCommand(8)] // 16.0.0+ + public Result GetWorkBufferSizeExEx(out int size, HardwareOpusDecoderParameterInternalEx parameter) + { + return GetWorkBufferSizeExImpl(out size, in parameter, fromDsp: true); + } + + [CmifCommand(9)] // 16.0.0+ + public Result GetWorkBufferSizeForMultiStreamExEx( + out int size, + [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer, 0x118)] in HardwareOpusMultiStreamDecoderParameterInternalEx parameter) + { + return GetWorkBufferSizeForMultiStreamExImpl(out size, in parameter, fromDsp: true); + } + + private Result GetWorkBufferSizeExImpl(out int size, in HardwareOpusDecoderParameterInternalEx parameter, bool fromDsp) + { + size = 0; + + if (!IsValidChannelCount(parameter.ChannelsCount)) + { + return CodecResult.InvalidChannelCount; + } + + if (!IsValidSampleRate(parameter.SampleRate)) + { + return CodecResult.InvalidSampleRate; + } + + int opusDecoderSize = fromDsp ? GetDspOpusDecoderSize(parameter.ChannelsCount) : GetOpusDecoderSize(parameter.ChannelsCount); + + int frameSizeMono48KHz = parameter.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920; + int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0; + int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * frameSizeMono48KHz / sampleRateRatio : 0, 64); + size = opusDecoderSize + 1536 + frameSize; + + return Result.Success; + } + + private Result GetWorkBufferSizeForMultiStreamExImpl(out int size, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter, bool fromDsp) + { + size = 0; + + if (!IsValidMultiChannelCount(parameter.ChannelsCount)) + { + return CodecResult.InvalidChannelCount; + } + + if (!IsValidSampleRate(parameter.SampleRate)) + { + return CodecResult.InvalidSampleRate; + } + + if (!IsValidNumberOfStreams(parameter.NumberOfStreams, parameter.NumberOfStereoStreams, parameter.ChannelsCount)) + { + return CodecResult.InvalidSampleRate; + } + + int opusDecoderSize = fromDsp + ? GetDspOpusMultistreamDecoderSize(parameter.NumberOfStreams, parameter.NumberOfStereoStreams) + : GetOpusMultistreamDecoderSize(parameter.NumberOfStreams, parameter.NumberOfStereoStreams); + + int frameSizeMono48KHz = parameter.Flags.HasFlag(OpusDecoderFlags.LargeFrameSize) ? 5760 : 1920; + int streamSize = BitUtils.AlignUp(parameter.NumberOfStreams * 1500, 64); + int sampleRateRatio = parameter.SampleRate != 0 ? 48000 / parameter.SampleRate : 0; + int frameSize = BitUtils.AlignUp(sampleRateRatio != 0 ? parameter.ChannelsCount * frameSizeMono48KHz / sampleRateRatio : 0, 64); + size = opusDecoderSize + streamSize + frameSize; + + return Result.Success; + } + + private static int GetDspOpusDecoderSize(int channelsCount) + { + // TODO: Figure out the size returned here. + // Not really important because we don't use the work buffer, and the size being lower is fine. + + return 0; + } + + private static int GetDspOpusMultistreamDecoderSize(int streams, int coupledStreams) + { + // TODO: Figure out the size returned here. + // Not really important because we don't use the work buffer, and the size being lower is fine. + + return 0; + } + + private static int GetOpusDecoderSize(int channelsCount) + { + const int SilkDecoderSize = 0x2160; + + if (channelsCount < 1 || channelsCount > 2) + { + return 0; + } + + int celtDecoderSize = GetCeltDecoderSize(channelsCount); + int opusDecoderSize = GetOpusDecoderAllocSize(channelsCount) | 0x50; + + return opusDecoderSize + SilkDecoderSize + celtDecoderSize; + } + + private static int GetOpusMultistreamDecoderSize(int streams, int coupledStreams) + { + if (streams < 1 || coupledStreams > streams || coupledStreams < 0) + { + return 0; + } + + int coupledSize = GetOpusDecoderSize(2); + int monoSize = GetOpusDecoderSize(1); + + return Align4(monoSize - GetOpusDecoderAllocSize(1)) * (streams - coupledStreams) + + Align4(coupledSize - GetOpusDecoderAllocSize(2)) * coupledStreams + 0xb920; + } + + private static int Align4(int value) + { + return BitUtils.AlignUp(value, 4); + } + + private static int GetOpusDecoderAllocSize(int channelsCount) + { + return channelsCount * 0x800 + 0x4800; + } + + private static int GetCeltDecoderSize(int channelsCount) + { + const int DecodeBufferSize = 0x2030; + const int Overlap = 120; + const int EBandsCount = 21; + + return (DecodeBufferSize + Overlap * 4) * channelsCount + EBandsCount * 16 + 0x54; + } + + private static bool IsValidChannelCount(int channelsCount) + { + return channelsCount > 0 && channelsCount <= 2; + } + + private static bool IsValidMultiChannelCount(int channelsCount) + { + return channelsCount > 0 && channelsCount <= 255; + } + + private static bool IsValidSampleRate(int sampleRate) + { + switch (sampleRate) + { + case 8000: + case 12000: + case 16000: + case 24000: + case 48000: + return true; + } + + return false; + } + + private static bool IsValidNumberOfStreams(int numberOfStreams, int numberOfStereoStreams, int channelsCount) + { + return numberOfStreams > 0 && + numberOfStreams + numberOfStereoStreams <= channelsCount && + numberOfStereoStreams >= 0 && + numberOfStereoStreams <= numberOfStreams; + } + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternal.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternal.cs new file mode 100644 index 000000000..271a592c1 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternal.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Codec.Detail +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x4)] + struct HardwareOpusDecoderParameterInternal + { + public int SampleRate; + public int ChannelsCount; + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternalEx.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternalEx.cs new file mode 100644 index 000000000..e2b81c771 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternalEx.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Codec.Detail +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)] + struct HardwareOpusDecoderParameterInternalEx + { + public int SampleRate; + public int ChannelsCount; + public OpusDecoderFlags Flags; + public uint Reserved; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternal.cs similarity index 65% rename from src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs rename to src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternal.cs index fd63a4f79..98536a4f8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternal.cs @@ -1,15 +1,15 @@ using Ryujinx.Common.Memory; using System.Runtime.InteropServices; -namespace Ryujinx.HLE.HOS.Services.Audio.Types +namespace Ryujinx.Horizon.Sdk.Codec.Detail { [StructLayout(LayoutKind.Sequential, Size = 0x110)] - struct OpusMultiStreamParameters + struct HardwareOpusMultiStreamDecoderParameterInternal { public int SampleRate; public int ChannelsCount; public int NumberOfStreams; public int NumberOfStereoStreams; - public Array64 ChannelMappings; + public Array256 ChannelMappings; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternalEx.cs similarity index 64% rename from src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs rename to src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternalEx.cs index 1315c734e..8f8615dff 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternalEx.cs @@ -1,19 +1,17 @@ using Ryujinx.Common.Memory; using System.Runtime.InteropServices; -namespace Ryujinx.HLE.HOS.Services.Audio.Types +namespace Ryujinx.Horizon.Sdk.Codec.Detail { [StructLayout(LayoutKind.Sequential, Size = 0x118)] - struct OpusMultiStreamParametersEx + struct HardwareOpusMultiStreamDecoderParameterInternalEx { public int SampleRate; public int ChannelsCount; public int NumberOfStreams; public int NumberOfStereoStreams; public OpusDecoderFlags Flags; - - Array4 Padding1; - - public Array64 ChannelMappings; + public uint Reserved; + public Array256 ChannelMappings; } } diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoder.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoder.cs new file mode 100644 index 000000000..ae09ad15a --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoder.cs @@ -0,0 +1,20 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; +using System; + +namespace Ryujinx.Horizon.Sdk.Codec.Detail +{ + interface IHardwareOpusDecoder : IServiceObject + { + Result DecodeInterleavedOld(out int outConsumed, out int outSamples, Span output, ReadOnlySpan input); + Result SetContext(ReadOnlySpan context); + Result DecodeInterleavedForMultiStreamOld(out int outConsumed, out int outSamples, Span output, ReadOnlySpan input); + Result SetContextForMultiStream(ReadOnlySpan context); + Result DecodeInterleavedWithPerfOld(out int outConsumed, out long timeTaken, out int outSamples, Span output, ReadOnlySpan input); + Result DecodeInterleavedForMultiStreamWithPerfOld(out int outConsumed, out long timeTaken, out int outSamples, Span output, ReadOnlySpan input); + Result DecodeInterleavedWithPerfAndResetOld(out int outConsumed, out long timeTaken, out int outSamples, Span output, ReadOnlySpan input, bool reset); + Result DecodeInterleavedForMultiStreamWithPerfAndResetOld(out int outConsumed, out long timeTaken, out int outSamples, Span output, ReadOnlySpan input, bool reset); + Result DecodeInterleaved(out int outConsumed, out long timeTaken, out int outSamples, Span output, ReadOnlySpan input, bool reset); + Result DecodeInterleavedForMultiStream(out int outConsumed, out long timeTaken, out int outSamples, Span output, ReadOnlySpan input, bool reset); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoderManager.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoderManager.cs new file mode 100644 index 000000000..fb6c787b6 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoderManager.cs @@ -0,0 +1,19 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Codec.Detail +{ + interface IHardwareOpusDecoderManager : IServiceObject + { + Result OpenHardwareOpusDecoder(out IHardwareOpusDecoder decoder, HardwareOpusDecoderParameterInternal parameter, int workBufferHandle, int workBufferSize); + Result GetWorkBufferSize(out int size, HardwareOpusDecoderParameterInternal parameter); + Result OpenHardwareOpusDecoderForMultiStream(out IHardwareOpusDecoder decoder, in HardwareOpusMultiStreamDecoderParameterInternal parameter, int workBufferHandle, int workBufferSize); + Result GetWorkBufferSizeForMultiStream(out int size, in HardwareOpusMultiStreamDecoderParameterInternal parameter); + Result OpenHardwareOpusDecoderEx(out IHardwareOpusDecoder decoder, HardwareOpusDecoderParameterInternalEx parameter, int workBufferHandle, int workBufferSize); + Result GetWorkBufferSizeEx(out int size, HardwareOpusDecoderParameterInternalEx parameter); + Result OpenHardwareOpusDecoderForMultiStreamEx(out IHardwareOpusDecoder decoder, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter, int workBufferHandle, int workBufferSize); + Result GetWorkBufferSizeForMultiStreamEx(out int size, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter); + Result GetWorkBufferSizeExEx(out int size, HardwareOpusDecoderParameterInternalEx parameter); + Result GetWorkBufferSizeForMultiStreamExEx(out int size, in HardwareOpusMultiStreamDecoderParameterInternalEx parameter); + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/OpusDecoderFlags.cs similarity index 72% rename from src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs rename to src/Ryujinx.Horizon/Sdk/Codec/Detail/OpusDecoderFlags.cs index 572535a92..d630b10f4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/OpusDecoderFlags.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.HLE.HOS.Services.Audio.Types +namespace Ryujinx.Horizon.Sdk.Codec.Detail { [Flags] enum OpusDecoderFlags : uint diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs index f3fe51940..b81e62a47 100644 --- a/src/Ryujinx.Horizon/ServiceTable.cs +++ b/src/Ryujinx.Horizon/ServiceTable.cs @@ -1,4 +1,5 @@ using Ryujinx.Horizon.Arp; +using Ryujinx.Horizon.Audio; using Ryujinx.Horizon.Bcat; using Ryujinx.Horizon.Friends; using Ryujinx.Horizon.Hshl; @@ -39,9 +40,11 @@ namespace Ryujinx.Horizon } RegisterService(); + RegisterService(); RegisterService(); RegisterService(); RegisterService(); + RegisterService(); // TODO: Merge with audio once we can start multiple threads. RegisterService(); RegisterService(); RegisterService(); diff --git a/src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs b/src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs index 2060782cc..44d008224 100644 --- a/src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs +++ b/src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs @@ -41,6 +41,7 @@ namespace Ryujinx.Horizon.Srepo public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Usb/UsbIpcServer.cs b/src/Ryujinx.Horizon/Usb/UsbIpcServer.cs index 38eeed496..a04b81f97 100644 --- a/src/Ryujinx.Horizon/Usb/UsbIpcServer.cs +++ b/src/Ryujinx.Horizon/Usb/UsbIpcServer.cs @@ -66,6 +66,7 @@ namespace Ryujinx.Horizon.Usb public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } } diff --git a/src/Ryujinx.Horizon/Wlan/WlanIpcServer.cs b/src/Ryujinx.Horizon/Wlan/WlanIpcServer.cs index c7b336231..776b9a7cb 100644 --- a/src/Ryujinx.Horizon/Wlan/WlanIpcServer.cs +++ b/src/Ryujinx.Horizon/Wlan/WlanIpcServer.cs @@ -54,6 +54,7 @@ namespace Ryujinx.Horizon.Wlan public void Shutdown() { _serverManager.Dispose(); + _sm.Dispose(); } } }