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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <atomic>
#include <memory>
#include <span>
#include <vector>
#include "audio_core/common/common.h"
#include "common/common_types.h"
#include "common/reader_writer_queue.h"
#include "common/ring_buffer.h"
namespace Core {
class System;
} // namespace Core
namespace AudioCore::Sink {
enum class StreamType {
Render,
Out,
In,
};
struct SinkBuffer {
u64 frames;
u64 frames_played;
u64 tag;
bool consumed;
};
/**
* Contains a real backend stream for outputting samples to hardware,
* created only via a Sink (See Sink::AcquireSinkStream).
*
* Accepts a SinkBuffer and samples in PCM16 format to be output (see AppendBuffer).
* Appended buffers act as a FIFO queue, and will be held until played.
* You should regularly call IsBufferConsumed with the unique SinkBuffer tag to check if the buffer
* has been consumed.
*
* Since these are a FIFO queue, IsBufferConsumed must be checked in the same order buffers were
* appended, skipping a buffer will result in the queue getting stuck, and all following buffers to
* never release.
*
* If the buffers appear to be stuck, you can stop and re-open an IAudioIn/IAudioOut service (this
* is what games do), or call ClearQueue to flush all of the buffers without a full restart.
*/
class SinkStream {
public:
explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {}
virtual ~SinkStream() {
Unstall();
}
/**
* Finalize the sink stream.
*/
virtual void Finalize() {}
/**
* Start the sink stream.
*
* @param resume - Set to true if this is resuming the stream a previously-active stream.
* Default false.
*/
virtual void Start(bool resume = false) {}
/**
* Stop the sink stream.
*/
virtual void Stop() {}
/**
* Check if the stream is paused.
*
* @return True if paused, otherwise false.
*/
bool IsPaused() const {
return paused;
}
/**
* Get the number of system channels in this stream.
*
* @return Number of system channels.
*/
u32 GetSystemChannels() const {
return system_channels;
}
/**
* Set the number of channels the system expects.
*
* @param channels - New number of system channels.
*/
void SetSystemChannels(u32 channels) {
system_channels = channels;
}
/**
* Get the number of channels the hardware supports.
*
* @return Number of channels supported.
*/
u32 GetDeviceChannels() const {
return device_channels;
}
/**
* Get the system volume.
*
* @return The current system volume.
*/
f32 GetSystemVolume() const {
return system_volume;
}
/**
* Get the device volume.
*
* @return The current device volume.
*/
f32 GetDeviceVolume() const {
return device_volume;
}
/**
* Set the system volume.
*
* @param volume_ - The new system volume.
*/
void SetSystemVolume(f32 volume_) {
system_volume = volume_;
}
/**
* Set the device volume.
*
* @param volume_ - The new device volume.
*/
void SetDeviceVolume(f32 volume_) {
device_volume = volume_;
}
/**
* Get the number of queued audio buffers.
*
* @return The number of queued buffers.
*/
u32 GetQueueSize() const {
return queued_buffers.load();
}
/**
* Set the maximum buffer queue size.
*/
void SetRingSize(u32 ring_size) {
max_queue_size = ring_size;
}
/**
* Append a new buffer and its samples to a waiting queue to play.
*
* @param buffer - Audio buffer information to be queued.
* @param samples - The s16 samples to be queue for playback.
*/
virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples);
/**
* Release a buffer. Audio In only, will fill a buffer with recorded samples.
*
* @param num_samples - Maximum number of samples to receive.
* @return Vector of recorded samples. May have fewer than num_samples.
*/
virtual std::vector<s16> ReleaseBuffer(u64 num_samples);
/**
* Empty out the buffer queue.
*/
void ClearQueue();
/**
* Callback for AudioIn.
*
* @param input_buffer - Input buffer to be filled with samples.
* @param num_frames - Number of frames to be filled.
*/
void ProcessAudioIn(std::span<const s16> input_buffer, std::size_t num_frames);
/**
* Callback for AudioOut and AudioRenderer.
*
* @param output_buffer - Output buffer to be filled with samples.
* @param num_frames - Number of frames to be filled.
*/
void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames);
/**
* Stall core processes if the audio thread falls too far behind.
*/
void Stall();
/**
* Unstall core processes.
*/
void Unstall();
protected:
/// Core system
Core::System& system;
/// Type of this stream
StreamType type;
/// Set by the audio render/in/out system which uses this stream
u32 system_channels{2};
/// Channels supported by hardware
u32 device_channels{2};
/// Is this stream currently paused?
std::atomic<bool> paused{true};
/// Name of this stream
std::string name{};
private:
/// Ring buffer of the samples waiting to be played or consumed
Common::RingBuffer<s16, 0x10000> samples_buffer;
/// Audio buffers queued and waiting to play
Common::ReaderWriterQueue<SinkBuffer> queue;
/// The currently-playing audio buffer
SinkBuffer playing_buffer{};
/// The last played (or received) frame of audio, used when the callback underruns
std::array<s16, MaxChannels> last_frame{};
/// Number of buffers waiting to be played
std::atomic<u32> queued_buffers{};
/// The ring size for audio out buffers (usually 4, rarely 2 or 8)
u32 max_queue_size{};
/// Set by the audio render/in/out system which uses this stream
f32 system_volume{1.0f};
/// Set via IAudioDevice service calls
f32 device_volume{1.0f};
/// True if coretiming has been stalled
bool stalled{false};
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;
} // namespace AudioCore::Sink
|