fix(ptt,ui): Continuous mode no longer self-disables after 30 s; rename PTT label to Mic
Issue 1: in Continuous transmit mode the talk indicator turned
gray-out / mic disabled after ~30 s and could only be revived by
toggling mic mute. Root cause: SAD-079 MissedKeyUpWatchdog
subscribed to AudioTransmitGate.transmit_active and force-cleared
it after 30 s of true. In PTT mode this is correct (stuck key =
bug). In Continuous mode transmit_active is *supposed* to stay
true indefinitely; the watchdog assumption doesn't hold.
Fix: the watchdog now subscribes to a new ptt_held watch on the
TransmitModeSelector (the raw key-state input, not the resolved
gate). In Continuous mode ptt_held is never set true, so the
watchdog never fires. In PTT mode it still fires on a stuck
key-down as before. The session owns the watchdog (was on the
engine) so it survives engine restarts; it's spawned lazily on the
first start_audio.
MissedKeyUpWatchdog gains spawn_on_signal(rx, on_timeout, timeout)
alongside the existing spawn(gate, timeout) — old shape preserved
for backwards compat. run_watchdog generalised to take any
watch::Receiver<bool> + Box<dyn Fn() + Send + Sync>.
Two new tests:
- watchdog_on_signal_does_not_fire_when_ptt_held_stays_false
(the Continuous-mode regression test)
- watchdog_on_signal_fires_when_signal_stays_true
(the stuck-key case still fires)
Issue 2: the Voice Bar stats line said 'PTT on/off' even when the
user was in Continuous mode where no PTT key is involved. Renamed
to 'Mic on/off' (mode-neutral) and l10n-ised the on/off literal:
- en: 'Mic on' / 'Mic off'
- zh: '麦克风 开启' / '麦克风 关闭'
cargo test --workspace --lib: 80 passed / 0 failed / 1 ignored
(was 78, +2 watchdog tests).
flutter analyze: clean (6 pre-existing Radio.groupValue infos).
This commit is contained in:
@@ -257,6 +257,13 @@ pub struct ChanoraSession {
|
||||
/// Release-tail timer (SDD-096). Drives the selector's
|
||||
/// `ptt_held` input from PTT key edges.
|
||||
release_tail: Arc<ReleaseTailTimer>,
|
||||
/// Missed-key-up watchdog (SAD-079 / DEC-028). Subscribes to
|
||||
/// `voice_selector.subscribe_ptt_held()` so it only fires when
|
||||
/// an actual PTT key has been "stuck" for the configured
|
||||
/// timeout (default 30 s). Lives on the session because it
|
||||
/// must outlive engine restarts. Spawned lazily on the first
|
||||
/// `start_audio` because it needs a tokio runtime context.
|
||||
ptt_watchdog: Arc<Mutex<Option<chanora_audio::MissedKeyUpWatchdog>>>,
|
||||
/// Last PTT binding the user requested via `set_ptt_binding`.
|
||||
/// Kept here so it survives the gap between user-saving a
|
||||
/// binding (which may happen before any audio is running) and
|
||||
@@ -291,6 +298,7 @@ impl ChanoraSession {
|
||||
voice_selector: selector,
|
||||
release_tail,
|
||||
pending_binding: Arc::new(Mutex::new(None)),
|
||||
ptt_watchdog: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,6 +562,27 @@ impl ChanoraSession {
|
||||
let controller = ptt::PttController::new(self.release_tail.clone());
|
||||
state.ptt_controller = Some(controller.clone());
|
||||
|
||||
// Spawn the missed-key-up watchdog on first start_audio.
|
||||
// It subscribes to the selector's `ptt_held` watch — NOT
|
||||
// the real gate — so it only fires when the user has
|
||||
// actually been holding the PTT key for >30 s. In
|
||||
// Continuous mode `ptt_held` is never set true, so the
|
||||
// watchdog never fires (the fix for the bug where
|
||||
// Continuous transmission would disable itself after the
|
||||
// 30 s default timeout).
|
||||
{
|
||||
let mut wd = self.ptt_watchdog.lock().await;
|
||||
if wd.is_none() {
|
||||
let signal = self.voice_selector.subscribe_ptt_held();
|
||||
let selector = self.voice_selector.clone();
|
||||
*wd = Some(chanora_audio::MissedKeyUpWatchdog::spawn_on_signal(
|
||||
signal,
|
||||
move || selector.set_ptt_held(false),
|
||||
chanora_audio::MissedKeyUpWatchdog::DEFAULT_TIMEOUT,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Apply any binding the user saved before audio was running
|
||||
// (SDD-094 follow-up). Persistence + caching happen in
|
||||
// `set_ptt_binding`; here we forward the cached value to
|
||||
|
||||
Reference in New Issue
Block a user