This section provides precise descriptions of all the features of the Syntoniq Language.
This section describes Syntoniq's forward and backward compatibility contract for the Syntoniq language. The compatibility contract does not apply to the keyboard application (hardware or prompt modes) or the command-line syntax of invoking the tools. It applies only to Syntoniq language files.
The first directive in a Syntoniq file must be
syntoniq(version=1)
Here are the specific guarantees. Each is followed by a few examples, but not an exhaustive list.
Within a specific version, we provide the following guarantees
Within a version, we may do any of the following:
If we change anything about the MIDI or Csound generation, release notes will describe the changes in detail, including what you need to do if you are migrating. If a change to MIDI or Csound output is too invasive, we may allow the old behavior to be selected.
Rationale for allowing some MIDI/Csound changes: while Syntoniq intentionally creates Csound and MIDI that are designed for additional manual enhancement, it is assumed that, people will not adopt a workflow where they are actively composing in Syntoniq and iterating by regenerating output and replacing it in a DAW or Csound file, and that people who are doing this would be able to handle the changes. We optimize for the case of having the unmodified output of Syntoniq being as good as possible.
A comment is started by ; and proceeds until the end of the line.
White space and newlines are ignored and are allowed in all contexts with the following exceptions:
A Syntoniq string is delimited by double quotes (") and may contain any valid UTF-8 text. To include \ or " in a string, precede with '. Strings may not contain embedded newlines.
Examples (preceded by directive parameters to be syntactically valid):
f(
s1="piano"
s2="a \" and a \\"
s3="ฯโฏ"
)A number may be
Examples (preceded by directive parameters to be syntactically valid):
f(
n1=0
n2=261.626
n3=1.5/12
n4=3/2
n5=31
)A pitch is one or more factors separated and optionally preceded by *.
In the description below, [x] indicates that x is optional. Letters represent numerical values. All other characters, including ^, /, and | are literal.
A factor represents either a rational number or a rational number raised to a rational power. It must be non-empty and adhere to the following pattern:
[a[/b]][^c|d]
where
a is a positive integer or decimal of up to three decimal placesb is a positive integerc is any integer, including negative numbers or zerod is a positive integerSince a pitch must be non-empty, at least one of a/[b] or ^c|d must be present.
If a[/b] is omitted, it takes the value of 2.
If a is present and [/b] is omitted, b has the value of 1.
See Pitch Primer for a detailed semantic description of pitches.
Examples (preceded by directive parameters to be syntactically valid):
f(
p1=2
p2=*3/2
p4=^7|17
p5=220*3^2|13
p6=3/2^-6|15
p7=261.3/2*2^1|2*3^1|3
)
## Directives
Directives take the form
```syntoniq
identifier(param="value" repeatable="value1" repeatable="value2")
Directive syntax details:
repeatable above. Repeating a parameter makes its value a list.=Example (syntactically valid but not valid Syntoniq):
identifier ( ; opening comment
one = 1
two = 22/7
three = "๐ฅโญ"
three = "ฯโฏ" ; repeated parameter
four = *3^-2|31*3/2
)
See Directive Reference below for the list of valid directives and their parameters. You can also run syntoniq doc.
Note names must start with an ASCII alphabetic character and may contain the following characters:
_*^/.|+-!\#%&The directive define_scale must be followed by a scale definition. A scale definition is delimited by << and >> and consists of sequence of pitches followed by note names.
You may define more than one note on a line. You may assign multiple names to a pitch. It is an error to have a duplicated pitch. Instead, add all the notes to the single definition of the pitch. This is based on the value of the pitch, not the representation. If you tried to add both ^2|12 and ^1|6, you would get an error message.
Scale definitions are described in detail with examples in Defining Scales.
The directive define_manual_mapping must be followed by a layout definition. A layout definition is delimited by << and >> and consists of a rectangular grid of note names optionally followed by cycle (e.g. octave) markers. A note name may be replaced by ~ to indicate an unmapped key. Exactly one item (either a note or ~) must be preceded by @ to indicate that it is the anchor note.
Layout definitions are described in detail with examples in Layout Engine.
Score blocks consist of groups of contiguous note lines and dynamic lines. A score block is terminated by a blank line or a line containing a directive.
You can find examples of score blocks throughout the manual including in the Complete Example section. Here's a simple example, repeated from the Quick Start section.
syntoniq(version=1)
; Here is some music
[p1.0] 1:g a b c'
[p1.1] 1:e f g g
[p1.2] 2:c 1:f e
[p1.3] 2:~ 1:d d
[p1.4] 1:~ a, g, c,
[p1] 64@0< 127@4
A Syntoniq part corresponds approximately to a part in a score. It is a container for notes and dynamics. A part may contain any number of simultaneous notes (subject to limitations of the instrument). In Syntoniq, certain properties apply at the part level, such as the following:
In the Directive Reference section, some directives take a part parameter to set part-specific parameters.
A part name must start with an alphabetic character and may contain only alphanumeric characters or underscore (_).
Note lines begin with [part_name.n], where, in this case the [ and ] characters are literal (not indicating an optional value) and n is a non-negative integer value (0 or positive) indicating a note number. Note that the note number is interpreted as a numerical value, ignoring leading zeroes. This is probably the least surprising behavior unless you are used to Csound. If you are accustomed to Csound, keep in mind that, since Syntoniq treats note numbers as numeric values, part.1 and part.01 refer to the same note. This is different from Csound, which treats these like floating point fractional parts. In Csound, part.1 and part.10 would be the same. We believe that, for anyone except a Csound user, it is less surprising to view the note line leader as a .-separated part name and numerical value.
After the line leader ([part_name.n]), a line consists of any of a sequence of
In this description of a note, the [ and ] characters represent optional values. The general syntax of a note is [duration:]name[cycle-markers][:modifiers]. Note pitches are absolute in Syntoniq. If you are coming from LilyPond, you might be accustomed to LilyPond's relative pitch mode. Syntoniq intentionally does not support relative pitch mode as this creates a lot of confusion when rearranging notes in a score. It is also impractical to define it in a meaningful way with arbitrary note names and pitches. We believe absolute pitch notation is the only sensible approach with Syntoniq, but it can be a source of momentary confusion if you are accustomed to using relative pitch notation in LilyPond.
Duration is mandatory for the first note in each note line. If omitted in subsequent notes, the value is repeated from the previous note. Default duration values do not carry across lines. This reduces surprises when splitting, joining, or otherwise rearranging lines in a score.
Duration is a rational number or decimal with up to three decimal places. It is measured in beats. This is similar to Csound. If you are used to LilyPond, notice the difference. In Syntoniq, 4:a means to play a for four beats. In LilyPond, a4 indicates the note a as a quarter note. In this case, Syntoniq's use of beat counts aligns it more with Csound. Note that, since Syntoniq durations are rational numbers, you can represent tuplets with perfect precision. For example: 1/3:c d e would be similar to eighth note triplets.
name is the note name, whose syntax is discussed above.
cycle-markers may be one of ' (one cycle up), , (one cycle down), 'n ($n$ cycles up) or ,n ($n$ cycles down). A cycle is usually an octave, but it may be defined to be any other interval using any of the scale definition directives. (See Directive Reference and Defining Scales.)
> โ slightly increases the velocity (MIDI) or amplitude (Csound) of the note; corresponds to an accent.^ โ like > but more; corresponds to marcato.. - may be repeated; shortens the note by one quarter of a beat as long as duration remains at least one quarter of a beat. This roughly corresponds to staccato. It is a shortcut and behaves the same regardless of the note length. For more precise control, you can use full-length notes with specific durations, such as 7/8.~ โ tie: sustains the note, holding the pitch constant across any subsequent holds (discussed below). If the subsequent note has the same pitch, this implements a tie. For Csound, if the next pitch is different, this acts like a slur, changing the pitch of the note without releasing and retriggering the note.& โ glide: sustains the note indicating the pitch should glide smoothly to the pitch of the next note. Like with ~, intervening holds extend its duration. The following note is re-articulated by default, but you can combine tie and glide to create chains of continuous pitch glides. For Csound, this implements smooth pitch changes. With MIDI, it causes several pitch-bend changes per second.You can indicate hold with ~. The ~ character can be preceded by a duration and must be preceded by a duration if it is the first item in the line. A hold means "keep doing what you're doing." That means that, following a tied note, a hold means to keep holding the pitch. Following a glide it extends the duration over which the pitch is changed. Following a non-sustained note, or as the first thing, it is a rest.
The | character may occur in any position in a note line except the beginning or end. When a bar check appears, Syntoniq performs the following validations:
If you are coming from LilyPond, this is similar to LilyPond bar checks, but since Syntoniq (intentionally) doesn't have the concept of time signatures, they are just alignment checks and visual separators.
Syntoniq ensures that every note line in a score block is the same length. If you make a mistake, the compiler will give you enough information to fix the mistake by telling you the computed duration of each line so you can easily find the error. Bar checks can help.
When generating MIDI output with MPE, every note with a given note number is always assigned to the same channel. This makes it easier to perform edits. When generating Csound output, there is a fixed mapping between Syntoniq note numbers and Csound note numbers that takes into consideration the differences between how each system uses note numbers.
Note numbers do not have to be sequential or contiguous.
Note numbers do not have to be in any particular order, but you may have only a single line per score block with a given part and note number.
If a score block doesn't sound any notes for a note number, you don't need a line for it. If the last occurrence of the note was sustained, the sustain eventually has to be resolved, but you can skip one or more score blocks.
The Syntoniq compiler is very thorough and gives clear errors with copious context. If you get it wrong, the compiler will help you fix it.
Dynamic lines start with [part_name] where, as with the note leader, [ and ] appear literally. Dynamic lines consist of a sequence of dynamics and bar checks.
A dynamic has the form (where [ and ] mean "optional") level@offset[change].
level is a value from 0 to 127 inclusiveoffset is a number of beats as an offset from the beginning of the line or the most recent bar checkchange, if present, may be < to indicate a crescendo. or > to indicate a diminuendo.When bar checks appear in note lines, Syntoniq validates that there are the same number as in the note blocks and that all offsets fall within the duration of the corresponding region of notes. Here again, if you get it wrong, the compiler will give you copious information to help you fix it.
If you use < or >, Syntoniq will enforce that there is a subsequent dynamic and that it is greater than (for crescendo) or less than (for diminuendo) the previous volume. This serves as an extra check. Volumes of 0 are allowed.
Below is an alphabetical list of directives. You can get this by running syntoniq doc.
Check that all pitches are the same. If multiple parts are specified, all specified notes must exist in all the parts' tunings. All parameters may be repeated.
Parameters:
Indicate the name or number of a Csound instrument number that must be
turned on at the beginning and must remain on for the direction of the
score. You may optionally provide a value for tail, which is a number of
beats beyond the total duration to leave the instrument on. This is useful
for effect instruments, like reverb. This is only useful when combined with
a custom Csound template that defines the instrument.
Parameters:
Set the Csound instrument number or name for zero or more parts. If no part is specified, this becomes the default instrument for all parts without a specific instrument. It is an error to name a part that doesn't appear somewhere in the score. You must specify exactly one of number or name.
Parameters:
Specify the name of a file, relative to the score file, that contains the Csound template to use. This can still be overridden from the command line.
Parameters:
Define a generated scale. Note pitches are generated according to the following rules:
+, -, #, %, !, and /.A and a represent the root of the scaleB through Y represent n/n-1 where n is the ordinal position of the
letter (B=2, C=3/2, D=4/3, etc.)b through y are n-1/n, the reciprocal of their upper-case
counterparts (b=1/2, c=2/3, d=3/4, etc.)Z followed by a number โฅ 2 represents n/n-1 (e.g. Z30 = 30/29)z followed by a number โฅ 2 represents n-n/n (e.g. z30 = 29/30)When divisions is specified, the following additional rules apply, noting that the divided
interval can be explicitly given and defaults to the cycle ratio, which defaults to 2.
An represents n scale steps up (divided_interval^n|divisions)an represents n scale steps down (divided_interval^-n|divisions)+ is short for A1 (raises the pitch by one scale degree)- is short for a1 (lowers the pitch by one scale degree)tolerance is not specified or the pitch is within tolerance of its
nearest scale degree, the pitch is rounded to the nearest scale degree,
and the # and % characters have no effect on the pitch.tolerance is specified and the pitch is farther away from its nearest
scale degree than tolerance:
# forces the pitch to the next highest scale degree% forces the pitch to the next lowest scale degreeThe specified divisions or divided_interval can be overridden by appending ! followed
by optional numbers separated by /. This causes the following additional changes:
! โ forces the exact ratio to be used, allowing pure ratios to be mixed with equal
divisions!n โ interprets the note as if divisions=n where specified!a/n โ interprets the notes as if divided_interval=a divisions=n where specified!a/b/n โ interprets the notes as if divided_interval=a/b divisions=n where specifiedExample: with divisions=17 and tolerance of 4ยข:
E is ^5|17 because 5/4 is between steps 5 and 6 (zero-based) but is
slightly closer to step 5E% is also ^5|17E# is ^6|17E! is 5/4E!12 is ^4|12See the manual for more details and examples.
Parameters:
divisions is given or specified as a single digit in an override;
defaults to cycle_ratio# and % โ # and % are ignored if computed pitch
is within tolerance of a scale degree; applies only when divisions is givenDefine an isomorphic mapping for a tuning. The mapping is placed into a layout with the 'place_mapping' directive.
Parameters:
Define a manual mapping of notes to keyboard positions. The mapping is placed into a layout with the 'place_mapping' directive.
This directive must be followed by a layout block.
Parameters:
Define a scale. The scale called "default" is pre-defined and corresponds to 12-EDO.
This directive must be followed by a scale block.
Parameters:
Mark a moment in the score. The mark may be used for repeats or to generate
a subset of musical output. There are no restrictions around the placement
of marks, but there are restrictions on what marks may be used as repeat
delimiters. See the repeat directive.
Parameters:
Set the MIDI instrument number for zero or more parts. If no part is specified, this becomes the default instrument for all parts without a specific instrument. It is an error to name a part that doesn't appear somewhere in the score.
Parameters:
Place a mapping onto a layout for a keyboard.
Parameters:
Repeat a section of the timeline delimited by two marks. The start mark must strictly precede the end mark. No tied notes or pending dynamic changes may be unresolved at the point of the end mark.
Parameters:
Reset tuning. If no parts are specified, clears all tunings. Otherwise, resets the tuning for each specified part to use the global tuning.
Parameters:
Tune the given parts so that the named note has the pitch that was previously saved to the given variable.
Parameters:
Save the pitch of a note to a variable that can be used with
restore_pitch. If no part is given, the note's pitch is retrieved from the
global tuning. If more than one part is specified, the note must have the
same pitch in all the parts. This can be used as a quick sanity check when
saving a note's pitch. See also restore_pitch and check_pitch.
Parameters:
Change the base pitch of the current tuning for the named parts, or if no
parts are named, for the default tuning. If absolute, use the pitch as the
absolute base pitch. If relative, multiply the base pitch by the given
factor. Example: set_base_pitch(relative="^1|12") would transpose the
tuning up one 12-TET half step. Only one of absolute or relative may be
given.
Parameters:
Set the syntoniq file format version. This must be the first functional item in the file.
Parameters:
Set tempo, with possible accelerando or ritardando (gradual change).
Parameters:
bpm to end_bpm over
duration beats.end_bpm to indicate the duration of a gradual tempo
change.Change the base pitch of the scale in a way that makes the new pitch of
written equal to the current pitch of pitch_from. For example, you could
transpose up a whole step in 12-TET with transpose(written=c pitch_from=d).
This method of specifying transposition is easily reversible even in non-EDO
tunings by simply swapping written and pitch_from. This can be applied
to multiple parts or to the default tuning. The parts do not all have to be
using the same scale as long as they are all using scales that have both
named notes.
Parameters:
pitch_from has before
the transposition.written note after transposition.Change the scale for the specified parts. If no parts are specified, change the scale used by parts with no explicit scale. This creates a tuning with the specified scale and the current base pitch.
Parameters: