voiceover · git:20261014.4e5e7e5 · 2026-10-14 · sha256 17626820ca3699ca
voiceover git:20261014.4e5e7e5A
Immutable. This exact content never changes and is served at /api/v1/blob/17626820ca3699ca.
---
name: voiceover
description: Write and produce voiceover in BashCut — measure the voice's speaking rate and the speech-free windows, size each line to its window, prepare the text so text-to-speech reads it right, synthesize takes with a voice.synthesize provider (for example VieNeu-TTS for Vietnamese, preset or cloned voices), check every take against its text, fit it to the picture, re-take one line in place, and dub or localise existing speech. Use for narration, voiceover, fixing a flubbed line without re-recording, dubbing, or when the user says "lồng tiếng", "thuyết minh", "đọc lời", "giọng AI", "clone giọng", "text to speech", "TTS", "dịch lồng tiếng", "lồng tiếng tiếng Anh".
---
# Voiceover
Reply in the user's language. Commands measure and act; the numbers below are ranges with their source, to
choose from with the measurements in hand. Measure → change → measure again.
## Provider and voice
`voice speak` uses the project's `voice.synthesize` provider. Check with `capabilities get voice.synthesize`:
`reason: missing` → `plugins search --capability voice.synthesize` and name one whose voices speak the content
language (`project get` › `contentLanguage`; the footage's language when it is not set) and install it (`bc:edit-workflow`);
`not_configured` → tell the user what the provider's `detail` says (turn it on, trust it again). Until one serves, finish
the rest of the edit without voiceover (captions or on-screen text carry the lines) and say so in the final summary;
`unhealthy` → the failing dependency is in `detail`.
```sh
bashcut capabilities get voice.synthesize --voices # per voice kind preset|clone, source, language, region, style, gender, supportsRate, measuredRate; projectVoice
bashcut voice use <clone-id|provider/voice|default> # the project's voice
bashcut voice speak "<line>" --voice <clone-id|voice> # one request with another voice
```
- Only voices with `speaksContentLanguage: true` read the script naturally; none → say so and offer a provider that
has one (or a recorded voiceover). Pick among them by their facts (region, style, measured rate), then audition 2–3 on the **hardest** line,
not the first; the first voice is rarely the pick (T10 §2).
- Saved clones (`kind: clone`, source `my-voices`) are the user's own voices from My voices, reusable in every
project; they carry their consent, so `voice speak --voice clone-…` needs no `--clone-consent`. Prefer the
user's clone when the video is theirs and they speak in it (a talking head, a vlog with their voice); a preset when
the voice is a narrator, the clone sounds off on the hardest line, or the language differs from the clone's.
- Making a clone: only the user's own voice or one whose owner agreed, and only when the user asks. Point them to
Voice panel › + Clone a voice… (record, a file or the selected clip; it picks a clean 3–8 s and checks it), or
run `bashcut voice clone-add --name "<name>" --from <media|file> [--at S --to S] --consent own|permission`
with the consent they gave in this conversation; it waits for their approval in the app. A refusal names the
reason (`tooShort`, `tooQuiet`, `mostlySilence`, `musicUnder`): pick another part, don't retry the same one.
- The older plugin option `referenceAudio` still clones without saving; a provider that `clones` refuses it without
`--clone-consent`; pass it only when the user said yes in this conversation, never by default (T10 §6).
Never mark AI voiceover on the video (no "AI voice" text or sticker); the voice should simply sound natural.
## 1. Measure before writing
There is no normal speaking rate: it varies by voice, language, genre and energy (T10 §3). Measure it in the
content language's unit (syllables for Vietnamese, characters for Chinese/Japanese/Korean, else words):
```sh
bashcut speech rate # each speaker in the transcribed footage: p10/p50/p90, overall, articulation
bashcut speech rate --voice PROVIDER/VOICE # the rates measured on that voice's earlier takes
```
- A voice with no measured rate yet: synthesize one test line (`voice speak "<line>"`, nothing is placed) and read
its `unitsPerSecond`.
- When the voiceover should sound like the creator, size lines from the creator's own measured rate.
- Never reuse an English words-per-second figure for a syllable language (T10 §3).
- Published Vietnamese rates are a sanity check for a measurement, never a budget. Pauses removed (compare
`articulation`): read speech 4.7–5.3 syllables a second (Bradlow, Interspeech 2019: 4.74; Pellegrino et al.,
Language 2011: 5.22). Pauses inside phrases kept (compare `overall`): 200–245 a minute, 3.3–4.1 a second
(Vu et al., ACL 2025, PhoAudiobook: audiobooks 201; news, YouTube and conversation sets 224–243). A measurement
far outside these: check the transcript for dropped or merged words before sizing lines from it.
Then find where voiceover can go and how much text fits:
```sh
bashcut narration windows --min-seconds S --rate R # speech-free windows; with rate, a unit budget each
```
Each window comes with what covers it (music, footage, silence), the shots under it and anchors (last word
before it, first cut, first beat). Write each line to its window's budget; the p10–p90 spread of the voice tells
you how much slack to leave. If a line does not fit, **cut text** first; do not plan to speed the voice
(T02 §4). TTS length drifts from any plan (a 50 s script came out 40–45 s, T10 §3), so the final timing comes
from the generated take, not from the script.
## 2. Prepare the text
Spell out what gets misread:
- Numbers, years, times: `2026` → "hai nghìn không trăm hai mươi sáu", `7h30` → "bảy giờ ba mươi". Captions
keep the digits (T10 §2).
- Units and symbols: km, %, °C, `k` (nghìn đồng).
- Acronyms as letters ("xê en xê" for CNC); show them as letters again in captions.
- English or unusual words: rephrase in plain words rather than respelling ("chill" was read "chiều").
- Very short exclamations (≤ 5 words) with an expressive voice fail often; use a calm voice or a longer line.
When a phrase is misread in every take, **rewording beats regenerating** ("kín xe" → "chật cứng xe"). Change
approved text minimally and tell the user which words changed.
## 3. Synthesize
**A whole script** (an explainer, a narrated ad, any edit whose picture follows the voice): write the lines into
`plan.cut.voiceover` [{id, text, section, pause}] and voice them all in one job; `plan apply` then plays each line's
best take one after another, with captions in the script's own words timed by the take:
```sh
bashcut voice lines --takes 2 --min-similarity 0.9 # job: every line voiced, transcribed and checked; kept
bashcut jobs wait JOB_ID --done --timeout 600 # per line: best {projectPath, similarity, seconds}, unmatched
bashcut voice lines --only l3,l7 # after rewording those lines in plan.cut
```
A line that already has a passing kept take is not voiced again (`--force` does). Choose `--min-similarity` from
§4. One line here, one sentence: the take is checked against it and the captions break on it.
**One line at a time** (a fix, a dub, a line over a fixed shot):
```sh
bashcut voice speak "Text of one line" --takes 3 # job; measures every take and keeps its file, places none
bashcut jobs wait JOB_ID --done --timeout 100 # returns when done; every take's facts and file
bashcut voice place TAKE_PROJECT_PATH --at-frame F # put the take you picked on the Voiceover track
bashcut voice speak "Text" --takes 1 --choose 1 --at-frame F # one take, placed at once
```
- Each take reports `seconds`, `units`, `unitsPerSecond`, `leadingSilence`, `trailingSilence`, `pauses`, the
provider's `score` when it gives one, and its `path`/`projectPath`. Pick the take yourself, for example the one
whose `unitsPerSecond` is closest to the rate you sized the line with. Core holds no pace formula and picks none.
- One line or short paragraph per call, placed at its window; `voice place` is one undoable edit and the item keeps
its text and the take's provenance.
- Give every call a stable `--request-id` (`vo-<section>-<n>`). A paid provider: `--dry-run` first and show the
user its `estimate`; the job's `usage` reports what it charged.
- Takes: 3–6, more for short or expressive lines (T10 §3).
- A take whose rate is far from the voice's measured rate (video-recap flags beyond about ±30 %, T10 §3) has
likely dropped, repeated or invented words: check it first.
- Style or emotion directives: compare with a neutral take of the same text. One source treats +25 % duration as
"took effect" and < 5 % as "not applied" (jianshuo, T10 §3); listen as well.
- Takes you do not place stay in `voiceover/generated`; place them with `voice place`, not `media import` (that
loses the voice text and the AI provenance).
## 4. Check every kept take
```sh
bashcut voice check --item ITEM # job: similarity, words matched/substituted/missing, extra words
bashcut voice check --media MEDIA --text "The text it should say"
bashcut voice check --items all # job: every voiceover item at once, similarity and unmatched words
```
The model misreads, drops or repeats words, mostly **at the start** of a line. Read `words` and `extra`, not
only `similarity`: a repeated word barely moves it. Pass `--min-similarity` only when you want a pass/fail flag
(one kit gates at 0.92 for Chinese, T10 §3); the call is yours. Recognition also mishears some regional accents
(s→x, final n→ng): count only errors a listener would hear. Fix by rewording or a re-take.
## 5. Fit to picture
Fix in order of naturalness (T10 §4, jianshuo ladder):
1. **Shorten or reword the text**, then re-take.
2. **Provider rate**, when the voice `supportsRate` (jianshuo slows −12…−15 % before stretching, T10 §3).
3. **Stretch with pitch kept**, within bounds you pass:
```sh
bashcut voice fit --item ITEM --to-frame F --min-ratio MIN --max-ratio MAX --base-rev N
```
Outside the bounds nothing changes and the error gives the speed it would need. Ranges seen: slowing to
0.82–0.95 (floors 0.82 jianshuo, 0.85 digitalsamba; imperceptible above ~0.92); speeding up to about 1.1 and
only after rewriting failed (T10 §3). How much stretch goes unheard varies with the voice's timbre and how
much music masks it (T10 §3).
4. **Change the text more**, with the user's consent (it changes the captions).
Silence at a line end is often better than a stretched voice. Ranges seen: tail after the last word 0.1–0.5 s,
longer before a cut or reveal; gaps between sentences 0.2–0.8 s, up to 1–3 s after a key line (T10 §3).
- Never over real speech. Keep a gap from real voices (0.3 s worked in our edits); set the project's
`review.voiceoverMarginSeconds` so `review run` checks it.
- Give each voiceover section b-roll longer than the line: a too-short source clip made the voiceover run into the
next spoken cut.
- Captions for voiceover: write them from your text (`bc:captions-text`); `voice speak` times the item's captions
from the take.
- Level: judged against the music, not one number. Measure both (`audio measure --media ID`: loudness and
`presenceShare`) and set the balance with `bc:audio-mix`, which holds the ranges and sources.
## 6. Re-take one line
```sh
bashcut voice speak --replace ITEM --takes 1 # same text, a new take in place
bashcut voice speak "Reworded line" --takes 3 # new text: pick a take, then
bashcut voice place TAKE_PROJECT_PATH --replace ITEM # put it into the item
```
The new take keeps the item's place and its captions are timed again. Check it (`voice check --item ITEM`) and
fit it again if its length changed.
## 7. Dubbing and localisation
1. **Transcribe** the original (`media transcribe --media ID`, then `media transcript --media ID`) and measure
both rates: the original speaker (`speech rate --media ID`) and the target voice.
2. **Translate** per cue, keeping cue boundaries at sentence ends. Languages differ in length (Mandarin takes
60–80 % of the time Spanish does, jianshuo, T10 §2), so size each cue from the target voice's measured rate.
3. **Sample one cue first**: the most performance-sensitive one, not the first. Get the user's yes on voice and
pace before batching (OpenMontage, T10 §2).
4. **Per cue, fit ladder**: shorten the text → re-take → `voice fit` within bounds → move the cue
(`timeline move ITEM --track TRACK --at-frame F --base-rev N`) into nearby silence. Tell the user which cues
were shortened (it changes the meaning) and which moved.
5. **Keep the original audio as a bed** under the dub, not muted: jianshuo keeps it at 0.15–0.25 linear
(≈ −16…−12 dB, T10 §3); set it with `volumeDb` and check with `bc:audio-mix`.
6. Check every cue (`voice check`), then re-time captions from the takes.