Feature request: keep the ALSA output running briefly between streams (--keep-output-alive <s>)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 42/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- cpp
- Área
- audio-video-rtc
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- C++
- Estrellas
- 18
- Forks
- 7
- Merge medio
- 1 d 12 h
- PR fusionados (30 d)
- 26
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de Sendspin/sendspin-cpp-cli
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Sendspin/sendspin-cpp-cli#111 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 35/100
Sendspin/sendspin-cpp-cli#108 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 62/100
Sendspin/sendspin-cpp-cli#106 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 35/100
Sendspin/sendspin-cpp-cli#97 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Idle CPU: a fixed 10 ms main loop, and a PulseAudio stream that stays open after the stream endsAbierto
Dificultad 4/5 3-5 días Aptitud para principiantes 42/100
Sendspin/sendspin-cpp-cli#92 ·
Los mantenedores suelen responder en 1 día
Todos los issues de Sendspin/sendspin-cpp-cli
Issues similares
-
bug libcudf
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 2 días
-
level/task module/cloud monitoring type/maintenance
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
LadybirdBrowser/ladybird#13809 ·
Los mantenedores suelen responder en 1 día
-
bug module:nodes tests
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
rocketride-org/rocketride-server#3020 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 9 días