/ src / Ryujinx.Audio / Input / AudioInputSystem.cs
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  }