Put the cans on.
Type a line. She speaks it.
macOS, Apple Silicon. No Python, no uv.
brew install --HEAD veronica-agent/tap/cansThe mouth is a native Qwen3-TTS worker. First cans doctor (or first cans) downloads it once, about 1.6 GB, into ~/.cans/native.
cans doctor
canscurl
curl -fsSL https://raw.githubusercontent.com/veronica-agent/cans/main/install.sh | shUntil a tagged release exists, that script needs Go and installs cans onto PATH. Then cans doctor.
From source
git clone https://github.com/veronica-agent/cans.git
cd cans
just installjust install (same as just run install) is go install into $(go env GOBIN), or $(go env GOPATH)/bin if GOBIN is empty. cans doctor fetches the mouth. just uninstall removes the binary. just uninstall --all also wipes ~/.cans (the native mouth).
cans
cans say "Put the cans on."
cans keep take.wav -text "the words in the wav"
cans doctorKeep needs the clip and the words spoken in it. Throat stays put for the session.
The CLI is Go. The mouth is a native Qwen3-TTS worker that clones a wav. No Python.
The script owns the document. cans speaks what it is handed.
--stream reads stdin a line at a time and speaks each line through one worker: one model load for the whole document. Blank lines are skipped and do not take an index.
# one wav per paragraph, script decides what a paragraph is
awk -v RS='' '{gsub(/\n/," "); print}' chapter.md | cans say --stream -o 'out/%03d.wav'
# a line at a time, with the script's own naming
i=0
while IFS= read -r line; do
i=$((i+1))
cans say "$line" -o "$(printf 'out/%03d.wav' "$i")"
done < lines.txt
# metadata for a build step
cans say --stream --json -o 'out/%03d.wav' < lines.txt | jq -r 'select(.error==null) | .wav' > manifest.txt| Flag | Effect |
|---|---|
-o, --out |
Write the wav here. Under --stream the path takes one %d, as in out/%03d.wav. |
--stream |
Read stdin line by line, one utterance per line, one worker for all of them. Text on argv is a usage error. |
--json |
One JSON record per utterance on stdout; --stream adds "line". wav is present only with -o. |
--play |
Play the wav as well as writing it. Needs -o. |
--nowait |
Do not queue behind another cans: give up at once. |
--wait <dur> |
Queue for at most that long. Without either flag, cans waits. |
- |
Read one utterance from stdin. Text on argv is a usage error. |
| Exit | Meaning |
|---|---|
0 |
Spoke it. |
1 |
Runtime failure, or a line in a stream failed. |
2 |
Usage error. |
75 |
Another cans holds the mouth and the wait was refused or ran out. |
130 |
Interrupted. |
stdout carries data — a JSON record, a wav path, or ttfa_ms=N, the worker's total synthesis time for that line. Prose goes to stderr.
Ctrl-C stops the stream: the line being spoken is dropped, finished wavs stay, exit 130. A second Ctrl-C stops at once.
Without -o the wav is a temp file removed after playback. --json without -o omits wav so a script is not handed a path that will not survive. Pass -o if the file is needed.
Built with Festival

