aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs25
-rw-r--r--src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs22
-rw-r--r--src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs8
-rw-r--r--src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs6
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs25
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs17
-rw-r--r--src/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs12
-rw-r--r--src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs8
-rw-r--r--src/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs4
-rw-r--r--src/Ryujinx.Audio/Input/AudioInputManager.cs8
-rw-r--r--src/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs4
-rw-r--r--src/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs4
-rw-r--r--src/Ryujinx.Audio/Output/AudioOutputManager.cs47
-rw-r--r--src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs34
-rw-r--r--src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs28
-rw-r--r--src/Ryujinx.HLE/HOS/Horizon.cs95
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/ExternalEvent.cs25
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs32
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs108
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs200
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs34
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs40
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs243
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs108
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs181
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs33
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs40
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs166
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDevice.cs174
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs320
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs25
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs122
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs215
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioDevice.cs18
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/IAudioRenderer.cs22
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs67
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs116
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs27
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/DecoderCommon.cs92
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IDecoder.cs11
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs116
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/MultiSampleDecoder.cs28
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs12
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForApplet.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioInManagerForDebugger.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs12
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForApplet.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManagerForDebugger.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs19
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForApplet.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManagerForDebugger.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IAudioSnoopManager.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManager.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForApplet.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IFinalOutputRecorderManagerForDebugger.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs227
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/ResultCode.cs21
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusPacketHeader.cs23
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusParametersEx.cs15
-rw-r--r--src/Ryujinx.HLE/Ryujinx.HLE.csproj6
-rw-r--r--src/Ryujinx.HLE/Switch.cs6
-rw-r--r--src/Ryujinx.Horizon.Common/IExternalEvent.cs10
-rw-r--r--src/Ryujinx.Horizon.Common/ISyscallApi.cs5
-rw-r--r--src/Ryujinx.Horizon.Common/Result.cs5
-rw-r--r--src/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs4
-rw-r--r--src/Ryujinx.Horizon/Arp/ArpIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Audio/AudioMain.cs17
-rw-r--r--src/Ryujinx.Horizon/Audio/AudioManagers.cs78
-rw-r--r--src/Ryujinx.Horizon/Audio/AudioUserIpcServer.cs55
-rw-r--r--src/Ryujinx.Horizon/Audio/HwopusIpcServer.cs46
-rw-r--r--src/Ryujinx.Horizon/Audio/HwopusMain.cs17
-rw-r--r--src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Friends/FriendsIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/HorizonOptions.cs14
-rw-r--r--src/Ryujinx.Horizon/Hshl/HshlIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Ins/InsIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Lbl/LblIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/LogManager/LmIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs2
-rw-r--r--src/Ryujinx.Horizon/Ovln/OvlnIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Psc/PscIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Ryujinx.Horizon.csproj7
-rw-r--r--src/Ryujinx.Horizon/Sdk/Account/Uid.cs2
-rw-r--r--src/Ryujinx.Horizon/Sdk/Applet/AppletId.cs71
-rw-r--r--src/Ryujinx.Horizon/Sdk/Applet/AppletResourceUserId.cs15
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs50
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs12
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs252
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs171
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs130
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs23
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocolName.cs8
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs154
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs93
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs187
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs132
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererParameterInternal.cs14
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs30
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs30
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs147
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderManager.cs23
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameter.cs17
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorderParameterInternal.cs21
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs24
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs26
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs43
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs25
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs32
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs24
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRendererManager.cs29
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs12
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs22
-rw-r--r--src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorderManager.cs16
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/CodecResult.cs16
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs336
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderManager.cs386
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternal.cs11
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoderParameterInternalEx.cs13
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternal.cs (renamed from src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParameters.cs)6
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternalEx.cs (renamed from src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs)10
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoder.cs20
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/IHardwareOpusDecoderManager.cs19
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/OpusDecoderFlags.cs (renamed from src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusDecoderFlags.cs)2
-rw-r--r--src/Ryujinx.Horizon/ServiceTable.cs3
-rw-r--r--src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Usb/UsbIpcServer.cs1
-rw-r--r--src/Ryujinx.Horizon/Wlan/WlanIpcServer.cs1
130 files changed, 3096 insertions, 3174 deletions
diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
index 744a4bc5..01286992 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 a5282161..3b912913 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<OpenALAudioBuffer> _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<OpenALAudioBuffer>();
@@ -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 b83e63db..e39bfe54 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 7a683f4e..00188ba5 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 ff039288..e3e5d291 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<SoundIoHardwareDeviceSession, byte> _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 123cfd27..f60982e3 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<SoundIoAudioBuffer>();
_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 3f3806c3..a2c2cdcd 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 bac21c44..3a3c1d1b 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 1c248faa..34cf653c 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 4d1796c9..d56997e9 100644
--- a/src/Ryujinx.Audio/Input/AudioInputManager.cs
+++ b/src/Ryujinx.Audio/Input/AudioInputManager.cs
@@ -166,7 +166,6 @@ namespace Ryujinx.Audio.Input
/// </summary>
/// <param name="filtered">If true, filter disconnected devices</param>
/// <returns>The list of all audio inputs name</returns>
-#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
/// <summary>
/// Open a new <see cref="AudioInputSystem"/>.
@@ -188,8 +186,6 @@ namespace Ryujinx.Audio.Input
/// <param name="inputDeviceName">The input device name wanted by the user</param>
/// <param name="sampleFormat">The sample format to use</param>
/// <param name="parameter">The user configuration</param>
- /// <param name="appletResourceUserId">The applet resource user id of the application</param>
- /// <param name="processHandle">The process handle of the application</param>
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
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 576954b9..1369f953 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 9c812fb9..95b0e4e5 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 5232357b..308cd156 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.
/// </summary>
/// <returns>The list of all audio outputs name</returns>
-#pragma warning disable CA1822 // Mark member as static
public string[] ListAudioOuts()
{
return new[] { Constants.DefaultDeviceOutputName };
}
-#pragma warning restore CA1822
/// <summary>
/// Open a new <see cref="AudioOutputSystem"/>.
@@ -182,9 +180,6 @@ namespace Ryujinx.Audio.Output
/// <param name="inputDeviceName">The input device name wanted by the user</param>
/// <param name="sampleFormat">The sample format to use</param>
/// <param name="parameter">The user configuration</param>
- /// <param name="appletResourceUserId">The applet resource user id of the application</param>
- /// <param name="processHandle">The process handle of the application</param>
- /// <param name="volume">The volume level to request</param>
/// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns>
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;
}
- /// <summary>
- /// Sets the volume for all output devices.
- /// </summary>
- /// <param name="volume">The volume to set.</param>
- public void SetVolume(float volume)
- {
- if (_sessions != null)
- {
- foreach (AudioOutputSystem session in _sessions)
- {
- session?.SetVolume(volume);
- }
- }
- }
-
- /// <summary>
- /// Gets the volume for all output devices.
- /// </summary>
- /// <returns>A float indicating the volume level.</returns>
- 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 9c885b2c..3e11df05 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<MailboxMessage>();
@@ -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 0dbbd26c..e334a89f 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
/// <summary>
/// Start the <see cref="AudioProcessor"/> and worker thread.
/// </summary>
- 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 <see cref="AudioRenderSystem"/>.
/// </summary>
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
- 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 cd3365ce..64b08e30 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 00000000..738d6b64
--- /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 b07f5194..6595ecef 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<KWritableEvent>(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<KTransferMemory>(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 acf83f48..00000000
--- 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<ulong> 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 3f138021..00000000
--- 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<nn::audio::AudioInBuffer, 5>)
- public ResultCode AppendAudioInBuffer(ServiceCtx context)
- {
- ulong position = context.Request.SendBuff[0].Position;
-
- ulong bufferTag = context.RequestData.ReadUInt64();
-
- AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
-
- return _impl.AppendBuffer(bufferTag, ref data);
- }
-
- [CommandCmif(4)]
- // RegisterBufferEvent() -> handle<copy>
- 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<u64, 6> 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<byte, ulong>(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<copy, unknown>, buffer<nn::audio::AudioInBuffer, 5>)
- 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<AudioUserBuffer>(context.Memory, position);
-
- return _impl.AppendUacBuffer(bufferTag, ref data, handle);
- }
-
- [CommandCmif(8)] // 3.0.0+
- // AppendAudioInBufferAuto(u64 tag, buffer<nn::audio::AudioInBuffer, 0x21>)
- public ResultCode AppendAudioInBufferAuto(ServiceCtx context)
- {
- (ulong position, _) = context.Request.GetBufferType0x21();
-
- ulong bufferTag = context.RequestData.ReadUInt64();
-
- AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
-
- return _impl.AppendBuffer(bufferTag, ref data);
- }
-
- [CommandCmif(9)] // 3.0.0+
- // GetReleasedAudioInBuffersAuto() -> (u32 count, buffer<u64, 0x22> 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<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount);
-
- context.ResponseData.Write(releasedCount);
-
- return result;
- }
-
- [CommandCmif(10)] // 3.0.0+
- // AppendUacInBufferAuto(u64 tag, handle<copy, event>, buffer<nn::audio::AudioInBuffer, 0x21>)
- 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<AudioUserBuffer>(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 4e67303d..00000000
--- 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<ulong> 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 1e759e0c..00000000
--- 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 1b35a62d..00000000
--- 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<bytes, 6>)
- 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<copy, process>, buffer<bytes, 5> name)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 6> name)
- public ResultCode OpenAudioIn(ServiceCtx context)
- {
- AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
- 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<bytes, 0x22>)
- 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<copy, process>, buffer<bytes, 0x21>)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 0x22> name)
- public ResultCode OpenAudioInAuto(ServiceCtx context)
- {
- AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
- 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<bytes, 0x22>)
- 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<copy, process>, buffer<bytes, 5> name)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioIn>, buffer<bytes, 6> 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<AudioInputConfiguration>();
- 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 2ccf0657..00000000
--- 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<ulong> 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 e1b25263..00000000
--- 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<nn::audio::AudioOutBuffer, 5> buffer)
- public ResultCode AppendAudioOutBuffer(ServiceCtx context)
- {
- ulong position = context.Request.SendBuff[0].Position;
-
- ulong bufferTag = context.RequestData.ReadUInt64();
-
- AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
-
- return _impl.AppendBuffer(bufferTag, ref data);
- }
-
- [CommandCmif(4)]
- // RegisterBufferEvent() -> handle<copy>
- 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<u64, 6> 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<byte, ulong>(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<nn::audio::AudioOutBuffer, 0x21>)
- public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
- {
- (ulong position, _) = context.Request.GetBufferType0x21();
-
- ulong bufferTag = context.RequestData.ReadUInt64();
-
- AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
-
- return _impl.AppendBuffer(bufferTag, ref data);
- }
-
- [CommandCmif(8)] // 3.0.0+
- // GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer<u64, 0x22> 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<byte, ulong>(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 8c8c6862..00000000
--- 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<ulong> 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 c45a485b..00000000
--- 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 79ae6a14..00000000
--- 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<bytes, 6>)
- 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<copy, process> process_handle, buffer<bytes, 5> name_in)
- // -> (AudioOutInputConfiguration output_config, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
- public ResultCode OpenAudioOut(ServiceCtx context)
- {
- AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
- 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<bytes, 0x22>)
- 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<copy, process> process_handle, buffer<bytes, 0x21> name_in)
- // -> (AudioOutInputConfiguration output_config, object<nn::audio::detail::IAudioOut>, buffer<bytes, 0x22> name_out)
- public ResultCode OpenAudioOutAuto(ServiceCtx context)
- {
- AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
- 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 6497a3b8..00000000
--- 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 6206215d..00000000
--- 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<bytes, 6>)
- public ResultCode ListAudioDeviceName(ServiceCtx context)
- {
- string[] deviceNames = _impl.ListAudioDeviceName();
-
- ulong position = context.Request.ReceiveBuff[0].Position;
- ulong size = context.Request.ReceiveBuff[0].Size;
-
- ulong basePosition = position;
-
- int count = 0;
-
- foreach (string name in deviceNames)
- {
- byte[] buffer = Encoding.ASCII.GetBytes(name);
-
- if ((position - basePosition) + (ulong)buffer.Length > size)
- {
- break;
- }
-
- context.Memory.Write(position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length);
-
- position += AudioDeviceNameSize;
- count++;
- }
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(1)]
- // SetAudioDeviceOutputVolume(f32 volume, buffer<bytes, 5> name)
- public ResultCode SetAudioDeviceOutputVolume(ServiceCtx context)
- {
- float volume = context.RequestData.ReadSingle();
-
- ulong position = context.Request.SendBuff[0].Position;
- ulong size = context.Request.SendBuff[0].Size;
-
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
-
- return _impl.SetAudioDeviceOutputVolume(deviceName, volume);
- }
-
- [CommandCmif(2)]
- // GetAudioDeviceOutputVolume(buffer<bytes, 5> name) -> f32 volume
- public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context)
- {
- ulong position = context.Request.SendBuff[0].Position;
- ulong size = context.Request.SendBuff[0].Size;
-
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
-
- ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume);
-
- if (result == ResultCode.Success)
- {
- context.ResponseData.Write(volume);
- }
-
- return result;
- }
-
- [CommandCmif(3)]
- // GetActiveAudioDeviceName() -> buffer<bytes, 6>
- public ResultCode GetActiveAudioDeviceName(ServiceCtx context)
- {
- string name = _impl.GetActiveAudioDeviceName();
-
- ulong position = context.Request.ReceiveBuff[0].Position;
- ulong size = context.Request.ReceiveBuff[0].Size;
-
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0");
-
- if ((ulong)deviceNameBuffer.Length <= size)
- {
- context.Memory.Write(position, deviceNameBuffer);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
- }
-
- return ResultCode.Success;
- }
-
- [CommandCmif(4)]
- // QueryAudioDeviceSystemEvent() -> handle<copy, event>
- public ResultCode QueryAudioDeviceSystemEvent(ServiceCtx context)
- {
- KEvent deviceSystemEvent = _impl.QueryAudioDeviceSystemEvent();
-
- if (context.Process.HandleTable.GenerateHandle(deviceSystemEvent.ReadableEvent, out int handle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
-
- Logger.Stub?.PrintStub(LogClass.ServiceAudio);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(5)]
- // GetActiveChannelCount() -> u32
- public ResultCode GetActiveChannelCount(ServiceCtx context)
- {
- context.ResponseData.Write(_impl.GetActiveChannelCount());
-
- Logger.Stub?.PrintStub(LogClass.ServiceAudio);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(6)] // 3.0.0+
- // ListAudioDeviceNameAuto() -> (u32, buffer<bytes, 0x22>)
- public ResultCode ListAudioDeviceNameAuto(ServiceCtx context)
- {
- string[] deviceNames = _impl.ListAudioDeviceName();
-
- (ulong position, ulong size) = context.Request.GetBufferType0x22();
-
- ulong basePosition = position;
-
- int count = 0;
-
- foreach (string name in deviceNames)
- {
- byte[] buffer = Encoding.ASCII.GetBytes(name);
-
- if ((position - basePosition) + (ulong)buffer.Length > size)
- {
- break;
- }
-
- context.Memory.Write(position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length);
-
- position += AudioDeviceNameSize;
- count++;
- }
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(7)] // 3.0.0+
- // SetAudioDeviceOutputVolumeAuto(f32 volume, buffer<bytes, 0x21> name)
- public ResultCode SetAudioDeviceOutputVolumeAuto(ServiceCtx context)
- {
- float volume = context.RequestData.ReadSingle();
-
- (ulong position, ulong size) = context.Request.GetBufferType0x21();
-
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
-
- return _impl.SetAudioDeviceOutputVolume(deviceName, volume);
- }
-
- [CommandCmif(8)] // 3.0.0+
- // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32
- public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context)
- {
- (ulong position, ulong size) = context.Request.GetBufferType0x21();
-
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size);
-
- ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume);
-
- if (result == ResultCode.Success)
- {
- context.ResponseData.Write(volume);
- }
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10)] // 3.0.0+
- // GetActiveAudioDeviceNameAuto() -> buffer<bytes, 0x22>
- public ResultCode GetActiveAudioDeviceNameAuto(ServiceCtx context)
- {
- string name = _impl.GetActiveAudioDeviceName();
-
- (ulong position, ulong size) = context.Request.GetBufferType0x22();
-
- byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0');
-
- if ((ulong)deviceNameBuffer.Length <= size)
- {
- context.Memory.Write(position, deviceNameBuffer);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
- }
-
- return ResultCode.Success;
- }
-
- [CommandCmif(11)] // 3.0.0+
- // QueryAudioDeviceInputEvent() -> handle<copy, event>
- public ResultCode QueryAudioDeviceInputEvent(ServiceCtx context)
- {
- KEvent deviceInputEvent = _impl.QueryAudioDeviceInputEvent();
-
- if (context.Process.HandleTable.GenerateHandle(deviceInputEvent.ReadableEvent, out int handle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
-
- Logger.Stub?.PrintStub(LogClass.ServiceAudio);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(12)] // 3.0.0+
- // QueryAudioDeviceOutputEvent() -> handle<copy, event>
- public ResultCode QueryAudioDeviceOutputEvent(ServiceCtx context)
- {
- KEvent deviceOutputEvent = _impl.QueryAudioDeviceOutputEvent();
-
- if (context.Process.HandleTable.GenerateHandle(deviceOutputEvent.ReadableEvent, out int handle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
-
- Logger.Stub?.PrintStub(LogClass.ServiceAudio);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(13)] // 13.0.0+
- // GetActiveAudioOutputDeviceName() -> buffer<bytes, 6>
- public ResultCode GetActiveAudioOutputDeviceName(ServiceCtx context)
- {
- string name = _impl.GetActiveAudioOutputDeviceName();
-
- ulong position = context.Request.ReceiveBuff[0].Position;
- ulong size = context.Request.ReceiveBuff[0].Size;
-
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0");
-
- if ((ulong)deviceNameBuffer.Length <= size)
- {
- context.Memory.Write(position, deviceNameBuffer);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
- }
-
- return ResultCode.Success;
- }
-
- [CommandCmif(14)] // 13.0.0+
- // ListAudioOutputDeviceName() -> (u32, buffer<bytes, 6>)
- public ResultCode ListAudioOutputDeviceName(ServiceCtx context)
- {
- string[] deviceNames = _impl.ListAudioOutputDeviceName();
-
- ulong position = context.Request.ReceiveBuff[0].Position;
- ulong size = context.Request.ReceiveBuff[0].Size;
-
- ulong basePosition = position;
-
- int count = 0;
-
- foreach (string name in deviceNames)
- {
- byte[] buffer = Encoding.ASCII.GetBytes(name);
-
- if ((position - basePosition) + (ulong)buffer.Length > size)
- {
- break;
- }
-
- context.Memory.Write(position, buffer);
- MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length);
-
- position += AudioDeviceNameSize;
- count++;
- }
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
deleted file mode 100644
index 414c70a4..00000000
--- 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 88456be3..00000000
--- 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<byte> output, Memory<byte> performanceOutput, ReadOnlyMemory<byte> input)
- {
- return (ResultCode)_impl.Update(output, performanceOutput, input);
- }
-
- public void SetRenderingTimeLimit(uint percent)
- {
- _impl.SetRenderingTimeLimitPercent(percent);
- }
-
- public ResultCode Start()
- {
- _impl.Start();
-
- return ResultCode.Success;
- }
-
- public ResultCode Stop()
- {
- _impl.Stop();
-
- return ResultCode.Success;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _impl.Dispose();
- }
- }
-
- public void SetVoiceDropParameter(float voiceDropParameter)
- {
- _impl.SetVoiceDropParameter(voiceDropParameter);
- }
-
- public float GetVoiceDropParameter()
- {
- return _impl.GetVoiceDropParameter();
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/src/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
deleted file mode 100644
index baea0107..00000000
--- 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<nn::audio::detail::AudioRendererUpdateDataHeader, 5> input)
- // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> performanceOutput)
- public ResultCode RequestUpdate(ServiceCtx context)
- {
- ulong inputPosition = context.Request.SendBuff[0].Position;
- ulong inputSize = context.Request.SendBuff[0].Size;
-
- ulong outputPosition = context.Request.ReceiveBuff[0].Position;
- ulong outputSize = context.Request.ReceiveBuff[0].Size;
-
- ulong performanceOutputPosition = context.Request.ReceiveBuff[1].Position;
- ulong performanceOutputSize = context.Request.ReceiveBuff[1].Size;
-
- ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
-
- using IMemoryOwner<byte> outputOwner = ByteMemoryPool.RentCleared(outputSize);
- using IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.RentCleared(performanceOutputSize);
- Memory<byte> output = outputOwner.Memory;
- Memory<byte> performanceOutput = performanceOutputOwner.Memory;
-
- using MemoryHandle outputHandle = output.Pin();
- using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
-
- ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
-
- if (result == ResultCode.Success)
- {
- context.Memory.Write(outputPosition, output.Span);
- context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
- }
-
- return result;
- }
-
- [CommandCmif(5)]
- // Start()
- public ResultCode Start(ServiceCtx context)
- {
- return _impl.Start();
- }
-
- [CommandCmif(6)]
- // Stop()
- public ResultCode Stop(ServiceCtx context)
- {
- return _impl.Stop();
- }
-
- [CommandCmif(7)]
- // QuerySystemEvent() -> handle<copy, event>
- public ResultCode QuerySystemEvent(ServiceCtx context)
- {
- ResultCode result = _impl.QuerySystemEvent(out KEvent systemEvent);
-
- if (result == ResultCode.Success)
- {
- if (context.Process.HandleTable.GenerateHandle(systemEvent.ReadableEvent, out int handle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
- }
-
- return result;
- }
-
- [CommandCmif(8)]
- // SetAudioRendererRenderingTimeLimit(u32 limit)
- public ResultCode SetAudioRendererRenderingTimeLimit(ServiceCtx context)
- {
- uint limit = context.RequestData.ReadUInt32();
-
- _impl.SetRenderingTimeLimit(limit);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(9)]
- // GetAudioRendererRenderingTimeLimit() -> u32 limit
- public ResultCode GetAudioRendererRenderingTimeLimit(ServiceCtx context)
- {
- uint limit = _impl.GetRenderingTimeLimit();
-
- context.ResponseData.Write(limit);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10)] // 3.0.0+
- // RequestUpdateAuto(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x21> input)
- // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> performanceOutput)
- public ResultCode RequestUpdateAuto(ServiceCtx context)
- {
- (ulong inputPosition, ulong inputSize) = context.Request.GetBufferType0x21();
- (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(0);
- (ulong performanceOutputPosition, ulong performanceOutputSize) = context.Request.GetBufferType0x22(1);
-
- ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
-
- Memory<byte> output = new byte[outputSize];
- Memory<byte> performanceOutput = new byte[performanceOutputSize];
-
- using MemoryHandle outputHandle = output.Pin();
- using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
-
- ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
-
- if (result == ResultCode.Success)
- {
- context.Memory.Write(outputPosition, output.Span);
- context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
- }
-
- return result;
- }
-
- [CommandCmif(11)] // 3.0.0+
- // ExecuteAudioRendererRendering()
- public ResultCode ExecuteAudioRendererRendering(ServiceCtx context)
- {
- return _impl.ExecuteAudioRendererRendering();
- }
-
- [CommandCmif(12)] // 15.0.0+
- // SetVoiceDropParameter(f32 voiceDropParameter)
- public ResultCode SetVoiceDropParameter(ServiceCtx context)
- {
- float voiceDropParameter = context.RequestData.ReadSingle();
-
- _impl.SetVoiceDropParameter(voiceDropParameter);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(13)] // 15.0.0+
- // GetVoiceDropParameter() -> f32 voiceDropParameter
- public ResultCode GetVoiceDropParameter(ServiceCtx context)
- {
- float voiceDropParameter = _impl.GetVoiceDropParameter();
-
- context.ResponseData.Write(voiceDropParameter);
-
- return ResultCode.Success;
- }
-
- protected override void Dispose(bool isDisposing)
- {
- if (isDisposing)
- {
- _impl.Dispose();
- }
- }
- }
-}
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 0f181a47..00000000
--- 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 6bb4a5de..00000000
--- 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<byte> output, Memory<byte> performanceOutput, ReadOnlyMemory<byte> input);
- ResultCode Start();
- ResultCode Stop();
- ResultCode QuerySystemEvent(out KEvent systemEvent);
- void SetRenderingTimeLimit(uint percent);
- uint GetRenderingTimeLimit();
- ResultCode ExecuteAudioRendererRendering();
- void SetVoiceDropParameter(float voiceDropParameter);
- float GetVoiceDropParameter();
- }
-}
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 87d0001e..00000000
--- 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 38a841d8..00000000
--- 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<copy> workBuffer, handle<copy> processHandle)
- // -> object<nn::audio::detail::IAudioRenderer>
- public ResultCode OpenAudioRenderer(ServiceCtx context)
- {
- AudioRendererConfiguration parameter = context.RequestData.ReadStruct<AudioRendererConfiguration>();
- ulong workBufferSize = context.RequestData.ReadUInt64();
- ulong appletResourceUserId = context.RequestData.ReadUInt64();
-
- int transferMemoryHandle = context.Request.HandleDesc.ToCopy[0];
- KTransferMemory workBufferTransferMemory = context.Process.HandleTable.GetObject<KTransferMemory>(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<AudioRendererConfiguration>();
-
- if (BehaviourContext.CheckValidRevision(parameter.Revision))
- {
- ulong size = _impl.GetWorkBufferSize(ref parameter);
-
- context.ResponseData.Write(size);
-
- Logger.Debug?.Print(LogClass.ServiceAudio, $"WorkBufferSize is 0x{size:x16}.");
-
- return ResultCode.Success;
- }
- else
- {
- context.ResponseData.Write(0L);
-
- Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{BehaviourContext.GetRevisionNumber(parameter.Revision)} is not supported!");
-
- return ResultCode.UnsupportedRevision;
- }
- }
-
- [CommandCmif(2)]
- // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object<nn::audio::detail::IAudioDevice>
- public ResultCode GetAudioDeviceService(ServiceCtx context)
- {
- ulong appletResourceUserId = context.RequestData.ReadUInt64();
-
- ResultCode result = _impl.GetAudioDeviceServiceWithRevisionInfo(context, out IAudioDevice device, InitialRevision, appletResourceUserId);
-
- if (result == ResultCode.Success)
- {
- MakeObject(context, new AudioDeviceServer(device));
- }
-
- return result;
- }
-
- [CommandCmif(4)] // 4.0.0+
- // GetAudioDeviceServiceWithRevisionInfo(s32 revision, nn::applet::AppletResourceUserId appletResourceId) -> object<nn::audio::detail::IAudioDevice>
- public ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context)
- {
- int revision = context.RequestData.ReadInt32();
- ulong appletResourceUserId = context.RequestData.ReadUInt64();
-
- ResultCode result = _impl.GetAudioDeviceServiceWithRevisionInfo(context, out IAudioDevice device, revision, appletResourceUserId);
-
- if (result == ResultCode.Success)
- {
- MakeObject(context, new AudioDeviceServer(device));
- }
-
- return result;
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs b/src/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/Decoder.cs
deleted file mode 100644
index c5dd2f00..00000000
--- 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 9ff511a5..00000000
--- 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<byte> 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<OpusPacketHeader>())
- {
- return ResultCode.OpusInvalidInput;
- }
-
- OpusPacketHeader header = OpusPacketHeader.FromSpan(input);
- int headerSize = Unsafe.SizeOf<OpusPacketHeader>();
- 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 cc83be5e..00000000
--- 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 3d5d2839..00000000
--- 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<unknown, 5>) -> (u32, u32, buffer<unknown, 6>)
- public ResultCode DecodeInterleavedOld(ServiceCtx context)
- {
- return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false);
- }
-
- [CommandCmif(2)]
- // DecodeInterleavedForMultiStreamOld(buffer<unknown, 5>) -> (u32, u32, buffer<unknown, 6>)
- public ResultCode DecodeInterleavedForMultiStreamOld(ServiceCtx context)
- {
- return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: false);
- }
-
- [CommandCmif(4)] // 6.0.0+
- // DecodeInterleavedWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
- public ResultCode DecodeInterleavedWithPerfOld(ServiceCtx context)
- {
- return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true);
- }
-
- [CommandCmif(5)] // 6.0.0+
- // DecodeInterleavedForMultiStreamWithPerfOld(buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
- public ResultCode DecodeInterleavedForMultiStreamWithPerfOld(ServiceCtx context)
- {
- return DecodeInterleavedInternal(context, OpusDecoderFlags.None, reset: false, withPerf: true);
- }
-
- [CommandCmif(6)] // 6.0.0+
- // DecodeInterleavedWithPerfAndResetOld(bool reset, buffer<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
- 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<unknown, 5>) -> (u32, u32, u64, buffer<unknown, 0x46>)
- 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<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>)
- 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<unknown, 0x45>) -> (u32, u32, u64, buffer<unknown, 0x46>)
- 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<byte> 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<short, byte>(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 910bb23e..00000000
--- 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 a250ec79..00000000
--- 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 861e9f2d..00000000
--- 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 d0c385b5..00000000
--- 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 12013615..00000000
--- 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 cd7cbe41..00000000
--- 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 9925777e..00000000
--- 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 c41767a0..00000000
--- 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 112e246c..00000000
--- 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 dd767993..00000000
--- 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 cd2af09b..00000000
--- 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 aa9789ac..00000000
--- 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 9b58213e..00000000
--- 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 e2d62eee..00000000
--- 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 7ded7943..00000000
--- 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 514b51a5..00000000
--- 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<copy>) -> object<nn::codec::detail::IHardwareOpusDecoder>
- 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<copy>, buffer<unknown<0x110>, 0x19>) -> object<nn::codec::detail::IHardwareOpusDecoder>
- public ResultCode InitializeForMultiStream(ServiceCtx context)
- {
- ulong parametersAddress = context.Request.PtrBuff[0].Position;
-
- OpusMultiStreamParameters parameters = context.Memory.Read<OpusMultiStreamParameters>(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<unknown<0x110>, 0x19>) -> u32
- public ResultCode GetWorkBufferSizeForMultiStream(ServiceCtx context)
- {
- ulong parametersAddress = context.Request.PtrBuff[0].Position;
-
- OpusMultiStreamParameters parameters = context.Memory.Read<OpusMultiStreamParameters>(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<copy>) -> object<nn::codec::detail::IHardwareOpusDecoder>
- public ResultCode InitializeEx(ServiceCtx context)
- {
- OpusParametersEx parameters = context.RequestData.ReadStruct<OpusParametersEx>();
-
- // 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<OpusParametersEx>();
-
- 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<copy>, buffer<unknown<0x118>, 0x19>) -> object<nn::codec::detail::IHardwareOpusDecoder>
- public ResultCode InitializeForMultiStreamEx(ServiceCtx context)
- {
- ulong parametersAddress = context.Request.PtrBuff[0].Position;
-
- OpusMultiStreamParametersEx parameters = context.Memory.Read<OpusMultiStreamParametersEx>(parametersAddress);
-
- byte[] mappings = MemoryMarshal.Cast<uint, byte>(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<unknown<0x118>, 0x19>) -> u32
- public ResultCode GetWorkBufferSizeForMultiStreamEx(ServiceCtx context)
- {
- ulong parametersAddress = context.Request.PtrBuff[0].Position;
-
- OpusMultiStreamParametersEx parameters = context.Memory.Read<OpusMultiStreamParametersEx>(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<unknown<0x118>, 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 c1d49109..00000000
--- 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 099769b3..00000000
--- 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<byte> data)
- {
- OpusPacketHeader header = MemoryMarshal.Cast<byte, OpusPacketHeader>(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 4d1e0c82..00000000
--- 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<byte> Padding1;
- }
-}
diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
index dbcb8221..0fcf9e4b 100644
--- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -21,7 +21,6 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Concentus" />
<PackageReference Include="LibHac" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
<PackageReference Include="MsgPack.Cli" />
@@ -30,11 +29,6 @@
<PackageReference Include="NetCoreServer" />
</ItemGroup>
- <!-- Due to Concentus. -->
- <PropertyGroup>
- <NoWarn>NU1605</NoWarn>
- </PropertyGroup>
-
<ItemGroup>
<None Remove="Homebrew.npdm" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index 912a39b0..81c3ab47 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 00000000..dedf4c72
--- /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 20277f34..3d6da041 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 d313554e..4b120b84 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 a65ec3ab..19667290 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 6080b475..a6017b8a 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 00000000..92c9e804
--- /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 00000000..493a6f9b
--- /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 00000000..20c824e1
--- /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 00000000..e60e033c
--- /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 00000000..04eee3fa
--- /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 dd4e5b53..8da3971c 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 523c617a..a12c0cae 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 79462056..a24ce7f6 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 d7d89e24..b1cc7259 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 bb2749d5..4e06dcad 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 6b542165..f25fc54b 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 d023ff92..6bb4e11c 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 c52a294f..b3ce8118 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 b2a74fb2..ec73f96a 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 c4580a86..d4257be8 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 1902cde2..669a6459 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 d6ac6568..8e574ddd 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 ae40f7b5..d1f572d5 100644
--- a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj
+++ b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj
@@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
+ <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
@@ -12,7 +13,13 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="Concentus" />
<PackageReference Include="LibHac" />
</ItemGroup>
+ <!-- Due to Concentus. -->
+ <PropertyGroup>
+ <NoWarn>NU1605</NoWarn>
+ </PropertyGroup>
+
</Project>
diff --git a/src/Ryujinx.Horizon/Sdk/Account/Uid.cs b/src/Ryujinx.Horizon/Sdk/Account/Uid.cs
index ada2c02b..d612f479 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 00000000..2b81fbf6
--- /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 00000000..00e2ad36
--- /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 00000000..efa8d5bc
--- /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 00000000..c18bfee9
--- /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 00000000..f67ea729
--- /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<DeviceName> 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<DeviceName> 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<DeviceName> 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<DeviceName> 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<DeviceName> names, out int nameCount)
+ {
+ return ListAudioDeviceName(names, out nameCount);
+ }
+
+ [CmifCommand(7)] // 3.0.0+
+ public Result SetAudioDeviceOutputVolumeAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> name, float volume)
+ {
+ return SetAudioDeviceOutputVolume(name, volume);
+ }
+
+ [CmifCommand(8)] // 3.0.0+
+ public Result GetAudioDeviceOutputVolumeAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> name, out float volume)
+ {
+ return GetAudioDeviceOutputVolume(name, out volume);
+ }
+
+ [CmifCommand(10)] // 3.0.0+
+ public Result GetActiveAudioDeviceNameAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> 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<DeviceName> 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<DeviceName> 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 00000000..464ede58
--- /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<AudioUserBuffer> 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<ulong> 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<AudioUserBuffer> 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<AudioUserBuffer> buffer)
+ {
+ return AppendAudioInBuffer(bufferTag, buffer);
+ }
+
+ [CmifCommand(9)] // 3.0.0+
+ public Result GetReleasedAudioInBuffersAuto(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<ulong> bufferTags)
+ {
+ return GetReleasedAudioInBuffers(out count, bufferTags);
+ }
+
+ [CmifCommand(10)] // 3.0.0+
+ public Result AppendUacInBufferAuto(
+ ulong bufferTag,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<AudioUserBuffer> 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 00000000..d5d04720
--- /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<DeviceName> 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<DeviceName> outName,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ [CopyHandle] int processHandle,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> 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<DeviceName> 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<DeviceName> outName,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ [CopyHandle] int processHandle,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> 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<DeviceName> 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<DeviceName> outName,
+ AudioInProtocol protocol,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ [CopyHandle] int processHandle,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> 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 00000000..48785f1c
--- /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<byte> 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 00000000..68d283cc
--- /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 00000000..7607e264
--- /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<AudioUserBuffer> 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<ulong> 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<AudioUserBuffer> buffer)
+ {
+ return AppendAudioOutBuffer(bufferTag, buffer);
+ }
+
+ [CmifCommand(8)] // 3.0.0+
+ public Result GetReleasedAudioOutBuffersAuto(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<ulong> 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 00000000..3d129470
--- /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<DeviceName> 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<DeviceName> outName,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ [CopyHandle] int processHandle,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> 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<DeviceName> 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<DeviceName> outName,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ [CopyHandle] int processHandle,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> 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 00000000..776df641
--- /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<byte> output,
+ [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> performanceOutput,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
+ {
+ using IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(output.Length);
+ using IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Rent(performanceOutput.Length);
+
+ Memory<byte> outputMemory = outputOwner.Memory;
+ Memory<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<byte> performanceOutput,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<byte> 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 00000000..7138d27c
--- /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 00000000..e5fcf7b3
--- /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 00000000..cf1fe3d1
--- /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 00000000..b77e2f40
--- /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<byte> 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 00000000..39391437
--- /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<byte> 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<byte> 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<byte> buffer, ulong bufferClientPtr)
+ {
+ return AppendFinalOutputRecorderBuffer(buffer, bufferClientPtr);
+ }
+
+ [CmifCommand(9)] // 3.0.0+
+ public Result GetReleasedFinalOutputRecorderBuffersAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<byte> 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 00000000..76491bb7
--- /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 00000000..afa060fc
--- /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 00000000..e88398eb
--- /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 00000000..3df1fe22
--- /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<DeviceName> names, out int nameCount);
+ Result SetAudioDeviceOutputVolume(ReadOnlySpan<DeviceName> name, float volume);
+ Result GetAudioDeviceOutputVolume(ReadOnlySpan<DeviceName> name, out float volume);
+ Result GetActiveAudioDeviceName(Span<DeviceName> name);
+ Result QueryAudioDeviceSystemEvent(out int eventHandle);
+ Result GetActiveChannelCount(out int channelCount);
+ Result ListAudioDeviceNameAuto(Span<DeviceName> names, out int nameCount);
+ Result SetAudioDeviceOutputVolumeAuto(ReadOnlySpan<DeviceName> name, float volume);
+ Result GetAudioDeviceOutputVolumeAuto(ReadOnlySpan<DeviceName> name, out float volume);
+ Result GetActiveAudioDeviceNameAuto(Span<DeviceName> name);
+ Result QueryAudioDeviceInputEvent(out int eventHandle);
+ Result QueryAudioDeviceOutputEvent(out int eventHandle);
+ Result GetActiveAudioOutputDeviceName(Span<DeviceName> name);
+ Result ListAudioOutputDeviceName(Span<DeviceName> 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 00000000..bdc3bcf6
--- /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<AudioUserBuffer> buffer);
+ Result RegisterBufferEvent(out int eventHandle);
+ Result GetReleasedAudioInBuffers(out uint count, Span<ulong> bufferTags);
+ Result ContainsAudioInBuffer(out bool contains, ulong bufferTag);
+ Result AppendUacInBuffer(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer, int eventHandle);
+ Result AppendAudioInBufferAuto(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer);
+ Result GetReleasedAudioInBuffersAuto(out uint count, Span<ulong> bufferTags);
+ Result AppendUacInBufferAuto(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> 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 00000000..e7f32fbd
--- /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<DeviceName> names);
+ Result OpenAudioIn(
+ out AudioOutputConfiguration outputConfig,
+ out IAudioIn audioIn,
+ Span<DeviceName> outName,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ int processHandle,
+ ReadOnlySpan<DeviceName> name,
+ ulong pid);
+ Result ListAudioInsAuto(out int count, Span<DeviceName> names);
+ Result OpenAudioInAuto(
+ out AudioOutputConfiguration outputConfig,
+ out IAudioIn audioIn,
+ Span<DeviceName> outName,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ int processHandle,
+ ReadOnlySpan<DeviceName> name,
+ ulong pid);
+ Result ListAudioInsAutoFiltered(out int count, Span<DeviceName> names);
+ Result OpenAudioInProtocolSpecified(
+ out AudioOutputConfiguration outputConfig,
+ out IAudioIn audioIn,
+ Span<DeviceName> outName,
+ AudioInProtocol protocol,
+ AudioInputConfiguration parameter,
+ AppletResourceUserId appletResourceId,
+ int processHandle,
+ ReadOnlySpan<DeviceName> 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 00000000..1b200926
--- /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<AudioUserBuffer> buffer);
+ Result RegisterBufferEvent(out int eventHandle);
+ Result GetReleasedAudioOutBuffers(out uint count, Span<ulong> bufferTags);
+ Result ContainsAudioOutBuffer(out bool contains, ulong bufferTag);
+ Result AppendAudioOutBufferAuto(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer);
+ Result GetReleasedAudioOutBuffersAuto(out uint count, Span<ulong> 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 00000000..40d62836
--- /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<DeviceName> names);
+ Result OpenAudioOut(
+ out AudioOutputConfiguration outputConfig,
+ out IAudioOut audioOut,
+ Span<DeviceName> outName,
+ AudioInputConfiguration inputConfig,
+ AppletResourceUserId appletResourceId,
+ int processHandle,
+ ReadOnlySpan<DeviceName> name,
+ ulong pid);
+ Result ListAudioOutsAuto(out int count, Span<DeviceName> names);
+ Result OpenAudioOutAuto(
+ out AudioOutputConfiguration outputConfig,
+ out IAudioOut audioOut,
+ Span<DeviceName> outName,
+ AudioInputConfiguration inputConfig,
+ AppletResourceUserId appletResourceId,
+ int processHandle,
+ ReadOnlySpan<DeviceName> 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 00000000..e4ca2e8e
--- /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<byte> output, Span<byte> performanceOutput, ReadOnlySpan<byte> input);
+ Result Start();
+ Result Stop();
+ Result QuerySystemEvent(out int eventHandle);
+ Result SetRenderingTimeLimit(int percent);
+ Result GetRenderingTimeLimit(out int percent);
+ Result RequestUpdateAuto(Span<byte> output, Span<byte> performanceOutput, ReadOnlySpan<byte> 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 00000000..fe95a208
--- /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 00000000..72853886
--- /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 00000000..be21c38b
--- /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<byte> buffer, ulong bufferClientPtr);
+ Result RegisterBufferEvent(out int eventHandle);
+ Result GetReleasedFinalOutputRecorderBuffers(Span<byte> buffer, out uint count, out ulong released);
+ Result ContainsFinalOutputRecorderBuffer(ulong bufferPointer, out bool contains);
+ Result GetFinalOutputRecorderBufferEndTime(ulong bufferPointer, out ulong released);
+ Result AppendFinalOutputRecorderBufferAuto(ReadOnlySpan<byte> buffer, ulong bufferClientPtr);
+ Result GetReleasedFinalOutputRecorderBuffersAuto(Span<byte> 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 00000000..bac41ca9
--- /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 00000000..21508b7f
--- /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 00000000..5d279858
--- /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<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
+ {
+ return DecodeInterleavedInternal(out outConsumed, out outSamples, out _, output, input, reset: false, withPerf: false);
+ }
+
+ [CmifCommand(1)]
+ public Result SetContext(ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
+ {
+ return DecodeInterleavedInternal(out outConsumed, out outSamples, out _, output, input, reset: false, withPerf: false);
+ }
+
+ [CmifCommand(3)] // 3.0.0+
+ public Result SetContextForMultiStream(ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] ReadOnlySpan<byte> 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<byte> output,
+ [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias | HipcBufferFlags.MapTransferAllowsNonSecure)] ReadOnlySpan<byte> 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<byte> output,
+ ReadOnlySpan<byte> 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<short, byte>(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<byte> 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<OpusPacketHeader>())
+ {
+ return CodecResult.InvalidLength;
+ }
+
+ OpusPacketHeader header = OpusPacketHeader.FromSpan(input);
+ int headerSize = Unsafe.SizeOf<OpusPacketHeader>();
+ 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 00000000..acec66e8
--- /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 00000000..271a592c
--- /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 00000000..e2b81c77
--- /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
index fd63a4f7..98536a4f 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<uint> ChannelMappings;
+ public Array256<byte> ChannelMappings;
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/Types/OpusMultiStreamParametersEx.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusMultiStreamDecoderParameterInternalEx.cs
index 1315c734..8f8615df 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<byte> Padding1;
-
- public Array64<uint> ChannelMappings;
+ public uint Reserved;
+ public Array256<byte> 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 00000000..ae09ad15
--- /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<byte> output, ReadOnlySpan<byte> input);
+ Result SetContext(ReadOnlySpan<byte> context);
+ Result DecodeInterleavedForMultiStreamOld(out int outConsumed, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
+ Result SetContextForMultiStream(ReadOnlySpan<byte> context);
+ Result DecodeInterleavedWithPerfOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
+ Result DecodeInterleavedForMultiStreamWithPerfOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input);
+ Result DecodeInterleavedWithPerfAndResetOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
+ Result DecodeInterleavedForMultiStreamWithPerfAndResetOld(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
+ Result DecodeInterleaved(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> input, bool reset);
+ Result DecodeInterleavedForMultiStream(out int outConsumed, out long timeTaken, out int outSamples, Span<byte> output, ReadOnlySpan<byte> 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 00000000..fb6c787b
--- /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
index 572535a9..d630b10f 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 f3fe5194..b81e62a4 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<ArpMain>();
+ RegisterService<AudioMain>();
RegisterService<BcatMain>();
RegisterService<FriendsMain>();
RegisterService<HshlMain>();
+ RegisterService<HwopusMain>(); // TODO: Merge with audio once we can start multiple threads.
RegisterService<InsMain>();
RegisterService<LblMain>();
RegisterService<LmMain>();
diff --git a/src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs b/src/Ryujinx.Horizon/Srepo/SrepoIpcServer.cs
index 2060782c..44d00822 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 38eeed49..a04b81f9 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 c7b33623..776b9a7c 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();
}
}
}