Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
403 lines (369 sloc)
18.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SAU language reference | |
====================== | |
SAU is a simple language for mathematical audio synthesis, | |
without support for the use of pre-recorded samples. | |
The core idea of the language is that of time-ordered steps for | |
configuring audio generation: add an oscillator, then later, | |
change a parameter and extend play duration, etc. A script is | |
basically a list of such timed instructions. Language constructs | |
also offer more flexible arrangement of steps to take than a | |
bare flat list of instructions and forward time movement. | |
The current syntax uses keywords followed by zero or more | |
parameters with arguments. Each main keyword provides an action, | |
either at run time (like a function call in other languages), | |
or at parse time (like a global script setting). | |
The most important keyword, "O" followed by a wavetype, adds an | |
operator (FM synth terminology for oscillator with a certain | |
configurability). They may be connected as carriers and modulators. | |
They, and the timing of their running, make the core features of | |
the scripting language. | |
Keyword parameters may either be set (named, followed by writing | |
their argument(s)), or left out to use the previous value or a | |
default value. Some default values can be changed using the "S" | |
(set option) parse-time action. | |
Modifiers are similar to keywords, but simpler and more flexible | |
in how they may be used. They may or may not be followed by an | |
argument (e.g. a delay time). | |
Scopes | |
------ | |
The flow of time and the nesting of scopes are like two dimensions | |
in which things are arranged. For altering the time-arrangement, | |
see 'Timing'; that includes sub-steps, which are considered part | |
of the same larger step involving the same reference to an object. | |
Beginning a new step, by introducing a new object or new reference | |
to an object, terminates the previous when done in the same scope. | |
The global level of a script is a top scope containing objects and | |
steps for them. Each list ("[...]") is a similar subscope. What is | |
written for an object inside a list is a step in the inner scope, | |
with timing connected to the outer scope. Termination of an inner | |
step does not terminate any outer it is related to (i.e. nesting). | |
Keywords | |
-------- | |
Each of these keywords is further described in its own section below. | |
S Set option (parameter default value or other); | |
runs during parsing. | |
O Add operator, as in oscillator with parameters; | |
a carrier and/or modulator which produces sound | |
for a duration; the "O" is followed by the (initial) | |
wave type, as in "Osin". | |
S: Set option | |
------------- | |
Set parameter default value or other option; runs during parsing. | |
Usage: "S", followed by zero or more parameters, each with a value. | |
Parameters: | |
c Default O "c" (channel mixing) value. Starts at 0.0, | |
i.e. C (center). See "Channel mixing values" for more. | |
Useful as a main way of setting the parameter value. | |
f Default O "f" (frequency, Hz) value. Starts at 440. | |
r Default O "r" (relative frequency) value. Starts at 1. | |
t Default O "t" (time, seconds) value. Starts at 1. | |
a Multiplier for O "a" (amplitude) value, applied when | |
adding a top-level carrier. If used, this disables automatic | |
down-scaling of amplitude by the number of voices, for | |
manual control instead. | |
n A4 tuning (Hz) for O "f" (frequency, Hz) value using | |
note syntax. Starts at 440. | |
O: Operator | |
----------- | |
Add operator, as in oscillator with parameters. If a carrier | |
and not a modulator, adds sound at current time position, | |
playing for the time duration. | |
Usage: "O" followed by (initial) wave type (e.g. "Osin"), | |
followed by zero or more parameters, each with a value. | |
Wave types: | |
sin Sine. | |
For cosine, use (1/4) phase. | |
sqr Square. | |
tri Triangle. | |
saw Saw. | |
Increasing slope; use negative amplitude | |
or frequency for decreasing slope. | |
ahs Absolute half-frequency sine (adjusted). | |
Sine-based parabola-like wave. | |
Frequency halved to restore base frequency, | |
peak amplitude centered and doubled, phase | |
shifted to begin and end at zero amplitude. | |
A softer alternative to "saw" (saw wave). | |
hrs Half-rectified sine (adjusted). | |
Positive half kept, negative half zero'd. | |
Peak amplitude centered and doubled, phase | |
shifted to begin and end at zero amplitude. | |
A warm and full sound. | |
srs Square root of sine. | |
Mirrored for the negative half. | |
A softer alternative to "sqr" (square wave). | |
ssr Squared & square root of sine. | |
A little more thin and sharp, it differs from | |
"srs" a little like "srs" differs from "szh". | |
Channel mixing values: | |
Panning, where 0.0 is centered. Named constants can be used in place | |
of numbers for the three classic channel "modes". Values outside the | |
range of L to R are allowed, amplifying one channel while giving the | |
other a negative amplitude. | |
C 0.0 | |
L (-1.0) | |
R 1.0 | |
Phase values: | |
Phase offset as a percentage of the wave cycle. Any value will be used | |
modulo 1.0. For example, (1/4) turns sine into cosine. Named constants | |
provide scaled angles which can be used in expressions, e.g. (G*n) for | |
some whole number n makes for the nth leaf-around-a-stem angle. | |
G 0.3819... (golden angle as cycle percentage) | |
Parameters: | |
t Time duration in seconds. If no "t" setting is given, the | |
time set depends on the context. | |
For a single non-nested operator, 1.0 is used unless | |
the default value is changed with "S t". | |
When several operators are specified, the default | |
time is based on the longest remaining (at the current time) | |
duration of play in use for any step at the current level, | |
in considering the surrounding sequence of steps and delays | |
(up to the next '|' time separator, or to the end of the | |
script if none). | |
The default time lengthens further when modulators | |
with longer definite times are specified for the current step. | |
For modulator operators, default time is however an | |
"implicit" time length (can be set using the literal 'i', as | |
in "ti"), meaning playing whenever a carrier it is linked to | |
does. (Implicit time is only supported for nested operators.) | |
When such a time length is retrieved as part of setting the | |
default time for something else, a definite default time in | |
seconds (e.g. 1 second) is however used instead. | |
For a compound step, the first sub-step is however | |
simply given the "S t" default value if no time is set. | |
The following sub-steps in turn each have the time of the | |
previous as its default time. The exception is modulators, | |
for which the last sub-step has implicit time ('i', "ti") | |
by default, just like for undivided steps for modulators. | |
f Frequency in Hz. Can be negative to flip wave shape timewise. | |
"Value ramp" values are supported; see section. | |
"Modulation with value range" is supported for FM | |
(frequency modulation); see section. | |
r (For modulator operators only.) Relative frequency, a value | |
which will be multiplied by the carrier frequency to give | |
the frequency to use. For an n:m carrier-modulator frequency | |
ratio, a value of the form (m/n) may be used; e.g., for a | |
3:4 frequency ratio, "r(4/3)". | |
Note that for FM modulators, the carrier frequency | |
used as a multiplier is simply the unmodulated value. For | |
other modulators, if FM is done the result will be used. | |
"Value ramp" values are supported; see section. | |
"Modulation with value range" is supported for FM | |
(frequency modulation); see section. | |
a Amplitude, where 1.0 corresponds to a level of 0dB and | |
0.0 is silence. (Note that the final output level is scaled | |
down by the number of voices; alternatively, the S "a" option | |
can be used to set a multiplier used when adding a top-level | |
carrier. Panning will further reduce output level unless | |
fully left or right.) Can be negative to flip sign of result. | |
"Value ramp" values are supported; see section. | |
"Modulation with value range" is supported for AM & RM | |
(amplitude and ring modulation); see section. | |
p Phase in percentage of the wave cycle modulo 1.0. Set to | |
reset the phase, e.g. to change the initial value from 0.0. | |
See "Phase values" for more. | |
After a value or by itself, "[]" (square brackets) | |
can be used to set a list of PM modulator operators specified | |
within the "[]"; the list replaces any previous set, and may | |
be empty. Also, after these or by itself, ",f[]" (",f" and | |
square brackets) can likewise set a list of | |
frequency-amplified PM modulators. This whole larger argument | |
cannot contain any whitespace outside parentheses or brackets. | |
The sum of modulator amplitudes will phase-modulate | |
the carrier(s). For frequency-amplified PM modulators, first | |
the amplitudes are multiplied by the carrier frequency divided | |
by 632.45... Hz (the geometric mean of 20 and 20000 Hz). | |
w Wave type. See "Wave types" for values. | |
c (For non-nested operators only.) Channel mixing, (-1.0) to 1.0. | |
See "Channel mixing values" for more. | |
"Value ramp" values are supported; see section. | |
Values and expressions | |
---------------------- | |
Whitespace is not allowed within multi-character names, keywords or | |
numbers, and separates values. Spaces and tabs may otherwise be used or | |
omitted anywhere. | |
Comment syntax: | |
"//" (C++-comment) comments out the rest of the line. | |
"/*" (C-comment) comments out text until the next "*/". Does not nest. | |
"#!" (Shebang) comments out the rest of the line. | |
"#Q" (Quit file) comments out the rest of the whole file. | |
Numerical expressions: | |
A number can be specified with or without a decimal point; | |
for a number with a decimal point, a leading zero can be omitted. | |
Number signs and arithmetic operation symbols can be used in infix | |
expressions, together with numbers and named constants and functions. | |
But unless something is written within parentheses, it cannot contain | |
any whitespace, as it ends the expression. For example, "-1" is fine, | |
but "- 1" is a dangling minus followed by a dangling number 1, if not | |
inside parentheses as "(- 1)". | |
The following operations are recognized, and grouped below by | |
priority (nested parentheses can be used to change evaluation order): | |
^ To the power of (right-associative) | |
* / % Multiplication, division, remainder | |
+ - Addition, subtraction | |
Parentheses also allow shorthand multiplication (leaving out a | |
"*" between two parts), e.g. "2(3)" and "(2)3" both give "6". | |
Some parameters support named constants specific to that type | |
of value; such a name can be written instead of a number. | |
The following universal mathematical symbols (functions and | |
constants) can also be used in any numerical expression; functions | |
require parentheses after the name (and most often require a value | |
inside), while constants are simply written as names: | |
abs(x) Absolute value. | |
cos(x) Cosine of value. | |
exp(x) Base-e exponential value. | |
log(x) Natural logarithmic value. | |
met(x) Metallic value, e.g. "met(1)" gives the golden ratio. | |
Positive integers give the series of metallic ratios. | |
Other values are also allowed: fractional, 0 giving 1 | |
and negative (gives how much the positive value would | |
be increased, approaching zero further from zero). | |
Note that met(-x) is also equal to (1/met(x)). | |
mf 632.45... (Geometric mean of 20 and 20000.) | |
pi 3.1415... | |
rand() Pseudo-random number in range 0-1. The value sequence | |
from a series of calls restarts each new script unit. | |
rint(x) Round value to the nearest integer. Halfway cases are | |
rounded to the nearest even integer. | |
seed(x) Reset the rand() value sequence with a passed number. | |
(Every bit counts; different expressions for the same | |
number, with e.g. rounding may give different seeds.) | |
Returns 0 so that e.g. "/seed(100)" will only reseed. | |
sin(x) Sine of value. | |
sqrt(x) Square root. | |
time() Get a system timestamp number changed each second. | |
It can be used for seeding in a randomized script. | |
(Note that the exact value is platform-dependent.) | |
Value ramp: | |
To ramp, or sweep, a parameter which supports this towards a | |
goal value, the following sub-parameters can be given values | |
within "{}" (curly brackets): | |
g Goal (go-to) value, assigned to the parameter after time. | |
This value has no default and must be provided. | |
r Ramp fill shape (default 'lin'): | |
hold Hold old value until time. | |
lin Linear trajectory over time. | |
sin Sine-like smooth ends trajectory over time. | |
exp Steep "exp(x)-1"-like increase or decrease. | |
log Steep "log(x+1)"-like increase or decrease. | |
xpe Exponential envelope shape (saturate or decay). | |
lge Logarithmic envelope shape (saturate or decay). | |
(The 'exp' and 'log' shapes use ear-tuned polynomial | |
approximations with definite beginnings and ends, | |
designed to sound natural for frequency sweeping, | |
and symmetric one to the other. The 'xpe' shape increases | |
like 'log' and decreases like 'exp', much like a capacitor | |
charges and discharges, natural-sounding for an envelope; | |
and 'lge' increases like 'exp' and decreases like 'log'. | |
The 'sin' shape sounds similar to 'lin', except it has a | |
smoothly curved start and stop, and a steeper middle.) | |
t Time to reach goal (default is the external "t" duration). | |
Modulation with value range: | |
Amplitude and frequency (and relative frequency) parameters support | |
modulation of the parameter values within a bounded value range. | |
(For amplitude, whether this modulation is called amplitude | |
modulation (AM) or ring modulation (RM) is a matter of the value | |
range. Ring modulation has the same magnitude for the upper and | |
lower bound, but with differing sign, while classic amplitude | |
modulation has one of the bounds set to zero.) | |
Following the letter specifying the parameter, and optionally | |
the main value, the second value used for the bounds of the range, | |
and/or a list of modulator operators, can follow after ",w" (where the | |
"w" is short for wave envelope). These parts of the larger argument | |
are ordered and cannot contain any whitespace outside parentheses or | |
brackets. The second value defaults to 0.0 if not set for the object. | |
After ",w" and optionally a value, "[]" (square brackets) can | |
be used to set a list of modulator operators specified within the | |
"[]"; the list replaces any previous set, and may be empty. | |
Each modulator in the list will produce a result in the range | |
of 0.0 to 1.0, i.e. a positive signal, multiplied by its amplitude | |
parameter (defaulting to 1.0), negative amplitude multipliers having | |
the effect of switching the top and bottom of the 0.0 to 1.0 range. | |
Setting (changing) the amplitude for modulators will thus change | |
the range when the absolute value is not 1.0, but this is allowed | |
for the sake of flexibility. | |
The product of modulator amplitudes is mapped to the value | |
range; 0.0 means the main value and 1.0 means the second value. | |
This adds a bias towards the main value with more modulators used. | |
Parameters and operator binding: | |
When specifying or referencing operators within "@[...]", any | |
parameters set following the closing ']' will be bound to and apply | |
to all of them. | |
Significantly, this allows multiple carriers (given within | |
the []) to be linked to the same modulator(s), whether for FM, PM, | |
or AM/RM. (Note: Support for this is experimental and incomplete.) | |
Labels and referencing: | |
An object can be assigned a label by inserting "'name " in front of | |
adding the object, where the "name" is a case-sensitive string with | |
alphanumeric characters and/or '_'. The label can then be used to | |
refer back to the object as "@name", to start a new step for the | |
object anywhere later in the script. | |
Such a new "@name" step does not automatically set a new | |
time duration for the object, so "t" (see "Parameters") or other | |
time-altering syntax (see "Timing") must be used in order for the | |
old time duration value to be changed. | |
Note that a "@name" reference placed in a nesting scope | |
different from the original (e.g. outside a list, or in a new list) | |
does not move the object into the new nesting scope. It will not be | |
added to, nor removed from, any list by being referenced anywhere. | |
The time scope is however new and of the reference. | |
Frequencies as notes: | |
Frequency values may be specified as notes. Currently, justly | |
intoned C-major scale is supported; "S n" sets the A4 tuning | |
(default 440 Hz). | |
The value consists of the note (C, D, E, F, G, A or B), | |
optionally given a prefix and/or suffix. The prefix is an optional | |
subnote (c, d, e, f, g, a or b) inside an inner octave (between | |
note and next note). The suffix is an optional s (sharp) or | |
f (flat), and/or, in the last position, an octave number | |
(from 0-10, default 4). | |
Timing | |
------ | |
Timing modifiers: | |
| Time separator. Delays all that follows by the duration of | |
prior steps. This also resets any other delays to be added | |
to later steps using other syntax like '/', so such should | |
be placed after, not before, if it is to take effect. | |
/number Forward shift, time in seconds. Delay the next step and | |
all steps placed after. The next step can be either a | |
split-out continuation of the current step, or new. | |
Does not automatically extend time duration on | |
splitting a step, unlike ';'. | |
Compound steps: | |
For a step written for some object, timing can be changed locally, | |
within only the step and for lists nested under it. Two varieties | |
of the ';' sub-step separator allow this. Their use can be repeated. | |
; The numberless ';' step split can be written after a step for | |
an object (on a new line or the same), to specify a new time | |
duration and new parameter arguments which apply just after | |
the previous time duration. The new duration generally has | |
the length of the previous by default. (For the default time | |
of the first sub-step, and special handling for the last, see | |
"t" under each "Parameters" for more). | |
The time handling is designed to simplify writing a | |
sequence of connected, non-overlapping timed updates for a | |
single object. For example (three frequencies, one a second): | |
"Osin f100 t1; f200; f300". Changing "t1" in this example | |
changes the time length for all three parts. | |
For more flexibility, especially for adding "silent" | |
gaps between parts, the numberless ';' can be combined with, | |
or replaced by, the ';number' gapshift. Combination is easy, | |
as "; ;number" will subdivide and shift the second sub-step | |
by "number" of seconds, and move the active time duration for | |
the second sub-step past the second split, creating a gap | |
"number" of seconds long. For example (1 second of silence | |
between frequency changes): "Osin f100 t1;;1 f200;;1 f300". | |
;number Gapshift, time in seconds. When a number immediately follows | |
the ';', then the new sub-step is placed in time that number | |
of seconds after the previous, instead of after the duration | |
of the previous. Depending on usage, may move, alternatively | |
extend, the current sound in time. | |
For ease of adding silent time padding, before the | |
";number" part (but not after it) the default time duration | |
is changed to 0, so that any time value automatically set | |
there will be 0. After the ";number" part, a time value is | |
always set, the last "t" or default (before zeroing) value. | |
Several uses of ';number' to separate sub-steps in | |
a row (no numberless ';' in-between!) will never zero the | |
default time after the first ';number', allowing a way to | |
always extend rather than move by adding a leading ';0'. |