saugns/sau/doc
Joel K. Pettersson 86879ecca0 Allow "[X][Y]" to "[X Y]" concat. Flense in parser.
Language changes:
 * Modulator list syntax. Writing two or more lists in
   direct succession now concatenates their contents.

Change log:
 * 2023-09-05: parser: Use PL close_c to test
       for expected closing symbol. Reuse new
       code for "{...}" support, pruning old.
 * 2023-09-06: "[A...][B...]" -> "[A...B...]"
       concat, by changing an "if" to a loop.
 * 2023-09-08: Make current line per-PL, make
       a later "[...]" sweep work after mods.
2023-09-08 14:24:05 +02:00
..
README.SAU Allow "[X][Y]" to "[X Y]" concat. Flense in parser. 2023-09-08 14:24:05 +02:00

README.SAU

SAU language reference
======================

SAU (Scriptable AUdio) is a simple and non-Turing-complete
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 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) 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	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 whitespace-separated 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[]" modulator list.) If used at the top level, this
		disables automatic down-scaling 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 frequency "f" value, in Hz. Starts at 440.
		.k	Key selection for "f" values using note syntax,
			default C4. Mainly changes the default and relative
			octave. Can be C, D, E, F, G, A, or B, with or
			without f (flat) or s (sharp) -- and/or an octave
			number (0-10) to move the default from a 4-5 range
			to one of the number to the number plus one.
		.n	A4 tuning frequency in Hz for "f" values
			using note syntax. Starts at 440.
			For example, use "S f.n432" for 432 Hz.
		.s	Tuning system, either 'e' (12TET, default),
			or 'j' (SAU justly intoned, may change).
	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 non-nested 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 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.
			Special non-number 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 top-level
		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.
			"Modulator list" values are supported for PM (phase
		modulation); see section. A phase percentage value can be set
		together with a list in one go, the value then going first
		after the 'p', for example "p0.25[...]". A subparameter can
		be used instead or in addition for another kind of PM.
		.f	Frequency-amplified PM modulators can be set in a
			separate list, as in "p.f[...]". See "Modulator list".
		The sum of all PM 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).
	c	(For non-nested generators only.) Channel mixing, mainly
		(-1.0) to 1.0. See "Channel mixing values" for more.
			"Value sweep" values are supported; see section.
			"Modulator list" values are supported for panning-AM
		(stereo placement amplitude modulation); see section. The sum
		of modulator amplitudes is added to the channel mixing used.
		(If you downmix the result to mono the effect will disappear.)

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
whitespace-separated 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 (0-9 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, soft-saturated 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.
		h	Half-shape waveform. Use with 'lin' for a decreasing
			sawtooth instead of a triangle wave; similarly changes
			the shape for all line types and randomness modes.
		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.
		v	Violet rather than white noise version of the function
			if available; missing for 'g' and 't'. Like high-pass
			filtering the lower end of the noise, 6 dB per octave.
		z	Zig-zag flip. Swap ends of each half-cycle, adding an
			inharmonic waveform jaggedness unless using 'h', or
			'f' level '9'; more difference from these adds larger
			sharp steps. Always flips the waveform top and bottom.

Line types:
	cos
		Half cosine (S-curve) 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 start-goal range.
	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'.
	For a less-steep 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) anti-aliased 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
whitespace-separated 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 odd-harmonics wave.
		Opposite of 'ean' relative to 'par'.
	srs
		Square root of sine. (Mirrored for the negative half.)
		Medium-bright odd-harmonics wave.
		Opposite of 'cat' relative to 'hsr'.
	sqr
		Square.
		Bright odd-harmonics wave.
		Opposite of 'eto' relative to 'saw'.
	ean
		Evenangle.
		Mellow even-harmonics wave.
		Opposite of 'tri' relative to 'par'.
		To begin at 0.0 amplitude, set phase 'p' to 6/93.
	cat
		Catear.
		Medium-bright even-harmonics wave.
		Opposite of 'srs' relative to 'hsr'.
		To begin at 0.0 amplitude, set phase 'p' to 1/16.
	eto
		Eventooth.
		Bright even-harmonics wave.
		Opposite of 'sqr' relative to 'saw'.
	par
		Parabola. (x^2, steep part up.)
		Mellow all-harmonics wave.
		Between 'tri' and 'ean'.
		To begin at 0.0 amplitude, set phase 'p' to 9/87.
	hsr
		Mellowtooth. (Half-rectified 'srs', amplitude doubled.)
		Medium-bright all-harmonics wave.
		Between 'srs' and 'cat'.
		To begin at 0.0 amplitude, set phase 'p' to 1/25.
	saw
		Sawtooth.
		Bright all-harmonics wave.
		Decreasing slope; use negative amplitude
		or frequency (but not both) for increasing slope.
		Between 'sqr' and 'eto'.
	hsi
		Half-rectified 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 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.

Modulator list:
	Within "[]", written after the name of a parameter that supports it,
	signal generators can be included for use with that parameter. For
	example, for PM the phase parameter 'p' is assigned a list as in
	"p[...]". It works the same for other parameters such as 'a'
	(for AM/RM) and 'f' (for FM), and subparameters such as 'a.r', etc.
		Assigning a list to a parameter will append the new list to
	any old one, expanding it rather than replacing the old items. Thus
	"p[]" changes nothing. To clear old items when setting a list, add
	'-' before the '[', as in "p-[]" (which removes all PM modulators).
		Lists can be assigned together with other values (numbers)
	for various parameters. For the whole expression beginning with the
	parameter name, whitespace can only be placed inside list brackets
	(or inside parentheses for any numerical expressions).
		"Value sweep" for a parameter can use the same list as one
	which contains modulators; the modulators simply need to be listed
	after any sweep subparameters (see section) which head the list.
		It's also possible to write multiple lists directly after
	one another when setting to a parameter (with or without the one
	leading '-'). These lists will be joined into one, meaning that
	"[X][Y]" for some contents "X" and "Y" is the same as "[X Y]".
	This also allows placing sweep subparameters in a later list.

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 (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 only available under
	those parameter names, e.g. "Frequencies as notes" values.
		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.)
			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 leaf-around-a-stem 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 at the start of a "[]" list. For
	example, "f[g220]" sweeps frequency to 220 Hz over a default time.
	g	Goal (go-to) 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 yet distinct from PM, whenever modulation happens.)
		Following a parameter name and optionally its main value(s),
	lists can be used (see "Modulator list") to assign modulators whose
	amplitudes are simply added to the main value. For example "a0[...]"
	will set amplitude to 0, and the modulators within "[...]" will have
	the effect of ring modulating, while with "a1" set the result is AM.
		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, lists can be used (see
		"Modulator list"). Each modulator here produces 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 can be used to switch the top and bottom
		of the 0.0 to 1.0 range, and is then used as if positive.
			When several modulators are used for this, their
		outputs are multiplied. The product of outputs is mapped to
		a range where 0.0 is matched to the main value, and 1.0 to
		the second. Using more modulators thus adds a bias towards
		the main value. 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 case-sensitive 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.)
		Numerical expressions for some named parameters can use
	context-sensitive constants; to allow such when assigning a variable,
	one of the below parameter namespace letters can be added after the
	"=", with whitespace and/or a mathematical symbol separating it from
	any number or name after. For example, "'freq=f A4" has the frequency
	value of the note "A4".
	c	Channel mixing values
	f	Frequencies as notes
	p	Phase values
		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
	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. The "S f.n" option
	sets the A4 tuning frequency, by default 440 Hz. The tuning system
	can also be changed using "S f.s".
		By default, notes use the 12-tone equal temperament system.
	There is also another option, a just intonation system with 3 times 7
	notes (each of the 7 notes having a natural, a flat, and a sharp
	variation, all of which are distinct).
		Each note is written with a C, D, E, F, G, A, or B. As a
	first suffix, an f (flat) or s (sharp) can be added. An octave
	number (0-10) can be written as a final suffix, e.g. "A5" matches
	twice the tuning frequency, "Af5" a little below that. With no number,
	the octave for the note will be relative to the key setting ("S f.k"),
	and by default 4-5, so that the note for the selected key is also the
	lowest using the low default octave.
		Additionally, for microtonal variation an optional subnote
	prefix can be added (c, d, e, f, g, a, or b) for a position in an
	"inner octave" between the note otherwise used and the note a capital
	letter above it. I.e., "cC" is the same as just "C", but "dC" moves
	up a little notch towards "D", "eC" another little notch, and so on.
	If the key selected is not C, this small letter scale rotates with it.
	In just intonation, the frequency increases apply rational fractions.

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):
		"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 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): "Wsin 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'.