Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/core_audio_stream.h
7197 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
6
#include "audio_stream.h"
7
8
#include "common/align.h"
9
10
#include <array>
11
#include <atomic>
12
#include <memory>
13
#include <optional>
14
15
class Error;
16
class SettingsInterface;
17
18
enum class AudioStretchMode : u8
19
{
20
Off,
21
Resample,
22
TimeStretch,
23
Count
24
};
25
26
struct AudioStreamParameters
27
{
28
AudioStretchMode stretch_mode = DEFAULT_STRETCH_MODE;
29
bool output_latency_minimal = DEFAULT_OUTPUT_LATENCY_MINIMAL;
30
u16 output_latency_ms = DEFAULT_OUTPUT_LATENCY_MS;
31
u16 buffer_ms = DEFAULT_BUFFER_MS;
32
33
u16 stretch_sequence_length_ms = DEFAULT_STRETCH_SEQUENCE_LENGTH;
34
u16 stretch_seekwindow_ms = DEFAULT_STRETCH_SEEKWINDOW;
35
u16 stretch_overlap_ms = DEFAULT_STRETCH_OVERLAP;
36
bool stretch_use_quickseek = DEFAULT_STRETCH_USE_QUICKSEEK;
37
bool stretch_use_aa_filter = DEFAULT_STRETCH_USE_AA_FILTER;
38
39
static constexpr AudioStretchMode DEFAULT_STRETCH_MODE = AudioStretchMode::TimeStretch;
40
#ifndef __ANDROID__
41
static constexpr u16 DEFAULT_BUFFER_MS = 50;
42
static constexpr u16 DEFAULT_OUTPUT_LATENCY_MS = 20;
43
#else
44
static constexpr u16 DEFAULT_BUFFER_MS = 100;
45
static constexpr u16 DEFAULT_OUTPUT_LATENCY_MS = 20;
46
#endif
47
static constexpr bool DEFAULT_OUTPUT_LATENCY_MINIMAL = false;
48
49
static constexpr u16 DEFAULT_STRETCH_SEQUENCE_LENGTH = 30;
50
static constexpr u16 DEFAULT_STRETCH_SEEKWINDOW = 20;
51
static constexpr u16 DEFAULT_STRETCH_OVERLAP = 10;
52
53
static constexpr bool DEFAULT_STRETCH_USE_QUICKSEEK = false;
54
static constexpr bool DEFAULT_STRETCH_USE_AA_FILTER = false;
55
56
void Load(const SettingsInterface& si, const char* section);
57
void Save(SettingsInterface& si, const char* section) const;
58
void Clear(SettingsInterface& si, const char* section);
59
60
bool operator==(const AudioStreamParameters& rhs) const;
61
bool operator!=(const AudioStreamParameters& rhs) const;
62
};
63
64
class CoreAudioStream final : private AudioStreamSource
65
{
66
public:
67
using SampleType = AudioStreamSource::SampleType;
68
69
static constexpr u32 NUM_CHANNELS = 2;
70
static constexpr u32 CHUNK_SIZE = 64;
71
72
CoreAudioStream();
73
~CoreAudioStream();
74
75
static u32 GetAlignedBufferSize(u32 size);
76
static u32 GetBufferSizeForMS(u32 sample_rate, u32 ms);
77
static u32 GetMSForBufferSize(u32 sample_rate, u32 buffer_size);
78
79
static const char* GetStretchModeName(AudioStretchMode mode);
80
static const char* GetStretchModeDisplayName(AudioStretchMode mode);
81
static std::optional<AudioStretchMode> ParseStretchMode(const char* name);
82
83
ALWAYS_INLINE u32 GetSampleRate() const { return m_sample_rate; }
84
ALWAYS_INLINE u32 GetBufferSize() const { return m_buffer_size; }
85
ALWAYS_INLINE u32 GetTargetBufferSize() const { return m_target_buffer_size; }
86
ALWAYS_INLINE u32 GetOutputVolume() const { return m_volume; }
87
ALWAYS_INLINE float GetNominalTempo() const { return m_nominal_rate; }
88
ALWAYS_INLINE bool IsPaused() const { return m_paused; }
89
90
u32 GetBufferedFramesRelaxed() const;
91
92
/// Creation/destruction.
93
bool Initialize(AudioBackend backend, u32 sample_rate, const AudioStreamParameters& params,
94
std::string_view driver_name, std::string_view device_name, Error* error);
95
void Destroy();
96
97
/// Updates stream parameters without recreating the stream.
98
/// NOTE: Cannot handle changes in output latency.
99
void UpdateParameters(const AudioStreamParameters& params);
100
101
/// Temporarily pauses the stream, preventing it from requesting data.
102
void SetPaused(bool paused);
103
104
void SetOutputVolume(u32 volume);
105
106
void BeginWrite(SampleType** buffer_ptr, u32* num_frames);
107
void EndWrite(u32 num_frames);
108
109
void EmptyBuffer();
110
111
/// Nominal rate is used for both resampling and timestretching, input samples are assumed to be this amount faster
112
/// than the sample rate.
113
void SetNominalRate(float tempo);
114
115
void SetStretchMode(AudioStretchMode mode);
116
117
/// Wipes out the time stretching buffer, call when reducing target speed.
118
void EmptyStretchBuffers();
119
120
private:
121
static constexpr u32 AVERAGING_BUFFER_SIZE = 256;
122
static constexpr u32 STRETCH_RESET_THRESHOLD = 5;
123
124
ALWAYS_INLINE bool IsStretchEnabled() const { return m_parameters.stretch_mode != AudioStretchMode::Off; }
125
126
void AllocateBuffer();
127
void DestroyBuffer();
128
129
void InternalWriteFrames(SampleType* samples, u32 num_frames);
130
131
void StretchAllocate();
132
void StretchUpdateParameters(const AudioStreamParameters& params);
133
void StretchDestroy();
134
void StretchWriteBlock(const float* block);
135
void StretchUnderrun();
136
void StretchOverrun();
137
138
float AddAndGetAverageTempo(float val);
139
void UpdateStretchTempo();
140
141
void ReadFrames(SampleType* samples, u32 num_frames) override;
142
143
std::unique_ptr<AudioStream> m_stream;
144
u32 m_sample_rate = 0;
145
u32 m_volume = 0;
146
AudioStreamParameters m_parameters;
147
bool m_stretch_inactive = false;
148
bool m_filling = false;
149
bool m_paused = false;
150
151
u32 m_buffer_size = 0;
152
Common::unique_aligned_ptr<s16[]> m_buffer;
153
154
// temporary staging buffer, used for timestretching
155
Common::unique_aligned_ptr<s16[]> m_staging_buffer;
156
157
// float buffer, soundtouch only accepts float samples as input
158
Common::unique_aligned_ptr<float[]> m_float_buffer;
159
160
std::atomic<u32> m_rpos{0};
161
std::atomic<u32> m_wpos{0};
162
163
void* m_soundtouch = nullptr;
164
165
u32 m_target_buffer_size = 0;
166
u32 m_stretch_reset = STRETCH_RESET_THRESHOLD;
167
u64 m_stretch_reset_time = 0;
168
169
u32 m_stretch_ok_count = 0;
170
float m_nominal_rate = 1.0f;
171
float m_dynamic_target_usage = 0.0f;
172
173
u32 m_average_position = 0;
174
u32 m_average_available = 0;
175
u32 m_staging_buffer_pos = 0;
176
177
std::array<float, AVERAGING_BUFFER_SIZE> m_average_fullness = {};
178
};
179
180