docs(spec): baseline 0.9.5 — v1 audio + PTT lifecycle redesign
Add SysRS-303/304, SysDes-149/150/151, SRS-204/205/206/207, SAD-081/082/083, SDD-094/095/096/097, DEC-029/030. Captures the v1 lifecycle redesign: - Drop manual Start-audio button; audio engine is bound to voice-channel join/leave (ensure_running on first join, shutdown_if_idle on last leave). Output stream opens regardless of mic-permission state so listen-only is a first-class flow. - TransmitMode enum (Ptt / Continuous / VoiceActivity-reserved). Default Ptt on fresh install. Persisted per identity. - PTT release tail: 200 ms default (0-500 ms configurable) before transmit gate closes, avoiding clipped trailing syllables. - Hard-mute toggle overrides transmit gate regardless of mode/PTT. - Bridge surface: drop start_audio/stop_audio; add voice_join(channel_id) / voice_leave() and BridgeEvent::VoiceState. DEC-029 rejects Flutter global-hotkey packages (hotkey_manager, super_hot_key) for PTT: they wrap RegisterHotKey/RegisterEventHotKey which consume the key and don't fire key-up, wrong primitive for PTT. Native Rust DesktopPttBackend (SDD-083/084/085) stays authoritative. DEC-030 defers Voice Activity Detection to P1. RMS / WebRTC VAD / Silero VAD trade-off review (binary-size, dependency-surface, CPU profile) postponed; TransmitMode::VoiceActivity reserved on the enum surface so a P1 increment is non-breaking. Validator clean: 304/151/207/83/97 IDs, strict layered sourcing preserved, no new warnings beyond the pre-existing 35 old-package-name filenames.
This commit is contained in:
@@ -197,3 +197,17 @@ SysDes-142..148 -> SYS4-SIV-016
|
||||
|---|---|---|
|
||||
| 0.9.3 | 2026-05-15 | Initial baseline-candidate architecture for capability-based desktop Push-to-Talk. Codifies the owner rulings for PTT-OPEN-001 through PTT-OPEN-006 as DEC-023 through DEC-028. |
|
||||
| 0.9.4 | 2026-05-15 | Promoted the Linux GNOME-Wayland backend from probe-only to the live `CreateSession` + `BindShortcuts` + `Activated` / `Deactivated` session flow. Owns an async tokio task with a dedicated `zbus::Connection`; publishes descriptor transitions through a `watch::Sender` consumed by `chanora_core::ChanoraSession::start_audio`. Adds the privacy-safe `trigger_description` classifier and the cancellation / failure path (downgrade to `L0Focused` + re-emit). Updates the Flutter UI to skip the in-app capture dialog on the portal backend (Q3a) and surface a SnackBar redirecting the user to the desktop's own dialog. |
|
||||
|
||||
## 8. v0.9.5 lifecycle and mode-picker addendum
|
||||
|
||||
This addendum captures the v1 audio + PTT lifecycle refactor recorded in SysRS-303..304, SysDes-149..151, SRS-204..207, SAD-081..083, and SDD-094..097. It does not change the desktop PTT backend design captured in §1–§7; it constrains how the backends are owned and how the rest of the audio path is exposed to the UI.
|
||||
|
||||
1. **No "Start audio" button anywhere.** The audio engine lifecycle is bound to voice-channel membership. Input and output streams open on the user's first voice-channel join of the session and close on the last voice-channel leave. The bridge surface drops `start_audio` / `stop_audio` and exposes `voice_join(channel_id) -> Result<(), VoiceError>` and `voice_leave() -> Result<(), VoiceError>`; `AudioEngine::ensure_running()` is invoked implicitly by `voice_join` and is idempotent. The Voice Bar renders from `BridgeEvent::VoiceState { in_channel, transmit_mode, mute, release_tail_ms }` and contains no manual-start affordance. Microphone permission is requested at app first launch rather than at first channel join; the output stream opens regardless of permission state, so listen-only is a first-class flow.
|
||||
2. **Transmit modes for v1 are `Ptt` and `Continuous`; `VoiceActivity` is reserved but not implemented.** `TransmitMode` is a `#[repr(u8)]` enum mirrored across the bridge; the default on fresh install is `Ptt`; the setting is persisted per identity in the identity store. The settings page renders `VoiceActivity` as a disabled "coming soon" radio row until DEC-030 is resolved and a VAD backend is allocated.
|
||||
3. **200 ms PTT release tail (configurable 0–500 ms).** A `ReleaseTailTimer` tokio adapter sits between the `DesktopPttBackend` `transmit` signal and `AudioTransmitGate::set()`. Key-down forwards `true` immediately; key-up starts a sleep of `release_tail_ms` (default 200, matching the TeamSpeak / Mumble default) before the gate closes; a key-down within the sleep window cancels the pending close. The tail affects only `transmit_active`; `capture_active` is unaffected. The adapter is independent of the missed-key-up watchdog (SAD-079 / SDD-092).
|
||||
4. **Hard-mute overrides everything.** The Voice Bar hard-mute toggle is applied by the `TransmitModeSelector` as a final clamp on `transmit_active` and takes precedence over the active transmit mode, the PTT key state, and every other internal signal. Hard-mute is in-session only and resets on the next voice-channel join.
|
||||
5. **Cross-references.** **DEC-029** locks the v1 PTT capture path to the native Rust `DesktopPttBackend` trait + per-OS implementations (SDD-083 / SDD-084 / SDD-085) and rejects Flutter global-hotkey packages such as `hotkey_manager` and `super_hot_key`, which wrap the OS `RegisterHotKey` semantics, consume the key, drop key-up events, and skip mouse side-buttons. **DEC-030** defers Voice Activity Detection to P1 pending a backend trade-off review (RMS vs WebRTC VAD vs Silero VAD); the enum surface is preserved so a P1 increment is non-breaking.
|
||||
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.5 | 2026-05-15 | Added the v0.9.5 lifecycle and mode-picker addendum capturing the no-manual-start audio engine bound to voice-channel membership, the v1 transmit-mode set (`Ptt` default + `Continuous`, with `VoiceActivity` reserved per DEC-030), the 200 ms (0–500 ms) PTT release tail, and the hard-mute override. Cross-references DEC-029 (native PTT backend locked) and DEC-030 (VAD deferred to P1). |
|
||||
|
||||
@@ -1364,6 +1364,33 @@ Communication evidence is maintained through change history and baseline approva
|
||||
- Source SRS: SRS-200
|
||||
- Verification method: Unit Test, Platform Test, UI Review
|
||||
|
||||
**SAD-081**: The software architecture shall remove the `start_audio` and `stop_audio` bridge calls from the public bridge surface and shall replace them with `voice_join(channel_id) -> Result<(), VoiceError>` and `voice_leave() -> Result<(), VoiceError>`. The audio engine's `ensure_running()` private method shall be invoked from the `voice_join` handler on first call of the session and shall be idempotent; the `voice_leave` handler shall call a private `shutdown_if_idle()` that closes the input and output streams when no voice channels remain. The Voice Bar UI shall not surface any manual-start affordance and shall instead key off the bridge event stream `BridgeEvent::VoiceState { in_channel, transmit_mode, mute, release_tail_ms }`, which the audio subsystem publishes on every lifecycle transition.
|
||||
|
||||
- Status: Baseline Candidate
|
||||
- Type: Software Architecture Item
|
||||
- Stage: P0 / MVP
|
||||
- Allocated to: Audio, Bridge, Flutter UI
|
||||
- Source SRS: SRS-204
|
||||
- Verification method: Integration Test, UI Review
|
||||
|
||||
**SAD-082**: The software architecture shall add a `ReleaseTailTimer` element to the audio subsystem, owned by the `PttController` and acting as an adapter between the `DesktopPttBackend` `transmit` signal and the `AudioTransmitGate::set()` call. On key-down the adapter forwards `true` immediately; on key-up the adapter starts a tokio sleep of `release_tail_ms` (default 200, configurable 0–500); a key-down within the sleep window cancels the pending close; a timer expiration calls `AudioTransmitGate::set(false)`. The adapter is independent of the missed-key-up watchdog defined in SAD-079 and does not interact with `capture_active`.
|
||||
|
||||
- Status: Baseline Candidate
|
||||
- Type: Software Architecture Item
|
||||
- Stage: P0 / MVP
|
||||
- Allocated to: Audio
|
||||
- Source SRS: SRS-206
|
||||
- Verification method: Unit Test, Integration Test
|
||||
|
||||
**SAD-083**: The software architecture shall add a `TransmitModeSelector` element owned by the audio engine that consults the current `TransmitMode` from the identity store on every state evaluation. In `Ptt` mode the selector defers to the `PttController` + `ReleaseTailTimer` pipeline; in `Continuous` mode the selector sets `transmit_active = !mute` while `in_channel` is true and clears `transmit_active` otherwise; in `VoiceActivity` mode (placeholder per DEC-030) the selector shall behave identically to `Continuous` until a VAD implementation is allocated in a later baseline. The selector shall be the only writer of `transmit_active` other than the missed-key-up watchdog defined in SAD-079, and the hard-mute toggle defined in SRS-207 shall be applied by the selector as a final clamp on the output value.
|
||||
|
||||
- Status: Baseline Candidate
|
||||
- Type: Software Architecture Item
|
||||
- Stage: P0 / MVP
|
||||
- Allocated to: Audio
|
||||
- Source SRS: SRS-205, SRS-207
|
||||
- Verification method: Unit Test, Integration Test
|
||||
|
||||
## 27. Updated SRS-to-SAD Coverage Matrix
|
||||
|
||||
| SRS Range | SAD Coverage |
|
||||
@@ -1372,6 +1399,7 @@ Communication evidence is maintained through change history and baseline approva
|
||||
| SRS-185 through SRS-194 | Covered by `SAD-061` through `SAD-070` |
|
||||
| SRS-195 through SRS-199, SRS-201 through SRS-203 | Covered by `SAD-071` through `SAD-079` |
|
||||
| SRS-200 | Covered by `SAD-080` |
|
||||
| SRS-204 through SRS-207 | Covered by `SAD-081` through `SAD-083` |
|
||||
|
||||
|
||||
## Baseline Candidate 0.9.1 Update
|
||||
@@ -1400,3 +1428,10 @@ Communication evidence is maintained through change history and baseline approva
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.4 | 2026-05-15 | Added `SAD-080` to close the SRS-200 coverage gap surfaced by a P0 traceability audit. The earlier baseline matrix folded SRS-200 under the `SRS-195..203 -> SAD-071..079` range without giving it a dedicated SAD item; the new entry restores the strict one-to-many SRS-to-SAD discipline (SRS-200 is now sourced by SAD-080; SAD-080 is in turn covered by the new SDD-093). No other rows mutate; the existing PTT SAD items (071..079) retain their original SRS sources. |
|
||||
|
||||
|
||||
## Baseline Candidate 0.9.5 Update
|
||||
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.5 | 2026-05-15 | Added v1 audio + PTT lifecycle architecture items SAD-081 through SAD-083 sourced from SRS-204..207: dropped the `start_audio` / `stop_audio` bridge calls and replaced them with `voice_join(channel_id)` / `voice_leave()` keyed off implicit engine init plus `BridgeEvent::VoiceState`; added the `ReleaseTailTimer` adapter on the `DesktopPttBackend` → `AudioTransmitGate` path (default 200 ms, range 0–500 ms, key-down within window cancels the pending close); added the `TransmitModeSelector` element as the sole writer of `transmit_active` (other than the SAD-079 watchdog) with hard-mute applied as a final clamp and `VoiceActivity` mapped to `Continuous` until DEC-030 resolves. Strict layered sourcing preserved (`SAD -> SRS` only). |
|
||||
|
||||
@@ -1122,6 +1122,42 @@ The registry supports consistency between architecture, detailed design, impleme
|
||||
- Source SAD: SAD-080
|
||||
- Verification method: Unit Test, Platform Test (Windows / macOS / Linux), UI Review
|
||||
|
||||
**SDD-094**: `chanora_bridge::voice::voice_join(channel_id: ChannelId) -> Result<(), VoiceError>` and `chanora_bridge::voice::voice_leave() -> Result<(), VoiceError>` shall be the only public-bridge voice lifecycle calls. Internally `chanora_audio::engine::AudioEngine::ensure_running()` shall be idempotent and shall open the cpal input and output streams on the first call of the session; `chanora_audio::engine::AudioEngine::shutdown_if_idle()` shall be called from `voice_leave` and shall close both streams when no voice channels remain. The Flutter widget tree shall subscribe to `BridgeEvent::VoiceState { in_channel, transmit_mode, mute, release_tail_ms }` through the existing FRB stream and shall render the Voice Bar from that stream. The pre-existing `start_audio` and `stop_audio` FRB symbols shall be deleted from `crates/chanora_bridge/src/api.rs` together with their generated bindings; the corresponding caller in `apps/chanora_flutter/lib/main.dart` shall be removed.
|
||||
|
||||
- Status: Draft
|
||||
- Type: Software Detailed Design Item
|
||||
- Stage: P0 / MVP
|
||||
- Software unit: `chanora_bridge::voice`, `chanora_audio::engine::AudioEngine`, `BridgeEvent::VoiceState`, `apps/chanora_flutter/lib/main.dart`
|
||||
- Source SAD: SAD-081
|
||||
- Verification method: Integration Test, UI Review
|
||||
|
||||
**SDD-095**: `chanora_audio::TransmitMode` shall be a `#[repr(u8)]` Rust enum with variants `Ptt = 0`, `Continuous = 1`, `VoiceActivity = 2`. Persistence shall be provided by `chanora_storage::IdentityStore::set_transmit_mode(&self, mode: TransmitMode)` and `chanora_storage::IdentityStore::get_transmit_mode(&self) -> TransmitMode`, the latter returning `TransmitMode::Ptt` on first read for a fresh install. The bridge surface shall carry the enum across the FFI boundary via `mirror` codegen as `BridgeTransmitMode`. The Flutter settings page in `apps/chanora_flutter/lib/widgets/voice_settings.dart` shall render three radio rows; the `VoiceActivity` row shall be rendered with `enabled: false` and a "Coming soon" trailing label until DEC-030 is resolved and an implementation is allocated.
|
||||
|
||||
- Status: Draft
|
||||
- Type: Software Detailed Design Item
|
||||
- Stage: P0 / MVP
|
||||
- Software unit: `chanora_audio::TransmitMode`, `chanora_storage::IdentityStore::{set_transmit_mode, get_transmit_mode}`, `BridgeTransmitMode`, `apps/chanora_flutter/lib/widgets/voice_settings.dart`
|
||||
- Source SAD: SAD-083
|
||||
- Verification method: Unit Test, UI Review
|
||||
|
||||
**SDD-096**: `chanora_audio::release_tail::ReleaseTailTimer` shall be a tokio-task-owning struct exposing `arm(&self, gate: AudioTransmitGate, tail_ms: u32)`, `key_down(&self)`, `key_up(&self)`, and `cancel(&self)`. Internally the struct shall hold a `tokio::sync::watch::Sender<bool>` plus a `JoinHandle<()>` for the pending close. `key_down` shall abort any pending close handle and shall set `transmit_active = true` via the gate; `key_up` shall abort the prior close handle and shall spawn a new task that sleeps for `tail_ms` milliseconds and then sets `transmit_active = false`. The struct shall use no `unsafe`, no mutex, and shall rely only on atomic refcounts on the watch handle. The configuration value `release_tail_ms` shall live in the identity store next to `transmit_mode`, with a default of 200 and validated range 0–500 inclusive on every write.
|
||||
|
||||
- Status: Draft
|
||||
- Type: Software Detailed Design Item
|
||||
- Stage: P0 / MVP
|
||||
- Software unit: `chanora_audio::release_tail::ReleaseTailTimer`, `chanora_storage::IdentityStore` (`release_tail_ms`)
|
||||
- Source SAD: SAD-082
|
||||
- Verification method: Unit Test, Integration Test
|
||||
|
||||
**SDD-097**: `apps/chanora_flutter/lib/widgets/voice_bar.dart` shall replace the existing `_AudioControls` widget in `apps/chanora_flutter/lib/main.dart` as the rendering surface for voice state. Its input shall be the `BridgeEvent::VoiceState` stream; its children, in render order, shall be: the channel-name pill (visible only when `in_channel` is true); the mode badge (`PTT: <key_label>` / `Continuous` / `Voice activity (coming soon)`); the hard-mute toggle button; the release-tail and threshold disclosure; the level meter; and the PTT capability badge already specified in SDD-091. The "Start audio" button shall be removed from the widget tree. The settings dialog in `apps/chanora_flutter/lib/widgets/voice_settings.dart` shall render the `TransmitMode` radio group, the bind-key button, and a release-tail slider over the range 0–500 ms with step 25 ms; the dialog shall never expose a "Start audio" affordance.
|
||||
|
||||
- Status: Draft
|
||||
- Type: Software Detailed Design Item
|
||||
- Stage: P0 / MVP
|
||||
- Software unit: `apps/chanora_flutter/lib/widgets/voice_bar.dart`, `apps/chanora_flutter/lib/widgets/voice_settings.dart`, `apps/chanora_flutter/lib/main.dart`
|
||||
- Source SAD: SAD-081, SAD-083
|
||||
- Verification method: UI Review, Widget Test
|
||||
|
||||
## 11. Updated SAD-to-SDD Coverage Matrix
|
||||
|
||||
| SAD Range | SDD Coverage |
|
||||
@@ -1130,6 +1166,7 @@ The registry supports consistency between architecture, detailed design, impleme
|
||||
| SAD-061 through SAD-070 | Covered by `SDD-071` through `SDD-080` |
|
||||
| SAD-071 through SAD-079 | Covered by `SDD-081` through `SDD-092` |
|
||||
| SAD-080 | Covered by `SDD-093` |
|
||||
| SAD-081 through SAD-083 | Covered by `SDD-094` through `SDD-097` |
|
||||
|
||||
|
||||
## Baseline Candidate 0.9.1 Update
|
||||
@@ -1158,3 +1195,10 @@ The registry supports consistency between architecture, detailed design, impleme
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.4 | 2026-05-15 | Added `SDD-093` covering `SAD-080`. `SDD-093` fixes the `PttInputClass` enum surface, the cross-backend rebind contract for `MouseSideButton`, and the Flutter side-button capture path against the `PointerEvent.buttons` bitmask. Strict layered sourcing preserved (`SDD -> SAD` only). |
|
||||
|
||||
|
||||
## Baseline Candidate 0.9.5 Update
|
||||
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.5 | 2026-05-15 | Added v1 audio + PTT lifecycle detailed design SDD-094 through SDD-097 sourced from SAD-081..083: bridge surface drops `start_audio` / `stop_audio` and exposes `chanora_bridge::voice::voice_join` / `voice_leave` with idempotent `AudioEngine::ensure_running` and `shutdown_if_idle`, `chanora_audio::TransmitMode` as `#[repr(u8)]` enum persisted via `chanora_storage::IdentityStore::{set,get}_transmit_mode` and mirrored across FRB as `BridgeTransmitMode`, `chanora_audio::release_tail::ReleaseTailTimer` as a tokio-task-owning adapter on the watch handle (no `unsafe`, no mutex; configurable 0–500 ms via `release_tail_ms`), and the new `apps/chanora_flutter/lib/widgets/voice_bar.dart` + `voice_settings.dart` surfaces replacing the legacy `_AudioControls` widget. Strict layered sourcing preserved (`SDD -> SAD` only). |
|
||||
|
||||
@@ -2852,15 +2852,52 @@ This SysDes version covers all known SysRS requirements from `SysRS-001` through
|
||||
- ASPICE SYS.3 alignment: Architecture constraints
|
||||
- Allocated SysRS: SysRS-298
|
||||
|
||||
**SysDes-149**: The system architecture shall define a `TransmitMode` element carried as an enum at the audio + bridge + UI boundary with variants `Ptt`, `Continuous`, and a reserved `VoiceActivity` placeholder that has no allocated implementation in this baseline (deferred per DEC-030). The active mode shall be persisted in the identity store; the UI shall surface `VoiceActivity` as a disabled "coming soon" option until an implementation is allocated in a later baseline.
|
||||
|
||||
- Status: Baseline Candidate
|
||||
- Type: Subsystem Interface
|
||||
- Stage: P0 / MVP
|
||||
- Allocated to: Audio Engine, Bridge, Flutter UI
|
||||
- Downstream artifact: SRS, SAD, SDD, Verification
|
||||
- Verification method: Unit Test, UI Review
|
||||
- Verification owner: Audio Owner + UX Owner
|
||||
- ASPICE SYS.3 alignment: Element behaviour, interface definition
|
||||
- Allocated SysRS: SysRS-303
|
||||
|
||||
**SysDes-150**: The audio engine lifecycle shall be allocated to voice-channel membership: the input and output streams shall open on the user's first voice-channel join of the session and shall close on the last voice-channel leave. The output-stream open shall be independent of the microphone-permission state so listen-only remains a first-class flow. No manual start affordance shall be exposed at any system interface (bridge, UI, or otherwise). A user-facing hard-mute element shall force the transmit gate closed and shall take precedence over the active transmit mode, the PTT key state, and every other internal signal.
|
||||
|
||||
- Status: Baseline Candidate
|
||||
- Type: Lifecycle Allocation
|
||||
- Stage: P0 / MVP
|
||||
- Allocated to: Audio Engine, Bridge, Flutter UI
|
||||
- Downstream artifact: SRS, SAD, SDD, Verification
|
||||
- Verification method: Integration Test, UI Review
|
||||
- Verification owner: Audio Owner + Software Architect
|
||||
- ASPICE SYS.3 alignment: Element behaviour, lifecycle
|
||||
- Allocated SysRS: SysRS-303
|
||||
|
||||
**SysDes-151**: A release-tail element shall be allocated to the audio subsystem as a system-level timer adapter on the `transmit_active` atomic defined in SysDes-144, parameterised by a configurable release-tail period (default 200 ms; user-configurable range 0–500 ms). When the PTT backend signals key-up, the adapter shall delay the `true → false` transition of `transmit_active` by the configured period; the adapter shall not affect `capture_active`.
|
||||
|
||||
- Status: Baseline Candidate
|
||||
- Type: Subsystem Element
|
||||
- Stage: P0 / MVP
|
||||
- Allocated to: Audio Engine
|
||||
- Downstream artifact: SRS, SAD, SDD, Verification
|
||||
- Verification method: Unit Test, Integration Test
|
||||
- Verification owner: Audio Owner
|
||||
- ASPICE SYS.3 alignment: Element behaviour
|
||||
- Allocated SysRS: SysRS-304
|
||||
|
||||
## 17. Updated SysRS Coverage Statement
|
||||
|
||||
This SysDes version covers all known SysRS requirements from `SysRS-001` through `SysRS-302`.
|
||||
This SysDes version covers all known SysRS requirements from `SysRS-001` through `SysRS-304`.
|
||||
|
||||
| SysRS Range | SysDes Coverage |
|
||||
|---|---|
|
||||
| SysRS-001 through SysRS-285 | Covered by inherited SysDes baseline `SysDes-001` through `SysDes-132` |
|
||||
| SysRS-286 through SysRS-295 | Covered by `SysDes-133` through `SysDes-141` |
|
||||
| SysRS-296 through SysRS-302 | Covered by `SysDes-142` through `SysDes-148` |
|
||||
| SysRS-303 through SysRS-304 | Covered by `SysDes-149` through `SysDes-151` |
|
||||
|
||||
|
||||
## Baseline Candidate 0.9.1 Update
|
||||
@@ -2882,3 +2919,10 @@ This SysDes version covers all known SysRS requirements from `SysRS-001` through
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.3 | 2026-05-15 | Added desktop PTT subsystem allocation SysDes-142 through SysDes-148: Platform PTT Backend layer, typed `PttCapabilityLevel` cross-interface, audio-pipeline split into `capture_active` and `transmit_active`, per-OS backend allocation (Windows Raw-Input ladder, macOS permission-aware, Linux GNOME-Wayland portal), diagnostics sanitizer for raw key events, UI capability badge, release-record evidence requirement. |
|
||||
|
||||
|
||||
## Baseline Candidate 0.9.5 Update
|
||||
|
||||
| Version | Date | Description |
|
||||
|---|---|---|
|
||||
| 0.9.5 | 2026-05-15 | Added v1 audio + PTT lifecycle allocation SysDes-149 through SysDes-151 sourced from SysRS-303 / SysRS-304: `TransmitMode` enum element (`Ptt` / `Continuous` / reserved `VoiceActivity` per DEC-030) at the audio + bridge + UI boundary, audio engine lifecycle bound to voice-channel membership with no manual start affordance and a listen-only path independent of mic permission, hard-mute override element, and the release-tail timer adapter (default 200 ms, range 0–500 ms) on `transmit_active`. Strict layered sourcing preserved (`SysDes -> SysRS` only). |
|
||||
|
||||
Reference in New Issue
Block a user