diff options
| author | 2023-04-01 14:34:59 -0700 | |
|---|---|---|
| committer | 2023-04-01 14:34:59 -0700 | |
| commit | 6775a6ee0260d9d7bedfa3922e32301e452f833f (patch) | |
| tree | 88ffbcf64f99feeefa4ce5f3b12131de2bfe4c11 /src/audio_core | |
| parent | Merge pull request #10006 from german77/profile_select (diff) | |
| parent | audio_core: No longer stall when sink queue is full (diff) | |
| download | yuzu-6775a6ee0260d9d7bedfa3922e32301e452f833f.tar.gz yuzu-6775a6ee0260d9d7bedfa3922e32301e452f833f.tar.xz yuzu-6775a6ee0260d9d7bedfa3922e32301e452f833f.zip | |
Merge pull request #9969 from bylaws/master
Audio synchronisation improvements
Diffstat (limited to 'src/audio_core')
| -rw-r--r-- | src/audio_core/device/device_session.cpp | 3 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/audio_renderer.cpp | 2 | ||||
| -rw-r--r-- | src/audio_core/renderer/adsp/audio_renderer.h | 1 | ||||
| -rw-r--r-- | src/audio_core/renderer/system_manager.cpp | 19 | ||||
| -rw-r--r-- | src/audio_core/renderer/system_manager.h | 7 | ||||
| -rw-r--r-- | src/audio_core/sink/cubeb_sink.cpp | 4 | ||||
| -rw-r--r-- | src/audio_core/sink/sdl2_sink.cpp | 2 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_stream.cpp | 67 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_stream.h | 29 |
9 files changed, 55 insertions, 79 deletions
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 043ce8875..b5c0ef0e6 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp | |||
| @@ -121,8 +121,7 @@ u64 DeviceSession::GetPlayedSampleCount() const { | |||
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | std::optional<std::chrono::nanoseconds> DeviceSession::ThreadFunc() { | 123 | std::optional<std::chrono::nanoseconds> DeviceSession::ThreadFunc() { |
| 124 | // Add 5ms of samples at a 48K sample rate. | 124 | played_sample_count = stream->GetExpectedPlayedSampleCount(); |
| 125 | played_sample_count += 48'000 * INCREMENT_TIME / 1s; | ||
| 126 | if (type == Sink::StreamType::Out) { | 125 | if (type == Sink::StreamType::Out) { |
| 127 | system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioOutManager, true); | 126 | system.AudioCore().GetAudioManager().SetEvent(Event::Type::AudioOutManager, true); |
| 128 | } else { | 127 | } else { |
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp index 42b4b167a..503f40349 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.cpp +++ b/src/audio_core/renderer/adsp/audio_renderer.cpp | |||
| @@ -189,6 +189,8 @@ void AudioRenderer::ThreadFunc() { | |||
| 189 | max_time = std::min(command_buffer.time_limit, max_time); | 189 | max_time = std::min(command_buffer.time_limit, max_time); |
| 190 | command_list_processor.SetProcessTimeMax(max_time); | 190 | command_list_processor.SetProcessTimeMax(max_time); |
| 191 | 191 | ||
| 192 | streams[index]->WaitFreeSpace(); | ||
| 193 | |||
| 192 | // Process the command list | 194 | // Process the command list |
| 193 | { | 195 | { |
| 194 | MICROPROFILE_SCOPE(Audio_Renderer); | 196 | MICROPROFILE_SCOPE(Audio_Renderer); |
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h index 151f38c1b..85ce6a269 100644 --- a/src/audio_core/renderer/adsp/audio_renderer.h +++ b/src/audio_core/renderer/adsp/audio_renderer.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "audio_core/renderer/adsp/command_buffer.h" | 10 | #include "audio_core/renderer/adsp/command_buffer.h" |
| 11 | #include "audio_core/renderer/adsp/command_list_processor.h" | 11 | #include "audio_core/renderer/adsp/command_list_processor.h" |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/polyfill_thread.h" | ||
| 13 | #include "common/reader_writer_queue.h" | 14 | #include "common/reader_writer_queue.h" |
| 14 | #include "common/thread.h" | 15 | #include "common/thread.h" |
| 15 | 16 | ||
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp index ce631f810..07d8ed093 100644 --- a/src/audio_core/renderer/system_manager.cpp +++ b/src/audio_core/renderer/system_manager.cpp | |||
| @@ -15,14 +15,9 @@ MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager", | |||
| 15 | MP_RGB(60, 19, 97)); | 15 | MP_RGB(60, 19, 97)); |
| 16 | 16 | ||
| 17 | namespace AudioCore::AudioRenderer { | 17 | namespace AudioCore::AudioRenderer { |
| 18 | constexpr std::chrono::nanoseconds RENDER_TIME{5'000'000UL}; | ||
| 19 | 18 | ||
| 20 | SystemManager::SystemManager(Core::System& core_) | 19 | SystemManager::SystemManager(Core::System& core_) |
| 21 | : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()}, | 20 | : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()} {} |
| 22 | thread_event{Core::Timing::CreateEvent( | ||
| 23 | "AudioRendererSystemManager", [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { | ||
| 24 | return ThreadFunc2(time); | ||
| 25 | })} {} | ||
| 26 | 21 | ||
| 27 | SystemManager::~SystemManager() { | 22 | SystemManager::~SystemManager() { |
| 28 | Stop(); | 23 | Stop(); |
| @@ -33,8 +28,6 @@ bool SystemManager::InitializeUnsafe() { | |||
| 33 | if (adsp.Start()) { | 28 | if (adsp.Start()) { |
| 34 | active = true; | 29 | active = true; |
| 35 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); }); | 30 | thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); }); |
| 36 | core.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), RENDER_TIME, | ||
| 37 | thread_event); | ||
| 38 | } | 31 | } |
| 39 | } | 32 | } |
| 40 | 33 | ||
| @@ -45,7 +38,6 @@ void SystemManager::Stop() { | |||
| 45 | if (!active) { | 38 | if (!active) { |
| 46 | return; | 39 | return; |
| 47 | } | 40 | } |
| 48 | core.CoreTiming().UnscheduleEvent(thread_event, {}); | ||
| 49 | active = false; | 41 | active = false; |
| 50 | update.store(true); | 42 | update.store(true); |
| 51 | update.notify_all(); | 43 | update.notify_all(); |
| @@ -111,16 +103,7 @@ void SystemManager::ThreadFunc() { | |||
| 111 | 103 | ||
| 112 | adsp.Signal(); | 104 | adsp.Signal(); |
| 113 | adsp.Wait(); | 105 | adsp.Wait(); |
| 114 | |||
| 115 | update.wait(false); | ||
| 116 | update.store(false); | ||
| 117 | } | 106 | } |
| 118 | } | 107 | } |
| 119 | 108 | ||
| 120 | std::optional<std::chrono::nanoseconds> SystemManager::ThreadFunc2(s64 time) { | ||
| 121 | update.store(true); | ||
| 122 | update.notify_all(); | ||
| 123 | return std::nullopt; | ||
| 124 | } | ||
| 125 | |||
| 126 | } // namespace AudioCore::AudioRenderer | 109 | } // namespace AudioCore::AudioRenderer |
diff --git a/src/audio_core/renderer/system_manager.h b/src/audio_core/renderer/system_manager.h index 415ddb74f..1f0bbd8b4 100644 --- a/src/audio_core/renderer/system_manager.h +++ b/src/audio_core/renderer/system_manager.h | |||
| @@ -68,11 +68,6 @@ private: | |||
| 68 | */ | 68 | */ |
| 69 | void ThreadFunc(); | 69 | void ThreadFunc(); |
| 70 | 70 | ||
| 71 | /** | ||
| 72 | * Signalling core timing thread to run ThreadFunc. | ||
| 73 | */ | ||
| 74 | std::optional<std::chrono::nanoseconds> ThreadFunc2(s64 time); | ||
| 75 | |||
| 76 | enum class StreamState { | 71 | enum class StreamState { |
| 77 | Filling, | 72 | Filling, |
| 78 | Steady, | 73 | Steady, |
| @@ -95,8 +90,6 @@ private: | |||
| 95 | ADSP::ADSP& adsp; | 90 | ADSP::ADSP& adsp; |
| 96 | /// AudioRenderer mailbox for communication | 91 | /// AudioRenderer mailbox for communication |
| 97 | ADSP::AudioRenderer_Mailbox* mailbox{}; | 92 | ADSP::AudioRenderer_Mailbox* mailbox{}; |
| 98 | /// Core timing event to signal main thread | ||
| 99 | std::shared_ptr<Core::Timing::EventType> thread_event; | ||
| 100 | /// Atomic for main thread to wait on | 93 | /// Atomic for main thread to wait on |
| 101 | std::atomic<bool> update{}; | 94 | std::atomic<bool> update{}; |
| 102 | }; | 95 | }; |
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 9133f5388..9a0801888 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp | |||
| @@ -101,8 +101,6 @@ public: | |||
| 101 | ~CubebSinkStream() override { | 101 | ~CubebSinkStream() override { |
| 102 | LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name); | 102 | LOG_DEBUG(Service_Audio, "Destructing cubeb stream {}", name); |
| 103 | 103 | ||
| 104 | Unstall(); | ||
| 105 | |||
| 106 | if (!ctx) { | 104 | if (!ctx) { |
| 107 | return; | 105 | return; |
| 108 | } | 106 | } |
| @@ -143,8 +141,6 @@ public: | |||
| 143 | * Stop the sink stream. | 141 | * Stop the sink stream. |
| 144 | */ | 142 | */ |
| 145 | void Stop() override { | 143 | void Stop() override { |
| 146 | Unstall(); | ||
| 147 | |||
| 148 | if (!ctx || paused) { | 144 | if (!ctx || paused) { |
| 149 | return; | 145 | return; |
| 150 | } | 146 | } |
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index c138dc628..ee1a0652f 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp | |||
| @@ -88,7 +88,6 @@ public: | |||
| 88 | * Finalize the sink stream. | 88 | * Finalize the sink stream. |
| 89 | */ | 89 | */ |
| 90 | void Finalize() override { | 90 | void Finalize() override { |
| 91 | Unstall(); | ||
| 92 | if (device == 0) { | 91 | if (device == 0) { |
| 93 | return; | 92 | return; |
| 94 | } | 93 | } |
| @@ -116,7 +115,6 @@ public: | |||
| 116 | * Stop the sink stream. | 115 | * Stop the sink stream. |
| 117 | */ | 116 | */ |
| 118 | void Stop() override { | 117 | void Stop() override { |
| 119 | Unstall(); | ||
| 120 | if (device == 0 || paused) { | 118 | if (device == 0 || paused) { |
| 121 | return; | 119 | return; |
| 122 | } | 120 | } |
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 39a21b0f0..f99dbd8ec 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include "common/fixed_point.h" | 14 | #include "common/fixed_point.h" |
| 15 | #include "common/settings.h" | 15 | #include "common/settings.h" |
| 16 | #include "core/core.h" | 16 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | ||
| 18 | #include "core/core_timing_util.h" | ||
| 17 | 19 | ||
| 18 | namespace AudioCore::Sink { | 20 | namespace AudioCore::Sink { |
| 19 | 21 | ||
| @@ -149,10 +151,6 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n | |||
| 149 | return; | 151 | return; |
| 150 | } | 152 | } |
| 151 | 153 | ||
| 152 | if (queued_buffers > max_queue_size) { | ||
| 153 | Stall(); | ||
| 154 | } | ||
| 155 | |||
| 156 | while (frames_written < num_frames) { | 154 | while (frames_written < num_frames) { |
| 157 | // If the playing buffer has been consumed or has no frames, we need a new one | 155 | // If the playing buffer has been consumed or has no frames, we need a new one |
| 158 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | 156 | if (playing_buffer.consumed || playing_buffer.frames == 0) { |
| @@ -187,10 +185,6 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n | |||
| 187 | } | 185 | } |
| 188 | 186 | ||
| 189 | std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes); | 187 | std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes); |
| 190 | |||
| 191 | if (queued_buffers <= max_queue_size) { | ||
| 192 | Unstall(); | ||
| 193 | } | ||
| 194 | } | 188 | } |
| 195 | 189 | ||
| 196 | void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames) { | 190 | void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames) { |
| @@ -198,10 +192,15 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz | |||
| 198 | const std::size_t frame_size = num_channels; | 192 | const std::size_t frame_size = num_channels; |
| 199 | const std::size_t frame_size_bytes = frame_size * sizeof(s16); | 193 | const std::size_t frame_size_bytes = frame_size * sizeof(s16); |
| 200 | size_t frames_written{0}; | 194 | size_t frames_written{0}; |
| 195 | size_t actual_frames_written{0}; | ||
| 201 | 196 | ||
| 202 | // If we're paused or going to shut down, we don't want to consume buffers as coretiming is | 197 | // If we're paused or going to shut down, we don't want to consume buffers as coretiming is |
| 203 | // paused and we'll desync, so just play silence. | 198 | // paused and we'll desync, so just play silence. |
| 204 | if (system.IsPaused() || system.IsShuttingDown()) { | 199 | if (system.IsPaused() || system.IsShuttingDown()) { |
| 200 | if (system.IsShuttingDown()) { | ||
| 201 | release_cv.notify_one(); | ||
| 202 | } | ||
| 203 | |||
| 205 | static constexpr std::array<s16, 6> silence{}; | 204 | static constexpr std::array<s16, 6> silence{}; |
| 206 | for (size_t i = frames_written; i < num_frames; i++) { | 205 | for (size_t i = frames_written; i < num_frames; i++) { |
| 207 | std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes); | 206 | std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes); |
| @@ -209,20 +208,6 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz | |||
| 209 | return; | 208 | return; |
| 210 | } | 209 | } |
| 211 | 210 | ||
| 212 | // Due to many frames being queued up with nvdec (5 frames or so?), a lot of buffers also get | ||
| 213 | // queued up (30+) but not all at once, which causes constant stalling here, so just let the | ||
| 214 | // video play out without attempting to stall. | ||
| 215 | // Can hopefully remove this later with a more complete NVDEC implementation. | ||
| 216 | const auto nvdec_active{system.AudioCore().IsNVDECActive()}; | ||
| 217 | |||
| 218 | // Core timing cannot be paused in single-core mode, so Stall ends up being called over and over | ||
| 219 | // and never recovers to a normal state, so just skip attempting to sync things on single-core. | ||
| 220 | if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) { | ||
| 221 | Stall(); | ||
| 222 | } else if (system.IsMulticore() && queued_buffers <= max_queue_size) { | ||
| 223 | Unstall(); | ||
| 224 | } | ||
| 225 | |||
| 226 | while (frames_written < num_frames) { | 211 | while (frames_written < num_frames) { |
| 227 | // If the playing buffer has been consumed or has no frames, we need a new one | 212 | // If the playing buffer has been consumed or has no frames, we need a new one |
| 228 | if (playing_buffer.consumed || playing_buffer.frames == 0) { | 213 | if (playing_buffer.consumed || playing_buffer.frames == 0) { |
| @@ -237,6 +222,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz | |||
| 237 | } | 222 | } |
| 238 | // Successfully dequeued a new buffer. | 223 | // Successfully dequeued a new buffer. |
| 239 | queued_buffers--; | 224 | queued_buffers--; |
| 225 | |||
| 226 | { std::unique_lock lk{release_mutex}; } | ||
| 227 | |||
| 228 | release_cv.notify_one(); | ||
| 240 | } | 229 | } |
| 241 | 230 | ||
| 242 | // Get the minimum frames available between the currently playing buffer, and the | 231 | // Get the minimum frames available between the currently playing buffer, and the |
| @@ -248,6 +237,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz | |||
| 248 | frames_available * frame_size); | 237 | frames_available * frame_size); |
| 249 | 238 | ||
| 250 | frames_written += frames_available; | 239 | frames_written += frames_available; |
| 240 | actual_frames_written += frames_available; | ||
| 251 | playing_buffer.frames_played += frames_available; | 241 | playing_buffer.frames_played += frames_available; |
| 252 | 242 | ||
| 253 | // If that's all the frames in the current buffer, add its samples and mark it as | 243 | // If that's all the frames in the current buffer, add its samples and mark it as |
| @@ -260,26 +250,29 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz | |||
| 260 | std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size], | 250 | std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size], |
| 261 | frame_size_bytes); | 251 | frame_size_bytes); |
| 262 | 252 | ||
| 263 | if (system.IsMulticore() && queued_buffers <= max_queue_size) { | 253 | { |
| 264 | Unstall(); | 254 | std::scoped_lock lk{sample_count_lock}; |
| 255 | last_sample_count_update_time = | ||
| 256 | Core::Timing::CyclesToUs(system.CoreTiming().GetClockTicks()); | ||
| 257 | min_played_sample_count = max_played_sample_count; | ||
| 258 | max_played_sample_count += actual_frames_written; | ||
| 265 | } | 259 | } |
| 266 | } | 260 | } |
| 267 | 261 | ||
| 268 | void SinkStream::Stall() { | 262 | u64 SinkStream::GetExpectedPlayedSampleCount() { |
| 269 | std::scoped_lock lk{stall_guard}; | 263 | std::scoped_lock lk{sample_count_lock}; |
| 270 | if (stalled_lock) { | 264 | auto cur_time{Core::Timing::CyclesToUs(system.CoreTiming().GetClockTicks())}; |
| 271 | return; | 265 | auto time_delta{cur_time - last_sample_count_update_time}; |
| 272 | } | 266 | auto exp_played_sample_count{min_played_sample_count + |
| 273 | stalled_lock = system.StallApplication(); | 267 | (TargetSampleRate * time_delta) / std::chrono::seconds{1}}; |
| 268 | |||
| 269 | return std::min<u64>(exp_played_sample_count, max_played_sample_count); | ||
| 274 | } | 270 | } |
| 275 | 271 | ||
| 276 | void SinkStream::Unstall() { | 272 | void SinkStream::WaitFreeSpace() { |
| 277 | std::scoped_lock lk{stall_guard}; | 273 | std::unique_lock lk{release_mutex}; |
| 278 | if (!stalled_lock) { | 274 | release_cv.wait( |
| 279 | return; | 275 | lk, [this]() { return queued_buffers < max_queue_size || system.IsShuttingDown(); }); |
| 280 | } | ||
| 281 | system.UnstallApplication(); | ||
| 282 | stalled_lock.unlock(); | ||
| 283 | } | 276 | } |
| 284 | 277 | ||
| 285 | } // namespace AudioCore::Sink | 278 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h index 5fea72ab7..23e289c7b 100644 --- a/src/audio_core/sink/sink_stream.h +++ b/src/audio_core/sink/sink_stream.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <chrono> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| 10 | #include <span> | 11 | #include <span> |
| @@ -14,6 +15,7 @@ | |||
| 14 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 15 | #include "common/reader_writer_queue.h" | 16 | #include "common/reader_writer_queue.h" |
| 16 | #include "common/ring_buffer.h" | 17 | #include "common/ring_buffer.h" |
| 18 | #include "common/thread.h" | ||
| 17 | 19 | ||
| 18 | namespace Core { | 20 | namespace Core { |
| 19 | class System; | 21 | class System; |
| @@ -53,9 +55,7 @@ struct SinkBuffer { | |||
| 53 | class SinkStream { | 55 | class SinkStream { |
| 54 | public: | 56 | public: |
| 55 | explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {} | 57 | explicit SinkStream(Core::System& system_, StreamType type_) : system{system_}, type{type_} {} |
| 56 | virtual ~SinkStream() { | 58 | virtual ~SinkStream() {} |
| 57 | Unstall(); | ||
| 58 | } | ||
| 59 | 59 | ||
| 60 | /** | 60 | /** |
| 61 | * Finalize the sink stream. | 61 | * Finalize the sink stream. |
| @@ -201,14 +201,16 @@ public: | |||
| 201 | void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames); | 201 | void ProcessAudioOutAndRender(std::span<s16> output_buffer, std::size_t num_frames); |
| 202 | 202 | ||
| 203 | /** | 203 | /** |
| 204 | * Stall core processes if the audio thread falls too far behind. | 204 | * Get the total number of samples expected to have been played by this stream. |
| 205 | * | ||
| 206 | * @return The number of samples. | ||
| 205 | */ | 207 | */ |
| 206 | void Stall(); | 208 | u64 GetExpectedPlayedSampleCount(); |
| 207 | 209 | ||
| 208 | /** | 210 | /** |
| 209 | * Unstall core processes. | 211 | * Waits for free space in the sample ring buffer |
| 210 | */ | 212 | */ |
| 211 | void Unstall(); | 213 | void WaitFreeSpace(); |
| 212 | 214 | ||
| 213 | protected: | 215 | protected: |
| 214 | /// Core system | 216 | /// Core system |
| @@ -237,12 +239,21 @@ private: | |||
| 237 | std::atomic<u32> queued_buffers{}; | 239 | std::atomic<u32> queued_buffers{}; |
| 238 | /// The ring size for audio out buffers (usually 4, rarely 2 or 8) | 240 | /// The ring size for audio out buffers (usually 4, rarely 2 or 8) |
| 239 | u32 max_queue_size{}; | 241 | u32 max_queue_size{}; |
| 242 | /// Locks access to sample count tracking info | ||
| 243 | std::mutex sample_count_lock; | ||
| 244 | /// Minimum number of total samples that have been played since the last callback | ||
| 245 | u64 min_played_sample_count{}; | ||
| 246 | /// Maximum number of total samples that can be played since the last callback | ||
| 247 | u64 max_played_sample_count{}; | ||
| 248 | /// The time the two above tracking variables were last written to | ||
| 249 | std::chrono::microseconds last_sample_count_update_time{}; | ||
| 240 | /// Set by the audio render/in/out system which uses this stream | 250 | /// Set by the audio render/in/out system which uses this stream |
| 241 | f32 system_volume{1.0f}; | 251 | f32 system_volume{1.0f}; |
| 242 | /// Set via IAudioDevice service calls | 252 | /// Set via IAudioDevice service calls |
| 243 | f32 device_volume{1.0f}; | 253 | f32 device_volume{1.0f}; |
| 244 | std::mutex stall_guard; | 254 | /// Signalled when ring buffer entries are consumed |
| 245 | std::unique_lock<std::mutex> stalled_lock; | 255 | std::condition_variable release_cv; |
| 256 | std::mutex release_mutex; | ||
| 246 | }; | 257 | }; |
| 247 | 258 | ||
| 248 | using SinkStreamPtr = std::unique_ptr<SinkStream>; | 259 | using SinkStreamPtr = std::unique_ptr<SinkStream>; |