mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2025-09-11 16:56:33 -05:00
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:
50
src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs
Normal file
50
src/Ryujinx.Horizon/Sdk/Audio/AudioEvent.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.OsTypes;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio
|
||||
{
|
||||
class AudioEvent : IWritableEvent, IDisposable
|
||||
{
|
||||
private SystemEventType _systemEvent;
|
||||
private readonly IExternalEvent _externalEvent;
|
||||
|
||||
public AudioEvent()
|
||||
{
|
||||
Os.CreateSystemEvent(out _systemEvent, EventClearMode.ManualClear, interProcess: true);
|
||||
|
||||
// We need to do this because the event will be signalled from a different thread.
|
||||
_externalEvent = HorizonStatic.Syscall.GetExternalEvent(Os.GetWritableHandleOfSystemEvent(ref _systemEvent));
|
||||
}
|
||||
|
||||
public void Signal()
|
||||
{
|
||||
_externalEvent.Signal();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_externalEvent.Clear();
|
||||
}
|
||||
|
||||
public int GetReadableHandle()
|
||||
{
|
||||
return Os.GetReadableHandleOfSystemEvent(ref _systemEvent);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Os.DestroySystemEvent(ref _systemEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
12
src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs
Normal file
12
src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio
|
||||
{
|
||||
static class AudioResult
|
||||
{
|
||||
private const int ModuleId = 153;
|
||||
|
||||
public static Result DeviceNotFound => new(ModuleId, 1);
|
||||
public static Result UnsupportedRevision => new(ModuleId, 2);
|
||||
}
|
||||
}
|
252
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs
Normal file
252
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
using Ryujinx.Audio.Renderer.Device;
|
||||
using Ryujinx.Audio.Renderer.Server;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.OsTypes;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioDevice : IAudioDevice, IDisposable
|
||||
{
|
||||
private readonly VirtualDeviceSessionRegistry _registry;
|
||||
private readonly VirtualDeviceSession[] _sessions;
|
||||
private readonly bool _isUsbDeviceSupported;
|
||||
|
||||
private SystemEventType _audioEvent;
|
||||
private SystemEventType _audioInputEvent;
|
||||
private SystemEventType _audioOutputEvent;
|
||||
|
||||
public AudioDevice(VirtualDeviceSessionRegistry registry, AppletResourceUserId appletResourceId, uint revision)
|
||||
{
|
||||
_registry = registry;
|
||||
|
||||
BehaviourContext behaviourContext = new();
|
||||
behaviourContext.SetUserRevision((int)revision);
|
||||
|
||||
_isUsbDeviceSupported = behaviourContext.IsAudioUsbDeviceOutputSupported();
|
||||
_sessions = registry.GetSessionByAppletResourceId(appletResourceId.Id);
|
||||
|
||||
Os.CreateSystemEvent(out _audioEvent, EventClearMode.AutoClear, interProcess: true);
|
||||
Os.CreateSystemEvent(out _audioInputEvent, EventClearMode.AutoClear, interProcess: true);
|
||||
Os.CreateSystemEvent(out _audioOutputEvent, EventClearMode.AutoClear, interProcess: true);
|
||||
}
|
||||
|
||||
private bool TryGetDeviceByName(out VirtualDeviceSession result, string name, bool ignoreRevLimitation = false)
|
||||
{
|
||||
result = null;
|
||||
|
||||
foreach (VirtualDeviceSession session in _sessions)
|
||||
{
|
||||
if (session.Device.Name.Equals(name))
|
||||
{
|
||||
if (!ignoreRevLimitation && !_isUsbDeviceSupported && session.Device.IsUsbDevice())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = session;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result ListAudioDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> names, out int nameCount)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (VirtualDeviceSession session in _sessions)
|
||||
{
|
||||
if (!_isUsbDeviceSupported && session.Device.IsUsbDevice())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count >= names.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
names[count] = new DeviceName(session.Device.Name);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
nameCount = count;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result SetAudioDeviceOutputVolume([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> name, float volume)
|
||||
{
|
||||
if (name.Length > 0 && TryGetDeviceByName(out VirtualDeviceSession result, name[0].ToString(), ignoreRevLimitation: true))
|
||||
{
|
||||
if (!_isUsbDeviceSupported && result.Device.IsUsbDevice())
|
||||
{
|
||||
result = _sessions[0];
|
||||
}
|
||||
|
||||
result.Volume = volume;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result GetAudioDeviceOutputVolume([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> name, out float volume)
|
||||
{
|
||||
if (name.Length > 0 && TryGetDeviceByName(out VirtualDeviceSession result, name[0].ToString()))
|
||||
{
|
||||
volume = result.Volume;
|
||||
}
|
||||
else
|
||||
{
|
||||
volume = 0f;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(3)]
|
||||
public Result GetActiveAudioDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> name)
|
||||
{
|
||||
VirtualDevice device = _registry.ActiveDevice;
|
||||
|
||||
if (!_isUsbDeviceSupported && device.IsUsbDevice())
|
||||
{
|
||||
device = _registry.DefaultDevice;
|
||||
}
|
||||
|
||||
if (name.Length > 0)
|
||||
{
|
||||
name[0] = new DeviceName(device.Name);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(4)]
|
||||
public Result QueryAudioDeviceSystemEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
eventHandle = Os.GetReadableHandleOfSystemEvent(ref _audioEvent);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(5)]
|
||||
public Result GetActiveChannelCount(out int channelCount)
|
||||
{
|
||||
VirtualDevice device = _registry.ActiveDevice;
|
||||
|
||||
if (!_isUsbDeviceSupported && device.IsUsbDevice())
|
||||
{
|
||||
device = _registry.DefaultDevice;
|
||||
}
|
||||
|
||||
channelCount = (int)device.ChannelCount;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(6)] // 3.0.0+
|
||||
public Result ListAudioDeviceNameAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> names, out int nameCount)
|
||||
{
|
||||
return ListAudioDeviceName(names, out nameCount);
|
||||
}
|
||||
|
||||
[CmifCommand(7)] // 3.0.0+
|
||||
public Result SetAudioDeviceOutputVolumeAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> name, float volume)
|
||||
{
|
||||
return SetAudioDeviceOutputVolume(name, volume);
|
||||
}
|
||||
|
||||
[CmifCommand(8)] // 3.0.0+
|
||||
public Result GetAudioDeviceOutputVolumeAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> name, out float volume)
|
||||
{
|
||||
return GetAudioDeviceOutputVolume(name, out volume);
|
||||
}
|
||||
|
||||
[CmifCommand(10)] // 3.0.0+
|
||||
public Result GetActiveAudioDeviceNameAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> name)
|
||||
{
|
||||
return GetActiveAudioDeviceName(name);
|
||||
}
|
||||
|
||||
[CmifCommand(11)] // 3.0.0+
|
||||
public Result QueryAudioDeviceInputEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
eventHandle = Os.GetReadableHandleOfSystemEvent(ref _audioInputEvent);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(12)] // 3.0.0+
|
||||
public Result QueryAudioDeviceOutputEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
eventHandle = Os.GetReadableHandleOfSystemEvent(ref _audioOutputEvent);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(13)] // 13.0.0+
|
||||
public Result GetActiveAudioOutputDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> name)
|
||||
{
|
||||
if (name.Length > 0)
|
||||
{
|
||||
name[0] = new DeviceName(_registry.ActiveDevice.GetOutputDeviceName());
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(14)] // 13.0.0+
|
||||
public Result ListAudioOutputDeviceName([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> names, out int nameCount)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (VirtualDeviceSession session in _sessions)
|
||||
{
|
||||
if (!_isUsbDeviceSupported && session.Device.IsUsbDevice())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count >= names.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
names[count] = new DeviceName(session.Device.GetOutputDeviceName());
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
nameCount = count;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Os.DestroySystemEvent(ref _audioEvent);
|
||||
Os.DestroySystemEvent(ref _audioInputEvent);
|
||||
Os.DestroySystemEvent(ref _audioOutputEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
171
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs
Normal file
171
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioIn.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Input;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioIn : IAudioIn, IDisposable
|
||||
{
|
||||
private readonly AudioInputSystem _impl;
|
||||
private int _processHandle;
|
||||
|
||||
public AudioIn(AudioInputSystem impl, int processHandle)
|
||||
{
|
||||
_impl = impl;
|
||||
_processHandle = processHandle;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result GetAudioInState(out AudioDeviceState state)
|
||||
{
|
||||
state = _impl.GetState();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result Start()
|
||||
{
|
||||
return new Result((int)_impl.Start());
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result Stop()
|
||||
{
|
||||
return new Result((int)_impl.Stop());
|
||||
}
|
||||
|
||||
[CmifCommand(3)]
|
||||
public Result AppendAudioInBuffer(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<AudioUserBuffer> buffer)
|
||||
{
|
||||
AudioUserBuffer userBuffer = default;
|
||||
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
userBuffer = buffer[0];
|
||||
}
|
||||
|
||||
return new Result((int)_impl.AppendBuffer(bufferTag, ref userBuffer));
|
||||
}
|
||||
|
||||
[CmifCommand(4)]
|
||||
public Result RegisterBufferEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
eventHandle = 0;
|
||||
|
||||
if (_impl.RegisterBufferEvent() is AudioEvent audioEvent)
|
||||
{
|
||||
eventHandle = audioEvent.GetReadableHandle();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(5)]
|
||||
public Result GetReleasedAudioInBuffers(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<ulong> bufferTags)
|
||||
{
|
||||
return new Result((int)_impl.GetReleasedBuffers(bufferTags, out count));
|
||||
}
|
||||
|
||||
[CmifCommand(6)]
|
||||
public Result ContainsAudioInBuffer(out bool contains, ulong bufferTag)
|
||||
{
|
||||
contains = _impl.ContainsBuffer(bufferTag);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(7)] // 3.0.0+
|
||||
public Result AppendUacInBuffer(
|
||||
ulong bufferTag,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<AudioUserBuffer> buffer,
|
||||
[CopyHandle] int eventHandle)
|
||||
{
|
||||
AudioUserBuffer userBuffer = default;
|
||||
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
userBuffer = buffer[0];
|
||||
}
|
||||
|
||||
return new Result((int)_impl.AppendUacBuffer(bufferTag, ref userBuffer, (uint)eventHandle));
|
||||
}
|
||||
|
||||
[CmifCommand(8)] // 3.0.0+
|
||||
public Result AppendAudioInBufferAuto(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<AudioUserBuffer> buffer)
|
||||
{
|
||||
return AppendAudioInBuffer(bufferTag, buffer);
|
||||
}
|
||||
|
||||
[CmifCommand(9)] // 3.0.0+
|
||||
public Result GetReleasedAudioInBuffersAuto(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<ulong> bufferTags)
|
||||
{
|
||||
return GetReleasedAudioInBuffers(out count, bufferTags);
|
||||
}
|
||||
|
||||
[CmifCommand(10)] // 3.0.0+
|
||||
public Result AppendUacInBufferAuto(
|
||||
ulong bufferTag,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<AudioUserBuffer> buffer,
|
||||
[CopyHandle] int eventHandle)
|
||||
{
|
||||
return AppendUacInBuffer(bufferTag, buffer, eventHandle);
|
||||
}
|
||||
|
||||
[CmifCommand(11)] // 4.0.0+
|
||||
public Result GetAudioInBufferCount(out uint bufferCount)
|
||||
{
|
||||
bufferCount = _impl.GetBufferCount();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(12)] // 4.0.0+
|
||||
public Result SetDeviceGain(float gain)
|
||||
{
|
||||
_impl.SetVolume(gain);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(13)] // 4.0.0+
|
||||
public Result GetDeviceGain(out float gain)
|
||||
{
|
||||
gain = _impl.GetVolume();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(14)] // 6.0.0+
|
||||
public Result FlushAudioInBuffers(out bool pending)
|
||||
{
|
||||
pending = _impl.FlushBuffers();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_impl.Dispose();
|
||||
|
||||
if (_processHandle != 0)
|
||||
{
|
||||
HorizonStatic.Syscall.CloseHandle(_processHandle);
|
||||
|
||||
_processHandle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
130
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs
Normal file
130
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInManager.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Input;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioInManager : IAudioInManager
|
||||
{
|
||||
private readonly AudioInputManager _impl;
|
||||
|
||||
public AudioInManager(AudioInputManager impl)
|
||||
{
|
||||
_impl = impl;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result ListAudioIns(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> names)
|
||||
{
|
||||
string[] deviceNames = _impl.ListAudioIns(filtered: false);
|
||||
|
||||
count = 0;
|
||||
|
||||
foreach (string deviceName in deviceNames)
|
||||
{
|
||||
if (count >= names.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
names[count++] = new DeviceName(deviceName);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result OpenAudioIn(
|
||||
out AudioOutputConfiguration outputConfiguration,
|
||||
out IAudioIn audioIn,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> outName,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[CopyHandle] int processHandle,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> name,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle);
|
||||
|
||||
ResultCode rc = _impl.OpenAudioIn(
|
||||
out string outputDeviceName,
|
||||
out outputConfiguration,
|
||||
out AudioInputSystem inSystem,
|
||||
clientMemoryManager,
|
||||
name.Length > 0 ? name[0].ToString() : string.Empty,
|
||||
SampleFormat.PcmInt16,
|
||||
ref parameter);
|
||||
|
||||
if (rc == ResultCode.Success && outName.Length > 0)
|
||||
{
|
||||
outName[0] = new DeviceName(outputDeviceName);
|
||||
}
|
||||
|
||||
audioIn = new AudioIn(inSystem, processHandle);
|
||||
|
||||
return new Result((int)rc);
|
||||
}
|
||||
|
||||
[CmifCommand(2)] // 3.0.0+
|
||||
public Result ListAudioInsAuto(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> names)
|
||||
{
|
||||
return ListAudioIns(out count, names);
|
||||
}
|
||||
|
||||
[CmifCommand(3)] // 3.0.0+
|
||||
public Result OpenAudioInAuto(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioIn audioIn,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> outName,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[CopyHandle] int processHandle,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> name,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
return OpenAudioIn(out outputConfig, out audioIn, outName, parameter, appletResourceId, processHandle, name, pid);
|
||||
}
|
||||
|
||||
[CmifCommand(4)] // 3.0.0+
|
||||
public Result ListAudioInsAutoFiltered(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> names)
|
||||
{
|
||||
string[] deviceNames = _impl.ListAudioIns(filtered: true);
|
||||
|
||||
count = 0;
|
||||
|
||||
foreach (string deviceName in deviceNames)
|
||||
{
|
||||
if (count >= names.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
names[count++] = new DeviceName(deviceName);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(5)] // 5.0.0+
|
||||
public Result OpenAudioInProtocolSpecified(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioIn audioIn,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> outName,
|
||||
AudioInProtocol protocol,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[CopyHandle] int processHandle,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> name,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
// NOTE: We always assume that only the default device will be plugged (we never report any USB Audio Class type devices).
|
||||
|
||||
return OpenAudioIn(out outputConfig, out audioIn, outName, parameter, appletResourceId, processHandle, name, pid);
|
||||
}
|
||||
}
|
||||
}
|
23
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs
Normal file
23
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioInProtocol.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x1)]
|
||||
struct AudioInProtocol
|
||||
{
|
||||
public AudioInProtocolName Name;
|
||||
public Array7<byte> Padding;
|
||||
|
||||
public AudioInProtocol(AudioInProtocolName name)
|
||||
{
|
||||
Name = name;
|
||||
Padding = new();
|
||||
}
|
||||
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return Name.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
enum AudioInProtocolName : byte
|
||||
{
|
||||
DeviceIn = 0,
|
||||
UacIn = 1,
|
||||
}
|
||||
}
|
154
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs
Normal file
154
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOut.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Output;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioOut : IAudioOut, IDisposable
|
||||
{
|
||||
private readonly AudioOutputSystem _impl;
|
||||
private int _processHandle;
|
||||
|
||||
public AudioOut(AudioOutputSystem impl, int processHandle)
|
||||
{
|
||||
_impl = impl;
|
||||
_processHandle = processHandle;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result GetAudioOutState(out AudioDeviceState state)
|
||||
{
|
||||
state = _impl.GetState();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result Start()
|
||||
{
|
||||
return new Result((int)_impl.Start());
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result Stop()
|
||||
{
|
||||
return new Result((int)_impl.Stop());
|
||||
}
|
||||
|
||||
[CmifCommand(3)]
|
||||
public Result AppendAudioOutBuffer(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<AudioUserBuffer> buffer)
|
||||
{
|
||||
AudioUserBuffer userBuffer = default;
|
||||
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
userBuffer = buffer[0];
|
||||
}
|
||||
|
||||
return new Result((int)_impl.AppendBuffer(bufferTag, ref userBuffer));
|
||||
}
|
||||
|
||||
[CmifCommand(4)]
|
||||
public Result RegisterBufferEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
eventHandle = 0;
|
||||
|
||||
if (_impl.RegisterBufferEvent() is AudioEvent audioEvent)
|
||||
{
|
||||
eventHandle = audioEvent.GetReadableHandle();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(5)]
|
||||
public Result GetReleasedAudioOutBuffers(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<ulong> bufferTags)
|
||||
{
|
||||
return new Result((int)_impl.GetReleasedBuffer(bufferTags, out count));
|
||||
}
|
||||
|
||||
[CmifCommand(6)]
|
||||
public Result ContainsAudioOutBuffer(out bool contains, ulong bufferTag)
|
||||
{
|
||||
contains = _impl.ContainsBuffer(bufferTag);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(7)] // 3.0.0+
|
||||
public Result AppendAudioOutBufferAuto(ulong bufferTag, [Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<AudioUserBuffer> buffer)
|
||||
{
|
||||
return AppendAudioOutBuffer(bufferTag, buffer);
|
||||
}
|
||||
|
||||
[CmifCommand(8)] // 3.0.0+
|
||||
public Result GetReleasedAudioOutBuffersAuto(out uint count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<ulong> bufferTags)
|
||||
{
|
||||
return GetReleasedAudioOutBuffers(out count, bufferTags);
|
||||
}
|
||||
|
||||
[CmifCommand(9)] // 4.0.0+
|
||||
public Result GetAudioOutBufferCount(out uint bufferCount)
|
||||
{
|
||||
bufferCount = _impl.GetBufferCount();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(10)] // 4.0.0+
|
||||
public Result GetAudioOutPlayedSampleCount(out ulong sampleCount)
|
||||
{
|
||||
sampleCount = _impl.GetPlayedSampleCount();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(11)] // 4.0.0+
|
||||
public Result FlushAudioOutBuffers(out bool pending)
|
||||
{
|
||||
pending = _impl.FlushBuffers();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(12)] // 6.0.0+
|
||||
public Result SetAudioOutVolume(float volume)
|
||||
{
|
||||
_impl.SetVolume(volume);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(13)] // 6.0.0+
|
||||
public Result GetAudioOutVolume(out float volume)
|
||||
{
|
||||
volume = _impl.GetVolume();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_impl.Dispose();
|
||||
|
||||
if (_processHandle != 0)
|
||||
{
|
||||
HorizonStatic.Syscall.CloseHandle(_processHandle);
|
||||
|
||||
_processHandle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
93
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs
Normal file
93
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioOutManager.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Output;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioOutManager : IAudioOutManager
|
||||
{
|
||||
private readonly AudioOutputManager _impl;
|
||||
|
||||
public AudioOutManager(AudioOutputManager impl)
|
||||
{
|
||||
_impl = impl;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result ListAudioOuts(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> names)
|
||||
{
|
||||
string[] deviceNames = _impl.ListAudioOuts();
|
||||
|
||||
count = 0;
|
||||
|
||||
foreach (string deviceName in deviceNames)
|
||||
{
|
||||
if (count >= names.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
names[count++] = new DeviceName(deviceName);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result OpenAudioOut(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioOut audioOut,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<DeviceName> outName,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[CopyHandle] int processHandle,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<DeviceName> name,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle);
|
||||
|
||||
ResultCode rc = _impl.OpenAudioOut(
|
||||
out string outputDeviceName,
|
||||
out outputConfig,
|
||||
out AudioOutputSystem outSystem,
|
||||
clientMemoryManager,
|
||||
name.Length > 0 ? name[0].ToString() : string.Empty,
|
||||
SampleFormat.PcmInt16,
|
||||
ref parameter);
|
||||
|
||||
if (rc == ResultCode.Success && outName.Length > 0)
|
||||
{
|
||||
outName[0] = new DeviceName(outputDeviceName);
|
||||
}
|
||||
|
||||
audioOut = new AudioOut(outSystem, processHandle);
|
||||
|
||||
return new Result((int)rc);
|
||||
}
|
||||
|
||||
[CmifCommand(2)] // 3.0.0+
|
||||
public Result ListAudioOutsAuto(out int count, [Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> names)
|
||||
{
|
||||
return ListAudioOuts(out count, names);
|
||||
}
|
||||
|
||||
[CmifCommand(3)] // 3.0.0+
|
||||
public Result OpenAudioOutAuto(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioOut audioOut,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<DeviceName> outName,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[CopyHandle] int processHandle,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<DeviceName> name,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
return OpenAudioOut(out outputConfig, out audioOut, outName, parameter, appletResourceId, processHandle, name, pid);
|
||||
}
|
||||
}
|
||||
}
|
187
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs
Normal file
187
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRenderer.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Audio.Renderer.Server;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioRenderer : IAudioRenderer, IDisposable
|
||||
{
|
||||
private readonly AudioRenderSystem _renderSystem;
|
||||
private int _workBufferHandle;
|
||||
private int _processHandle;
|
||||
|
||||
public AudioRenderer(AudioRenderSystem renderSystem, int workBufferHandle, int processHandle)
|
||||
{
|
||||
_renderSystem = renderSystem;
|
||||
_workBufferHandle = workBufferHandle;
|
||||
_processHandle = processHandle;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result GetSampleRate(out int sampleRate)
|
||||
{
|
||||
sampleRate = (int)_renderSystem.GetSampleRate();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result GetSampleCount(out int sampleCount)
|
||||
{
|
||||
sampleCount = (int)_renderSystem.GetSampleCount();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result GetMixBufferCount(out int mixBufferCount)
|
||||
{
|
||||
mixBufferCount = (int)_renderSystem.GetMixBufferCount();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(3)]
|
||||
public Result GetState(out int state)
|
||||
{
|
||||
state = _renderSystem.IsActive() ? 0 : 1;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(4)]
|
||||
public Result RequestUpdate(
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> output,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> performanceOutput,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> input)
|
||||
{
|
||||
using IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(output.Length);
|
||||
using IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Rent(performanceOutput.Length);
|
||||
|
||||
Memory<byte> outputMemory = outputOwner.Memory;
|
||||
Memory<byte> performanceOutputMemory = performanceOutputOwner.Memory;
|
||||
|
||||
using MemoryHandle outputHandle = outputMemory.Pin();
|
||||
using MemoryHandle performanceOutputHandle = performanceOutputMemory.Pin();
|
||||
|
||||
Result result = new Result((int)_renderSystem.Update(outputMemory, performanceOutputMemory, input.ToArray()));
|
||||
|
||||
outputMemory.Span.CopyTo(output);
|
||||
performanceOutputMemory.Span.CopyTo(performanceOutput);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[CmifCommand(5)]
|
||||
public Result Start()
|
||||
{
|
||||
_renderSystem.Start();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(6)]
|
||||
public Result Stop()
|
||||
{
|
||||
_renderSystem.Stop();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(7)]
|
||||
public Result QuerySystemEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
ResultCode rc = _renderSystem.QuerySystemEvent(out IWritableEvent systemEvent);
|
||||
|
||||
eventHandle = 0;
|
||||
|
||||
if (rc == ResultCode.Success && systemEvent is AudioEvent audioEvent)
|
||||
{
|
||||
eventHandle = audioEvent.GetReadableHandle();
|
||||
}
|
||||
|
||||
return new Result((int)rc);
|
||||
}
|
||||
|
||||
[CmifCommand(8)]
|
||||
public Result SetRenderingTimeLimit(int percent)
|
||||
{
|
||||
_renderSystem.SetRenderingTimeLimitPercent((uint)percent);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(9)]
|
||||
public Result GetRenderingTimeLimit(out int percent)
|
||||
{
|
||||
percent = (int)_renderSystem.GetRenderingTimeLimit();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(10)] // 3.0.0+
|
||||
public Result RequestUpdateAuto(
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<byte> output,
|
||||
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<byte> performanceOutput,
|
||||
[Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<byte> input)
|
||||
{
|
||||
return RequestUpdate(output, performanceOutput, input);
|
||||
}
|
||||
|
||||
[CmifCommand(11)] // 3.0.0+
|
||||
public Result ExecuteAudioRendererRendering()
|
||||
{
|
||||
return new Result((int)_renderSystem.ExecuteAudioRendererRendering());
|
||||
}
|
||||
|
||||
[CmifCommand(12)] // 15.0.0+
|
||||
public Result SetVoiceDropParameter(float voiceDropParameter)
|
||||
{
|
||||
_renderSystem.SetVoiceDropParameter(voiceDropParameter);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(13)] // 15.0.0+
|
||||
public Result GetVoiceDropParameter(out float voiceDropParameter)
|
||||
{
|
||||
voiceDropParameter = _renderSystem.GetVoiceDropParameter();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_renderSystem.Dispose();
|
||||
|
||||
if (_workBufferHandle != 0)
|
||||
{
|
||||
HorizonStatic.Syscall.CloseHandle(_workBufferHandle);
|
||||
|
||||
_workBufferHandle = 0;
|
||||
}
|
||||
|
||||
if (_processHandle != 0)
|
||||
{
|
||||
HorizonStatic.Syscall.CloseHandle(_processHandle);
|
||||
|
||||
_processHandle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
132
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs
Normal file
132
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioRendererManager.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Ryujinx.Audio.Renderer.Device;
|
||||
using Ryujinx.Audio.Renderer.Server;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioRendererManager : IAudioRendererManager
|
||||
{
|
||||
private const uint InitialRevision = ('R' << 0) | ('E' << 8) | ('V' << 16) | ('1' << 24);
|
||||
|
||||
private readonly Ryujinx.Audio.Renderer.Server.AudioRendererManager _impl;
|
||||
private readonly VirtualDeviceSessionRegistry _registry;
|
||||
|
||||
public AudioRendererManager(Ryujinx.Audio.Renderer.Server.AudioRendererManager impl, VirtualDeviceSessionRegistry registry)
|
||||
{
|
||||
_impl = impl;
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result OpenAudioRenderer(
|
||||
out IAudioRenderer renderer,
|
||||
AudioRendererParameterInternal parameter,
|
||||
[CopyHandle] int workBufferHandle,
|
||||
[CopyHandle] int processHandle,
|
||||
ulong workBufferSize,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle);
|
||||
ulong workBufferAddress = HorizonStatic.Syscall.GetTransferMemoryAddress(workBufferHandle);
|
||||
|
||||
Result result = new Result((int)_impl.OpenAudioRenderer(
|
||||
out var renderSystem,
|
||||
clientMemoryManager,
|
||||
ref parameter.Configuration,
|
||||
appletResourceId.Id,
|
||||
workBufferAddress,
|
||||
workBufferSize,
|
||||
(uint)processHandle));
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
renderer = new AudioRenderer(renderSystem, workBufferHandle, processHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer = null;
|
||||
|
||||
HorizonStatic.Syscall.CloseHandle(workBufferHandle);
|
||||
HorizonStatic.Syscall.CloseHandle(processHandle);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result GetWorkBufferSize(out long workBufferSize, AudioRendererParameterInternal parameter)
|
||||
{
|
||||
if (BehaviourContext.CheckValidRevision(parameter.Configuration.Revision))
|
||||
{
|
||||
workBufferSize = (long)Ryujinx.Audio.Renderer.Server.AudioRendererManager.GetWorkBufferSize(ref parameter.Configuration);
|
||||
|
||||
Logger.Debug?.Print(LogClass.ServiceAudio, $"WorkBufferSize is 0x{workBufferSize:x16}.");
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
workBufferSize = 0;
|
||||
|
||||
Logger.Warning?.Print(LogClass.ServiceAudio, $"Library Revision REV{BehaviourContext.GetRevisionNumber(parameter.Configuration.Revision)} is not supported!");
|
||||
|
||||
return AudioResult.UnsupportedRevision;
|
||||
}
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result GetAudioDeviceService(out IAudioDevice audioDevice, AppletResourceUserId appletResourceId)
|
||||
{
|
||||
audioDevice = new AudioDevice(_registry, appletResourceId, InitialRevision);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(3)] // 3.0.0+
|
||||
public Result OpenAudioRendererForManualExecution(
|
||||
out IAudioRenderer renderer,
|
||||
AudioRendererParameterInternal parameter,
|
||||
ulong workBufferAddress,
|
||||
[CopyHandle] int processHandle,
|
||||
ulong workBufferSize,
|
||||
AppletResourceUserId appletResourceId,
|
||||
[ClientProcessId] ulong pid)
|
||||
{
|
||||
var clientMemoryManager = HorizonStatic.Syscall.GetMemoryManagerByProcessHandle(processHandle);
|
||||
|
||||
Result result = new Result((int)_impl.OpenAudioRenderer(
|
||||
out var renderSystem,
|
||||
clientMemoryManager,
|
||||
ref parameter.Configuration,
|
||||
appletResourceId.Id,
|
||||
workBufferAddress,
|
||||
workBufferSize,
|
||||
(uint)processHandle));
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
renderer = new AudioRenderer(renderSystem, 0, processHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer = null;
|
||||
|
||||
HorizonStatic.Syscall.CloseHandle(processHandle);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[CmifCommand(4)] // 4.0.0+
|
||||
public Result GetAudioDeviceServiceWithRevisionInfo(out IAudioDevice audioDevice, AppletResourceUserId appletResourceId, uint revision)
|
||||
{
|
||||
audioDevice = new AudioDevice(_registry, appletResourceId, revision);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
struct AudioRendererParameterInternal
|
||||
{
|
||||
public AudioRendererConfiguration Configuration;
|
||||
|
||||
public AudioRendererParameterInternal(AudioRendererConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
}
|
||||
}
|
30
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs
Normal file
30
src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioSnoopManager.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class AudioSnoopManager : IAudioSnoopManager
|
||||
{
|
||||
// Note: The interface changed completely on firmware 17.0.0, this implementation is for older firmware.
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result EnableDspUsageMeasurement()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result DisableDspUsageMeasurement()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(6)]
|
||||
public Result GetDspUsage(out uint usage)
|
||||
{
|
||||
usage = 0;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
30
src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs
Normal file
30
src/Ryujinx.Horizon/Sdk/Audio/Detail/DeviceName.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x100, Pack = 1)]
|
||||
struct DeviceName
|
||||
{
|
||||
public Array256<byte> Name;
|
||||
|
||||
public DeviceName(string name)
|
||||
{
|
||||
Name = new();
|
||||
Encoding.ASCII.GetBytes(name, Name.AsSpan());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
int length = Name.AsSpan().IndexOf((byte)0);
|
||||
if (length < 0)
|
||||
{
|
||||
length = 0x100;
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString(Name.AsSpan()[..length]);
|
||||
}
|
||||
}
|
||||
}
|
147
src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs
Normal file
147
src/Ryujinx.Horizon/Sdk/Audio/Detail/FinalOutputRecorder.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.OsTypes;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class FinalOutputRecorder : IFinalOutputRecorder, IDisposable
|
||||
{
|
||||
private int _processHandle;
|
||||
private SystemEventType _event;
|
||||
|
||||
public FinalOutputRecorder(int processHandle)
|
||||
{
|
||||
_processHandle = processHandle;
|
||||
Os.CreateSystemEvent(out _event, EventClearMode.ManualClear, interProcess: true);
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result GetFinalOutputRecorderState(out uint state)
|
||||
{
|
||||
state = 0;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)]
|
||||
public Result Start()
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result Stop()
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(3)]
|
||||
public Result AppendFinalOutputRecorderBuffer([Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> buffer, ulong bufferClientPtr)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { bufferClientPtr });
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(4)]
|
||||
public Result RegisterBufferEvent([CopyHandle] out int eventHandle)
|
||||
{
|
||||
eventHandle = Os.GetReadableHandleOfSystemEvent(ref _event);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(5)]
|
||||
public Result GetReleasedFinalOutputRecorderBuffers([Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> buffer, out uint count, out ulong released)
|
||||
{
|
||||
count = 0;
|
||||
released = 0;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(6)]
|
||||
public Result ContainsFinalOutputRecorderBuffer(ulong bufferPointer, out bool contains)
|
||||
{
|
||||
contains = false;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { bufferPointer });
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(7)]
|
||||
public Result GetFinalOutputRecorderBufferEndTime(ulong bufferPointer, out ulong released)
|
||||
{
|
||||
released = 0;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { bufferPointer });
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(8)] // 3.0.0+
|
||||
public Result AppendFinalOutputRecorderBufferAuto([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] ReadOnlySpan<byte> buffer, ulong bufferClientPtr)
|
||||
{
|
||||
return AppendFinalOutputRecorderBuffer(buffer, bufferClientPtr);
|
||||
}
|
||||
|
||||
[CmifCommand(9)] // 3.0.0+
|
||||
public Result GetReleasedFinalOutputRecorderBuffersAuto([Buffer(HipcBufferFlags.Out | HipcBufferFlags.AutoSelect)] Span<byte> buffer, out uint count, out ulong released)
|
||||
{
|
||||
return GetReleasedFinalOutputRecorderBuffers(buffer, out count, out released);
|
||||
}
|
||||
|
||||
[CmifCommand(10)] // 6.0.0+
|
||||
public Result FlushFinalOutputRecorderBuffers(out bool pending)
|
||||
{
|
||||
pending = false;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(11)] // 9.0.0+
|
||||
public Result AttachWorkBuffer(FinalOutputRecorderParameterInternal parameter)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio, new { parameter });
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Os.DestroySystemEvent(ref _event);
|
||||
|
||||
if (_processHandle != 0)
|
||||
{
|
||||
HorizonStatic.Syscall.CloseHandle(_processHandle);
|
||||
|
||||
_processHandle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
partial class FinalOutputRecorderManager : IFinalOutputRecorderManager
|
||||
{
|
||||
[CmifCommand(0)]
|
||||
public Result OpenFinalOutputRecorder(
|
||||
out IFinalOutputRecorder recorder,
|
||||
FinalOutputRecorderParameter parameter,
|
||||
[CopyHandle] int processHandle,
|
||||
out FinalOutputRecorderParameterInternal outParameter,
|
||||
AppletResourceUserId appletResourceId)
|
||||
{
|
||||
recorder = new FinalOutputRecorder(processHandle);
|
||||
outParameter = new(parameter.SampleRate, 2, 0);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 0x4)]
|
||||
readonly struct FinalOutputRecorderParameter
|
||||
{
|
||||
public readonly uint SampleRate;
|
||||
public readonly uint Padding;
|
||||
|
||||
public FinalOutputRecorderParameter(uint sampleRate)
|
||||
{
|
||||
SampleRate = sampleRate;
|
||||
Padding = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)]
|
||||
readonly struct FinalOutputRecorderParameterInternal
|
||||
{
|
||||
public readonly uint SampleRate;
|
||||
public readonly uint ChannelCount;
|
||||
public readonly uint UseLargeFrameSize;
|
||||
public readonly uint Padding;
|
||||
|
||||
public FinalOutputRecorderParameterInternal(uint sampleRate, uint channelCount, uint useLargeFrameSize)
|
||||
{
|
||||
SampleRate = sampleRate;
|
||||
ChannelCount = channelCount;
|
||||
UseLargeFrameSize = useLargeFrameSize;
|
||||
Padding = 0;
|
||||
}
|
||||
}
|
||||
}
|
24
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs
Normal file
24
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioDevice.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioDevice : IServiceObject
|
||||
{
|
||||
Result ListAudioDeviceName(Span<DeviceName> names, out int nameCount);
|
||||
Result SetAudioDeviceOutputVolume(ReadOnlySpan<DeviceName> name, float volume);
|
||||
Result GetAudioDeviceOutputVolume(ReadOnlySpan<DeviceName> name, out float volume);
|
||||
Result GetActiveAudioDeviceName(Span<DeviceName> name);
|
||||
Result QueryAudioDeviceSystemEvent(out int eventHandle);
|
||||
Result GetActiveChannelCount(out int channelCount);
|
||||
Result ListAudioDeviceNameAuto(Span<DeviceName> names, out int nameCount);
|
||||
Result SetAudioDeviceOutputVolumeAuto(ReadOnlySpan<DeviceName> name, float volume);
|
||||
Result GetAudioDeviceOutputVolumeAuto(ReadOnlySpan<DeviceName> name, out float volume);
|
||||
Result GetActiveAudioDeviceNameAuto(Span<DeviceName> name);
|
||||
Result QueryAudioDeviceInputEvent(out int eventHandle);
|
||||
Result QueryAudioDeviceOutputEvent(out int eventHandle);
|
||||
Result GetActiveAudioOutputDeviceName(Span<DeviceName> name);
|
||||
Result ListAudioOutputDeviceName(Span<DeviceName> names, out int nameCount);
|
||||
}
|
||||
}
|
26
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs
Normal file
26
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioIn.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioIn : IServiceObject
|
||||
{
|
||||
Result GetAudioInState(out AudioDeviceState state);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result AppendAudioInBuffer(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer);
|
||||
Result RegisterBufferEvent(out int eventHandle);
|
||||
Result GetReleasedAudioInBuffers(out uint count, Span<ulong> bufferTags);
|
||||
Result ContainsAudioInBuffer(out bool contains, ulong bufferTag);
|
||||
Result AppendUacInBuffer(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer, int eventHandle);
|
||||
Result AppendAudioInBufferAuto(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer);
|
||||
Result GetReleasedAudioInBuffersAuto(out uint count, Span<ulong> bufferTags);
|
||||
Result AppendUacInBufferAuto(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer, int eventHandle);
|
||||
Result GetAudioInBufferCount(out uint bufferCount);
|
||||
Result SetDeviceGain(float gain);
|
||||
Result GetDeviceGain(out float gain);
|
||||
Result FlushAudioInBuffers(out bool pending);
|
||||
}
|
||||
}
|
43
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs
Normal file
43
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioInManager.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioInManager : IServiceObject
|
||||
{
|
||||
Result ListAudioIns(out int count, Span<DeviceName> names);
|
||||
Result OpenAudioIn(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioIn audioIn,
|
||||
Span<DeviceName> outName,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
int processHandle,
|
||||
ReadOnlySpan<DeviceName> name,
|
||||
ulong pid);
|
||||
Result ListAudioInsAuto(out int count, Span<DeviceName> names);
|
||||
Result OpenAudioInAuto(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioIn audioIn,
|
||||
Span<DeviceName> outName,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
int processHandle,
|
||||
ReadOnlySpan<DeviceName> name,
|
||||
ulong pid);
|
||||
Result ListAudioInsAutoFiltered(out int count, Span<DeviceName> names);
|
||||
Result OpenAudioInProtocolSpecified(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioIn audioIn,
|
||||
Span<DeviceName> outName,
|
||||
AudioInProtocol protocol,
|
||||
AudioInputConfiguration parameter,
|
||||
AppletResourceUserId appletResourceId,
|
||||
int processHandle,
|
||||
ReadOnlySpan<DeviceName> name,
|
||||
ulong pid);
|
||||
}
|
||||
}
|
25
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs
Normal file
25
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOut.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioOut : IServiceObject
|
||||
{
|
||||
Result GetAudioOutState(out AudioDeviceState state);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result AppendAudioOutBuffer(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer);
|
||||
Result RegisterBufferEvent(out int eventHandle);
|
||||
Result GetReleasedAudioOutBuffers(out uint count, Span<ulong> bufferTags);
|
||||
Result ContainsAudioOutBuffer(out bool contains, ulong bufferTag);
|
||||
Result AppendAudioOutBufferAuto(ulong bufferTag, ReadOnlySpan<AudioUserBuffer> buffer);
|
||||
Result GetReleasedAudioOutBuffersAuto(out uint count, Span<ulong> bufferTags);
|
||||
Result GetAudioOutBufferCount(out uint bufferCount);
|
||||
Result GetAudioOutPlayedSampleCount(out ulong sampleCount);
|
||||
Result FlushAudioOutBuffers(out bool pending);
|
||||
Result SetAudioOutVolume(float volume);
|
||||
Result GetAudioOutVolume(out float volume);
|
||||
}
|
||||
}
|
32
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs
Normal file
32
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioOutManager.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioOutManager : IServiceObject
|
||||
{
|
||||
Result ListAudioOuts(out int count, Span<DeviceName> names);
|
||||
Result OpenAudioOut(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioOut audioOut,
|
||||
Span<DeviceName> outName,
|
||||
AudioInputConfiguration inputConfig,
|
||||
AppletResourceUserId appletResourceId,
|
||||
int processHandle,
|
||||
ReadOnlySpan<DeviceName> name,
|
||||
ulong pid);
|
||||
Result ListAudioOutsAuto(out int count, Span<DeviceName> names);
|
||||
Result OpenAudioOutAuto(
|
||||
out AudioOutputConfiguration outputConfig,
|
||||
out IAudioOut audioOut,
|
||||
Span<DeviceName> outName,
|
||||
AudioInputConfiguration inputConfig,
|
||||
AppletResourceUserId appletResourceId,
|
||||
int processHandle,
|
||||
ReadOnlySpan<DeviceName> name,
|
||||
ulong pid);
|
||||
}
|
||||
}
|
24
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs
Normal file
24
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioRenderer.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioRenderer : IServiceObject
|
||||
{
|
||||
Result GetSampleRate(out int sampleRate);
|
||||
Result GetSampleCount(out int sampleCount);
|
||||
Result GetMixBufferCount(out int mixBufferCount);
|
||||
Result GetState(out int state);
|
||||
Result RequestUpdate(Span<byte> output, Span<byte> performanceOutput, ReadOnlySpan<byte> input);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result QuerySystemEvent(out int eventHandle);
|
||||
Result SetRenderingTimeLimit(int percent);
|
||||
Result GetRenderingTimeLimit(out int percent);
|
||||
Result RequestUpdateAuto(Span<byte> output, Span<byte> performanceOutput, ReadOnlySpan<byte> input);
|
||||
Result ExecuteAudioRendererRendering();
|
||||
Result SetVoiceDropParameter(float voiceDropParameter);
|
||||
Result GetVoiceDropParameter(out float voiceDropParameter);
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioRendererManager : IServiceObject
|
||||
{
|
||||
Result OpenAudioRenderer(
|
||||
out IAudioRenderer renderer,
|
||||
AudioRendererParameterInternal parameter,
|
||||
int processHandle,
|
||||
int workBufferHandle,
|
||||
ulong workBufferSize,
|
||||
AppletResourceUserId appletUserId,
|
||||
ulong pid);
|
||||
Result GetWorkBufferSize(out long workBufferSize, AudioRendererParameterInternal parameter);
|
||||
Result GetAudioDeviceService(out IAudioDevice audioDevice, AppletResourceUserId appletUserId);
|
||||
Result OpenAudioRendererForManualExecution(
|
||||
out IAudioRenderer renderer,
|
||||
AudioRendererParameterInternal parameter,
|
||||
ulong workBufferAddress,
|
||||
int processHandle,
|
||||
ulong workBufferSize,
|
||||
AppletResourceUserId appletUserId,
|
||||
ulong pid);
|
||||
Result GetAudioDeviceServiceWithRevisionInfo(out IAudioDevice audioDevice, AppletResourceUserId appletUserId, uint revision);
|
||||
}
|
||||
}
|
12
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs
Normal file
12
src/Ryujinx.Horizon/Sdk/Audio/Detail/IAudioSnoopManager.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IAudioSnoopManager : IServiceObject
|
||||
{
|
||||
Result EnableDspUsageMeasurement();
|
||||
Result DisableDspUsageMeasurement();
|
||||
Result GetDspUsage(out uint usage);
|
||||
}
|
||||
}
|
22
src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs
Normal file
22
src/Ryujinx.Horizon/Sdk/Audio/Detail/IFinalOutputRecorder.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IFinalOutputRecorder : IServiceObject
|
||||
{
|
||||
Result GetFinalOutputRecorderState(out uint state);
|
||||
Result Start();
|
||||
Result Stop();
|
||||
Result AppendFinalOutputRecorderBuffer(ReadOnlySpan<byte> buffer, ulong bufferClientPtr);
|
||||
Result RegisterBufferEvent(out int eventHandle);
|
||||
Result GetReleasedFinalOutputRecorderBuffers(Span<byte> buffer, out uint count, out ulong released);
|
||||
Result ContainsFinalOutputRecorderBuffer(ulong bufferPointer, out bool contains);
|
||||
Result GetFinalOutputRecorderBufferEndTime(ulong bufferPointer, out ulong released);
|
||||
Result AppendFinalOutputRecorderBufferAuto(ReadOnlySpan<byte> buffer, ulong bufferClientPtr);
|
||||
Result GetReleasedFinalOutputRecorderBuffersAuto(Span<byte> buffer, out uint count, out ulong released);
|
||||
Result FlushFinalOutputRecorderBuffers(out bool pending);
|
||||
Result AttachWorkBuffer(FinalOutputRecorderParameterInternal parameter);
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
{
|
||||
interface IFinalOutputRecorderManager : IServiceObject
|
||||
{
|
||||
Result OpenFinalOutputRecorder(
|
||||
out IFinalOutputRecorder recorder,
|
||||
FinalOutputRecorderParameter parameter,
|
||||
int processHandle,
|
||||
out FinalOutputRecorderParameterInternal outParameter,
|
||||
AppletResourceUserId appletResourceId);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user