using SoundIOSharp; using System; using System.Collections.Concurrent; using System.Linq; namespace Ryujinx.Audio.SoundIo { /// /// An object pool containing a set of audio tracks /// internal class SoundIoAudioTrackPool : IDisposable { /// /// The current size of the /// private int m_Size; /// /// The maximum size of the /// private int m_MaxSize; /// /// The audio context this track pool belongs to /// private SoundIO m_Context; /// /// The audio device this track pool belongs to /// private SoundIODevice m_Device; /// /// The queue that keeps track of the available in the pool. /// private ConcurrentQueue m_Queue; /// /// The dictionary providing mapping between a TrackID and /// private ConcurrentDictionary m_TrackList; /// /// Gets the current size of the /// public int Size { get => m_Size; } /// /// Gets the maximum size of the /// public int MaxSize { get => m_MaxSize; } /// /// Gets a value that indicates whether the is empty /// public bool IsEmpty { get => m_Queue.IsEmpty; } /// /// Constructs a new instance of a that is empty /// /// The maximum amount of tracks that can be created public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize) { m_Size = 0; m_Context = context; m_Device = device; m_MaxSize = maxSize; m_Queue = new ConcurrentQueue(); m_TrackList = new ConcurrentDictionary(); } /// /// Constructs a new instance of a that contains /// the specified amount of /// /// The maximum amount of tracks that can be created /// The initial number of tracks that the pool contains public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize, int initialCapacity) : this(context, device, maxSize) { var trackCollection = Enumerable.Range(0, initialCapacity) .Select(TrackFactory); m_Size = initialCapacity; m_Queue = new ConcurrentQueue(trackCollection); } /// /// Creates a new with the proper AudioContext and AudioDevice /// and the specified /// /// The ID of the track to be created /// A new AudioTrack with the specified ID private SoundIoAudioTrack TrackFactory(int trackId) { // Create a new AudioTrack SoundIoAudioTrack track = new SoundIoAudioTrack(trackId, m_Context, m_Device); // Keep track of issued tracks m_TrackList[trackId] = track; return track; } /// /// Retrieves a from the pool /// /// An AudioTrack from the pool public SoundIoAudioTrack Get() { // If we have a track available, reuse it if (m_Queue.TryDequeue(out SoundIoAudioTrack track)) { return track; } // Have we reached the maximum size of our pool? if (m_Size >= m_MaxSize) { return null; } // We don't have any pooled tracks, so create a new one return TrackFactory(m_Size++); } /// /// Retrieves the associated with the specified from the pool /// /// The ID of the track to retrieve public SoundIoAudioTrack Get(int trackId) { if (m_TrackList.TryGetValue(trackId, out SoundIoAudioTrack track)) { return track; } return null; } /// /// Attempers to get a from the pool /// /// The track retrieved from the pool /// True if retrieve was successful public bool TryGet(out SoundIoAudioTrack track) { track = Get(); return track != null; } /// /// Attempts to get the associated with the specified from the pool /// /// The ID of the track to retrieve /// The track retrieved from the pool public bool TryGet(int trackId, out SoundIoAudioTrack track) { return m_TrackList.TryGetValue(trackId, out track); } /// /// Returns an back to the pool for reuse /// /// The track to be returned to the pool public void Put(SoundIoAudioTrack track) { // Ensure the track is disposed and not playing audio track.Close(); // Requeue the track for reuse later m_Queue.Enqueue(track); } /// /// Releases the unmanaged resources used by the /// public void Dispose() { foreach (var track in m_TrackList) { track.Value.Close(); track.Value.Dispose(); } m_Size = 0; m_Queue.Clear(); m_TrackList.Clear(); } } }