aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE
diff options
context:
space:
mode:
authorMary <me@thog.eu>2021-02-26 01:11:56 +0100
committerGitHub <noreply@github.com>2021-02-26 01:11:56 +0100
commitf556c80d0230056335632b60c71f1567e177239e (patch)
tree748aa6be62b93a8e941e25dbd83f39e1dbb37035 /Ryujinx.HLE
parent1c49089ff00fc87dc4872f135dc6a0d36169a970 (diff)
Haydn: Part 1 (#2007)
* Haydn: Part 1 Based on my reverse of audio 11.0.0. As always, core implementation under LGPLv3 for the same reasons as for Amadeus. This place the bases of a more flexible audio system while making audout & audin accurate. This have the following improvements: - Complete reimplementation of audout and audin. - Audin currently only have a dummy backend. - Dramatically reduce CPU usage by up to 50% in common cases (SoundIO and OpenAL). - Audio Renderer now can output to 5.1 devices when supported. - Audio Renderer init its backend on demand instead of keeping two up all the time. - All backends implementation are now in their own project. - Ryujinx.Audio.Renderer was renamed Ryujinx.Audio and was refactored because of this. As a note, games having issues with OpenAL haven't improved and will not because of OpenAL design (stopping when buffers finish playing causing possible audio "pops" when buffers are very small). * Update for latest hexkyz's edits on Switchbrew * audren: Rollback channel configuration changes * Address gdkchan's comments * Fix typo in OpenAL backend driver * Address last comments * Fix a nit * Address gdkchan's comments
Diffstat (limited to 'Ryujinx.HLE')
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs54
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs108
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs209
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs34
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs41
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs235
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs108
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs190
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs33
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs41
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs228
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs14
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs162
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs98
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs85
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs147
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs13
-rw-r--r--Ryujinx.HLE/Ryujinx.HLE.csproj1
-rw-r--r--Ryujinx.HLE/Switch.cs15
21 files changed, 1223 insertions, 597 deletions
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 9309ae41..16b4c376 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -2,9 +2,11 @@ using LibHac;
using LibHac.Bcat;
using LibHac.Fs;
using LibHac.FsSystem;
-using Ryujinx.Audio.Renderer;
+using Ryujinx.Audio;
+using Ryujinx.Audio.Input;
+using Ryujinx.Audio.Integration;
+using Ryujinx.Audio.Output;
using Ryujinx.Audio.Renderer.Device;
-using Ryujinx.Audio.Renderer.Integration;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.Common;
using Ryujinx.Configuration;
@@ -51,6 +53,9 @@ namespace Ryujinx.HLE.HOS
internal Switch Device { get; private set; }
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; }
@@ -206,29 +211,48 @@ namespace Ryujinx.HLE.HOS
private void InitializeAudioRenderer()
{
+ AudioManager = new AudioManager();
+ AudioOutputManager = new AudioOutputManager();
+ AudioInputManager = new AudioInputManager();
AudioRendererManager = new AudioRendererManager();
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
- IWritableEvent[] writableEvents = new IWritableEvent[RendererConstants.AudioRendererSessionCountMax];
+ IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
- for (int i = 0; i < writableEvents.Length; i++)
+ for (int i = 0; i < audioOutputRegisterBufferEvents.Length; i++)
{
- KEvent systemEvent = new KEvent(KernelContext);
+ KEvent registerBufferEvent = new KEvent(KernelContext);
- writableEvents[i] = new AudioKernelEvent(systemEvent);
+ audioOutputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
}
- HardwareDevice[] devices = new HardwareDevice[RendererConstants.AudioRendererSessionCountMax];
+ AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
+
+ IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax];
- // TODO: don't hardcode those values.
- // TODO: keep the device somewhere and dispose it when exiting.
- // TODO: This is kind of wrong, we should have an high level API for that and mix all buffers between them.
- for (int i = 0; i < devices.Length; i++)
+ for (int i = 0; i < audioInputRegisterBufferEvents.Length; i++)
{
- devices[i] = new AalHardwareDevice(i, Device.AudioOut, 2, RendererConstants.TargetSampleRate);
+ KEvent registerBufferEvent = new KEvent(KernelContext);
+
+ audioInputRegisterBufferEvents[i] = new AudioKernelEvent(registerBufferEvent);
}
- AudioRendererManager.Initialize(writableEvents, devices);
+ AudioInputManager.Initialize(Device.AudioDeviceDriver, audioInputRegisterBufferEvents);
+
+ IWritableEvent[] systemEvents = new IWritableEvent[Constants.AudioRendererSessionCountMax];
+
+ for (int i = 0; i < systemEvents.Length; i++)
+ {
+ KEvent systemEvent = new KEvent(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()
@@ -363,6 +387,10 @@ 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();
KernelContext.Dispose();
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs
new file mode 100644
index 00000000..ee85ded9
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioIn.cs
@@ -0,0 +1,108 @@
+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 AudioInputSystem _system;
+ private uint _processHandle;
+ private 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)
+ {
+ return ((AudioKernelEvent)outEvent).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();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
new file mode 100644
index 00000000..1a1a3b6e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs
@@ -0,0 +1,209 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn
+{
+ class AudioInServer : IpcService, IDisposable
+ {
+ private IAudioIn _impl;
+
+ public AudioInServer(IAudioIn impl)
+ {
+ _impl = impl;
+ }
+
+ [Command(0)]
+ // GetAudioInState() -> u32 state
+ public ResultCode GetAudioInState(ServiceCtx context)
+ {
+ context.ResponseData.Write((uint)_impl.GetState());
+
+ return ResultCode.Success;
+ }
+
+ [Command(1)]
+ // Start()
+ public ResultCode Start(ServiceCtx context)
+ {
+ return _impl.Start();
+ }
+
+ [Command(2)]
+ // Stop()
+ public ResultCode StopAudioIn(ServiceCtx context)
+ {
+ return _impl.Stop();
+ }
+
+ [Command(3)]
+ // AppendAudioInBuffer(u64 tag, buffer<nn::audio::AudioInBuffer, 5>)
+ public ResultCode AppendAudioInBuffer(ServiceCtx context)
+ {
+ long 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);
+ }
+
+ [Command(4)]
+ // RegisterBufferEvent() -> handle<copy>
+ public ResultCode RegisterBufferEvent(ServiceCtx context)
+ {
+ KEvent bufferEvent = _impl.RegisterBufferEvent();
+
+ if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+
+ return ResultCode.Success;
+ }
+
+ [Command(5)]
+ // GetReleasedAudioInBuffers() -> (u32 count, buffer<u64, 6> tags)
+ public ResultCode GetReleasedAudioInBuffers(ServiceCtx context)
+ {
+ long position = context.Request.ReceiveBuff[0].Position;
+ long 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;
+ }
+ }
+
+ [Command(6)]
+ // ContainsAudioInBuffer(u64 tag) -> b8
+ public ResultCode ContainsAudioInBuffer(ServiceCtx context)
+ {
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ context.ResponseData.Write(_impl.ContainsBuffer(bufferTag));
+
+ return ResultCode.Success;
+ }
+
+ [Command(7)] // 3.0.0+
+ // AppendUacInBuffer(u64 tag, handle<copy, unknown>, buffer<nn::audio::AudioInBuffer, 5>)
+ public ResultCode AppendUacInBuffer(ServiceCtx context)
+ {
+ long 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);
+ }
+
+ [Command(8)] // 3.0.0+
+ // AppendAudioInBufferAuto(u64 tag, buffer<nn::audio::AudioInBuffer, 0x21>)
+ public ResultCode AppendAudioInBufferAuto(ServiceCtx context)
+ {
+ (long position, _) = context.Request.GetBufferType0x21();
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendBuffer(bufferTag, ref data);
+ }
+
+ [Command(9)] // 3.0.0+
+ // GetReleasedAudioInBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags)
+ public ResultCode GetReleasedAudioInBuffersAuto(ServiceCtx context)
+ {
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ 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;
+ }
+ }
+
+ [Command(10)] // 3.0.0+
+ // AppendUacInBufferAuto(u64 tag, handle<copy, event>, buffer<nn::audio::AudioInBuffer, 0x21>)
+ public ResultCode AppendUacInBufferAuto(ServiceCtx context)
+ {
+ (long 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);
+ }
+
+ [Command(11)] // 4.0.0+
+ // GetAudioInBufferCount() -> u32
+ public ResultCode GetAudioInBufferCount(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetBufferCount());
+
+ return ResultCode.Success;
+ }
+
+ [Command(12)] // 4.0.0+
+ // SetAudioInVolume(s32)
+ public ResultCode SetAudioInVolume(ServiceCtx context)
+ {
+ float volume = context.RequestData.ReadSingle();
+
+ _impl.SetVolume(volume);
+
+ return ResultCode.Success;
+ }
+
+ [Command(13)] // 4.0.0+
+ // GetAudioInVolume() -> s32
+ public ResultCode GetAudioInVolume(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetVolume());
+
+ return ResultCode.Success;
+ }
+
+ [Command(14)] // 6.0.0+
+ // FlushAudioInBuffers() -> b8
+ public ResultCode FlushAudioInBuffers(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.FlushBuffers());
+
+ return ResultCode.Success;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _impl.Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs
new file mode 100644
index 00000000..b5073fce
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/IAudioIn.cs
@@ -0,0 +1,34 @@
+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/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs
new file mode 100644
index 00000000..2d342206
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManager.cs
@@ -0,0 +1,41 @@
+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 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/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
new file mode 100644
index 00000000..079b91ca
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs
@@ -0,0 +1,235 @@
+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 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;
+ }
+
+ [Command(0)]
+ // ListAudioIns() -> (u32, buffer<bytes, 6>)
+ public ResultCode ListAudioIns(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioIns(false);
+
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+
+ position += AudioInNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(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();
+
+ long deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ long deviceNameInputSize = context.Request.SendBuff[0].Size;
+
+ long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, 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((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioInServer(obj));
+ }
+
+ return resultCode;
+ }
+
+ [Command(2)] // 3.0.0+
+ // ListAudioInsAuto() -> (u32, buffer<bytes, 0x22>)
+ public ResultCode ListAudioInsAuto(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioIns(false);
+
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+
+ position += AudioInNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(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();
+
+ (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21();
+ (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22();
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, 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((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioInServer(obj));
+ }
+
+ return resultCode;
+ }
+
+ [Command(4)] // 3.0.0+
+ // ListAudioInsAutoFiltered() -> (u32, buffer<bytes, 0x22>)
+ public ResultCode ListAudioInsAutoFiltered(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioIns(true);
+
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length);
+
+ position += AudioInNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(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).
+ bool protocolSpecifiedRelated = context.RequestData.ReadUInt64() == 1;
+
+ AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>();
+ ulong appletResourceUserId = context.RequestData.ReadUInt64();
+
+ long deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ long deviceNameInputSize = context.Request.SendBuff[0].Size;
+
+ long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, 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((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioInServer(obj));
+ }
+
+ return resultCode;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs
new file mode 100644
index 00000000..f2588452
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOut.cs
@@ -0,0 +1,108 @@
+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 AudioOutputSystem _system;
+ private uint _processHandle;
+ private 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)
+ {
+ return ((AudioKernelEvent)outEvent).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/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
new file mode 100644
index 00000000..4242eb7e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs
@@ -0,0 +1,190 @@
+using Ryujinx.Audio.Common;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut
+{
+ class AudioOutServer : IpcService, IDisposable
+ {
+ private IAudioOut _impl;
+
+ public AudioOutServer(IAudioOut impl)
+ {
+ _impl = impl;
+ }
+
+ [Command(0)]
+ // GetAudioOutState() -> u32 state
+ public ResultCode GetAudioOutState(ServiceCtx context)
+ {
+ context.ResponseData.Write((uint)_impl.GetState());
+
+ return ResultCode.Success;
+ }
+
+ [Command(1)]
+ // Start()
+ public ResultCode Start(ServiceCtx context)
+ {
+ return _impl.Start();
+ }
+
+ [Command(2)]
+ // Stop()
+ public ResultCode Stop(ServiceCtx context)
+ {
+ return _impl.Stop();
+ }
+
+ [Command(3)]
+ // AppendAudioOutBuffer(u64 bufferTag, buffer<nn::audio::AudioOutBuffer, 5> buffer)
+ public ResultCode AppendAudioOutBuffer(ServiceCtx context)
+ {
+ long 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);
+ }
+
+ [Command(4)]
+ // RegisterBufferEvent() -> handle<copy>
+ public ResultCode RegisterBufferEvent(ServiceCtx context)
+ {
+ KEvent bufferEvent = _impl.RegisterBufferEvent();
+
+ if (context.Process.HandleTable.GenerateHandle(bufferEvent.ReadableEvent, out int handle) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+
+ return ResultCode.Success;
+ }
+
+ [Command(5)]
+ // GetReleasedAudioOutBuffers() -> (u32 count, buffer<u64, 6> tags)
+ public ResultCode GetReleasedAudioOutBuffers(ServiceCtx context)
+ {
+ long position = context.Request.ReceiveBuff[0].Position;
+ long 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;
+ }
+ }
+
+ [Command(6)]
+ // ContainsAudioOutBuffer(u64 tag) -> b8
+ public ResultCode ContainsAudioOutBuffer(ServiceCtx context)
+ {
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ context.ResponseData.Write(_impl.ContainsBuffer(bufferTag));
+
+ return ResultCode.Success;
+ }
+
+ [Command(7)] // 3.0.0+
+ // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
+ public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
+ {
+ (long position, _) = context.Request.GetBufferType0x21();
+
+ ulong bufferTag = context.RequestData.ReadUInt64();
+
+ AudioUserBuffer data = MemoryHelper.Read<AudioUserBuffer>(context.Memory, position);
+
+ return _impl.AppendBuffer(bufferTag, ref data);
+ }
+
+ [Command(8)] // 3.0.0+
+ // GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags)
+ public ResultCode GetReleasedAudioOutBuffersAuto(ServiceCtx context)
+ {
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ 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;
+ }
+ }
+
+ [Command(9)] // 4.0.0+
+ // GetAudioOutBufferCount() -> u32
+ public ResultCode GetAudioOutBufferCount(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetBufferCount());
+
+ return ResultCode.Success;
+ }
+
+ [Command(10)] // 4.0.0+
+ // GetAudioOutPlayedSampleCount() -> u64
+ public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetPlayedSampleCount());
+
+ return ResultCode.Success;
+ }
+
+ [Command(11)] // 4.0.0+
+ // FlushAudioOutBuffers() -> b8
+ public ResultCode FlushAudioOutBuffers(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.FlushBuffers());
+
+ return ResultCode.Success;
+ }
+
+ [Command(12)] // 6.0.0+
+ // SetAudioOutVolume(s32)
+ public ResultCode SetAudioOutVolume(ServiceCtx context)
+ {
+ float volume = context.RequestData.ReadSingle();
+
+ _impl.SetVolume(volume);
+
+ return ResultCode.Success;
+ }
+
+ [Command(13)] // 6.0.0+
+ // GetAudioOutVolume() -> s32
+ public ResultCode GetAudioOutVolume(ServiceCtx context)
+ {
+ context.ResponseData.Write(_impl.GetVolume());
+
+ return ResultCode.Success;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _impl.Dispose();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs
new file mode 100644
index 00000000..8533d3c5
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/IAudioOut.cs
@@ -0,0 +1,33 @@
+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/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
new file mode 100644
index 00000000..29490553
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
@@ -0,0 +1,41 @@
+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 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)
+ {
+ 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);
+
+ if (result == ResultCode.Success)
+ {
+ obj = new AudioOut.AudioOut(outSystem, context.Device.System.KernelContext, processHandle);
+ }
+ else
+ {
+ obj = null;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
deleted file mode 100644
index c941cf4f..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
+++ /dev/null
@@ -1,228 +0,0 @@
-using Ryujinx.Audio;
-using Ryujinx.Cpu;
-using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel;
-using Ryujinx.HLE.HOS.Kernel.Common;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
-{
- class IAudioOut : IpcService, IDisposable
- {
- private readonly KernelContext _kernelContext;
- private readonly IAalOutput _audioOut;
- private readonly KEvent _releaseEvent;
- private int _releaseEventHandle;
- private readonly int _track;
- private readonly int _clientHandle;
-
- public IAudioOut(KernelContext kernelContext, IAalOutput audioOut, KEvent releaseEvent, int track, int clientHandle)
- {
- _kernelContext = kernelContext;
- _audioOut = audioOut;
- _releaseEvent = releaseEvent;
- _track = track;
- _clientHandle = clientHandle;
- }
-
- [Command(0)]
- // GetAudioOutState() -> u32 state
- public ResultCode GetAudioOutState(ServiceCtx context)
- {
- context.ResponseData.Write((int)_audioOut.GetState(_track));
-
- return ResultCode.Success;
- }
-
- [Command(1)]
- // StartAudioOut()
- public ResultCode StartAudioOut(ServiceCtx context)
- {
- _audioOut.Start(_track);
-
- return ResultCode.Success;
- }
-
- [Command(2)]
- // StopAudioOut()
- public ResultCode StopAudioOut(ServiceCtx context)
- {
- _audioOut.Stop(_track);
-
- return ResultCode.Success;
- }
-
- [Command(3)]
- // AppendAudioOutBuffer(u64 tag, buffer<nn::audio::AudioOutBuffer, 5>)
- public ResultCode AppendAudioOutBuffer(ServiceCtx context)
- {
- return AppendAudioOutBufferImpl(context, context.Request.SendBuff[0].Position);
- }
-
- [Command(4)]
- // RegisterBufferEvent() -> handle<copy>
- public ResultCode RegisterBufferEvent(ServiceCtx context)
- {
- if (_releaseEventHandle == 0)
- {
- if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out _releaseEventHandle) != KernelResult.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_releaseEventHandle);
-
- return ResultCode.Success;
- }
-
- [Command(5)]
- // GetReleasedAudioOutBuffer() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 6>)
- public ResultCode GetReleasedAudioOutBuffer(ServiceCtx context)
- {
- long position = context.Request.ReceiveBuff[0].Position;
- long size = context.Request.ReceiveBuff[0].Size;
-
- return GetReleasedAudioOutBufferImpl(context, position, size);
- }
-
- [Command(6)]
- // ContainsAudioOutBuffer(u64 tag) -> b8
- public ResultCode ContainsAudioOutBuffer(ServiceCtx context)
- {
- long tag = context.RequestData.ReadInt64();
-
- context.ResponseData.Write(_audioOut.ContainsBuffer(_track, tag));
-
- return ResultCode.Success;
- }
-
- [Command(7)] // 3.0.0+
- // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
- public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
- {
- (long position, _) = context.Request.GetBufferType0x21();
-
- return AppendAudioOutBufferImpl(context, position);
- }
-
- public ResultCode AppendAudioOutBufferImpl(ServiceCtx context, long position)
- {
- long tag = context.RequestData.ReadInt64();
-
- AudioOutData data = MemoryHelper.Read<AudioOutData>(context.Memory, position);
-
- // NOTE: Assume PCM16 all the time, change if new format are found.
- short[] buffer = new short[data.SampleBufferSize / sizeof(short)];
-
- context.Process.HandleTable.GetKProcess(_clientHandle).CpuMemory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast<short, byte>(buffer));
-
- _audioOut.AppendBuffer(_track, tag, buffer);
-
- return ResultCode.Success;
- }
-
- [Command(8)] // 3.0.0+
- // GetReleasedAudioOutBufferAuto() -> (u32 count, buffer<nn::audio::AudioOutBuffer, 0x22>)
- public ResultCode GetReleasedAudioOutBufferAuto(ServiceCtx context)
- {
- (long position, long size) = context.Request.GetBufferType0x22();
-
- return GetReleasedAudioOutBufferImpl(context, position, size);
- }
-
- public ResultCode GetReleasedAudioOutBufferImpl(ServiceCtx context, long position, long size)
- {
- uint count = (uint)((ulong)size >> 3);
-
- long[] releasedBuffers = _audioOut.GetReleasedBuffers(_track, (int)count);
-
- for (uint index = 0; index < count; index++)
- {
- long tag = 0;
-
- if (index < releasedBuffers.Length)
- {
- tag = releasedBuffers[index];
- }
-
- context.Memory.Write((ulong)(position + index * 8), tag);
- }
-
- context.ResponseData.Write(releasedBuffers.Length);
-
- return ResultCode.Success;
- }
-
- [Command(9)] // 4.0.0+
- // GetAudioOutBufferCount() -> u32
- public ResultCode GetAudioOutBufferCount(ServiceCtx context)
- {
- uint bufferCount = _audioOut.GetBufferCount(_track);
-
- context.ResponseData.Write(bufferCount);
-
- return ResultCode.Success;
- }
-
- [Command(10)] // 4.0.0+
- // GetAudioOutPlayedSampleCount() -> u64
- public ResultCode GetAudioOutPlayedSampleCount(ServiceCtx context)
- {
- ulong playedSampleCount = _audioOut.GetPlayedSampleCount(_track);
-
- context.ResponseData.Write(playedSampleCount);
-
- return ResultCode.Success;
- }
-
- [Command(11)] // 4.0.0+
- // FlushAudioOutBuffers() -> b8
- public ResultCode FlushAudioOutBuffers(ServiceCtx context)
- {
- bool heldBuffers = _audioOut.FlushBuffers(_track);
-
- context.ResponseData.Write(heldBuffers);
-
- return ResultCode.Success;
- }
-
- [Command(12)] // 6.0.0+
- // SetAudioOutVolume(s32)
- public ResultCode SetAudioOutVolume(ServiceCtx context)
- {
- float volume = context.RequestData.ReadSingle();
-
- _audioOut.SetVolume(_track, volume);
-
- return ResultCode.Success;
- }
-
- [Command(13)] // 6.0.0+
- // GetAudioOutVolume() -> s32
- public ResultCode GetAudioOutVolume(ServiceCtx context)
- {
- float volume = _audioOut.GetVolume(_track);
-
- context.ResponseData.Write(volume);
-
- return ResultCode.Success;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _kernelContext.Syscall.CloseHandle(_clientHandle);
- _audioOut.CloseTrack(_track);
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs
deleted file mode 100644
index 2598d0f8..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/Types/AudioOutData.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
-{
- [StructLayout(LayoutKind.Sequential)]
- struct AudioOutData
- {
- public long NextBufferPtr;
- public long SampleBufferPtr;
- public long SampleBufferCapacity;
- public long SampleBufferSize;
- public long SampleBufferInnerOffset;
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
new file mode 100644
index 00000000..13930d31
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
@@ -0,0 +1,162 @@
+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 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;
+ }
+
+ [Command(0)]
+ // ListAudioOuts() -> (u32, buffer<bytes, 6>)
+ public ResultCode ListAudioOuts(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioOuts();
+
+ long position = context.Request.ReceiveBuff[0].Position;
+ long size = context.Request.ReceiveBuff[0].Size;
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length);
+
+ position += AudioOutNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(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();
+
+ long deviceNameInputPosition = context.Request.SendBuff[0].Position;
+ long deviceNameInputSize = context.Request.SendBuff[0].Size;
+
+ long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position;
+ long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size;
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioOutServer(obj));
+ }
+
+ return resultCode;
+ }
+
+ [Command(2)] // 3.0.0+
+ // ListAudioOutsAuto() -> (u32, buffer<bytes, 0x22>)
+ public ResultCode ListAudioOutsAuto(ServiceCtx context)
+ {
+ string[] deviceNames = _impl.ListAudioOuts();
+
+ (long position, long size) = context.Request.GetBufferType0x22();
+
+ long basePosition = position;
+
+ int count = 0;
+
+ foreach (string name in deviceNames)
+ {
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+
+ if ((position - basePosition) + buffer.Length > size)
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
+
+ break;
+ }
+
+ context.Memory.Write((ulong)position, buffer);
+ MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length);
+
+ position += AudioOutNameSize;
+ count++;
+ }
+
+ context.ResponseData.Write(count);
+
+ return ResultCode.Success;
+ }
+
+ [Command(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();
+
+ (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21();
+ (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22();
+
+ uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0];
+
+ string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize);
+
+ ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
+
+ if (resultCode == ResultCode.Success)
+ {
+ context.ResponseData.WriteStruct(outputConfiguration);
+
+ byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName);
+
+ context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw);
+ MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length);
+
+ MakeObject(context, new AudioOutServer(obj));
+ }
+
+ return resultCode;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs
deleted file mode 100644
index fdc23604..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AalHardwareDevice.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using Ryujinx.Audio;
-using Ryujinx.Audio.Renderer;
-using Ryujinx.Audio.Renderer.Integration;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
-{
- public class AalHardwareDevice : HardwareDevice
- {
- private IAalOutput _output;
- private int _trackId;
- private int _bufferTag;
- private int _nextTag;
- private AutoResetEvent _releaseEvent;
-
- private uint _channelCount;
- private uint _sampleRate;
-
- private short[] _buffer;
-
- private Queue<long> _releasedTags;
-
- public AalHardwareDevice(int bufferTag, IAalOutput output, uint channelCount, uint sampleRate)
- {
- _bufferTag = bufferTag;
- _channelCount = channelCount;
- _sampleRate = sampleRate;
- _output = output;
- _releaseEvent = new AutoResetEvent(true);
- _trackId = _output.OpenTrack((int)sampleRate, (int)channelCount, AudioCallback);
- _releasedTags = new Queue<long>();
-
- _buffer = new short[RendererConstants.TargetSampleCount * channelCount];
-
- _output.Start(_trackId);
- }
-
- private void AudioCallback()
- {
- long[] released = _output.GetReleasedBuffers(_trackId, int.MaxValue);
-
- lock (_releasedTags)
- {
- foreach (long tag in released)
- {
- _releasedTags.Enqueue(tag);
- }
- }
- }
-
- private long GetReleasedTag()
- {
- lock (_releasedTags)
- {
- if (_releasedTags.Count > 0)
- {
- return _releasedTags.Dequeue();
- }
-
- return (_bufferTag << 16) | (_nextTag++);
- }
- }
-
- public void AppendBuffer(ReadOnlySpan<short> data, uint channelCount)
- {
- data.CopyTo(_buffer.AsSpan());
-
- _output.AppendBuffer(_trackId, GetReleasedTag(), _buffer);
- }
-
- public uint GetChannelCount()
- {
- return _channelCount;
- }
-
- public uint GetSampleRate()
- {
- return _sampleRate;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _output.Stop(_trackId);
- _output.CloseTrack(_trackId);
- _releaseEvent.Dispose();
- }
- }
- }
-}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
index 1a132b91..55bf29ae 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioKernelEvent.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
index 702648dd..d69bde03 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRenderer.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Audio.Renderer.Integration;
+using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
index b3f7f5e0..9bbe5b0e 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
@@ -1,87 +1,12 @@
-using Ryujinx.Cpu;
-using Ryujinx.Memory;
-using System;
-using System.Text;
+using Ryujinx.Audio.Common;
+using Ryujinx.HLE.HOS.Services.Audio.AudioIn;
namespace Ryujinx.HLE.HOS.Services.Audio
{
- [Service("audin:u")]
- class IAudioInManager : IpcService
+ interface IAudioInManager
{
- private const string DefaultAudioInsName = "BuiltInHeadset";
+ public string[] ListAudioIns(bool filtered);
- public IAudioInManager(ServiceCtx context) { }
-
- [Command(0)]
- // ListAudioIns() -> (u32 count, buffer<bytes, 6> names)
- public ResultCode ListAudioIns(ServiceCtx context)
- {
- long bufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
-
- // NOTE: The service check if AudioInManager thread is started, if not it starts it.
-
- uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, false);
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- [Command(2)] // 3.0.0+
- // ListAudioInsAuto() -> (u32 count, buffer<bytes, 0x22> names)
- public ResultCode ListAudioInsAuto(ServiceCtx context)
- {
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
-
- // NOTE: The service check if AudioInManager thread is started, if not it starts it.
-
- uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, false);
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- [Command(4)] // 3.0.0+
- // ListAudioInsAutoFiltered() -> (u32 count, buffer<bytes, 0x22> names)
- public ResultCode ListAudioInsAutoFiltered(ServiceCtx context)
- {
- (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
-
- // NOTE: The service check if AudioInManager thread is started, if not it starts it.
-
- uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, true);
-
- context.ResponseData.Write(count);
-
- return ResultCode.Success;
- }
-
- private uint ListAudioInsImpl(IVirtualMemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false)
- {
- uint count = 0;
-
- MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize);
-
- if (bufferSize > 0)
- {
- // NOTE: The service also check that the input target is enabled when in filtering mode, as audctl and most of the audin logic isn't supported, we don't support it.
- if (!filtered)
- {
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioInsName + "\0");
-
- memory.Write((ulong)bufferPosition, deviceNameBuffer);
-
- count++;
- }
-
- // NOTE: The service adds other input devices names available in the buffer,
- // every name is aligned to 0x100 bytes.
- // Since we don't support it for now, it's fine to do nothing here.
- }
-
- return count;
- }
+ public ResultCode OpenAudioIn(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle);
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
index 6204a7be..0b164019 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
@@ -1,147 +1,12 @@
-using Ryujinx.Audio;
-using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services.Audio.AudioOutManager;
-using System.Text;
+using Ryujinx.Audio.Common;
+using Ryujinx.HLE.HOS.Services.Audio.AudioOut;
namespace Ryujinx.HLE.HOS.Services.Audio
{
- [Service("audout:u")]
- class IAudioOutManager : IpcService
+ interface IAudioOutManager
{
- private const string DefaultAudioOutput = "DeviceOut";
- private const int DefaultSampleRate = 48000;
- private const int DefaultChannelsCount = 2;
+ public string[] ListAudioOuts();
- public IAudioOutManager(ServiceCtx context) : base(context.Device.System.AudOutServer) { }
-
- [Command(0)]
- // ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
- public ResultCode ListAudioOuts(ServiceCtx context)
- {
- return ListAudioOutsImpl(context, context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
- }
-
- [Command(1)]
- // OpenAudioOut(u32 sample_rate, u16 unused, u16 channel_count, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 5> name_in)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
- public ResultCode OpenAudioOut(ServiceCtx context)
- {
- return OpenAudioOutImpl(context, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size,
- context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
- }
-
- [Command(2)] // 3.0.0+
- // ListAudioOutsAuto() -> (u32 count, buffer<bytes, 0x22>)
- public ResultCode ListAudioOutsAuto(ServiceCtx context)
- {
- (long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
-
- return ListAudioOutsImpl(context, recvPosition, recvSize);
- }
-
- [Command(3)] // 3.0.0+
- // OpenAudioOutAuto(u32 sample_rate, u16 unused, u16 channel_count, nn::applet::AppletResourceUserId, pid, handle<copy, process>, buffer<bytes, 0x21>)
- // -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 0x22> name_out)
- public ResultCode OpenAudioOutAuto(ServiceCtx context)
- {
- (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
- (long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
-
- return OpenAudioOutImpl(context, sendPosition, sendSize, recvPosition, recvSize);
- }
-
- private ResultCode ListAudioOutsImpl(ServiceCtx context, long position, long size)
- {
- int nameCount = 0;
-
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioOutput + "\0");
-
- if ((ulong)deviceNameBuffer.Length <= (ulong)size)
- {
- context.Memory.Write((ulong)position, deviceNameBuffer);
-
- nameCount++;
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
- }
-
- context.ResponseData.Write(nameCount);
-
- return ResultCode.Success;
- }
-
- private ResultCode OpenAudioOutImpl(ServiceCtx context, long sendPosition, long sendSize, long receivePosition, long receiveSize)
- {
- string deviceName = MemoryHelper.ReadAsciiString(context.Memory, sendPosition, sendSize);
-
- if (deviceName == string.Empty)
- {
- deviceName = DefaultAudioOutput;
- }
-
- if (deviceName != DefaultAudioOutput)
- {
- Logger.Warning?.Print(LogClass.Audio, "Invalid device name!");
-
- return ResultCode.DeviceNotFound;
- }
-
- byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(deviceName + "\0");
-
- if ((ulong)deviceNameBuffer.Length <= (ulong)receiveSize)
- {
- context.Memory.Write((ulong)receivePosition, deviceNameBuffer);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {receiveSize} too small!");
- }
-
- int sampleRate = context.RequestData.ReadInt32();
- int channels = context.RequestData.ReadInt32();
-
- if (sampleRate == 0)
- {
- sampleRate = DefaultSampleRate;
- }
-
- if (sampleRate != DefaultSampleRate)
- {
- Logger.Warning?.Print(LogClass.Audio, "Invalid sample rate!");
-
- return ResultCode.UnsupportedSampleRate;
- }
-
- channels = (ushort)channels;
-
- if (channels == 0)
- {
- channels = DefaultChannelsCount;
- }
-
- KEvent releaseEvent = new KEvent(context.Device.System.KernelContext);
-
- ReleaseCallback callback = () =>
- {
- releaseEvent.ReadableEvent.Signal();
- };
-
- IAalOutput audioOut = context.Device.AudioOut;
-
- int track = audioOut.OpenTrack(sampleRate, channels, callback);
-
- MakeObject(context, new IAudioOut(context.Device.System.KernelContext, audioOut, releaseEvent, track, context.Request.HandleDesc.ToCopy[0]));
-
- context.ResponseData.Write(sampleRate);
- context.ResponseData.Write(channels);
- context.ResponseData.Write((int)SampleFormat.PcmInt16);
- context.ResponseData.Write((int)PlaybackState.Stopped);
-
- return ResultCode.Success;
- }
+ public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle);
}
-} \ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs b/Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs
deleted file mode 100644
index 654436e4..00000000
--- a/Ryujinx.HLE/HOS/Services/Audio/Types/SampleFormat.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Audio
-{
- enum SampleFormat : byte
- {
- Invalid = 0,
- PcmInt8 = 1,
- PcmInt16 = 2,
- PcmInt24 = 3,
- PcmInt32 = 4,
- PcmFloat = 5,
- Adpcm = 6
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index c3b5ac7a..3d48d893 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -6,7 +6,6 @@
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\Ryujinx.Audio.Renderer\Ryujinx.Audio.Renderer.csproj" />
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 2306e5d3..865a86d7 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -1,5 +1,6 @@
using LibHac.FsSystem;
-using Ryujinx.Audio;
+using Ryujinx.Audio.Backends.CompatLayer;
+using Ryujinx.Audio.Integration;
using Ryujinx.Common;
using Ryujinx.Configuration;
using Ryujinx.Graphics.GAL;
@@ -22,7 +23,7 @@ namespace Ryujinx.HLE
{
public class Switch : IDisposable
{
- public IAalOutput AudioOut { get; private set; }
+ public IHardwareDeviceDriver AudioDeviceDriver { get; private set; }
internal MemoryBlock Memory { get; private set; }
@@ -48,16 +49,16 @@ namespace Ryujinx.HLE
public bool EnableDeviceVsync { get; set; } = true;
- public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, UserChannelPersistence userChannelPersistence, IRenderer renderer, IAalOutput audioOut)
+ public Switch(VirtualFileSystem fileSystem, ContentManager contentManager, UserChannelPersistence userChannelPersistence, IRenderer renderer, IHardwareDeviceDriver audioDeviceDriver)
{
if (renderer == null)
{
throw new ArgumentNullException(nameof(renderer));
}
- if (audioOut == null)
+ if (audioDeviceDriver == null)
{
- throw new ArgumentNullException(nameof(audioOut));
+ throw new ArgumentNullException(nameof(audioDeviceDriver));
}
if (userChannelPersistence == null)
@@ -67,7 +68,7 @@ namespace Ryujinx.HLE
UserChannelPersistence = userChannelPersistence;
- AudioOut = audioOut;
+ AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(audioDeviceDriver);
Memory = new MemoryBlock(1UL << 32);
@@ -210,7 +211,7 @@ namespace Ryujinx.HLE
System.Dispose();
Host1x.Dispose();
- AudioOut.Dispose();
+ AudioDeviceDriver.Dispose();
FileSystem.Unload();
Memory.Dispose();
}