assemble, as one filter graph over a silent anchor as long as
the picture. The narration and the audio of every clip are always in it. The music, the ambience bed
and the effects are the score, and each of them is in the mix only because the project named a
file for it. A sound placed on a cue lands on the second that cue resolved to, which
Cues explains. To put one sound on one cue from nothing, follow
Put a sound effect on a cue.
The parts of the mix
The anchor is what fixes the length. The picture carries no audio of its own, so without a silence as
long as the film the soundtrack would run as long as whichever layer happened to be longest.
decktalk assemble --skip score leaves the music, the ambience and the effects out and mixes
the narration and the clip audio alone. Nothing else changes.
What [mix] holds
[mix] lists every key that names a file and its level, and
[[mix.effect]] every key of an effect. A cued sound takes
a caption, and Captions carry the whole soundtrack
says what a viewer reads in its place.
The score stage
score is the fourth stage of the six. It generates the music, the ambience bed and the effects
this project describes, from the prompts in [score], through the sound provider
[score] provider names, which is ElevenLabs’s sound and music endpoints with the key and
base_url of [elevenlabs]. It spends, which is why it runs where it does: it is the last stage that can buy anything,
so decktalk build --to record stops before it and an unpaid draft loop never reaches it.
It is a stage like any other, so decktalk build reaches it with --from score,
--to score and --skip score, decktalk assemble --skip score mixes without it,
and decktalk score runs it alone.
--no-spend reports what it would ask for and buys nothing, which is the plan an author
reads before approving a spend. With neither flag, a run that would buy something prices it and asks
on a terminal, and a run with nothing left to buy asks nothing. A build asks once about its takes
and its sounds added together, and The ceiling is how --max-cost
refuses one. --replace-score is the one way to buy
again a sound the ledger holds. It acts only with --spend, is confirmed once on a terminal, and
never buys a take, while --force never buys a sound.
A sound is billed by the second of audio, at the rate in the sound provider’s own table. Each kind
states its rate in US dollars per minute of audio as dollars_per_minute in its own table,
[score.ambience], [score.effects] or [score.music], and a request costs the seconds it asks
for at that rate. The result’s cost reports the rate in the same unit, under the same name, so the
rate you typed is the rate you read back. A rate nobody stated is zero from the default layer, and --max-cost refuses a run that buys
at one, because the cap would guard a made-up price. The speech rate,
[elevenlabs] dollars_per_1000_characters, never prices a sound.
Every request the provider is paid for is a sound.charged line on the stream the moment it answers,
one per bed or effect and one per part of the music, and a run that bought something reports its
spend as charged.
What each table declares
Each table holds its prompt and its file beside the settings that size its request, spelled the
same way in every table.
[score] lists every key of each table,
and Configuration every setting with its range.
Music longer than [score.music] chunk_max_seconds is asked for in equal parts, because the
service will not write a long piece in one answer. Each part is told which one it is so it keeps the
key and the tempo, and the parts are joined with a crossfade of [score.music] crossfade_seconds.
decktalk.toml
How each generated file reaches the mix
This is the one join to get right.score writes files and the mix reads [mix], so the two
have to name the same path.
So naming the file in
[mix] is enough for the music and the ambience: score writes to the
path the mix will read. An effect has no such shortcut, because one generated sound can be cued in
several places, so a [[mix.effect]] file points at score/<name>.mp3 by hand.
The score directory
Every sound the score buys lands in the score directory,score/ unless [score] dir names
another folder inside the project, and you commit it the way you commit takes/. Each file is
named by its item, so score/ reads ambience.mp3, tick.mp3, music-part1.mp3 and the ledger
beside them. Deleting the build directory then costs nothing: the next run finds what it bought,
and joins the music again from its parts under build/score/, which is free. A run without
--spend reads the score directory and never writes it, so a clone that commits it, such as the
one the GitHub Action checks out, mixes the film with no key and leaves no tracked file changed.
--replace-score with --spend buys each item again and writes over the same file, after it asks
on a terminal.
score/ledger.json records what this project has already bought, keyed by the request
that bought it: the endpoint as the sound provider publishes it and the body it sends, and never
base_url, so a project that moves to another host of the same service buys nothing again. An item whose request still matches its row and whose audio is still on disk is kept
rather than bought again, and the ledger is written after every paid call, so a run stopped half way
through a long piece keeps what it has and asks only for the rest.
A planned item nobody has bought is a SOUND_MISSING warning
against the path a mix would look for, and assemble reports the same code when it meets that path
empty. The layer plays as silence and the film is still made, so decktalk build --no-spend on a
project with a score exits 0, and --fail-on warning makes it fail. A music file, an ambience file
or an effect file the project names and that no score item writes is the author’s own, so its
absence is a FILE_MISSING error. That layer plays as silence
too, and the build stops after assemble. An effect whose cue did not resolve is a sentence on the stream, and the sound is left out
of the mix and out of the captions.
Ducking
The music ducks by[mix] music_duck_db under every span of the film that carries speech. At the
default of -6 dB against a music_db of -24, the music plays at -30 dB there.
- A spoken section carries speech from where it starts until the end of its last word, or until it ends when it says nothing at all.
- A clip section carries speech for the whole of its own length, because its dialogue is its own.
- Each edge of a span ramps over
[mix] duck_ramp_seconds, which is 0.5 seconds by default.
The music rises as the speech of section 4 ends, then stays down under the clip.
[mix] ambience_ramp_seconds, and it reaches
[mix] ambience_pad_seconds past each edge of its section, so the room does not appear and vanish
exactly on a cut.
The mix mechanics
The settings of[mix] set the shape of the mix, beside the files and the levels that are the
table’s project content: the ramp of the music duck at each edge of a spoken span or a clip, the ramp
and the reach of the ambience bed, and the ramps into and out of a marker’s mute and swell.
Configuration lists each with its default and its range.
Markers
A markers file raises or mutes the music at spoken phrases, which gives the bed some structure without an edit to its audio.[mix] music_markers names the file.
media/markers.json
markers.json lists every key.
A marker resolves exactly as a cue does, against the words of its own section, by the rules in
Cues. Markers apply only where there is a music file. A markers file
that is not there leaves the music with no structure and says so, a marker whose phrase is not spoken
shapes nothing and says so, and a file that is not valid JSON fails the load with the file and the
line named.
Loudness
A clip’s sound is matched to the voice
A clip is made somewhere else, at whatever level its maker left it, so each clip’s sound is matched to the narration before it joins the mix.assemble measures the integrated loudness of the joined
narration and of each clip, and gains each clip by the difference, so a film playing in a clip section is as
loud as the voice beside it and the loudness pass below lifts both by one gain.
A narration that is a placeholder has no level of its own, so a clip is then matched to
[audio] target_lufs, which is where the pass would bring a real narration. A clip with no sound
is laid at unity.
The programme target
After the layers are summed, the loudness pass brings the whole mix to one programme target.- It measures the integrated loudness, the true peak and the loudness range of the mix.
- It applies one plain gain, which is the distance from the measured loudness to
[audio] target_lufs. A plain gain keeps the dynamics of the mix intact. - It runs a true-peak limiter at 192 kHz, so an inter-sample peak becomes a sample the limiter can
see. The limiter sits 0.3 dB under
[audio] true_peak_max_dbtp, attacks in 5 milliseconds and releases in 50, which is long enough not to pump on speech. - It measures the result again.
[audio] range_max_lu is the loudness range the measurement is made against, and its default
is 11 LU.
MIX_LOUDNESS reports what the pass could not fix. It is raised
when the measured true peak is above the ceiling, and when the measured integrated loudness sits more
than 1 LU from the target. It is a warning, because it is a measurement of a film rather
than a fault in it, and a reader decides.
A run with a placeholder take skips the pass. The placeholder is clicks and silence, and
normalising them would move the clicks the offset check listens for.
Your own audio files
Point[mix] music, [mix] ambience and each [[mix.effect]] file at any audio file ffmpeg can
read. A project that generates nothing needs no [score] table at all.
Keep a file of your own at a path no [score] table writes to. An item whose ledger row does
not match its request is bought again and written over whatever is at its output path, so a hand-made
bed under a generated bed’s path is replaced the next time the prompt changes.
decktalk init writes no [mix] sound keys and no [score] tables, so a new project makes no
sound beyond its narration until you add them.
Next
- Put one sound on one cue: Put a sound effect on a cue
- Give a sound its moment: Cues
- Look up every key and its range: Configuration
- See what costs money: Requirements