feat(voice): standalone TTS CLI, and where markdown-for-speech preprocessing should live
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Use case**
Buzz can speak in a huddle, but there's no way to listen to a long agent report outside one. We want a small command — text in, WAV out — so a long run report can be listened to instead of read.
**Closest existing work**
- **#4403** (*play a message aloud, with a TL;DR audio mode*) is the closest *issue*. This is not a duplicate — that one is a Desktop UI affordance, this is a headless command. It does ask that Markdown be rendered *for the ear*: "headings become pauses, list items become sentences, tables are announced by row, and `**bold**` does not become 'asterisk asterisk'." That sentence is the part we share. #4403's proposed *backend* is different: system TTS (`SpeechSynthesis` / platform TTS) on top of #3720, with huddles out of scope. We are not assuming #4403 will consume a `buzz-voice` module.
- **PR #3240** (*feat(desktop): add message read-aloud playback*, still a draft) is the closest *PR*. It already calls `huddle::preprocessing::preprocess_for_tts` for per-message playback. Mentioning it because CONTRIBUTING asks for a PR search, not because this CLI would replace that work.
- Adjacent, not duplicate: #3720 (pluggable STT/TTS backends), #4166 (voice notes), #2708 (dictation).
**What we have**
A working CLI built against `buzz-voice`, using the pinned April Pocket bundle, as an `example` so your manifest is untouched. No length cap (`synth_chunk` already splits at the bundle's 50-token limit). Thirteen text tests and two model-gated audio tests. Measured locally on a debug build at roughly 5× realtime.
It has its own hand-rolled preprocessing, deliberately: we didn't want to touch your manifest before asking.
**The question**
`desktop/src-tauri/src/huddle/preprocessing.rs` (543 lines) and `huddle/tts_audio.rs` (168) — counts at `c8c8eb5` — already do most of this — sentence splitting, code-fence stripping, emoji, whitespace, fade-out, sentence packing. We reimplemented roughly nine of those functions without `regex` purely to avoid adding a dependency.
They are already not the same function. Desktop keeps the first sentence of every utterance alone (a huddle latency choice), expands numbers to words, and does not strip headings, lists, or tables. The CLI packs greedily, skips number-to-words, and adds block-markup handling so a report doesn't say "hash" or recite pipes. **Two callers of similar logic, already drifting** — and PR #3240 is a third caller of the Desktop copy.
So, two shapes:
1. **Take it as-is** — a self-contained example, no manifest change, zero risk to your build, and you inherit the duplication.
2. **Move the shared markdown-for-speech helpers down into `buzz-voice`** and add a `[[bin]]`, so Desktop, the CLI, and any later read-aloud path share one implementation — with the huddle-only choices (first-sentence-alone, number expansion) kept as options, not silently imposed on a headless command. `desktop/src-tauri` already depends on `buzz-voice` by path, so the dependency edge points the right way — no new coupling and no cycle.
**We'd rather build the second one.** It's the one that doesn't rot. But it touches Desktop and your manifest, so we're asking before building rather than sending a large PR you didn't ask for.
**The one thing only you can answer:** is adding `regex` to `buzz-voice` acceptable? `regex 1.12.3` is already in `Cargo.lock` via `desktop/src-tauri`, so this adds a dependency edge, not a new crate to the tree. If the answer is no, option 1 is the only honest shape and we'll stop there.
Happy to do the work either way — we just don't want to aim it at the wrong target.
Contributor guide
Research direction
Start by reading desktop/src-tauri/src/huddle/preprocessing.rs and huddle/tts_audio.rs, then inspect the existing buzz-voice example and manifest. The first step is to get a maintainer decision on the shared-library versus self-contained CLI shape and whether regex is acceptable. Done means the selected design is implemented without duplicating markdown-for-speech behavior, with the existing text and model-gated audio tests passing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- audio-video-rtc, backend, cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100