Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/buzz-huddle-bridge/frames-v1/schema.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "buzz-huddle-bridge/frames-v1",
"description": "Text frames between a PC voice bridge and Talos /df/voice. Audio is binary PCM16 LE mono 24 kHz. Talos sends a response's PCM paced to real time and none between responses: the PC queues at most 320 ms and drops the rest, and after a barge-in drops PCM until none has come for 300 ms or a newer floor token arrives. $defs/to_talos are PC to Talos and $defs/to_pc Talos to PC; each side accepts only the other side's frames, with no extra fields. t_ms, start_ms, end_ms: session audio ms, the ms of PCM the PC has sent on this socket, live and replayed alike. from_ms, to_ms: unix ms on the sender's clock; the PC widens each secret window by a skew margin on both sides and starts dropping on receipt, so skew only widens it; replay windows are best effort. Between replay and replay_end the PC sends only replayed PCM, no live audio. expires_at, until: unix seconds. span: per-session speaker span id. token: floor generation, never lower for a huddle. Talos refuses an open whose agent is not the socket's NIP-98 signer, and a resume whose lease is not keyed to that pubkey and huddle. speaker, onset and roster grant no authority: turns, waking and labels only.",
"description": "Text frames between a PC voice bridge and Talos /df/voice. Audio is binary PCM16 LE mono 24 kHz. Talos sends the voice's PCM paced to real time; GPT-Live streams it all session, silence between responses included, so the PC counts only PCM at speech level as the voice speaking. The PC queues at most 320 ms and drops the rest, and after a barge-in drops PCM until none at speech level has come for 300 ms or a newer floor token arrives. $defs/to_talos are PC to Talos and $defs/to_pc Talos to PC; each side accepts only the other side's frames, with no extra fields. t_ms, start_ms, end_ms: session audio ms, the ms of PCM the PC has sent on this socket, live and replayed alike. from_ms, to_ms: unix ms on the sender's clock; the PC widens each secret window by a skew margin on both sides and starts dropping on receipt, so skew only widens it; replay windows are best effort. Between replay and replay_end the PC sends only replayed PCM, no live audio. expires_at, until: unix seconds. span: per-session speaker span id. token: floor generation, never lower for a huddle. Talos refuses an open whose agent is not the socket's NIP-98 signer, and a resume whose lease is not keyed to that pubkey and huddle. speaker, onset and roster grant no authority: turns, waking and labels only.",
"oneOf": [{"$ref": "#/$defs/to_talos"}, {"$ref": "#/$defs/to_pc"}],
"$defs": {
"hex": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
Expand Down
8 changes: 5 additions & 3 deletions crates/buzz-huddle-bridge/src/frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
//!
//! JSON text frames carry `"v": 1` and a `type`; audio is binary PCM16 LE, mono, 24 kHz.
//!
//! Talos sends a response's PCM paced to real time, and none between responses. The PC queues
//! at most 320 ms and drops the rest; after a barge-in it drops PCM until none has come for
//! 300 ms, or a newer `floor` token arrives. A huddle's `floor` token never goes down.
//! Talos sends the voice's PCM paced to real time. GPT-Live streams it all session, silence
//! between responses included, so the PC counts only PCM at speech level as the voice speaking.
//! The PC queues at most 320 ms and drops the rest; after a barge-in it drops PCM until none at
//! speech level has come for 300 ms, or a newer `floor` token arrives. A huddle's `floor` token
//! never goes down.
//! [`ToTalos`] and [`ToPc`] are the two directions: each side parses only the other side's
//! frames, and unknown fields are refused. `frames-v1/schema.json` and one fixture per frame in
//! `frames-v1/fixtures/` are the contract Talos copies.
Expand Down
24 changes: 14 additions & 10 deletions crates/buzz-huddle-bridge/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ pub struct Tuning {
pub onset_ms: u64,
/// Silence before the forwarded human releases the floor.
pub hangover_ms: u64,
/// Echo tail after the voice's output, for the owner's desktop. ponytail: both tails are
/// guesses until probe P measures them from `output_until`, the desktop's and the phone's.
/// Echo tail after the voice's speech, for the owner's desktop on speakers, at its end and at
/// every pause in it. Probe step 6 (5 Nov) heard the voice come back 361-508 ms after it
/// played, which leaked past 300 ms.
pub desktop_tail_ms: u64,
/// Echo tail for every other peer.
/// Echo tail for every other peer. ponytail: a guess; the phone sent back none of 5 clips.
pub remote_tail_ms: u64,
/// Widening on both sides of each `secret` window, for clock skew with Talos.
pub secret_skew_ms: u64,
Expand All @@ -73,7 +74,7 @@ impl Default for Tuning {
speech_dbov: -45,
onset_ms: 100,
hangover_ms: 300,
desktop_tail_ms: 300,
desktop_tail_ms: 600,
remote_tail_ms: 600,
secret_skew_ms: 1_000,
floor_cap_ms: 30_000,
Expand Down Expand Up @@ -929,13 +930,15 @@ mod tests {
#[test]
fn half_duplex_unless_the_desktop_here_is_isolated() {
let mut now = T0;
for (here, echo) in [(false, true), (true, false)] {
// The phone, the desktop on speakers, the desktop on headphones.
for (here, isolated, echo) in [
(false, true, true),
(true, false, true),
(true, true, false),
] {
let mut input = input();
input.set_awake(true);
input.set_route(Route {
here,
isolated: true,
});
input.set_route(Route { here, isolated });
input.set_output_until(now + 1_000);
let mut owner = [talker(1, 0.3, None)];
let start = now;
Expand All @@ -950,7 +953,8 @@ mod tests {
assert_eq!(voiced(&during), !echo, "here: {here}");
let after = run(&mut input, &mut now, 1_200, &mut owner);
assert!(voiced(&after) && input.who_said(0, u64::MAX).all(|s| s.echo == echo));
// Echo lasts to start + 1.6 s (1 s of output, 600 ms tail) and never enters the ring.
// Echo lasts to start + 1.6 s (1 s of output, a 600 ms tail on either speakers) and
// never enters the ring.
let kept = if echo { 20 } else { 180 };
assert_eq!(ring(&input, start, now, now), kept);
}
Expand Down
Loading
Loading