AudioInputSystem.cs
1 using Ryujinx.Audio.Common; 2 using Ryujinx.Audio.Integration; 3 using System; 4 using System.Threading; 5 6 namespace Ryujinx.Audio.Input 7 { 8 /// <summary> 9 /// Audio input system. 10 /// </summary> 11 public class AudioInputSystem : IDisposable 12 { 13 /// <summary> 14 /// The session id associated to the <see cref="AudioInputSystem"/>. 15 /// </summary> 16 private int _sessionId; 17 18 /// <summary> 19 /// The session the <see cref="AudioInputSystem"/>. 20 /// </summary> 21 private readonly AudioDeviceSession _session; 22 23 /// <summary> 24 /// The target device name of the <see cref="AudioInputSystem"/>. 25 /// </summary> 26 public string DeviceName { get; private set; } 27 28 /// <summary> 29 /// The target sample rate of the <see cref="AudioInputSystem"/>. 30 /// </summary> 31 public uint SampleRate { get; private set; } 32 33 /// <summary> 34 /// The target channel count of the <see cref="AudioInputSystem"/>. 35 /// </summary> 36 public uint ChannelCount { get; private set; } 37 38 /// <summary> 39 /// The target sample format of the <see cref="AudioInputSystem"/>. 40 /// </summary> 41 public SampleFormat SampleFormat { get; private set; } 42 43 /// <summary> 44 /// The <see cref="AudioInputManager"/> owning this. 45 /// </summary> 46 private readonly AudioInputManager _manager; 47 48 /// <summary> 49 /// The lock of the parent. 50 /// </summary> 51 private readonly object _parentLock; 52 53 /// <summary> 54 /// The dispose state. 55 /// </summary> 56 private int _disposeState; 57 58 /// <summary> 59 /// Create a new <see cref="AudioInputSystem"/>. 60 /// </summary> 61 /// <param name="manager">The manager instance</param> 62 /// <param name="parentLock">The lock of the manager</param> 63 /// <param name="deviceSession">The hardware device session</param> 64 /// <param name="bufferEvent">The buffer release event of the audio input</param> 65 public AudioInputSystem(AudioInputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) 66 { 67 _manager = manager; 68 _parentLock = parentLock; 69 _session = new AudioDeviceSession(deviceSession, bufferEvent); 70 } 71 72 /// <summary> 73 /// Get the default device name on the system. 74 /// </summary> 75 /// <returns>The default device name on the system.</returns> 76 private static string GetDeviceDefaultName() 77 { 78 return Constants.DefaultDeviceInputName; 79 } 80 81 /// <summary> 82 /// Check if a given configuration and device name is valid on the system. 83 /// </summary> 84 /// <param name="configuration">The configuration to check.</param> 85 /// <param name="deviceName">The device name to check.</param> 86 /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns> 87 private static ResultCode IsConfigurationValid(ref AudioInputConfiguration configuration, string deviceName) 88 { 89 if (deviceName.Length != 0 && !deviceName.Equals(GetDeviceDefaultName())) 90 { 91 return ResultCode.DeviceNotFound; 92 } 93 94 if (configuration.SampleRate != 0 && configuration.SampleRate != Constants.TargetSampleRate) 95 { 96 return ResultCode.UnsupportedSampleRate; 97 } 98 99 if (configuration.ChannelCount != 0 && configuration.ChannelCount != 1 && configuration.ChannelCount != 2 && configuration.ChannelCount != 6) 100 { 101 return ResultCode.UnsupportedChannelConfiguration; 102 } 103 104 return ResultCode.Success; 105 } 106 107 /// <summary> 108 /// Get the released buffer event. 109 /// </summary> 110 /// <returns>The released buffer event</returns> 111 public IWritableEvent RegisterBufferEvent() 112 { 113 lock (_parentLock) 114 { 115 return _session.GetBufferEvent(); 116 } 117 } 118 119 /// <summary> 120 /// Update the <see cref="AudioInputSystem"/>. 121 /// </summary> 122 public void Update() 123 { 124 lock (_parentLock) 125 { 126 _session.Update(); 127 } 128 } 129 130 /// <summary> 131 /// Get the id of this session. 132 /// </summary> 133 /// <returns>The id of this session</returns> 134 public int GetSessionId() 135 { 136 return _sessionId; 137 } 138 139 /// <summary> 140 /// Initialize the <see cref="AudioInputSystem"/>. 141 /// </summary> 142 /// <param name="inputDeviceName">The input device name wanted by the user</param> 143 /// <param name="sampleFormat">The sample format to use</param> 144 /// <param name="parameter">The user configuration</param> 145 /// <param name="sessionId">The session id associated to this <see cref="AudioInputSystem"/></param> 146 /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns> 147 public ResultCode Initialize(string inputDeviceName, SampleFormat sampleFormat, ref AudioInputConfiguration parameter, int sessionId) 148 { 149 _sessionId = sessionId; 150 151 ResultCode result = IsConfigurationValid(ref parameter, inputDeviceName); 152 153 if (result == ResultCode.Success) 154 { 155 if (inputDeviceName.Length == 0) 156 { 157 DeviceName = GetDeviceDefaultName(); 158 } 159 else 160 { 161 DeviceName = inputDeviceName; 162 } 163 164 if (parameter.ChannelCount == 6) 165 { 166 ChannelCount = 6; 167 } 168 else 169 { 170 ChannelCount = 2; 171 } 172 173 SampleFormat = sampleFormat; 174 SampleRate = Constants.TargetSampleRate; 175 } 176 177 return result; 178 } 179 180 /// <summary> 181 /// Append a new audio buffer to the audio input. 182 /// </summary> 183 /// <param name="bufferTag">The unique tag of this buffer.</param> 184 /// <param name="userBuffer">The buffer informations.</param> 185 /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns> 186 public ResultCode AppendBuffer(ulong bufferTag, ref AudioUserBuffer userBuffer) 187 { 188 lock (_parentLock) 189 { 190 AudioBuffer buffer = new() 191 { 192 BufferTag = bufferTag, 193 DataPointer = userBuffer.Data, 194 DataSize = userBuffer.DataSize, 195 }; 196 197 if (_session.AppendBuffer(buffer)) 198 { 199 return ResultCode.Success; 200 } 201 202 return ResultCode.BufferRingFull; 203 } 204 } 205 206 /// <summary> 207 /// Append a new audio buffer to the audio input. 208 /// </summary> 209 /// <remarks>This is broken by design, only added for completness.</remarks> 210 /// <param name="bufferTag">The unique tag of this buffer.</param> 211 /// <param name="userBuffer">The buffer informations.</param> 212 /// <param name="handle">Some unknown handle.</param> 213 /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns> 214 public ResultCode AppendUacBuffer(ulong bufferTag, ref AudioUserBuffer userBuffer, uint handle) 215 { 216 lock (_parentLock) 217 { 218 AudioBuffer buffer = new() 219 { 220 BufferTag = bufferTag, 221 DataPointer = userBuffer.Data, 222 DataSize = userBuffer.DataSize, 223 }; 224 225 if (AudioDeviceSession.AppendUacBuffer(buffer, handle)) 226 { 227 return ResultCode.Success; 228 } 229 230 return ResultCode.BufferRingFull; 231 } 232 } 233 234 /// <summary> 235 /// Get the release buffers. 236 /// </summary> 237 /// <param name="releasedBuffers">The buffer to write the release buffers</param> 238 /// <param name="releasedCount">The count of released buffers</param> 239 /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns> 240 public ResultCode GetReleasedBuffers(Span<ulong> releasedBuffers, out uint releasedCount) 241 { 242 releasedCount = 0; 243 244 // Ensure that the first entry is set to zero if no entries are returned. 245 if (releasedBuffers.Length > 0) 246 { 247 releasedBuffers[0] = 0; 248 } 249 250 lock (_parentLock) 251 { 252 for (int i = 0; i < releasedBuffers.Length; i++) 253 { 254 if (!_session.TryPopReleasedBuffer(out AudioBuffer buffer)) 255 { 256 break; 257 } 258 259 releasedBuffers[i] = buffer.BufferTag; 260 releasedCount++; 261 } 262 } 263 264 return ResultCode.Success; 265 } 266 267 /// <summary> 268 /// Get the current state of the <see cref="AudioInputSystem"/>. 269 /// </summary> 270 /// <returns>Return the curent sta\te of the <see cref="AudioInputSystem"/></returns> 271 public AudioDeviceState GetState() 272 { 273 lock (_parentLock) 274 { 275 return _session.GetState(); 276 } 277 } 278 279 /// <summary> 280 /// Start the audio session. 281 /// </summary> 282 /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns> 283 public ResultCode Start() 284 { 285 lock (_parentLock) 286 { 287 return _session.Start(); 288 } 289 } 290 291 /// <summary> 292 /// Stop the audio session. 293 /// </summary> 294 /// <returns>A <see cref="ResultCode"/> reporting an error or a success</returns> 295 public ResultCode Stop() 296 { 297 lock (_parentLock) 298 { 299 return _session.Stop(); 300 } 301 } 302 303 /// <summary> 304 /// Get the volume of the session. 305 /// </summary> 306 /// <returns>The volume of the session</returns> 307 public float GetVolume() 308 { 309 lock (_parentLock) 310 { 311 return _session.GetVolume(); 312 } 313 } 314 315 /// <summary> 316 /// Set the volume of the session. 317 /// </summary> 318 /// <param name="volume">The new volume to set</param> 319 public void SetVolume(float volume) 320 { 321 lock (_parentLock) 322 { 323 _session.SetVolume(volume); 324 } 325 } 326 327 /// <summary> 328 /// Get the count of buffer currently in use (server + driver side). 329 /// </summary> 330 /// <returns>The count of buffer currently in use</returns> 331 public uint GetBufferCount() 332 { 333 lock (_parentLock) 334 { 335 return _session.GetBufferCount(); 336 } 337 } 338 339 /// <summary> 340 /// Check if a buffer is present. 341 /// </summary> 342 /// <param name="bufferTag">The unique tag of the buffer</param> 343 /// <returns>Return true if a buffer is present</returns> 344 public bool ContainsBuffer(ulong bufferTag) 345 { 346 lock (_parentLock) 347 { 348 return _session.ContainsBuffer(bufferTag); 349 } 350 } 351 352 /// <summary> 353 /// Get the count of sample played in this session. 354 /// </summary> 355 /// <returns>The count of sample played in this session</returns> 356 public ulong GetPlayedSampleCount() 357 { 358 lock (_parentLock) 359 { 360 return _session.GetPlayedSampleCount(); 361 } 362 } 363 364 /// <summary> 365 /// Flush all buffers to the initial state. 366 /// </summary> 367 /// <returns>True if any buffers was flushed</returns> 368 public bool FlushBuffers() 369 { 370 lock (_parentLock) 371 { 372 return _session.FlushBuffers(); 373 } 374 } 375 376 public void Dispose() 377 { 378 GC.SuppressFinalize(this); 379 380 if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0) 381 { 382 Dispose(true); 383 } 384 } 385 386 protected virtual void Dispose(bool disposing) 387 { 388 if (disposing) 389 { 390 _session.Dispose(); 391 392 _manager.Unregister(this); 393 } 394 } 395 } 396 }