lotus(#1): fix restart-silence (A7), 48kHz ctx; protocol + CI hardening
CI / Build embedded bundle (push) Successful in 1m20s
CI / Publish to Gitea npm registry (push) Has been skipped

Denoise deep-review (CRITICAL): restart() read opts.audioContext, which
LiveKit does NOT pass on restart — so reconnect (the A7 scenario) and mic
device-switch threw after stopping the old track, leaving the mic SILENT
(A7 reintroduced). Fix:
- Processor owns a dedicated 48kHz AudioContext (sapphi worklets require
  48kHz; H1), reused across restart, closed on destroy.
- restart() never throws and never leaves a stopped track on the sender:
  builds the new graph first, then disposes the old; on failure degrades
  to RAW mic audio rather than silence.
- Cache wasm per URL (no re-fetch each reconnect); gate threshold default
  -45 and accept an explicit 0 (M2); document the cross-repo asset contract.

Protocol audit:
- Non-silent warning when an unsupported denoise model (dtln/deepfilternet)
  is requested instead of silent rnnoise fallback (F3).
- Correct the call_state enum comment (immediate error-reply, not 10s) (F2).

Build/CI audit:
- Stamp VITE_APP_VERSION in CI; document the vX.Y.Z-lotus.N version scheme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lotus CI
2026-06-30 00:15:59 -04:00
co-authored by Claude Opus 4.8
parent b8543c3fe1
commit 29592fbb18
4 changed files with 116 additions and 37 deletions
+19 -2
View File
@@ -53,11 +53,28 @@ function safeAssetBase(raw: string | null): string {
export function startLotusDenoise(vm: CallViewModel): () => void {
if (lotusParam("lotusDenoise") !== "ml") return () => undefined;
const requestedModel = lotusParam("lotusModel");
// Only RNNoise/Speex are implemented in-source so far. Don't silently
// degrade an unsupported model (e.g. the host's dtln/deepfilternet) to
// rnnoise — log it so the mismatch is visible.
let model: LotusDenoiseConfig["model"] = "rnnoise";
if (requestedModel === "speex") model = "speex";
else if (requestedModel && requestedModel !== "rnnoise")
logger.warn(
`[lotus] denoise model "${requestedModel}" not implemented in-source; using rnnoise`,
);
const rawThreshold = lotusParam("lotusGateThreshold");
const config: LotusDenoiseConfig = {
model: lotusParam("lotusModel") === "speex" ? "speex" : "rnnoise",
model,
assetBase: safeAssetBase(lotusParam("lotusDenoiseBase")),
gate: lotusFlag("lotusGate"),
gateThreshold: Number(lotusParam("lotusGateThreshold")) || -50,
// Default -45 (matches the reference shim); accept an explicit 0 (don't
// coerce it away via `|| default`).
gateThreshold:
rawThreshold !== null && Number.isFinite(Number(rawThreshold))
? Number(rawThreshold)
: -45,
};
const micOf = (room: LivekitRoom): LocalAudioTrack | undefined =>