cues.json is the cue file. It says which spoken phrase each moment the deck declares lands on,
section by section. The file owns seconds and the page owns moments: the page says what a thing
looks like and what it means, this file says when it happens, and neither ever writes the other’s
half. That is why no attribute in the markup carries a second and no row here carries a class or a
style.
cues.json
Shape
The file is one JSON object with asections key. Beside it only _comment may stand, which is
where the starter keeps its comment and which nothing reads. Any other key at the top level is
refused as an INPUT error naming the key, because the usual one is a section block pasted outside
sections, whose cues would never fire. Each key of sections is a section number
written as a string, and it must name a [[section]] in decktalk.toml. Each section is an object
with a cues array and an optional min_seconds.
The file is optional. A project without it resolves no cues at all, and the deck earns one
CUE_UNLISTED finding counting every moment it declares, whose fix writes the whole file with a row
for each of them.
The load refuses a file that is not valid JSON, a file with no top-level
sections object, a top-level key other than sections and _comment, a section
key that is not a number, a section number that decktalk.toml does not declare, a section whose
value is not an object, a row with a key no cue reads, a row with no id or no phrase, a row
whose id is empty, and a row whose occurrence is below 1. Each refusal names the file, and a JSON syntax error names the line as well. A
row whose phrase is empty loads, and CUE_UNRESOLVED says it has no phrase yet.
The keys of one row
Any other key is refused by name, so a typo can never move a cue in silence.
id, the cue id
id is the cue id of one moment the page declares, such as
4.1:expand. decktalk schema page prints every attribute that declares a moment.
phrase
The phrase is matched against the words of that section and nothing else. narrate wrote them
beside the take as <digest>.words.json, in seconds after the take starts. cue moves them onto the
section’s own clock, which begins at its lead. decktalk words prints them on that clock. Matching rules says how a phrase
meets a spoken word: case, punctuation, a hyphenated word and a word the voice spells out.
occurrence picks which match, counting from 1, and with no occurrence the cue takes the first and
cue names every second the phrase occurs at.
offset_seconds
offset_seconds is added to the matched moment, and it may be negative. The result is never allowed before
the section’s own start: a cue that would resolve earlier than zero lands at zero instead, which is
the case CUE_NO_ONSET names, because a second that is the section’s start rather than a word’s has
nothing to measure an onset against.
offset_seconds here is not the one of a sound effect. A [[mix.effect]] row in decktalk.toml
carries its own offset_seconds, which shifts the sound and not the picture.
The two anchors
Both anchors are literal strings, so a phrase is never mistaken for one. Neither is affected by
occurrence or case_sensitive.
A second counts from the start of its own section, and the section starts where its silence starts.
A section’s lead_seconds therefore moves every word, and so every phrase cue and $end, that much
later, while $start stays at zero.
What resolves a cue
decktalk cue reads the words each section speaks off the take index and writes
build/cue-times.json, one row per cue with the cue id, the phrase it matched, the second it fires
and the offset that was added. That file is what record, assemble and verify read, and no stage
works a second out from cues.json again.
Three findings come out of that pass.
The two files read against each other
A cue and the moment it fires are one thing written in two places, so each is read against the other. The page’s side is the catalog its runtime publishes, which names every slide of a scene and every moment its elements declare, already qualified into a cue id. It is read from that catalog and never from a scan of the markup, becausedata-spotlight, data-class and data-owns all declare moments no
text scan can see.
decktalk check makes the comparison against a catalog it reads live, and names every moment that
this file has no row for.
decktalk check --fix applies that fix, and Project.apply(finding) applies it from the library.
The fix only ever adds: it writes one row per moment nothing lists, it never rewrites a phrase you
wrote and it never deletes a row, so applying it a second time adds nothing, because by then every
moment of that section is listed. The row it writes leaves phrase empty, because the phrase a cue
lands on is your line and not DeckTalk’s. The file still loads with an empty phrase, and the row raises
CUE_UNRESOLVED until you write the phrase.
A row whose cue id no page declares is the mirror of it, CUE_UNKNOWN. It is named rather than
deleted. When exactly one unlisted row sits beside exactly one undeclared moment in the same section,
the two are read as a rename, because the phrase is what survives a rename, and the fix changes the
id alone.
Related
- Understand what a cue is: Cues
- See which slide owns a moment: The page contract
- Write phrases that time well: Writing for the ear
- Read what
cuewrote: Build artifacts