Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/audio_stream.h
4211 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
6
#include "common/align.h"
7
#include "common/types.h"
8
9
#include <array>
10
#include <atomic>
11
#include <memory>
12
#include <optional>
13
#include <string>
14
#include <vector>
15
16
class Error;
17
class SettingsInterface;
18
19
namespace soundtouch {
20
class SoundTouch;
21
}
22
23
enum class AudioBackend : u8
24
{
25
Null,
26
#ifndef __ANDROID__
27
Cubeb,
28
SDL,
29
#else
30
AAudio,
31
OpenSLES,
32
#endif
33
Count
34
};
35
36
enum class AudioStretchMode : u8
37
{
38
Off,
39
Resample,
40
TimeStretch,
41
Count
42
};
43
44
struct AudioStreamParameters
45
{
46
AudioStretchMode stretch_mode = DEFAULT_STRETCH_MODE;
47
bool output_latency_minimal = DEFAULT_OUTPUT_LATENCY_MINIMAL;
48
u16 output_latency_ms = DEFAULT_OUTPUT_LATENCY_MS;
49
u16 buffer_ms = DEFAULT_BUFFER_MS;
50
51
u16 stretch_sequence_length_ms = DEFAULT_STRETCH_SEQUENCE_LENGTH;
52
u16 stretch_seekwindow_ms = DEFAULT_STRETCH_SEEKWINDOW;
53
u16 stretch_overlap_ms = DEFAULT_STRETCH_OVERLAP;
54
bool stretch_use_quickseek = DEFAULT_STRETCH_USE_QUICKSEEK;
55
bool stretch_use_aa_filter = DEFAULT_STRETCH_USE_AA_FILTER;
56
57
static constexpr AudioStretchMode DEFAULT_STRETCH_MODE = AudioStretchMode::TimeStretch;
58
#ifndef __ANDROID__
59
static constexpr u16 DEFAULT_BUFFER_MS = 50;
60
static constexpr u16 DEFAULT_OUTPUT_LATENCY_MS = 20;
61
#else
62
static constexpr u16 DEFAULT_BUFFER_MS = 100;
63
static constexpr u16 DEFAULT_OUTPUT_LATENCY_MS = 20;
64
#endif
65
static constexpr bool DEFAULT_OUTPUT_LATENCY_MINIMAL = false;
66
67
static constexpr u16 DEFAULT_STRETCH_SEQUENCE_LENGTH = 30;
68
static constexpr u16 DEFAULT_STRETCH_SEEKWINDOW = 20;
69
static constexpr u16 DEFAULT_STRETCH_OVERLAP = 10;
70
71
static constexpr bool DEFAULT_STRETCH_USE_QUICKSEEK = false;
72
static constexpr bool DEFAULT_STRETCH_USE_AA_FILTER = false;
73
74
void Load(const SettingsInterface& si, const char* section);
75
void Save(SettingsInterface& si, const char* section) const;
76
void Clear(SettingsInterface& si, const char* section);
77
78
bool operator==(const AudioStreamParameters& rhs) const;
79
bool operator!=(const AudioStreamParameters& rhs) const;
80
};
81
82
class AudioStream
83
{
84
public:
85
using SampleType = s16;
86
87
static constexpr u32 NUM_CHANNELS = 2;
88
static constexpr u32 CHUNK_SIZE = 64;
89
90
#ifndef __ANDROID__
91
static constexpr AudioBackend DEFAULT_BACKEND = AudioBackend::Cubeb;
92
#else
93
static constexpr AudioBackend DEFAULT_BACKEND = AudioBackend::AAudio;
94
#endif
95
96
struct DeviceInfo
97
{
98
std::string name;
99
std::string display_name;
100
u32 minimum_latency_frames;
101
102
DeviceInfo(std::string name_, std::string display_name_, u32 minimum_latency_);
103
~DeviceInfo();
104
};
105
106
public:
107
virtual ~AudioStream();
108
109
static u32 GetAlignedBufferSize(u32 size);
110
static u32 GetBufferSizeForMS(u32 sample_rate, u32 ms);
111
static u32 GetMSForBufferSize(u32 sample_rate, u32 buffer_size);
112
113
static std::optional<AudioBackend> ParseBackendName(const char* str);
114
static const char* GetBackendName(AudioBackend backend);
115
static const char* GetBackendDisplayName(AudioBackend backend);
116
117
static const char* GetStretchModeName(AudioStretchMode mode);
118
static const char* GetStretchModeDisplayName(AudioStretchMode mode);
119
static std::optional<AudioStretchMode> ParseStretchMode(const char* name);
120
121
ALWAYS_INLINE u32 GetSampleRate() const { return m_sample_rate; }
122
ALWAYS_INLINE u32 GetBufferSize() const { return m_buffer_size; }
123
ALWAYS_INLINE u32 GetTargetBufferSize() const { return m_target_buffer_size; }
124
ALWAYS_INLINE u32 GetOutputVolume() const { return m_volume; }
125
ALWAYS_INLINE float GetNominalTempo() const { return m_nominal_rate; }
126
ALWAYS_INLINE bool IsPaused() const { return m_paused; }
127
128
u32 GetBufferedFramesRelaxed() const;
129
130
/// Temporarily pauses the stream, preventing it from requesting data.
131
virtual void SetPaused(bool paused);
132
133
void SetOutputVolume(u32 volume);
134
135
void BeginWrite(SampleType** buffer_ptr, u32* num_frames);
136
void EndWrite(u32 num_frames);
137
138
void EmptyBuffer();
139
140
/// Nominal rate is used for both resampling and timestretching, input samples are assumed to be this amount faster
141
/// than the sample rate.
142
void SetNominalRate(float tempo);
143
144
void SetStretchMode(AudioStretchMode mode);
145
146
/// Wipes out the time stretching buffer, call when reducing target speed.
147
void EmptyStretchBuffers();
148
149
static std::vector<std::pair<std::string, std::string>> GetDriverNames(AudioBackend backend);
150
static std::vector<DeviceInfo> GetOutputDevices(AudioBackend backend, const char* driver, u32 sample_rate);
151
static std::unique_ptr<AudioStream> CreateStream(AudioBackend backend, u32 sample_rate,
152
const AudioStreamParameters& parameters, const char* driver_name,
153
const char* device_name, Error* error = nullptr);
154
static std::unique_ptr<AudioStream> CreateNullStream(u32 sample_rate, u32 buffer_ms);
155
156
protected:
157
AudioStream(u32 sample_rate, const AudioStreamParameters& parameters);
158
void BaseInitialize();
159
160
void ReadFrames(SampleType* samples, u32 num_frames);
161
162
u32 m_sample_rate = 0;
163
u32 m_volume = 100;
164
AudioStreamParameters m_parameters;
165
bool m_stretch_inactive = false;
166
bool m_filling = false;
167
bool m_paused = false;
168
169
private:
170
static constexpr u32 AVERAGING_BUFFER_SIZE = 256;
171
static constexpr u32 STRETCH_RESET_THRESHOLD = 5;
172
173
#ifndef __ANDROID__
174
static std::vector<std::pair<std::string, std::string>> GetCubebDriverNames();
175
static std::vector<DeviceInfo> GetCubebOutputDevices(const char* driver, u32 sample_rate);
176
static std::unique_ptr<AudioStream> CreateCubebAudioStream(u32 sample_rate, const AudioStreamParameters& parameters,
177
const char* driver_name, const char* device_name,
178
Error* error);
179
static std::unique_ptr<AudioStream> CreateSDLAudioStream(u32 sample_rate, const AudioStreamParameters& parameters,
180
Error* error);
181
#else
182
static std::unique_ptr<AudioStream> CreateAAudioAudioStream(u32 sample_rate, const AudioStreamParameters& parameters,
183
Error* error);
184
static std::unique_ptr<AudioStream> CreateOpenSLESAudioStream(u32 sample_rate,
185
const AudioStreamParameters& parameters, Error* error);
186
#endif
187
188
ALWAYS_INLINE bool IsStretchEnabled() const { return m_parameters.stretch_mode != AudioStretchMode::Off; }
189
190
void AllocateBuffer();
191
void DestroyBuffer();
192
193
void InternalWriteFrames(SampleType* samples, u32 num_frames);
194
195
void StretchAllocate();
196
void StretchDestroy();
197
void StretchWriteBlock(const float* block);
198
void StretchUnderrun();
199
void StretchOverrun();
200
201
float AddAndGetAverageTempo(float val);
202
void UpdateStretchTempo();
203
204
u32 m_buffer_size = 0;
205
Common::unique_aligned_ptr<s16[]> m_buffer;
206
207
std::atomic<u32> m_rpos{0};
208
std::atomic<u32> m_wpos{0};
209
210
void* m_soundtouch = nullptr;
211
212
u32 m_target_buffer_size = 0;
213
u32 m_stretch_reset = STRETCH_RESET_THRESHOLD;
214
u64 m_stretch_reset_time = 0;
215
216
u32 m_stretch_ok_count = 0;
217
float m_nominal_rate = 1.0f;
218
float m_dynamic_target_usage = 0.0f;
219
220
u32 m_average_position = 0;
221
u32 m_average_available = 0;
222
u32 m_staging_buffer_pos = 0;
223
224
std::array<float, AVERAGING_BUFFER_SIZE> m_average_fullness = {};
225
226
// temporary staging buffer, used for timestretching
227
Common::unique_aligned_ptr<s16[]> m_staging_buffer;
228
229
// float buffer, soundtouch only accepts float samples as input
230
Common::unique_aligned_ptr<float[]> m_float_buffer;
231
};
232
233