AudioInputManager.cs
1 using Ryujinx.Audio.Common; 2 using Ryujinx.Audio.Integration; 3 using Ryujinx.Common.Logging; 4 using Ryujinx.Memory; 5 using System; 6 using System.Diagnostics; 7 using System.Linq; 8 using System.Threading; 9 10 namespace Ryujinx.Audio.Input 11 { 12 /// <summary> 13 /// The audio input manager. 14 /// </summary> 15 public class AudioInputManager : IDisposable 16 { 17 private readonly object _lock = new(); 18 19 /// <summary> 20 /// Lock used for session allocation. 21 /// </summary> 22 private readonly object _sessionLock = new(); 23 24 /// <summary> 25 /// The session ids allocation table. 26 /// </summary> 27 private readonly int[] _sessionIds; 28 29 /// <summary> 30 /// The device driver. 31 /// </summary> 32 private IHardwareDeviceDriver _deviceDriver; 33 34 /// <summary> 35 /// The events linked to each session. 36 /// </summary> 37 private IWritableEvent[] _sessionsBufferEvents; 38 39 /// <summary> 40 /// The <see cref="AudioInputSystem"/> session instances. 41 /// </summary> 42 private readonly AudioInputSystem[] _sessions; 43 44 /// <summary> 45 /// The count of active sessions. 46 /// </summary> 47 private int _activeSessionCount; 48 49 /// <summary> 50 /// The dispose state. 51 /// </summary> 52 private int _disposeState; 53 54 /// <summary> 55 /// Create a new <see cref="AudioInputManager"/>. 56 /// </summary> 57 public AudioInputManager() 58 { 59 _sessionIds = new int[Constants.AudioInSessionCountMax]; 60 _sessions = new AudioInputSystem[Constants.AudioInSessionCountMax]; 61 _activeSessionCount = 0; 62 63 for (int i = 0; i < _sessionIds.Length; i++) 64 { 65 _sessionIds[i] = i; 66 } 67 } 68 69 /// <summary> 70 /// Initialize the <see cref="AudioInputManager"/>. 71 /// </summary> 72 /// <param name="deviceDriver">The device driver.</param> 73 /// <param name="sessionRegisterEvents">The events associated to each session.</param> 74 public void Initialize(IHardwareDeviceDriver deviceDriver, IWritableEvent[] sessionRegisterEvents) 75 { 76 _deviceDriver = deviceDriver; 77 _sessionsBufferEvents = sessionRegisterEvents; 78 } 79 80 /// <summary> 81 /// Acquire a new session id. 82 /// </summary> 83 /// <returns>A new session id.</returns> 84 private int AcquireSessionId() 85 { 86 lock (_sessionLock) 87 { 88 int index = _activeSessionCount; 89 90 Debug.Assert(index < _sessionIds.Length); 91 92 int sessionId = _sessionIds[index]; 93 94 _sessionIds[index] = -1; 95 96 _activeSessionCount++; 97 98 Logger.Info?.Print(LogClass.AudioRenderer, $"Registered new input ({sessionId})"); 99 100 return sessionId; 101 } 102 } 103 104 /// <summary> 105 /// Release a given <paramref name="sessionId"/>. 106 /// </summary> 107 /// <param name="sessionId">The session id to release.</param> 108 private void ReleaseSessionId(int sessionId) 109 { 110 lock (_sessionLock) 111 { 112 Debug.Assert(_activeSessionCount > 0); 113 114 int newIndex = --_activeSessionCount; 115 116 _sessionIds[newIndex] = sessionId; 117 } 118 119 Logger.Info?.Print(LogClass.AudioRenderer, $"Unregistered input ({sessionId})"); 120 } 121 122 /// <summary> 123 /// Used to update audio input system. 124 /// </summary> 125 public void Update() 126 { 127 lock (_sessionLock) 128 { 129 foreach (AudioInputSystem input in _sessions) 130 { 131 input?.Update(); 132 } 133 } 134 } 135 136 /// <summary> 137 /// Register a new <see cref="AudioInputSystem"/>. 138 /// </summary> 139 /// <param name="input">The <see cref="AudioInputSystem"/> to register.</param> 140 private void Register(AudioInputSystem input) 141 { 142 lock (_sessionLock) 143 { 144 _sessions[input.GetSessionId()] = input; 145 } 146 } 147 148 /// <summary> 149 /// Unregister a new <see cref="AudioInputSystem"/>. 150 /// </summary> 151 /// <param name="input">The <see cref="AudioInputSystem"/> to unregister.</param> 152 internal void Unregister(AudioInputSystem input) 153 { 154 lock (_sessionLock) 155 { 156 int sessionId = input.GetSessionId(); 157 158 _sessions[input.GetSessionId()] = null; 159 160 ReleaseSessionId(sessionId); 161 } 162 } 163 164 /// <summary> 165 /// Get the list of all audio inputs names. 166 /// </summary> 167 /// <param name="filtered">If true, filter disconnected devices</param> 168 /// <returns>The list of all audio inputs name</returns> 169 public string[] ListAudioIns(bool filtered) 170 { 171 if (filtered) 172 { 173 // TODO: Detect if the driver supports audio input 174 } 175 176 return new[] { Constants.DefaultDeviceInputName }; 177 } 178 179 /// <summary> 180 /// Open a new <see cref="AudioInputSystem"/>. 181 /// </summary> 182 /// <param name="outputDeviceName">The output device name selected by the <see cref="AudioInputSystem"/></param> 183 /// <param name="outputConfiguration">The output audio configuration selected by the <see cref="AudioInputSystem"/></param> 184 /// <param name="obj">The new <see cref="AudioInputSystem"/></param> 185 /// <param name="memoryManager">The memory manager that will be used for all guest memory operations</param> 186 /// <param name="inputDeviceName">The input device name wanted by the user</param> 187 /// <param name="sampleFormat">The sample format to use</param> 188 /// <param name="parameter">The user configuration</param> 189 /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns> 190 public ResultCode OpenAudioIn(out string outputDeviceName, 191 out AudioOutputConfiguration outputConfiguration, 192 out AudioInputSystem obj, 193 IVirtualMemoryManager memoryManager, 194 string inputDeviceName, 195 SampleFormat sampleFormat, 196 ref AudioInputConfiguration parameter) 197 { 198 int sessionId = AcquireSessionId(); 199 200 _sessionsBufferEvents[sessionId].Clear(); 201 202 IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Input, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount); 203 204 AudioInputSystem audioIn = new(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]); 205 206 ResultCode result = audioIn.Initialize(inputDeviceName, sampleFormat, ref parameter, sessionId); 207 208 if (result == ResultCode.Success) 209 { 210 outputDeviceName = audioIn.DeviceName; 211 outputConfiguration = new AudioOutputConfiguration 212 { 213 ChannelCount = audioIn.ChannelCount, 214 SampleFormat = audioIn.SampleFormat, 215 SampleRate = audioIn.SampleRate, 216 AudioOutState = audioIn.GetState(), 217 }; 218 219 obj = audioIn; 220 221 Register(audioIn); 222 } 223 else 224 { 225 ReleaseSessionId(sessionId); 226 227 obj = null; 228 outputDeviceName = null; 229 outputConfiguration = default; 230 } 231 232 return result; 233 } 234 235 public void Dispose() 236 { 237 GC.SuppressFinalize(this); 238 239 if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) 240 { 241 Dispose(true); 242 } 243 } 244 245 protected virtual void Dispose(bool disposing) 246 { 247 if (disposing) 248 { 249 // Clone the sessions array to dispose them outside the lock. 250 AudioInputSystem[] sessions; 251 252 lock (_sessionLock) 253 { 254 sessions = _sessions.ToArray(); 255 } 256 257 foreach (AudioInputSystem input in sessions) 258 { 259 input?.Dispose(); 260 } 261 } 262 } 263 } 264 }