// // Copyright (c) 2019-2021 Ryujinx // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . // 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 object _lock = new object(); /// /// 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; /// /// Create a new . /// public AudioManager() { _updateRequiredEvents = new ManualResetEvent[2]; _actions = new Action[2]; // 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(); } _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 (true) { 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(); } } } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if (disposing) { _updateRequiredEvents[1].Set(); _workerThread.Join(); _updateRequiredEvents[1].Dispose(); } } } }