using System; using System.Threading; namespace Ryujinx.Audio { /// /// Manage audio input and output system. /// public class AudioManager : IDisposable { /// /// Lock used to control the waiters registration. /// private readonly object _lock = new(); /// /// Events signaled when the driver played audio buffers. /// private ManualResetEvent[] _updateRequiredEvents; /// /// Action to execute when the driver played audio buffers. /// private Action[] _actions; /// /// The worker thread in charge of handling sessions update. /// private Thread _workerThread; private bool _isRunning; /// /// Create a new . /// public AudioManager() { _updateRequiredEvents = new ManualResetEvent[2]; _actions = new Action[2]; _isRunning = false; // Termination event. _updateRequiredEvents[1] = new ManualResetEvent(false); _workerThread = new Thread(Update) { Name = "AudioManager.Worker" }; } /// /// Start the . /// public void Start() { if (_workerThread.IsAlive) { throw new InvalidOperationException(); } _isRunning = true; _workerThread.Start(); } /// /// Initialize update handlers. /// /// The driver event that will get signaled by the device driver when an audio buffer finished playing/being captured /// The callback to call when an audio buffer finished playing /// The callback to call when an audio buffer was captured public void Initialize(ManualResetEvent updatedRequiredEvent, Action outputCallback, Action inputCallback) { lock (_lock) { _updateRequiredEvents[0] = updatedRequiredEvent; _actions[0] = outputCallback; _actions[1] = inputCallback; } } /// /// Entrypoint of the in charge of updating the . /// private void Update() { while (_isRunning) { 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(); } } } /// /// Stop updating the without stopping the worker thread. /// public void StopUpdates() { _isRunning = false; } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing) { _updateRequiredEvents[1].Set(); _workerThread.Join(); _updateRequiredEvents[1].Dispose(); } } } }