1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
using SoundIOSharp;
using System;
using System.Collections.Concurrent;
using System.Linq;
namespace Ryujinx.Audio.SoundIo
{
/// <summary>
/// An object pool containing a set of audio tracks
/// </summary>
internal class SoundIoAudioTrackPool : IDisposable
{
/// <summary>
/// The current size of the <see cref="SoundIoAudioTrackPool"/>
/// </summary>
private int m_Size;
/// <summary>
/// The maximum size of the <see cref="SoundIoAudioTrackPool"/>
/// </summary>
private int m_MaxSize;
/// <summary>
/// The <see cref="SoundIO"/> audio context this track pool belongs to
/// </summary>
private SoundIO m_Context;
/// <summary>
/// The <see cref="SoundIODevice"/> audio device this track pool belongs to
/// </summary>
private SoundIODevice m_Device;
/// <summary>
/// The queue that keeps track of the available <see cref="SoundIoAudioTrack"/> in the pool.
/// </summary>
private ConcurrentQueue<SoundIoAudioTrack> m_Queue;
/// <summary>
/// The dictionary providing mapping between a TrackID and <see cref="SoundIoAudioTrack"/>
/// </summary>
private ConcurrentDictionary<int, SoundIoAudioTrack> m_TrackList;
/// <summary>
/// Gets the current size of the <see cref="SoundIoAudioTrackPool"/>
/// </summary>
public int Size { get => m_Size; }
/// <summary>
/// Gets the maximum size of the <see cref="SoundIoAudioTrackPool"/>
/// </summary>
public int MaxSize { get => m_MaxSize; }
/// <summary>
/// Gets a value that indicates whether the <see cref="SoundIoAudioTrackPool"/> is empty
/// </summary>
public bool IsEmpty { get => m_Queue.IsEmpty; }
/// <summary>
/// Constructs a new instance of a <see cref="SoundIoAudioTrackPool"/> that is empty
/// </summary>
/// <param name="maxSize">The maximum amount of tracks that can be created</param>
public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize)
{
m_Size = 0;
m_Context = context;
m_Device = device;
m_MaxSize = maxSize;
m_Queue = new ConcurrentQueue<SoundIoAudioTrack>();
m_TrackList = new ConcurrentDictionary<int, SoundIoAudioTrack>();
}
/// <summary>
/// Constructs a new instance of a <see cref="SoundIoAudioTrackPool"/> that contains
/// the specified amount of <see cref="SoundIoAudioTrack"/>
/// </summary>
/// <param name="maxSize">The maximum amount of tracks that can be created</param>
/// <param name="initialCapacity">The initial number of tracks that the pool contains</param>
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<SoundIoAudioTrack>(trackCollection);
}
/// <summary>
/// Creates a new <see cref="SoundIoAudioTrack"/> with the proper AudioContext and AudioDevice
/// and the specified <paramref name="trackId" />
/// </summary>
/// <param name="trackId">The ID of the track to be created</param>
/// <returns>A new AudioTrack with the specified ID</returns>
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;
}
/// <summary>
/// Retrieves a <see cref="SoundIoAudioTrack"/> from the pool
/// </summary>
/// <returns>An AudioTrack from the pool</returns>
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++);
}
/// <summary>
/// Retrieves the <see cref="SoundIoAudioTrack"/> associated with the specified <paramref name="trackId"/> from the pool
/// </summary>
/// <param name="trackId">The ID of the track to retrieve</param>
public SoundIoAudioTrack Get(int trackId)
{
if (m_TrackList.TryGetValue(trackId, out SoundIoAudioTrack track))
{
return track;
}
return null;
}
/// <summary>
/// Attempers to get a <see cref="SoundIoAudioTrack"/> from the pool
/// </summary>
/// <param name="track">The track retrieved from the pool</param>
/// <returns>True if retrieve was successful</returns>
public bool TryGet(out SoundIoAudioTrack track)
{
track = Get();
return track != null;
}
/// <summary>
/// Attempts to get the <see cref="SoundIoAudioTrack" /> associated with the specified <paramref name="trackId"/> from the pool
/// </summary>
/// <param name="trackId">The ID of the track to retrieve</param>
/// <param name="track">The track retrieved from the pool</param>
public bool TryGet(int trackId, out SoundIoAudioTrack track)
{
return m_TrackList.TryGetValue(trackId, out track);
}
/// <summary>
/// Returns an <see cref="SoundIoAudioTrack"/> back to the pool for reuse
/// </summary>
/// <param name="track">The track to be returned to the pool</param>
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);
}
/// <summary>
/// Releases the unmanaged resources used by the <see cref="SoundIoAudioTrackPool" />
/// </summary>
public void Dispose()
{
foreach (var track in m_TrackList)
{
track.Value.Close();
track.Value.Dispose();
}
m_Size = 0;
m_Queue.Clear();
m_TrackList.Clear();
}
}
}
|