Migrate Audio service to new IPC (#6285)

* Migrate audren to new IPC

* Migrate audout

* Migrate audin

* Migrate hwopus

* Bye bye old audio service

* Switch volume control to IHardwareDeviceDriver

* Somewhat unrelated changes

* Remove Concentus reference from HLE

* Implement OpenAudioRendererForManualExecution

* Remove SetVolume/GetVolume methods that are not necessary

* Remove SetVolume/GetVolume methods that are not necessary (2)

* Fix incorrect volume update

* PR feedback

* PR feedback

* Stub audrec

* Init outParameter

* Make FinalOutputRecorderParameter/Internal readonly

* Make FinalOutputRecorder IDisposable

* Fix HardwareOpusDecoderManager parameter buffers

* Opus work buffer size and error handling improvements

* Add AudioInProtocolName enum

* Fix potential divisions by zero
This commit is contained in:
gdkchan 2024-02-22 16:58:33 -03:00 committed by GitHub
parent 57d8afd0c9
commit d4d0a48bfe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
130 changed files with 3096 additions and 3174 deletions

View file

@ -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();

View file

@ -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();
}
}
}

View file

@ -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;

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audctl")]
class IAudioController : IpcService
{
public IAudioController(ServiceCtx context) { }
}
}

View file

@ -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);
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audin:a")]
class IAudioInManagerForApplet : IpcService
{
public IAudioInManagerForApplet(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audin:d")]
class IAudioInManagerForDebugger : IpcService
{
public IAudioInManagerForDebugger(ServiceCtx context) { }
}
}

View file

@ -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);
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audout:a")]
class IAudioOutManagerForApplet : IpcService
{
public IAudioOutManagerForApplet(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audout:d")]
class IAudioOutManagerForDebugger : IpcService
{
public IAudioOutManagerForDebugger(ServiceCtx context) { }
}
}

View file

@ -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);
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audren:a")]
class IAudioRendererManagerForApplet : IpcService
{
public IAudioRendererManagerForApplet(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audren:d")]
class IAudioRendererManagerForDebugger : IpcService
{
public IAudioRendererManagerForDebugger(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("auddev")] // 6.0.0+
class IAudioSnoopManager : IpcService
{
public IAudioSnoopManager(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audrec:u")]
class IFinalOutputRecorderManager : IpcService
{
public IFinalOutputRecorderManager(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audrec:a")]
class IFinalOutputRecorderManagerForApplet : IpcService
{
public IFinalOutputRecorderManagerForApplet(ServiceCtx context) { }
}
}

View file

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Audio
{
[Service("audrec:d")]
class IFinalOutputRecorderManagerForDebugger : IpcService
{
public IFinalOutputRecorderManagerForDebugger(ServiceCtx context) { }
}
}

View file

@ -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;
}
}
}

View file

@ -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,
}
}

View file

@ -1,11 +0,0 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Audio.Types
{
[Flags]
enum OpusDecoderFlags : uint
{
None,
LargeFrameSize = 1 << 0,
}
}

View file

@ -1,15 +0,0 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Audio.Types
{
[StructLayout(LayoutKind.Sequential, Size = 0x110)]
struct OpusMultiStreamParameters
{
public int SampleRate;
public int ChannelsCount;
public int NumberOfStreams;
public int NumberOfStereoStreams;
public Array64<uint> ChannelMappings;
}
}

View file

@ -1,19 +0,0 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Audio.Types
{
[StructLayout(LayoutKind.Sequential, Size = 0x118)]
struct OpusMultiStreamParametersEx
{
public int SampleRate;
public int ChannelsCount;
public int NumberOfStreams;
public int NumberOfStereoStreams;
public OpusDecoderFlags Flags;
Array4<byte> Padding1;
public Array64<uint> ChannelMappings;
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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" />

View file

@ -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()