You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('') and can be up to 35 characters long.
571 lines
26 KiB
571 lines
26 KiB
SAU language reference


======================




SAU is a simple language for mathematical audio synthesis,


without support for the use of prerecorded samples.




The core idea of the language is that of timeordered 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 keywords "W" and "R" are type names, and are used to add instances


of oscillators and rumbly noise generators. They may be used as carriers


and modulators; such objects, the timing of their running, and their


parameters, 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"


(Script options) parsetime 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 timearrangement,


see 'Timing'; that includes substeps, 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 Script options (parameter default value or other);


runs during parsing. Changes made inside a


nested list scope don't apply outside of it.


R Random segments generator  "R", optionally followed


by the initial "Line types" value, e.g. "Rlin".


W Wave oscillator  "W", optionally followed


by the initial "Wave types" value, e.g. "Wtri".




S: Script options







Set parameter default value or other option; runs during parsing.


Changes made inside a nested list scope don't apply outside of it.




Usage: "S", followed by zero or more whitespaceseparated parameters,


each with a value.




Parameters:


a Multiplier for amplitude "a" values, for the current


scope of "[]" nesting. (The multiplier also applies to


"a.r" values, and to the multiplier in any deeper main


"a[]" modulation list.) If used at the top level, this


disables automatic downscaling of amplitude per voice


by the number of voices, for manual control instead.


c Default channel mixing "c" value. Starts at 0.0,


i.e. C (center). Useful as a main way of setting


the parameter value.


f Default freqency "f" value, in Hz. Starts at 440.


.n A4 tuning frequency in Hz for "f" values


using note syntax. Starts at 440.


r Default relative frequency "r" value, a


modulator:carrier ratio. Starts at 1 (1/1, a "1:1" ratio).


t Default short definite time "t" value, in seconds.


Default times may be longer (and occasionally shorter)


depending on the context. Starts at 1.0.




Signal generator common parameters







The "R" and "W" types have these parameters in common:


t Time duration in seconds. If no "t" setting is given, the


time set depends on the context.


For a single nonnested generator, 1.0 is used unless


the default value is changed with "S t".


When several generators 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 generators, default time is however an


"implicit" time length (see 'i' below), meaning playing


whenever a carrier it is linked to does. (Implicit time is


only supported for nested generators.) 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 substep is however


simply given the "S t" default value if no time is set.


The following substeps in turn each have the time of the


previous as its default time. The exception is modulators,


for which the last substep has implicit time ('i', "ti")


by default, just like for undivided steps for modulators.


Special nonnumber literals can also specify time:


d Definite default time can be set using "td", always.


i Implicit time can be set using "ti", for modulators.


f Frequency in Hz. Can be negative to flip wave shape timewise.


"Value sweep" values are supported; see section.


"Modulation with value range" is supported for FM


(frequency modulation); see section. Note that the modulator


lists for "f" and "r" expressions are shared and identical.


r (For modulator generators only.) Relative frequency, a value


which will be multiplied by the frequency of closest carrier


in the chain for the modulator, to give the frequency to use.


For an n:m modulator:carrier frequency ratio, a value of the


form (n/m) may be used; e.g., for a 4:3 frequency ratio,


"r(4/3)".


When using "r" the same values are changed as when


using "f", the difference simply being whether multiplication


by carrier frequency is switched on or off. Specific values


like the main value or the ".r" second value can be set again


under "f" or "r" to toggle just that value.


"Value sweep" values are supported; see section.


"Modulation with value range" is supported for FM


(frequency modulation); see section. Note that the modulator


lists for "f" and "r" expressions are shared and identical.


Note that for FM modulators, the carrier frequency


used as a multiplier is simply the unmodulated value. (If


several types of FM list are set and used at the same time,


the result of earlier stages will be used for later.) For


other kinds of modulation, if FM is done the result is used.


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 toplevel


carrier. Panning will further reduce output level unless


fully left or right.) Can be negative to flip sign of result.


"Value sweep" 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 generators 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


frequencyamplified PM modulators. This whole larger argument


cannot contain any whitespace outside parentheses or brackets.


The sum of modulator amplitudes will phasemodulate


the carrier(s). For frequencyamplified PM modulators, first


the amplitudes are multiplied by the carrier frequency divided


by 632.45... Hz (the geometric mean of 20 and 20000 Hz).


c (For nonnested generators only.) Channel mixing, mainly


(1.0) to 1.0. See "Channel mixing values" for more.


"Value sweep" values are supported; see section.




R: Random segments generator







Value noise generator, connecting random values generated at a frequency


with line segments of a selected shape. Two random values are generated


each "cycle", so that the base frequency matches a normal oscillator.




As a signal generator, if not enclosed within a "[]" list, then


it will run and output at the current time, for its duration.




Usage: "R", optionally followed by a line type (e.g. "Rlin"),


by the default the same as "Rcos". Followed by zero or more


whitespaceseparated parameters, each with a value. Several


'm' modes are available, for several types of random


distribution, as well as a naive oscillator mode.


See "Line types" for the available line types.




The "seed(x)" mathematical function changes


the starting seed of new "R" instances.




Parameters:


The "R" random segments generator has the


"Signal generator common parameters", and additionally:


l Line type  see "Line types" for values.


m Mode for line start and goal value variation; consists of


a letter (random function) and a digit (09 shaping level);


one or both may be set at a time; the default level is '9'.


Roughly, each level above 0 halves what remains of the


unshaped underlying randomness. The functions are...


r Uniform random (default). Ignores the level setting.


g Gaussian random, softsaturated approximation. On


average ~6 dB quieter. Ignores the level setting.


b Binary random. Extreme levels, more repetitive runs.


t Ternary smooth random. Never repeats twice in a row;


cycles above or below zero, randomly flips polarity.


f Fixed cycle. Plain naive oscillator at the top level;


below it, mixed with randomness at reduced amplitude.


In addition to the function and level, these flags can be set.


v Violet rather than white noise version of the function


if available; missing for 'g' and 't'. Like highpass


filtering the lower end of the noise, 6 dB per octave.


s Square, then restore sign, of the start/goal values.


Turns uniform value variation into uniform energy


variation; somewhat quieter, and more tremulant.


Doesn't affect 'b', 't', nor 'f' with level '9'.


Distorts 'v' violet noise toward white, as if mixed.




Line types:


cos


Half cosine (Scurve) trajectory over time.


lin


Linear trajectory over time.


sah


Sample and hold until time (then jump to goal).


exp


Steep "exp(x)1"like increase or decrease.


log


Steep "log(x+1)"like increase or decrease.


xpe


Exponential shape envelope (saturate or decay).


lge


Logarithmic shape envelope (saturate or decay).


sqe


Square polynomial envelope (saturate or decay).


cub


Cubic polynomial segment (1 to +1) trajectory.


ncl


Noise camel line; softer, two noise bulges.


nhl


Noise hump line; harder, one broad noise bulge.


uwh


Uniform random white noise in startgoal range.


The 'exp' and 'log' shapes use eartuned 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, naturalsounding for an envelope;


and 'lge' increases like 'exp' and decreases like 'log'.


For a lesssteep alternative to 'xpe', 'sqe' can be used.


The 'cos' shape sounds similar to 'lin', except it has a


smoothly curved start and stop, and a steeper middle.




W: Wave oscillator







Wave oscillator. The sine variety is a fairly typical "FM synth operator".


Producing a (weakly) antialiased signal, including for FM/PM, amplitude


can be a little lower for frequencies close to half the sample rate.




As a signal generator, if not enclosed within a "[]" list, then


it will run and output at the current time, for its duration.




Usage: "W", optionally followed by a wave type (e.g. "Wtri"),


by the default the same as "Wsin". Followed by zero or more


whitespaceseparated parameters, each with a value.


See "Wave types" for the available wave types.




Parameters:


The "W" wave oscillator has the


"Signal generator common parameters", and additionally:


w Wave type  see "Wave types" for values.




Wave types:


Beyond 'sin', there's 3 times 3 complementary wave types, in terms


of the added harmonics (odd, even, or all), and mellow vs. bright.


Additionally, there's 2 more, listed after these first 10.


sin


Sine. For cosine, set phase 'p' to 1/4.


tri


Triangle.


Mellow oddharmonics wave.


Opposite of 'ean' relative to 'par'.


srs


Square root of sine. (Mirrored for the negative half.)


Mediumbright oddharmonics wave.


Opposite of 'cat' relative to 'hsr'.


sqr


Square.


Bright oddharmonics wave.


Opposite of 'eto' relative to 'saw'.


ean


Evenangle.


Mellow evenharmonics wave.


Opposite of 'tri' relative to 'par'.


To begin at 0.0 amplitude, set phase 'p' to 6/93.


cat


Catear.


Mediumbright evenharmonics wave.


Opposite of 'srs' relative to 'hsr'.


To begin at 0.0 amplitude, set phase 'p' to 1/16.


eto


Eventooth.


Bright evenharmonics wave.


Opposite of 'sqr' relative to 'saw'.


par


Parabola. (x^2, steep part up.)


Mellow allharmonics wave.


Between 'tri' and 'ean'.


To begin at 0.0 amplitude, set phase 'p' to 9/87.


hsr


Mellowtooth. (Halfrectified 'srs', amplitude doubled.)


Mediumbright allharmonics wave.


Between 'srs' and 'cat'.


To begin at 0.0 amplitude, set phase 'p' to 1/25.


saw


Sawtooth.


Bright allharmonics wave.


Decreasing slope; use negative amplitude


or frequency (but not both) for increasing slope.


Between 'sqr' and 'eto'.


hsi


Halfrectified sine. (Amplitude doubled.)


Like a somewhat louder 'ean', harmonics decreasing as fast.


To begin at 0.0 amplitude, set phase 'p' to 1/12.


spa


Sine parabola. (First half, amplitude doubled.)


Slightly cleaner than 'par'. Mainly useful for modulation.


To begin at 0.0 amplitude, set phase 'p' to 1/12.




Values and expressions







Whitespace is not allowed within multicharacter 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.


"/*" (Ccomment) 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, variables,


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 (rightassociative)


* / % 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) Basee 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() Pseudorandom number in range 01. 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 platformdependent.)


If disabled (deterministic mode), instead gives 0.




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 leafaroundastem angle.


G 0.3819... (golden angle as cycle percentage)




Value sweep:


To sweep a parameter which supports sweep subparameters towards a


goal value  the ordinary value being the start for a trajectory 


following the ordinary value or by itself, the following value sweep


subparameters can be given values within "{}" (curly brackets):


g Goal (goto) value, assigned to the parameter after time.


This value has no default and must be provided. If changed


again before the full time, the current point reached on the


previous trajectory will be used to change the start value.


l Line fill shape (default 'lin', or the previous shape if any)


 see "Line types" for values.


t Time to reach goal (default is the external "t" duration,


or the remaining previous time, if any, for this parameter).


If longer than the active time for the object which has the


swept parameter, the trajectory will be left unfinished.


v Start (state) value, the ordinary parameter value.


It can alternatively be set here after a 'v',


if not set before the enclosing "{}".




Modulation with value range:


Amplitude ("a"), frequency ("f"), and relative frequency ("r")


parameters all support modulation of the parameter values in the


same ways. For amplitude, whether the result is called amplitude


modulation (AM), or ring modulation (RM), depends on how carrier


and modulator amplitude are set up relative to one another. (For


frequency modulation, the result is however always the "real FM"


related to PM, yet distinct from it, whenever modulation happens.)


Following a parameter name and optionally its main value(s),


"[]" (square brackets) can be used to set a list of modulators whose


amplitudes are simply added to the main value. For example "a0[...]"


will set amplitude to 0 and then modulators within "[...]" will have


the effect of ring modulating, while with "a1" set the result is AM.


A new list if set, even if empty, will replace the previous one set.


A subparameter can be used instead or in addition, to set up


modulation in a complementary way (the whole larger argument, for


example "f200.r(200 * 2)[...]", cannot contain any whitespace


outside of parentheses or brackets):


.r Following a main parameter name and optionally the things


mentioned above, under ".r" a subparameter with a second


value can be set. This second value is the other boundary


for a range to which modulator amplitudes can be mapped;


it has no other uses, and defaults to 0.0. "Value sweep" is


also supported for this subparameter whenever for the main.


After a value or by itself, "[]" (square brackets)


can be used to set a list of modulators. Each modulator in


this list will produce a result in the range of 0.0 to 1.0,


i.e. a positive signal, multiplied by its amplitude (with


a default of 1.0). However, a negative amplitude multiplier


will switch the top and bottom of the 0.0 to 1.0 range, and


then be used as if positive.


The result from these modulators are multiplied if


there's several, and finally the product mapped to a range


where 0.0 corresponds to the main value, and 1.0 to the


second. This adds a bias towards the main value with more


modulators used. Furthermore, changing amplitude multipliers


for modulators from the default can change the range.


To use this for classic 100% modulation depth AM,


one of the bounds should be 0.0 (like the default for the


second value); while for classic RM, the two bounds should


have the same magnitude, but with opposite sign.


If this type of modulation is used, it is done first


and the output from the other, main additive modulator list


is then added to the result.




Parameters and object binding:


When specifying or referencing objects 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.)




Variables:


A named variable can be assigned by writing an expression beginning


with "'name", where the "name" is a casesensitive string with


alphanumeric characters and/or '_'. Variables are dynamically typed,


can be assigned several times, and can either be assigned to a number


or made to point to an object as a label for it.


To assign a number, "'name=" can be written just before a


numerical expression. Once it holds a number, it can be used in any


numerical expression using "$name". (It's possible to use such a


variable value as part of redefining its value.)


To point a variable to an object, "'name " can be written


just before an object is added or referenced. The name 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.


A new "@name" step differs in not automatically setting a new


time duration for the object, so "t" (see "Parameters") or other


timealtering 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 Cmajor 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 010, 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


splitout 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 ';' substep 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 substep, 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, nonoverlapping timed updates for a


single object. For example (three frequencies, one a second):


"Wsin 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 substep


by "number" of seconds, and move the active time duration for


the second substep past the second split, creating a gap


"number" of seconds long. For example (1 second of silence


between frequency changes): "Wsin f100 t1;;1 f200;;1 f300".


;number Gapshift, time in seconds. When a number immediately follows


the ';', then the new substep 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 substeps in


a row (no numberless ';' inbetween!) will never zero the


default time after the first ';number', allowing a way to


always extend rather than move by adding a leading ';0'.
