2021-02-26 00:11:56 +00:00
using System ;
using System.Threading ;
namespace Ryujinx.Audio
{
/// <summary>
/// Manage audio input and output system.
/// </summary>
public class AudioManager : IDisposable
{
/// <summary>
/// Lock used to control the waiters registration.
/// </summary>
2023-06-15 00:34:55 +00:00
private readonly object _lock = new ( ) ;
2021-02-26 00:11:56 +00:00
/// <summary>
/// Events signaled when the driver played audio buffers.
/// </summary>
2023-07-01 23:27:18 +00:00
private readonly ManualResetEvent [ ] _updateRequiredEvents ;
2021-02-26 00:11:56 +00:00
/// <summary>
/// Action to execute when the driver played audio buffers.
/// </summary>
2023-07-01 23:27:18 +00:00
private readonly Action [ ] _actions ;
2021-02-26 00:11:56 +00:00
/// <summary>
/// The worker thread in charge of handling sessions update.
/// </summary>
2023-07-01 23:27:18 +00:00
private readonly Thread _workerThread ;
2021-02-26 00:11:56 +00:00
2021-09-11 20:08:25 +00:00
private bool _isRunning ;
2021-02-26 00:11:56 +00:00
/// <summary>
/// Create a new <see cref="AudioManager"/>.
/// </summary>
public AudioManager ( )
{
_updateRequiredEvents = new ManualResetEvent [ 2 ] ;
_actions = new Action [ 2 ] ;
2021-09-11 20:08:25 +00:00
_isRunning = false ;
2021-02-26 00:11:56 +00:00
// Termination event.
_updateRequiredEvents [ 1 ] = new ManualResetEvent ( false ) ;
_workerThread = new Thread ( Update )
{
2023-07-01 23:27:18 +00:00
Name = "AudioManager.Worker" ,
2021-02-26 00:11:56 +00:00
} ;
}
/// <summary>
/// Start the <see cref="AudioManager"/>.
/// </summary>
public void Start ( )
{
if ( _workerThread . IsAlive )
{
throw new InvalidOperationException ( ) ;
}
2021-09-11 20:08:25 +00:00
_isRunning = true ;
2021-02-26 00:11:56 +00:00
_workerThread . Start ( ) ;
}
/// <summary>
/// Initialize update handlers.
/// </summary>
/// <param name="updatedRequiredEvent ">The driver event that will get signaled by the device driver when an audio buffer finished playing/being captured</param>
/// <param name="outputCallback">The callback to call when an audio buffer finished playing</param>
/// <param name="inputCallback">The callback to call when an audio buffer was captured</param>
public void Initialize ( ManualResetEvent updatedRequiredEvent , Action outputCallback , Action inputCallback )
{
lock ( _lock )
{
_updateRequiredEvents [ 0 ] = updatedRequiredEvent ;
_actions [ 0 ] = outputCallback ;
_actions [ 1 ] = inputCallback ;
}
}
/// <summary>
/// Entrypoint of the <see cref="_workerThread"/> in charge of updating the <see cref="AudioManager"/>.
/// </summary>
private void Update ( )
{
2021-09-11 20:08:25 +00:00
while ( _isRunning )
2021-02-26 00:11:56 +00:00
{
int index = WaitHandle . WaitAny ( _updateRequiredEvents ) ;
// Last index is here to indicate thread termination.
if ( index + 1 = = _updateRequiredEvents . Length )
{
break ;
}
lock ( _lock )
{
foreach ( Action action in _actions )
{
action ? . Invoke ( ) ;
}
_updateRequiredEvents [ 0 ] . Reset ( ) ;
}
}
}
2021-09-11 20:08:25 +00:00
/// <summary>
/// Stop updating the <see cref="AudioManager"/> without stopping the worker thread.
/// </summary>
public void StopUpdates ( )
{
_isRunning = false ;
}
2021-02-26 00:11:56 +00:00
public void Dispose ( )
{
2023-07-01 23:27:18 +00:00
GC . SuppressFinalize ( this ) ;
2021-02-26 00:11:56 +00:00
Dispose ( true ) ;
}
protected virtual void Dispose ( bool disposing )
{
if ( disposing )
{
_updateRequiredEvents [ 1 ] . Set ( ) ;
_workerThread . Join ( ) ;
_updateRequiredEvents [ 1 ] . Dispose ( ) ;
}
}
}
2023-07-01 23:27:18 +00:00
}