From e53f2d482246480d5c3ec2746cc621dc13399042 Mon Sep 17 00:00:00 2001 From: GuoHui <767428+guohui85@users.noreply.github.com> Date: Thu, 12 Nov 2026 15:55:27 +0800 Subject: [PATCH] fix(audio): close the idle system-sound engine - The system-sound engine stayed open after its first sound, keeping a PulseAudio/PipeWire playback stream running: the ES8389 codec, the sound server and the launcher kept processing silence while idle. - The lock screen made it permanent: entering it suspends the engine and then plays lock.mp3, which reopens it for as long as the device sleeps. - The worker now closes the engine after 10 s with no queued command and no sound playing; the next sound reopens it (with the existing 300 ms warm-up). - Measured on a CardputerZero (0.6.105 + this change, screen locked, on battery): 268 mA -> 215/217 mA, context switches 4600/s -> ~800/s, and the ES8389 playback PCM goes from RUNNING to closed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0135vUaG2FpgpGRJfB9LEPvZ --- .../src/cp0/cp0_audio_system_sound_player.cpp | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/ext_components/cp0_lvgl/src/cp0/cp0_audio_system_sound_player.cpp b/ext_components/cp0_lvgl/src/cp0/cp0_audio_system_sound_player.cpp index f69e32fe..058d1372 100644 --- a/ext_components/cp0_lvgl/src/cp0/cp0_audio_system_sound_player.cpp +++ b/ext_components/cp0_lvgl/src/cp0/cp0_audio_system_sound_player.cpp @@ -33,6 +33,10 @@ constexpr ma_uint32 kPeriodMilliseconds = 20; // The first output frames after a PulseAudio stream is opened can be swallowed // while the sink settles. Let the device warm up before starting short sounds. constexpr auto kDeviceWarmupDuration = std::chrono::milliseconds(300); +// Close the output stream once no system sound has played for this long. An open +// engine keeps a playback stream running in the sound server, so the codec, the +// sound server and this process keep processing silence while the device idles. +constexpr auto kIdleCloseAfter = std::chrono::seconds(10); std::string resolve_asset(const std::string &name) { @@ -244,6 +248,7 @@ class Cp0SystemSoundPlayer::Impl for (;;) { Command command; bool have_command = false; + bool idle_close = false; { std::unique_lock lock(mutex_); for (;;) { @@ -255,11 +260,23 @@ class Cp0SystemSoundPlayer::Impl break; } - wake_.wait(lock); + if (!engine_initialized_) { + wake_.wait(lock); + continue; + } + if (wake_.wait_for(lock, kIdleCloseAfter) == std::cv_status::timeout && + commands_.empty() && !stopping_ && !any_sound_playing()) { + idle_close = true; + break; + } } if (stopping_) break; } + if (idle_close) { + close_engine(); + continue; + } if (!have_command) continue; switch (command.type) { @@ -419,6 +436,14 @@ class Cp0SystemSoundPlayer::Impl return false; } + bool any_sound_playing() + { + for (std::size_t i = 0; i < sounds_.size(); ++i) { + if (sound_initialized_[i] && ma_sound_is_playing(&sounds_[i])) return true; + } + return false; + } + void close_engine() { if (!engine_initialized_) return;