using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Diagnostics; using System.Linq; using System.Threading; namespace Ryujinx.Audio.Input { /// /// The audio input manager. /// public class AudioInputManager : IDisposable { private readonly object _lock = new(); /// /// Lock used for session allocation. /// private readonly object _sessionLock = new(); /// /// The session ids allocation table. /// private int[] _sessionIds; /// /// The device driver. /// private IHardwareDeviceDriver _deviceDriver; /// /// The events linked to each session. /// private IWritableEvent[] _sessionsBufferEvents; /// /// The session instances. /// private AudioInputSystem[] _sessions; /// /// The count of active sessions. /// private int _activeSessionCount; /// /// The dispose state. /// private int _disposeState; /// /// Create a new . /// public AudioInputManager() { _sessionIds = new int[Constants.AudioInSessionCountMax]; _sessions = new AudioInputSystem[Constants.AudioInSessionCountMax]; _activeSessionCount = 0; for (int i = 0; i < _sessionIds.Length; i++) { _sessionIds[i] = i; } } /// /// Initialize the . /// /// The device driver. /// The events associated to each session. public void Initialize(IHardwareDeviceDriver deviceDriver, IWritableEvent[] sessionRegisterEvents) { _deviceDriver = deviceDriver; _sessionsBufferEvents = sessionRegisterEvents; } /// /// Acquire a new session id. /// /// A new session id. private int AcquireSessionId() { lock (_sessionLock) { int index = _activeSessionCount; Debug.Assert(index < _sessionIds.Length); int sessionId = _sessionIds[index]; _sessionIds[index] = -1; _activeSessionCount++; Logger.Info?.Print(LogClass.AudioRenderer, $"Registered new input ({sessionId})"); return sessionId; } } /// /// Release a given . /// /// The session id to release. private void ReleaseSessionId(int sessionId) { lock (_sessionLock) { Debug.Assert(_activeSessionCount > 0); int newIndex = --_activeSessionCount; _sessionIds[newIndex] = sessionId; } Logger.Info?.Print(LogClass.AudioRenderer, $"Unregistered input ({sessionId})"); } /// /// Used to update audio input system. /// public void Update() { lock (_sessionLock) { foreach (AudioInputSystem input in _sessions) { input?.Update(); } } } /// /// Register a new . /// /// The to register. private void Register(AudioInputSystem input) { lock (_sessionLock) { _sessions[input.GetSessionId()] = input; } } /// /// Unregister a new . /// /// The to unregister. internal void Unregister(AudioInputSystem input) { lock (_sessionLock) { int sessionId = input.GetSessionId(); _sessions[input.GetSessionId()] = null; ReleaseSessionId(sessionId); } } /// /// Get the list of all audio inputs names. /// /// If true, filter disconnected devices /// The list of all audio inputs name public string[] ListAudioIns(bool filtered) { if (filtered) { // TODO: Detect if the driver supports audio input } return new string[] { Constants.DefaultDeviceInputName }; } /// /// Open a new . /// /// The output device name selected by the /// The output audio configuration selected by the /// The new /// The memory manager that will be used for all guest memory operations /// The input device name wanted by the user /// The sample format to use /// The user configuration /// The applet resource user id of the application /// The process handle of the application /// A reporting an error or a success public ResultCode OpenAudioIn(out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out AudioInputSystem obj, IVirtualMemoryManager memoryManager, string inputDeviceName, SampleFormat sampleFormat, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle) { int sessionId = AcquireSessionId(); _sessionsBufferEvents[sessionId].Clear(); IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Input, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount); AudioInputSystem audioIn = new AudioInputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); ResultCode result = audioIn.Initialize(inputDeviceName, sampleFormat, ref parameter, sessionId); if (result == ResultCode.Success) { outputDeviceName = audioIn.DeviceName; outputConfiguration = new AudioOutputConfiguration { ChannelCount = audioIn.ChannelCount, SampleFormat = audioIn.SampleFormat, SampleRate = audioIn.SampleRate, AudioOutState = audioIn.GetState(), }; obj = audioIn; Register(audioIn); } else { ReleaseSessionId(sessionId); obj = null; outputDeviceName = null; outputConfiguration = default; } return result; } public void Dispose() { if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) { Dispose(true); } } protected virtual void Dispose(bool disposing) { if (disposing) { // Clone the sessions array to dispose them outside the lock. AudioInputSystem[] sessions; lock (_sessionLock) { sessions = _sessions.ToArray(); } foreach (AudioInputSystem input in sessions) { input?.Dispose(); } } } } }