Kovati Docs

SpeechForge

Write a line, generate it, and get a sound wave with character-level timing attached.

Documented in depthSpeechForge0.2.2Open sourceSpeechForgeElevenLabs0.1.1Open sourceSpeechForgeDeepL0.1.2Paid

Write a line, generate it with a speech provider, and get a USoundWave imported into the project with character-level timing data attached.

One pipeline end to end: resolve the voice, price it, synthesise, import, cache.

SpeechForge in context — the video covers all three plugins, because in use they are one route.

For the route end to end — cast, write, generate, record, solve, localise — see the dialogue pipeline. This page is about SpeechForge's own half of it.

PluginRoleLicenceVersion
SpeechForge sourcecoreThe subsystem. Install this one.Open0.2.2
SpeechForgeDeepLproviderA route to a generator — a vendor API, or a container on your own GPU.Paid0.1.2
SpeechForgeElevenLabs sourceproviderA route to a generator — a vendor API, or a container on your own GPU.Open0.1.1
SpeechForgeToolset sourcetoolsetThe same subsystem as typed MCP tools, for an agent. Adds nothing of its own.Open0.2.2

What it deliberately does not know

SpeechForge knows nothing about dialogue trees, subtitles, montages, facial animation or any gameplay framework.

Those are adapters built on top — the Narrative Pro one lives outside this set. It also ships no providers itself; each registers against it at module startup.

The screenwriter model

The shape it was designed around: any dialogue scene has speakers. Define them — identity, voice, provider. Then write lines and assign them to speakers. Then generation and performance on top.

Cast

A Speaker (SP_) is the character sheet: an id, a display name, casting notes, a voice profile, and bindings out to whatever framework you use.

The set of speaker assets is the cast list. There is no separate list to keep in step.

Write

Lines, with text and direction, each assigned to a speaker — and a per-line voice override where one line needs something different.

Produce

Price the selection, generate, import. The pipeline table shows what is current, what is stale and why.

Perform

Take the generated audio onward — faces, body motion, and the rest.

A fifth page, Ingest, brings lines in from somewhere else.

Voice resolution walks four steps

StepBeats
1. Line overrideEverything
2. Speaker sheetThe bank and project defaults
3. Bank defaultThe project default
4. Project defaultNothing

Every result is stamped with which step answered. A voice that turned out wrong is then a question with an answer, rather than an archaeology expedition.

Staleness is computed live, over the resolved request

The content hash is taken over the resolved request — text, concrete voice id, model, settings, seed — rather than stored, and rather than taken over an asset reference.

So re-pointing a voice profile somewhere else is caught, not silently missed.

A hash is an answer to a question, and the staleness check must ask the same question.

Synthesis is a function of the text, so a generated line hashes its text. A voice conversion never reads the text at all, so it hashes its source audio and its voice. An early version stored a conversion hash but recomputed a text hash to test freshness — two different questions compared as one — so every converted line read as permanently stale, blaming the one thing that could not have mattered.

Anywhere a hash is written down, check every place it is recomputed.

Graduation has two axes

Not one status, but two independent ones:

AxisValues
StatusWhere the line is in the pipeline
OriginWhere the audio came from: Generated, Accepted, Edited, Recorded

That separation makes "stale and already recorded" expressible — and that is the one report with real money attached, because it is the list of lines somebody paid an actor for and then changed the script under.

They are two columns in the panel for the same reason:

The Produce page of the Speech Library listing seven lines with separate Status and Origin columns. Every Status cell reads Generated. The Origin column reads Generated for five lines and 'Recorded (revo…' for GEN_Doric_Rule and the player's line. Lengths run 2.80s to 7.20s.
Every line here has Status 'Generated' — that is how far down the pipeline it got. Two have Origin 'Recorded' — that is where the audio came from. One column could not have said both, and the pipeline needs the second to know what it may overwrite.

The four buttons on Produce

They look like variations on one action. They are four different promises about money.

ButtonWhat it does
Generate SelectedThe selection, skipping what is current
Generate All"Generate every line in this bank that is missing audio or stale. Current lines cost nothing and are skipped; recorded lines are never touched. Safe to press twice — the second press finds nothing to do."
Dub from SourceOnly on a localised bank. See localisation
Re-generate Selected"Force: re-generate the selected lines whether they are current, stale or missing, after a confirmation with the cost. The one thing force never does is overwrite a recorded performance — those lines are skipped."

Nothing spends without a priced question first

Every one of them raises a confirmation naming the count and the figure, and the figures come from the same resolver that submits — so the estimate is the price of the request that would go.

Generate All, on a bank with work to do:

Generate N line(s) that are missing audio or stale, ~X.XXX USD?

M current line(s) are skipped and cost nothing. Recorded and hand-edited lines are never touched.

Re-generate Selected adds a paragraph per thing you should know, and the whole scene selected looks like this:

A modal Unreal Message dialog with a warning triangle. It reads: 'Force re-generate 7 line(s), ~0.041 USD?' Then: '5 of them are current - this re-pays for audio that did not need it.' Then: '2 recorded, edited or accepted line(s) stay untouched - the pipeline never overwrites audio it no longer owns. Re-record or run a pickup session for those.' A copy icon sits bottom left; Yes and No buttons bottom right, with Yes highlighted blue.
Seven lines, four cents, and two sentences that between them describe the whole graduation model. Five are current and would be re-paid for; two are recorded and cannot be overwritten at any price.

The second paragraph exists to talk you out of it. Force is for the case where a line looks current and is not; the dialog counts how many of your selection are in fact current and says plainly that you are about to pay for them again.

The third is the one that cannot be overridden. "The pipeline never overwrites audio it no longer owns" is not a warning about this press — it is a statement that those two lines are outside force's reach entirely, and the only routes to them are a re-record or a pickup session.

On a bank where there is nothing to do, Generate All does not open a dialog at all — it answers "Everything is current — nothing to generate."

For scale: the seven-line scene these captures come from is 411 characters, which is the ~0.041 USD in the dialog above. Character-billed synthesis is cheap per line; the dialogs exist because a bank is not seven lines, and because a forced re-generation of a thousand current lines is a real way to waste a real amount of money.

Generated audio is never discarded when a line graduates to Recorded, and a forced regeneration explicitly refuses to touch anything that is not Generated.

Provenance follows the source, not the operation. Re-voicing a recording — or any audio the pipeline cannot attribute — graduates to Recorded. Unattributable audio counts as a recording deliberately, so that nothing can overwrite a performance that cannot be regenerated.

Alignment

Timing always comes from the provider's timestamped endpoint, trimmed and re-based so direction tags inside the sent text do not throw off subtitle timing.

Where that re-basing cannot be trusted, the result says so with a flag rather than quietly shipping timings that are a few hundred milliseconds out.

Where to go next

Release notes

Every version of every plugin in this set.

On this page