Files
buzz/desktop/public/worklet.js
T
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

68 lines
2.5 KiB
JavaScript

// AudioWorklet processor — runs in the AudioWorklet thread.
// Accumulates PCM Float32 samples and sends 20ms batches to the main thread.
//
// ASSUMPTION: AudioContext runs at 48 kHz (Tauri WebView default on desktop).
// The 960-sample buffer = exactly one 20ms Opus frame at 48 kHz. If the
// sample rate differs (e.g. 44.1 kHz), frames will be slightly misaligned
// with the Opus encoder. getUserMedia should request sampleRate: 48000.
//
// Supports push-to-talk (PTT) gating: when `this.transmitting` is false,
// incoming audio frames are discarded and the buffer is reset. The main thread
// sends `{ type: 'ptt', active: boolean }` messages to toggle transmission.
// Default: transmitting=true (open mic for VAD mode compatibility).
//
// Note: when the worklet is disconnected, any partial buffer (< 960 samples)
// is silently dropped. The last ~20ms of speech may be lost on huddle leave.
// This is acceptable for voice — losing a partial syllable at disconnect is
// imperceptible compared to the natural end-of-conversation flow.
class SttTapProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.buffer = new Float32Array(960); // ~20ms at 48kHz
this.offset = 0;
this.transmitting = true; // default: open (VAD mode). PTT mode sets false on init.
// Direction: main→worklet (receives). The worklet→main direction uses
// this.port.postMessage for PCM data — these don't conflict.
this.port.onmessage = (e) => {
if (e.data && e.data.type === "ptt") {
this.transmitting = e.data.active;
}
};
}
process(inputs) {
const input = inputs[0]?.[0]; // mono channel
if (!input) return true;
// PTT gating: discard frames when not transmitting.
// Reset buffer offset so we don't send stale audio when PTT activates.
if (!this.transmitting) {
this.offset = 0;
return true;
}
const remaining = this.buffer.length - this.offset;
const toCopy = Math.min(input.length, remaining);
this.buffer.set(input.subarray(0, toCopy), this.offset);
this.offset += toCopy;
if (this.offset >= this.buffer.length) {
// Transfer ownership for zero-copy
this.port.postMessage(this.buffer, [this.buffer.buffer]);
this.buffer = new Float32Array(960);
this.offset = 0;
if (toCopy < input.length) {
const leftover = input.subarray(toCopy);
this.buffer.set(leftover);
this.offset = leftover.length;
}
}
return true;
}
}
registerProcessor("stt-tap-processor", SttTapProcessor);