Visual Click Track — Track File Format (.vct) v0.1
A plain-text, line-based format that describes the structure of one song: its tempo, meter and the ordered list of sections (count-in, intro, verse, chorus, break, …). The player turns this into a bar/beat timeline that drives
- the screen: current section, next section, bar and beat position, and a big countdown to the next section
- the voice cues: "Chorus… 3, 4" announced before each section
- an optional backing track later on, synced to the same timeline
The format is meant to be written by hand, quickly, by a worship leader or MD, in any text editor or on a phone.
This is the specification. To learn the format step by step, with examples, start with the guide, guide.md.
1. At a glance
title: Example Song
tempo: 72
time: 4/4
count-in: 1
Intro 4
Verse 1 8
Chorus 8 x2
Bridge 8 @76
Chorus 8 loop
Ending 1 hold
That is a complete, valid file.
2. File basics
| Rule | Detail |
|---|---|
| Encoding | UTF-8. |
| Extension | .vct (recommended; the player also accepts .txt). |
| One song per file | A set (a list of songs) is a separate kind of file: see §12. |
| Lines | Each line is one of: blank, comment, header, section, marker, lyric. |
| Comments | # to end of line, when the # starts the line or follows a space/tab. So key: F#m keeps its sharp, and # inside a "quoted string" is literal. |
| Whitespace | Leading/trailing whitespace is ignored except to make markers readable. Runs of spaces/tabs separate tokens, so columns can be aligned freely. |
| Case | Header keys and modifiers are case-insensitive. Section names keep their case for display. |
A file has two parts, in this order:
- Header —
key: valuelines describing the song and its defaults. - Body — section lines (with optional marker and lyric lines under them), in playback order.
The body starts at the first line that is not a header line.
2.1 Including another file
include: stock/ending.vct
An include: line (in the header or the body) reads another file in its place.
The file is named relative to the song's own folder and can't leave it
(no .., no absolute path). It holds what the body holds: sections, markers,
lyrics and comments, and can include other files (up to 4 deep). A header line
in it is an error. Problems in it are reported at the include: line, with the
included file's name and line number in the message. Players that can't read
files (the web version) report include: as an error. A set list stores each
song's own text, so included content is read from the folder each time a song
loads. The editor reads each included file once while it's open, so reopen it
after changing one.
3. Header
key: value
| Key | Value | Default | Meaning |
|---|---|---|---|
title |
text | file name | Song title, shown on screen. |
artist |
text | — | Display only. |
key |
text, e.g. A, Bb, F#m |
— | Musical key, display only. A section can modulate with key= (§4.5). |
key[Name] |
text, e.g. key[Sam]: G |
— | A singer's own key for the song. Kept for tools and display; one line per singer. |
capo |
fret, 0–11 |
— | Capo position, display only. |
ccli, copyright, tags, source |
text | — | Song information for tools and lists: licence number, copyright line, free tags, where the chart came from. Not shown on the stage. |
layout |
layout name or file | — | The layout the player's own screen wears while this song is on stage, in place of the set's or the player's (the same names as a set's layout:, §12). L on stage still switches it. |
stage-layout |
layout name or file | — | The layout every stage display (the monitors other than the player's own) shows while this song is on stage, in place of the set's stage-layout: lines and each display's own, e.g. click (§12). |
stage-layout[Display] |
layout name or file | — | The layout one stage display shows while this song is on stage, e.g. stage-layout[EPSON PJ]: singers, over stage-layout:. The name is the display's as the player's displays list shows it (a second of the same name is Name (2)), matched ignoring case. One line per display; a display named twice, or a name left empty, is an error. |
lang |
language code, e.g. en |
— | The language of the lyric lines with no language label (§5.1). |
tempo |
number, e.g. 72, 140.5 |
required | Starting tempo in BPM (see §6). |
time |
N/D, e.g. 4/4, 6/8 |
4/4 |
Starting time signature. |
count-in |
integer bars, 0–4 |
1 |
Bars of spoken count before the first section. |
countdown |
up | down | off |
up |
Style of the spoken count in cue bars (§7). |
cue-ahead |
1 | 2 |
1 |
How many bars before a section its name is announced (§7). |
vct |
0.1 | 0.2 |
— | Format version. Informational. |
audio |
file path / URL | — | Reserved — backing track file. |
audio[Label] |
file path / URL | — | One labelled stem of a multi-track backing track, e.g. audio[Drums]: drums.wav. Repeat it for each stem; all stems play together, and the player has a mixer to mute them and set their volume and pan live. Stems must be sample-aligned (same start, e.g. exported from one DAW session). May be combined with audio:, which then plays as one more stem labelled Backing unless audio-mode picks one or the other. After the file, in any order: muted (audio[All]: full.wav muted) starts the stem muted in the mixer; gain= sets the stem's level in decibels (gain=-3, gain=+1.5, gain=-3dB; at most ±40, boosts limited, not clipped), applied before and on top of audio-gain; pan= sets where the stem starts in the mixer's pan, from -100 (left) through 0 (centre) to 100 (right), e.g. pan=-20, and a right click on the pan slider goes back to it. Both default to 0, and both apply to --render's mixdown. A bad value is an error (invalid stem gain …, invalid stem pan …). audio-offset and audio-gain apply to all stems. Labels are unique, and Backing is reserved. |
audio-mode |
backing | stems |
both | Which audio plays: backing the plain audio: track, stems the audio[Label]: stems. Not given, they play together. So a song can name both a backing track and its stems and switch with one line. |
audio-offset |
seconds, e.g. 0.35 |
— | Reserved — time in the audio file where bar 1 of the first section (after count-in) begins. Without it the player finds it by beat detection; 0 means bar 1 is at the start of the file. vct align --write sets it from a measurement. |
| audio-gain | decibels, e.g. -3, +1.2, -3 dB | — | The backing track's level: the player applies this gain when it plays the track, so recordings made at different levels sit together. Boosts are limited (peaks held at -1 dBFS), not clipped. A song with audio-gain is left as written when the player's Normalise is on; without it the track is normalised. vct loudness --write sets it from a measurement. |
| version | text, e.g. Acoustic | — | This file is an alternate version of the song. Shown in the song list's version column; the song list says Original when there is none. Original itself is reserved. Songs that share a title but have different versions aren't flagged as duplicates (§12). |
| arrangement | Name = Section, Section, … | — | A named order in which to play the song's sections (§3.1). May be repeated, once per arrangement; the first is what plays by default. |
Unknown keys produce a warning and are otherwise ignored, so newer files still open in older players.
3.1 Arrangements
Write each section once, then list the orders it can be played in. The first arrangement plays unless a set picks another (§12).
arrangement: Full = Intro, Verse 1, Chorus, Verse 2, Chorus x2, Bridge, Ending
arrangement: Short = Intro, Verse 1, Chorus, Ending
arrangement: Radio = Verse 1, (Chorus, Verse 2) x2, Chorus*4
An arrangement lists section names separated by commas, and plays them in that order; a name may be listed more than once. Each item is:
| Item | Meaning |
|---|---|
Name |
Play the section once, as written. |
Name x2 |
Play it twice in a row (the xN modifier of §4.3, so the screen shows 1/2, 2/2). Replaces the section's own xN. Not allowed on a loop section. |
Name*4 |
Play it at 4 bars this time. Markers and lyrics past bar 4 are an error. |
(A, B) x2 |
Play a group twice. Groups nest, and x2 is optional. |
The section's markers and lyrics are written once and used wherever it plays.
- Names match case-insensitively and ignoring spaces (
Verse1findsVerse 1). Every name must be a section in the file (an error otherwise), and if a name is used by more than one section line the first one is used. - Tempo and time signature changes are resolved in file order, so a section plays at the tempo it has in the file wherever the arrangement puts it. The count-in stays.
- Arrangement names must be unique and can't be
Original. - A file with no
arrangement:lines plays its sections as written, once each, in order. A file with some always plays an arrangement: the first, or the one a set picks. - A section only needed by one arrangement still has to be in the file. List every section you want in the default arrangement, in the first one.
4. Sections
A section line has a name, a length, and optional modifiers:
<name> <length> [modifier ...]
Verse 1 8
Chorus 8 x2 say="Big chorus"
Break 2 2/4
Bridge 8 4/4 @76>80
4.1 How a section line is read
Tokens are split on whitespace (a "quoted string" is one token). Then,
scanning from the right:
- Tokens that match a modifier pattern (table in §4.3) are modifiers.
- The next token must be a length — otherwise it is an error.
- Everything to the left of the length is the name.
This lets names contain spaces and numbers without quoting: in
Verse 1 8 x2, x2 is a modifier, 8 is the length and Verse 1 is the
name. To use a name that would itself look like a modifier or length
(rare), quote it: "x2" 4.
Gotcha: a numbered name with the bar count forgotten,
Verse 1, is a valid line: a 1-bar section called Verse.vct outlinemakes this easy to spot.
A few looser spellings are accepted and mean the same thing:
Verse 1: 8, [Verse 1] 8 and Verse 1 (8) are all Verse 1 8. (Before the
first section, Intro: 4 is a section, not an unknown header, when the word
is one of the usual section names of §4.5 and what follows the colon is a bar
count; any other word: 120 there is an unknown header and just a warning.)
A section line can carry on onto the next line, which helps on a phone:
Chorus 8 x2 @76
+ say="Big chorus" show="Chorus!"
+ ahead=2
A line starting with + and a space adds its modifiers to the section line
above (and the lines after it, until something else starts). Anything wrong in
the joined line is reported at the section's own line.
4.2 Length
| Form | Meaning | Example |
|---|---|---|
N |
N whole bars | 8 |
N+B |
N bars plus B extra beats (a final partial bar) | 2+2 = two bars, then a 2-beat bar |
0+B |
A single partial bar of B beats (pickup / push) | 0+2 |
A partial bar uses the current beat unit, so 1+2 in 4/4 plays one 4-beat bar
then one 2-beat bar. If a song changes meter for longer than a bar, prefer a
time-signature modifier instead.
4.3 Modifiers
| Modifier | Meaning | Persists? |
|---|---|---|
xN |
Play the section N times in a row (x2, x3, up to x99). Screen shows Chorus 1/2, 2/2. |
no |
@T |
Change tempo to T BPM from the start of this section. | yes |
@A>B |
Tempo ramp: starts at A, reaches B by the end of the section. B carries on afterwards. | yes (B) |
N/D |
Change time signature from the start of this section. | yes |
say="…" |
Text the voice speaks when announcing this section, if different from the name. | no |
show="…" |
Text shown on screen instead of the name (e.g. lyrics hint). | no |
silent |
Don't speak the announcement for this section (screen still shows it). | no |
ahead=N |
Override cue-ahead for this section's announcement. |
no |
loop |
Open repeat: keep repeating until the operator presses Next; then the current pass finishes and playback continues. | no |
hold |
After the section's bars, stop the click and wait (fermata / free time). The operator presses Next to continue. | no |
quiet |
Mute the click (but not voice cues or the screen) during this section — e.g. an a-cappella moment. | no |
half |
The click sounds on every second beat (1, 3, …) of each bar; the others are silent. Voice cues and the screen are unchanged. | no |
nojump |
A controller pad can't jump to this section. Only the first sixteen sections without it get a pad (vct-player, session pads). Changes nothing in the timeline. |
no |
tacet |
Nobody plays: the click is muted (as quiet) and the screen shows TACET. Voice cues still work. |
no |
dyn=D |
How loud the section is (§4.4). dyn=D1>D2 ramps across the section. Shown as a tag on the stage. |
yes (D2) |
feel=F |
The feel, shown as a tag: straight, half-time, double-time, swing, rubato, build or drop. Display only. |
yes |
key=K |
Modulate: the section is in key K (a note name such as D, Bb, F#m), and so is every section after it until another key=. See §4.5. |
yes |
lead=name |
Who leads from this section on. Shown as a tag. | yes |
harm=name |
Who harmonises in this section only. Shown as a tag. | no |
note=text |
A reminder for whoever edits or prints the chart. Not shown on the stage. | no |
band=text |
Who plays, e.g. band="keys, pad". Kept for display; any text. |
no |
pulse=N |
Accent every N beats instead of the meter's own grouping (pulse=2 in 6/8 accents beats 1, 3 and 5). N must divide the bar's beat count. |
no |
"Persists" means the value applies to this section and every following section until changed again.
4.4 Dynamics and feel
dyn= takes one of six levels, written as a letter, a number or a word
(case-insensitive); anything else is an error:
| Level | Letter | Word |
|---|---|---|
| 1 | pp |
whisper |
| 2 | p |
quiet |
| 3 | mp |
soft |
| 4 | mf |
medium |
| 5 | f |
loud |
| 6 | ff |
full |
The three spellings mean the same, and can be mixed in a ramp
(dyn=quiet>ff). A level carries on to the following sections until changed,
like tempo; a section before the first dyn= has none. feel= carries on the
same way (feel=straight cancels half-time). As with tempo, they carry on in the
order the sections are written, so an arrangement (§3.1) that moves a section
plays it at the level it has in the file. Neither changes the timeline:
they appear in vct dump, in the stage's tags and for tools reading the track.
A marker can change them part-way through a section: a marker whose text
is only dyn= and/or feel= settings changes the dynamic or feel from that
beat, shows no banner, and fires every pass:
Verse 1 8 dyn=p
> 5 dyn=mf
> 7.3 dyn=f feel=half-time
A marker takes one level, not a ramp. If any word is something else
(> 5 dyn=p but louder), it's an ordinary banner. What the last such marker
sets carries on into the following sections.
The stage shows the current dynamic as its letter and six rising bars (a ramp
dims the bars it climbs through), the feel as a tag, and band= as one tag per
comma-separated part.
4.5 Keys and singers
A section's key is the header key:, or the latest key= before it. A
key= that differs from the key before it (ignoring case) is a modulation:
the section is marked with the key it came from, and when both keys are note
names, how many semitones up the new root is (C to D is +2; D to
Bb is +8). The stage shows KEY C > D +2 on that section, and vct dump
has key, key_from and key_shift. The key is information for the band;
nothing is transposed.
lead= names who leads from that section on, until another lead=. harm=
names who harmonises in that section only. Names are free text (quote them if
they contain spaces).
4.6 Section names
Any text. The player recognises common names case-insensitively for styling (colour on screen) but attaches no behaviour to them:
Count-in, Intro, Verse, Pre-Chorus, Chorus, Post-Chorus,
Bridge, Interlude, Instrumental, Turnaround, Break, Breakdown,
Fill, Build, Tag, Vamp, Refrain, Outro, Ending.
A trailing number (Verse 2, Chorus 3) is part of the name.
5. Markers (in-section events)
Indented lines starting with > attach a timed message to the section above:
Verse 1 8
> 5 Drums in
> 8.3 Hits on 3 & 4
> <bar>[.<beat>] <text>
baris 1-based within the section (and within each pass of anxNrepeat — the marker fires every pass).beatis optional, 1-based, default1.textis shown on screen as a flash/banner from that point until the next marker or section change. Markers are not spoken unless the line ends withsay(speaks the text:> 5 Drums in say) orsay="…"(speaks something else:> 5 Drums in say="Drums").
5.2 Marker options
A marker can say more than "bar and text". Between the position and the text, in any order:
> 5-8 Hold this # the banner stays through bar 8
> last-bar.3 Big finish # the section's last bar, whatever its length
> 5 [drums] Hits on 3 & 4 # for drums only
> 6 !watch Eyes on the MD # a kind of cue
> 8 !stop # a kind with no text uses its name: STOP
> 4 pass=2 Add the harmony # second pass of an xN or loop only
| Option | Meaning |
|---|---|
5-8 |
In the position: the banner stays up through bar 8 instead of until the next marker or section. The end is a whole bar, not before the start, and inside the section. |
last-bar, last-bar.3 |
The section's final bar (and beat 3 of it). Follows the section when an arrangement plays it at another length (Name*4). |
[role] |
Who it's for ([drums], [lead vocals]). The stage shows every cue with its role as a tag, or with vct-player --role drums only that role's and those for everyone (no role, or [all]). --role is a testing option (scripts/screenshots.sh uses it); no screen, layout or set setting filters by role yet. |
!kick !stop !build !breathe !watch |
The kind of cue; the stage titles the banner with it instead of CUE (!stop in red). Any other !word is an error. |
pass=N |
Only on pass N of a repeat (xN, or a loop pass). The coming-up list shows it only when that pass is next. |
Options are only recognised in these exact forms, so older marker text keeps
its meaning: a [role] needs more text after it (or a !kind), !word is
an option only for the five kinds (any other !word, such as !!!, is plain
text, and vct lint warns about it), and pass= only with digits (pass=0 or
over 99 is an error). [pause] hold is a role pause with the text hold.
A marker made only of dyn= / feel= settings (§4.4) takes the same options
except that it shows no banner.
Chords. A marker whose text starts with | is a chord line: the chords of
each bar from the marker's bar on, one |-separated group per bar:
Verse 1 4
> 1 | A E | F#m D | A | E |
> last-bar | E |
Chords are free text (F#m, D/F#, N.C.), kept as written. There's no
banner; the stage shows the chords of the current bar, and the next bar's,
dimmed, at the right of its top row. A line that runs past the end of the
section is an error, as is an empty bar. They repeat every pass like other
markers (and take pass=).
A chord line says which bar each chord is in. To say which word a chord falls on, write it in the words instead (§5.1); a song can have both, and usually does: chord lines for the bars nobody sings over, chords in the words for the rest.
5.1 Lyrics
Indented lines starting with " and a space give the words, timed like
markers, for the section above:
Verse 1 8
" 1 Amazing grace, how sweet the sound
" 3 That saved a wretch like me
Chorus 8
" 1 How precious did that grace appear
" 4.3 The hour I first believed
" <bar>[.<beat>] <words>
-
The position works as for markers: 1-based within the section, and the line comes back on every pass of an
xNrepeat orloop. -
The words run to the end of the line. Quotes in them are literal; a
#after a space starts a comment as usual. -
A section's lyric lines must be in order of position, one per beat.
-
" 1 one / two / three / four(words separated by/, with spaces) is several lines spread evenly from the position to the end of the section: an 8-bar verse gives lines at bars 1, 3, 5 and 7. The position must be on a bar line, and the lines must fit the bars exactly; otherwise the words are shown as written. Write\/for a slash that never splits (Father \/ Son). A[tag]applies to all of the lines. -
A
[tag]straight after the position says who sings the line:" 5 [Ann] That saved a wretch like me. The stage shows it beside the line (with theON 3tag when there is one); it's not part of the words. With no words after it (" 5 [Instrumental]) the brackets are the words. Anything else in the words, such as(echo)or(response), is shown as written. -
Chords in the words.
[C]in the words puts a chord over the character it is written against, as a chord sheet does:Verse 1 13 " 1 [C]I don't know you [C/F]But I want you " 3 And games that never a[G]mountThe brackets are not part of the words: the chord is shown above them (
Lyric.chordsgives each chord's name and its column intext, andvct dumphas them). A chord written after the last word belongs to the end of the line. Names are free text, as in a chord line.A tag is told from a chord by the space after it:
[Ann] wordsis a tag,[C]wordsis a chord. So a chord must sit against the word it falls on, which is what you want anyway. A bracketed word on its own (" 5 [Instrumental]) is still the words, and brackets with a space in them ([2 bars]) are still text; for a bar nobody sings over, use a chord line (§5.2).vct lintwarns about a[tag]that reads as a chord, and about a chord name that doesn't. -
A language label after the quote gives another language's version of the words:
"fr 1 Quelle grâce. A label is 2 or 3 lower-case letters, then optionally-and two capitals ("fr,"fr-CA), then a space and a position. Lines with no label are in the languagelang:names. The player shows one language at a time: the unlabelled one unless a set picks another withlang=fr(§12). Each language keeps its own position order, and a section with no lines in the chosen language shows none.Track.langslists the labels found. -
On screen, a line stays up until the next line starts, or until a section with no lyrics starts. So a pickup line written at the end of one section carries on into the next. The coming line is shown dimmed beneath it, with an
ON 3tag in front when it doesn't start on beat 1. -
A lyric line with a position but no words (
" 5) clears the lyrics: from that beat nothing is shown as the current line. The line that follows is then shown dimmed only from one bar before it starts, rather than all the way through the gap. -
A
"followed by a space always starts a lyric line, so a quoted section name can't begin with a space.
6. Tempo and meter
- Tempo is beats per minute, where a beat is the denominator of the current
time signature:
@72in4/4is 72 quarter notes per minute;@180in6/8is 180 eighth notes per minute (the click sounds on every eighth, accented on 1 and 4). - Each beat produces one click; beat 1 of the bar is accented.
- A ramp
@A>Bchanges tempo linearly per beat across the whole section (including allxNpasses).
7. Voice cues and the countdown
Every section except the first is announced before it starts. Two styles,
chosen with cue-ahead (globally) or ahead= (per section):
cue-ahead: 1 (compact, default) — in the last bar of the previous
section, the name is spoken on beat 1, then the second half of the bar is
counted:
4/4, countdown: up | "Chorus" · "3" "4" | ▶ Chorus
4/4, countdown: down | "Chorus" · "2" "1" | ▶ Chorus
3/4, countdown: up | "Chorus" · "3" | ▶ Chorus
(Counted beats are those after the first half of the bar: beats
ceil(n/2)+1 … n of an n-beat bar. A 2-beat bar counts only beat 2; a
1-beat bar only gets the name.)
cue-ahead: 2 (full) — the name is spoken on beat 1 of the second-to-last
bar, and the whole last bar is counted:
4/4, up | "Chorus" · · · | "1" "2" "3" "4" | ▶ Chorus
4/4, down | "Chorus" · · · | "4" "3" "2" "1" | ▶ Chorus
With countdown: off only the name is spoken.
Rules:
- Count-in bars count every beat ("1, 2, 3, 4"), whatever
countdownsays. The first section's name is shown on screen during the count-in but not spoken. silentsuppresses both the name and the count for that section.- A voice cue wins over a spoken marker on the same beat.
- A section repeated with
xNannounces the repeat as "Again" (override withsay=). - If the previous section is shorter than the cue window (e.g. a 1-bar
Fillbefore acue-ahead: 2chorus), the cue falls back to the compact style inside the bar(s) available. - Before a
loopsection ends, the cue for the following section is only spoken on the pass after Next has been pressed. - The screen shows the countdown regardless of voice settings: the big "beats until next section" number appears for the cue window, and bars remaining in the section are always visible.
- The player's built-in layouts also count the last bar before each
section (only that bar, even with
cue-ahead: 2) over the whole screen, to 1, hiding on the next section's first beat; the count-in's last bar is counted the same way. - When a song with a count-in is started from its first beat, the player waits
two beats (at the first beat's tempo) before the count-in, silent, while
the screen flashes a dot on each. The timeline is unchanged: this lead comes
before
t = 0, and a backing track starts that much later with it.
8. Errors and warnings
| Situation | Result |
|---|---|
Missing tempo |
Error. |
| Section line without a valid length | Error (line number reported). |
| Marker before any section, or marker bar/beat outside its section | Error. |
| Lyric line before any section, without a position, outside its section, or out of order | Error. |
Unknown header key, unknown key=value modifier |
Warning, ignored. |
| Time signature denominator not 2, 4, 8 or 16, or numerator not 1–32 | Error. |
| Tempo outside 20–400 BPM | Error. |
Partial bar (N+B) with B not shorter than the bar |
Error. |
loop combined with xN |
Error. |
dyn= not one of the six levels, feel= not one of the seven feels (also in a marker, which can't ramp) |
Error. |
Marker option errors: a range ending before it starts or outside the section, an unknown !kind, an unterminated [role, pass= out of range |
Error. |
pulse= not from 1 to 32, or not dividing the bar's beat count |
Error. |
| Header line after the first section | Error. |
Unterminated " |
Error. |
Diagnostics carry a line and, where it is meaningful, a column:
song.vct:12:9: error: ….
The player should refuse to start playback on any error and show the line number — a wrong bar count discovered mid-service is worse than no click.
9. Grammar (informal EBNF)
file = { line } ;
line = ws ( comment | header | section | marker | lyric | "" ) ws [ comment ] EOL ;
comment = "#" { any } ;
header = key ws? ":" ws? text ; (* only before the first section *)
section = name ws length { ws modifier } ;
marker = ">" ws mpos { ws moption } ws [ text ] [ ws say ] ;
mpos = position [ "-" int ] | "last-bar" [ "." int ] ;
moption = "[" text "]" | "!" word | "pass=" int ;
lyric = '"' [ lang ] ws position [ ws [ "[" tag "]" ] text ] ; (* quotes in text are literal; no text clears *)
lang = lower lower [ lower ] [ "-" upper upper ] ;
name = word { ws word } | quoted ;
length = int [ "+" int ] ;
position = int [ "." int ] ;
modifier = repeat | tempo | meter | kv | flag ;
repeat = "x" int ;
tempo = "@" num [ ">" num ] ;
meter = int "/" int ;
kv = ( "say" | "show" | "ahead" | "dyn" | "feel" | "band" | "pulse" | "key" | "lead" | "harm" ) "=" ( quoted | word ) ;
flag = "silent" | "loop" | "hold" | "quiet" | "tacet" | "half" | "nojump" ;
say = "say" | "say=" quoted ;
10. What the player builds from it
Parsing produces a flat timeline that the screen, voice and (later) audio
engine all read from. vct dump song.vct prints it as JSON
(tests/corpus/ok/minimal.json is a complete small example):
- sections — the count-in (if any) followed by one entry per pass:
xNrepeats are expanded, soChorus 8 x2gives two sections (pass1 and 2,of2). Each has its first bar and beat, bar and beat counts, start/end time, tempo at start and end, time signature, flags, markers and lyrics. - beats — one entry per click: time
tin seconds from the first count-in beat, globalbar,beatwithin the bar,accent(2 = beat 1, 1 = secondary, e.g. beat 4 of 6/8, 0 = none), the word to speak (say) and which section that word announces (cue), and the marker and lyric line starting on that beat, if any.
Conventions:
- Bar 1 is the first bar of the first real section. Count-in bars are numbered 0, −1, … backwards, so bar numbers match the chart.
- Times ignore pauses:
holdandloopstay as section flags for the transport to act on at runtime. A loop's cues for the following section sit in its last bar; the transport speaks them only on the pass after Next. - Because each beat has an absolute time, a backing track only needs
audio-offsetto line up: bar 1 is att = count-in length.
10.1 Runtime behaviour of Next
What the transport does when Next is pressed:
- Loop: Next ends the loop at the end of the current pass. Pressed in a loop's last bar, the pass still finishes and only the cue words still to come are spoken.
- Hold: Next before the hold point cancels the hold. Next during the hold starts the next section immediately, with no extra count-in.
- Neither: Next in a section with no
looporholdis ignored. - Hold on the last section waits for Next before the song ends.
loopandholdon one section take two presses: the first ends the loop, the second releases the hold.- Countdown window: the last bar of a section (the last two with
cue-ahead: 2); during the count-in, the whole count-in; the last section counts to the end of the song; blank while a loop is unreleased. - Timing: loop and hold decisions are made when the beat after the decision point is polled, so a press counts only before then. Hosts keep the lookahead short (tens of milliseconds; 0.1 s while stage sync is on).
11. Deliberately left out (for now)
- Short aliases for arrangement names (
V,PC,B): name the section what you want to type, or useVerse1forVerse 1. - Reusable section definitions (define Chorus once). Plain sequential
lines are easier to read on a phone;
arrangement:(§3.1) reorders them. - Swing / subdivisions / click sound choice — player settings, not song data.
- Compound-meter pulse (clicking dotted quarters in 6/8) — open question; a
pulse=modifier is the likely shape.
12. Sets
A set is a list of songs for one service. It is a .vct file in the same style, marked as a set by its set: header; the player keeps sets in the sets folder next to the songs folder (Documents/StageDisplay/ on a desktop).
set: Sunday Morning
layout: lyrics
stage-layout: click
stage-layout[EPSON PJ]: singers
theme: contrast
song: falling-slowly.vct
song: example-song.vct arrangement="Short" key=B tempo=90
| Line | Meaning |
|---|---|
set: Name |
Required. Names the set and makes the file a set. |
layout: Name |
The layout the player's own screen wears while this set is open, in place of the one its settings name. |
stage-layout: Name |
The layout every stage display (the other monitors) shows while this set is open, in place of each display's own. |
stage-layout[Display]: Name |
The layout the stage display called Display shows instead, as for a song (§3). One line per display. |
theme: Name |
The theme (colours, lines, fonts, sizes) for the same. |
song: file |
A song, by its file name in the songs folder (quote it if it has spaces). In playing order. |
arrangement="Name" |
Plays one of the song's arrangement:s instead of its first. |
key=K |
Shows this key instead of the song's. |
lang=fr |
Shows this language of the song's lyrics instead of the unlabelled ones (§5.1). |
tempo=N |
Plays the song at this starting tempo (20 to 400); every section's tempo scales with it. |
The set's look. layout: and theme: are names the player resolves as the
ones in its settings file are: your own NAME.json5 in its layouts or themes
folder first, then the built-in ones, and a value with a slash or ending in
.json5 is a file path. They apply from the moment the set is opened until it
is closed, to the stage, the stage displays that have no layout of their own
and the synced screens; the player's own come back with the next set. One
that can't be found leaves the player's own in its place and says why on
screen. A player's --theme or --layout, being for one run, wins over the
set's. Every line is optional: a set without them changes nothing.
Which screen shows what. The player's own screen (the host) and the
stage displays (every other monitor turned on in its displays list) can
wear different layouts. For the host, strongest first: a layout picked with
L on stage, --layout, the song's layout: (§3), the set's layout:, the
settings file's. For each stage display: the song's
stage-layout[Display]: for it, the song's stage-layout:, the set's
stage-layout[Display]: for it, the set's stage-layout:, the display's own
(from the displays list), else whatever the host shows. A song's plain
stage-layout: so wins over a set's line for one display: the song is
the nearer of the two. A song's lines apply while it is on stage and give
way to the set's when it leaves; a song that names a layout: forgets one
picked with L. A stage-layout: that can't be found leaves the displays on
what they would otherwise show, and says why on screen. A display the set
or song names that isn't connected is ignored. The built-in
click layout shows only the metronome, the count-in to each section and
the cues, for a band's screen.
Comments and blank lines work as in songs. A song that isn't in the songs folder shows in the set as one that can't be played, and an arrangement the song no longer has plays its first (or the song as written, if it has none), with a warning.
Duplicate titles. If two song files have the same title and the same
version (none counts as the original), the player shows each as Title (file name) and warns, since a list can't tell them apart. Giving one a version:
makes the warning go away.