Feature request: keep the ALSA output running briefly between streams (--keep-output-alive <s>)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 42/100
- issue の種類
- 機能追加
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- cpp
調査の方向性
Start with alsa_sink.h and alsa_sink.cpp, then trace the existing fixed-volume option through CLI parsing, configuration, and sendspin-cli.conf.example. Use the attached patch and the modeled parse/config tests as references, and run the build-armv6 container with SENDSPIN_CLI_WERROR=ON. Done means the ALSA keep-alive behavior, timeout, CLI/config bounds, and no-op behavior for other backends are implemented and all 122 tests pass.
索引モデルが issue の本文から書いたものです。
説明
Problem
With an HDMI (or S/PDIF) receiver behind the player, the start of every stream is cut off.
At each stream end and start, AlsaAudioSink stops the PCM:
clear()callssnd_pcm_drop()+snd_pcm_prepare()configure()does the same when the next stream has the same format
The PCM then sits in PREPARED (or in XRUN, if the ring runs dry before clear() arrives)
until the next stream writes. While it does, no audio packets go out over HDMI, so the receiver
drops the input and has to re-lock when audio comes back. That takes about 2-3 s on an Onkyo
TX-NR626. For Music Assistant announcements (TTS) this is a real problem: MA ends the music
stream, plays the announcement as its own stream and then resumes the music, so the receiver
re-locks twice, and most of a short announcement is lost. A pre-announce chime is too short
to cover the re-lock.
Setup: Raspberry Pi Zero W, sendspin-cli 0.4.0 (linux-armv6), Music Assistant 2.10.8.
Output is onkyo_hdmi, a copy of the hdmi:CARD=vc4hdmi,DEV=0 chain in asound.conf with the
hw rate pinned to 48 kHz, plus audio-format = flac:48000:24:2,pcm:48000:24:2, so the format
never changes between streams and only the same-format path below is involved.
Measured by polling /proc/asound/card0/pcm0p/sub0/status every 250 ms during
radio -> TTS -> radio:
RUNNING (radio)
PREPARED ~1 s <- stream end, clear()
RUNNING (TTS, ~6 s)
XRUN ~0.3 s <- ring ran dry before the stream-end message
PREPARED ~2 s
RUNNING (radio)
Workarounds that do not work
Both try to keep the device busy from outside the player. Each puts a layer between the player
and the device that reports snd_pcm_delay() too coarsely or too jittery for the 5 ms
HARD_SYNC_THRESHOLD_US. The result is a constant Lost sync / hard-sync loop, which you hear
as choppy audio:
snd-aloop+alsaloopfeeding HDMI. The loopback pointer moves in jiffies, and the Pi
kernel runsHZ=100. Result:Lost sync (~90 ms off)about every second, with any
alsalooplatency or sync mode.- System-mode PulseAudio with
module-suspend-on-idleunloaded (-o pulse). The same
Lost syncstorm, see #32, plus 100 % CPU on a Pi Zero.
So this has to happen inside the sink, where the delay reported to the sync task comes straight
from the device.
Proposal
An opt-in option --keep-output-alive <seconds> (config key keep-output-alive), 0-3600,
default 0 (off), ALSA backend only. Other backends get a no-op AudioSink::set_keep_alive().
The goal is only to bridge the switch from one stream to the next, not to keep the receiver
awake forever: a few seconds (e.g. keep-output-alive = 10) are enough.
When enabled:
clear()and the same-format path inconfigure()no longer drop the PCM. If it is
running, they rewind the queued audio withsnd_pcm_rewind(), keeping one period in front
of the hardware pointer as a DMA safety margin. Then they write a short run of silence right
away. If the rewind fails, the queued tail simply plays out.poll()(main loop, ~10 ms) tops the ring up with silence to about 2 periods oncewrite()
has not delivered real audio for 60 ms. It usestry_lockondevice_mutex_: awrite()
blocked insnd_pcm_wait()holds the mutex, so a stream that is running is never touched
and the main loop never stalls. The same path re-prepares anXRUN(e.g. the ring ran dry
at the end of a stream beforeclear()), so the signal does not drop there either.- Time limit. Every real
write()and every same-formatconfigure()(re)starts a window
of<seconds>; aclear()restarts it while it is still open. When it runs out, the PCM is
dropped and prepared exactly as without the option, so the receiver sees the signal end and
its auto standby still works. After that,clear()behaves as without the option, and the
next stream starts the PCM again and opens a new window. - Sync is not touched: the keep-alive silence is just queued frames, and the next real
write()measures it throughsnd_pcm_delay()as usual.
Results
A patch against v0.4.0 (about 170 added lines in alsa_sink.{h,cpp}, plus option plumbing, help
text, sendspin-cli.conf.example and parse/config tests modeled on --fixed-volume) is
attached. It builds with SENDSPIN_CLI_WERROR=ON in the build-armv6 container, and all
122 tests pass. clear() and configure() are unchanged on current main, so it should apply
there with little or no change.
On the Pi Zero W above, with keep-output-alive = 10:
- HDMI PCM
RUNNINGwithout a break through radio -> TTS -> radio (polled every 250 ms) - the receiver no longer re-locks, and TTS announcements play from the first word
- no
Lost syncduring the switches; CPU the same as without the option (~75 % idle) - after a pause the PCM went from
RUNNINGtoPREPAREDexactly 10 s after the last audio
(so the receiver can go to standby), and the next stream started normally
Open questions
- Name and unit: is
keep-output-alive <seconds>fine, or would you prefer milliseconds or an
output-level setting? - Is a hard cap (3600 s) sensible, or should there be a "forever" value for setups that want
it? I left that out on purpose so that receivers can always go to standby. - PortAudio, Pulse and PipeWire are left out on purpose. Pulse and PipeWire already have their
own idle/suspend handling.
- 主要言語
- C++
- スター
- 18
- フォーク
- 7
- 平均マージ
- 1日 12時間
- マージ済み PR(30日)
- 26
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Sendspin/sendspin-cpp-cli のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
Sendspin/sendspin-cpp-cli#111 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 35/100
Sendspin/sendspin-cpp-cli#108 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 62/100
Sendspin/sendspin-cpp-cli#106 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 35/100
Sendspin/sendspin-cpp-cli#97 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Idle CPU: a fixed 10 ms main loop, and a PulseAudio stream that stays open after the stream endsオープン
難易度 4/5 3〜5日 初心者へのやさしさ 42/100
Sendspin/sendspin-cpp-cli#92 ·
メンテナーはふだん 1 日以内に返信
Sendspin/sendspin-cpp-cli の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 80/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 82/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
eclipse-score/lifecycle#929 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
colbymchenry/codegraph#3223 ·
メンテナーはふだん 1 日以内に返信