maketable can be used
for direct RTcmix Instrument envelope, waveform and other
control purposes. It returns
a table 'handle' which can be stored in a scorefile variable. This
'table-handle' can generally operate like other RTcmix scorefile variables,
i.e. it can be treated arithmetically, a single table can
control multiple aspects of an Instrument (without the annoyance of
maintaining the correct makegen "table slot number"), and the syntax of
the maketable command is fairly transparent for
understanding how a particular table is employed in Instrument
control.
maketable was introduced in RTcmix version 4.0, and
you may encounter some very old scores that use the
outdated
makegen
system. Many RTcmix instruments
maintain backwards-compatibility with this older system, but support
for the use of
makegen
constructs was dropped several years ago.
This string value (i.e. enclosed in "double quotes" in the scorefile)
determines the kind of table that will be constructed. This specifier
thus dictates how the rest of the arguments will be interpreted.
Most of the older
makegen
table-creation functions are subsumed by the "table_type".
See
below
for a listing of current table-construction specifiers.
These optional arguments determine global characteristics of how tables
are constructed and subsequently accessed. Presently there are two
sets of string specifiers:
"norm"/"nonorm" -- this option determines whether or
not the table will be normalized; with "norm" specified
the table contents will be scaled to fit within the range 0.0 -- 1.0
or -1.0 -- 1.0. "nonorm" will turn off this scaling, allowing
direct values to be specified when the table is constructed.
"norm" is the default.
"interp"/"interp2"/"nointerp" -- this option dictates
how values will be read from the table. "interp" enables
simple first-order linear interpolation, i.e. if a requested
table value lies between two elements in the table, setting
"interp" will generate an intermediate value based upon
a linear interpolation of nearest sample values. For example,
if the value at table location 314.15 were requested, the value
returned would be 0.15 between the value at location 314 and the
value at location 315. "interp2"
uses a spline-interpolation scheme that may be more accurate
for many curves, but more costly in terms of computation
required. "nointerp" turns off interpolation; the values
returned from the table will be 'rounded-down' (truncated) to the
nearest-lowest point in the table. For example, if the table value
at location 149.78 were requested, the "nointerp" option
would return the value stored at location 149.
NOTE: For instruments that directly access certain table-types,
like
WAVETABLE's
employment of the waveform function table, the interp_type
will probably be ignored because the instrument itself has
coded how it will interpolate an oscillator waveform.
The size of the table: how many numbers it stores. For most interpolated
tables, you can use 1000 or 2000 here. You might want to use more or
less, depending on the specific purpose. (For example, you might want
50 (and only 50) random numbers, so you would use the
random
table type and set the
table size to 50.) There is no requirement that this size be a power
of two.
Any number of arguments that define the table. These depend on the
table type. See documentation for each type below.
Note that the number of arguments used to define the table is independent
of the table size. So, depending on the table type, you can create
a table with a size of 2000 using only one or two arguments.
Fill a table with numbers read from a text file. The syntax is:
table = maketable("textfile", size, "filename")
The function loads as many as size numbers into the table. If there
are not that many numbers in the text file, it zeros out the extra
table values. The function reports one warning if at least one piece
of text (delimited by whitespace) cannot be interpreted as a double.
This means the file may contain any number of free text comments
interspersed with the numbers, as long as the comments themselves
do not contain numbers!
filename should be a string (i.e. enclosed in quotes). The filename
can be a relative pathname to the directory where the scorefile was
invoked, or it may be an absolute pathname within the filesystem.
This replaces the older makegen function
gen 2, except that it does not return
the length of the table created. This can be retrieved using the
new
tablelen scorefile command.
filename is obligatory. As with the filename argument for
the
textfile
table, it is a string that can be a relative or an
absolute pathname to a soundfile. All of the supported RTcmix soundfile
types can be read (aiff, wav, NeXT, bsd, etc.).
The other arguments are optional, but if an
argument further to the right is given, all the ones to its left must also
be given. These optional arguments are:
filename -- the name of a sound file in any of the header
types RTcmix can
read; data formats: 16bit signed int, 32bit float, 24bit
3-byte signed int; either endian.
duration -- duration (in seconds) to read from file.
If negative, its
absolute value is the number of sample frames to read.
If duration is missing, or if it's zero, then the whole
file is read. Beware with large files -- there is no check
on memory consumption!
inskip -- time (in seconds) to skip before reading,
or if negative,
its absolute value is the number of sample frames to skip.
If inskip is missing, it's assumed to be zero.
inchan -- channel number to read (with zero as first channel).
If inchan is missing all channels are read, with samples
from each frame interleaved.
This replaces the older makegen function
gen 1, except that it does not normalize
the sample values. The arguments are slightly different also,
and this does not return the number of frames read. The number of
samples in the table (not frames!) can be retrieved using the new
tablelen scorefile command.
n1, n2, n3, etc. are the numbers that go into the table. The
"nonorm" tag is recommended, unless you want the numbers to be
normalized to [-1.0, 1.0] or [0.0, 1.0] (if no negative values are present).
The function loads as many as size numbers into the table. If there
are not that many number arguments, it sets the extra table values to zero.
If size is zero, the table will be sized to fit the number arguments
exactly. If one (or more) of the n1, n2, ... variables is
an array created in the scripting language, then the array will be
'unpacked' into the table sequentially.
This replaces the older makegen function
gen 2, except that
it has the option to size the table to fit the number of arguments.
It also does not return the number of elements in the table.
This value can be retrieved using the new
tablelen scorefile command.
The function loads as many as size numbers into the table.
This is very similar in function to the
textfile
table type discribed above, except that the file contains 'raw'
binary data, not text-formatted data. If there
are not that many numbers in the file, it zeros out the extra table values.
If size is zero, the table will be sized to fit the data exactly. Be
careful if you use this option with a very large file: you may run out
of memory!
number_type can be any of "float" (the default),
"double", "int", "int64", "int32", "int16" or "byte".
This just means that with the
"double" type, for example, every 8 bytes will be interpreted as one
floating point number; with the "int16" type, every 2 bytes will be
interpreted as one integer; and so on.
"int" is "int32" on a 32-bit
platform and "int64" on a 64-bit platform. No byte-swapping is done.
This replaces the older makegen function
gen 3, except that
it has choices for the type of number, it has the option to
size the table to fit the data, and it does not return the number of
elements in the array.
This value can be retrieved using the new
tablelen scorefile command.
This table type fills the table with exponential line-segments
in the same way that the older makegen function
gen 5 did. All values must be > 0. The
npointsN arguments specify how many table elements are to
be used in constructing the curve from one value to the next. All
of the npointsN values should probably add up to equal the
size of the table. If the npointsN values are less
than the size of the table, then the last value will be
repeated to fill the remaining table elements.
This is like the
curve
syntax, except that straight
line-segments are used to interpolate values over the time between the
valueN points and the curvature values are therefore absent.
Values can cross 0, and can indeed be 0. Times
will be scaled by actual duration of use.
This is nearly identical to several older makegen functions that
are heavily used in RTcmix,
gen 6,
gen 18 and
gen 24.
There are some subtle internal differences in the line table type
and these older makegen functions mainly having to do with how the final
point in the table is reached -- see the code if this is of concern.
This table type fills the table with straight line-segments. The endpoints
of the line segments are defined by each valueN to
valueN+1arguments, and the 'length' is determined by each
npointsN argument between the two endpoints. This is very similar
to how the
expbrk
table type works, except (of course) that lines are used to 'connect the
dots' instead of exponential segments. It is also identical in
functionality to the older makegen
gen 7
function table routine. Crossing 0 and values of 0 are permitted.
All of the npointsN values should probably add up to equal the
size of the table. If the npointsN values are less
than the size of the table, then the last value will be
repeated to fill the remaining table elements.
Fill a table with a spline curve, defined by at least three [time, value]
points.
The curve travels smoothly between the points, and all points lie on the
curve. The syntax is:
The curvature argument controls the character of the slope
between points. A value of 0 will produce a relatively 'flat' curve
connecting the points, higher values will produce more pronounced
curved excursions.
The optional specifier "closed" is another way of affecting
the curvature.
This interacts with the curvature value. To see the resulting
shape of the curve, use the
plottable
command. Note that it is possible that the curve will loop outside of the
area you expect,
especially if the "nonorm" optional
specifier is used.
Adapted from cspline in the UCSD Carl package,
described in F.R. Moore, Elements of Computer Music.
Fill a table with one cycle of a waveform, composed of any number of
partials. The partials are specified by a multiplier of the fundamental,
amplitude and phase. The syntax is:
The partial_multiplier adds a component to the waveform being
constructed in the table. The fundamental frequency that the
partial_multiplier uses for the multiplication is set so that
exactly one cycle of the fundamental will fit into the table. The
amplitude and phase arguments that follow the
partial_multiplier then set the amplitude and phase
of that partial. The partial_multiplier does not have
to be an integer (i.e. it can be fractional, like 1.78), but a
fractionally-specified component of the waveform will be truncated to
fit into the table determined by the basic fundamental frequency.
amplitude generally operates on a scale of 0.0 -- 1.0,
but values higher than 1.0 are allowed. The default action will
rescale the resulting waveform to -1.0 -- 1.0. Negative amplitudes
are also allowed, but this is identical to a positive amplitude with
a 180-degree phase shift.
The phase argument is in degrees, 0.0 -- 360.0. Negative
phases and values > 360 are allowed, but they are equivalent to
corresponding phases between 0.0 -- 360.0.
What does all this mean? For example, the following use
of maketable:
table = maketable("wave3", 1000, 1, 1, 0)
will create a single cyle of a sine wave (the fundamental frequency
multiplied by 1.0, relative amplitude of 1.0, and phase shift of 0.0)
and store it -- in digital form of course! -- in a 1000-point table.
Changing the above scorefile command to this:
table = maketable("wave3", 1000, 1, 1, 90)
will create a cosine wave -- the sine wave is shifted in phase by
90 degrees in the table. This specification:
table = maketable("wave3", 1000, 2, 1, 0)
builds a waveform in the table that has two complete cycles of a sine
wave; the partial_multiplier causes a partial to be
built that is twice the fundamental frequency. And the following:
table = maketable("wave3", 1000, 3.14, 1, 0)
will place exactly 3.14 cycles of a sine wave into the table. These
specifications can be mixed:
creates a waveform with the fundamental at relative amplitude 1, the
second harmonic with an amplitude of 0.4 and a 90-degree phase shift,
and a part-component of 3.14 with an amplitude of 0.01 also included.
Most instruments that rely upon constructed wavetable
(such as
WAVETABLE or
FMINST)
in RTcmix 4.0
have been modified to include an optional parameter that will
allow the maketable waveform to be used. If this
parameter is present, any makegen-constructed wavetables will be
ignored.
See the older makegen routine
gen 9
for more examples of how this waveform-constructing scheme works, as
well as some plot images to show the effects of different
[partial, amplitude, phase] combinations.
Fill a table with one cycle of a waveform, composed of any number of harmonic
partials. The partials are specified by a positional parameter
corresponding to the relative amplitude of each partial included in the
waveform construction.
The syntax is:
In the first use,
each partialN_amp relative amplitude will contribute the Nth
harmonic at the specified amplitude to the composite waveform stored
in the table. The phase of each harmonic is 0.0. Obviously non-harmonic
(non-integer multiples of the fundamental) partials are not specifiable.
size is the number of points allocated for the table.
For example:
table = maketable("wave", 2000, 1)
will build a 2000-element table containing a single cycle of a
sine wave (harmonic 1 at amplitude 1),
table = maketable("wave", 2000, 0, 0, 1)
will build a table containing three cycles of a sine wave (harmonic
3 at amplitude 1), and
table = maketable("wave", 2000, 1.0, 0.5, 0.1)
will create a composite waveform with the amplitudes 1.0, 0.5, and 0.1 of the
first, second and third harmonics (respectively).
The second use of the wave table type allows for specifying the
waveform to be constructed by name. The following string values for
the waveform_string parameter are valid:
"sine" -- create a sine waveform
"saw" -- create a sawtooth wavefrom, going 'down' from 1.0 to -1.0
"sawX" -- create a sawtooth waveform like saw using only the first X harmonics
"sawdown" -- create a sawtooth wavefrom, going 'down' from 1.0 to -1.0 (identical to saw)
"sawup" -- create a sawtooth wavefrom, going 'up' from -1.0 to 1.0
"square" -- create a square wavefrom
"squareX" -- create a square waveform like square using only the first X harmonics
"tri" -- create a triangular wavefrom
"triX" -- create a triangular waveform like tri using only the first X harmonics
"buzz" -- create a pulse waveform
"buzzX" -- create a pulse waveform like pulse using only the first X harmonics
If the second waveform_string specifying method is used
for the wave table type, the wavetable
values will be normalized to fit between +1.0 and -1.0
Similar to the way that the
wave3
table type works, the constructed table can be used by
instruments such as
WAVETABLE or
FMINST).
In RTcmix 4.0, most of these
have been modified to include an optional parameter that will
allow the maketable waveform to be used. If this
parameter is present, any makegen-constructed wavetables will be
ignored.
The constructed wavetable will usually be used in its normalized
form, i.e. the waveform will travel between -1.0 and 1.0.
See the older makegen routine
gen 10
for more examples of how this waveform-constructing scheme operates.
Fill a table with a curve computed using
Chebyshev polynomials.
These curves have the property that when used as a transfer function, the
polynomial coefficients determine the harmonic content of the resulting
signal for a given index value. This is very useful for instruments
like
WAVESHAPE),
for it allows the used to specify the harmonics that will occur
in the output sound.
The syntax is:
The harmonicN arguments determine the relative amplitude of
that harmonic in the output spectrum when the table-lookup index is
at the value index.
Although this sounds complicated, it is actually very easy to use.
See the older makegen routine
gen 17
for more explanation and examples of how to use Chebyshev polynomials
in waveshaping. "cheby" is functionally equivalent to this
gen routine.
The random numbers filling the table will be between the min
value and the max value. Both of these are required arguments.
The table can be filled with random numbers using different
random-number distributions, as specified by the type.
The different types are (either the string specifiers
or the numeric specifiers may be used):
0/"even"/"linear" -- randomly select numbers between
min and max.
1/"low" -- randomly select numbers between
min and max, but with a higher probability
of choosing numbers nearer the min value.
2/"high" -- randomly select numbers between
min and max, but with a higher probability
of choosing numbers nearer the max value.
3/"triangle" -- randomly select numbers between
min and max, but with the probability of
choosing a value determined by a triangular curve with the
apex at the midpoint between the min and max
values. In other words, numbers near either min or
max will have a very low probability of being in the
table, but numbers half-way between will have a high
probability of being chosen.
4/"gaussian" -- randomly select numbers between
min and max, but with the probability of
choosing a value determined using a
Gaussian
('normal'; 'bell curve') probability distribution with the
apex at the midpoint between the min and max
values. Similar to how the "triangle" specifier operates.
5/"cauchy" -- randomly select numbers between
min and max, but with the probability of
choosing a value determined using a
Cauchy
function with the
apex at the midpoint between the min and max
values. Similar to how the "triangle" and "gaussian"
specifiers operate.
6/"prob" -- Mara Helmuth's configurable probability
distribution. This has a slightly different syntax:
min and max set the range within which the
random numbers fall, as before. mid sets the mid-point
of the range, which is used when tight is not 1.
tight governs the degree to which the random numbers
adhere either to the mid-point set by mid or to
the extremes of the range set by min and max:
tight effect
---------------------------------------------------------------
0 only the min and max values appear
1 even distribution
> 1 numbers cluster ever more tightly around mid
100 almost all numbers are equal to mid
For all of the above, if the optional seed argument is 0, then
the 'seed' for the psuedorandom number algorithm comes from the microsecond
system clock, otherwise the value of seed
is used as the 'seed'. Different seed values will generate
different sequences of random numbers.
If no seed argument is present, the 'seed' used is 1.
Note that if you don't give the "nonorm" optional specifier
argument after "random", then the
table will be normalized, thus disregarding your min and
max values.
This table type is similar in function to the older makegen routine
gen 20.
The original version of gen 20 was written by Luke Dubois;
later additions and this adaptation by John Gibson. Some distribution
equations were adapted from Dodge and Jerse, Computer Music.
Fill a table using a
window
function curve. The syntax is:
table = maketable("window", size, type)
Window functions are used for many signal-processing operations. Using
this maketable, two common window types can be constructed. The
type specifier determines the kind of window function to use:
1/"hanning" -- this will build a
hanning
window function in the table.
2/"hamming" -- this will build a
hamming
window function in the table.
Either the numeric specifier or the string specifier may be used.
This table type is similar in function to the older makegen routine
gen 25.
Examples
ampenv = maketable("line", 1000, 0,0, 1,1)
The ampenv table-handle variable will reference a table
containing a straight line from 0 to 1; the table will
have 1000 elements.
wave = maketable("wave", "nonorm", 2000, 0.5)
The wave table-handle variable will reference a table
containing a sine wave at half amplitude, ranging from
-0.5 to 0.5. The "nonorm" optional specifier prevents the
table from being normalized (scaled to fall between -1 and 1). The table
will have 2000 elements.
The following score shows how the table-handle can be used arithmetically
to operate upon other parameters in an instrument (in this case the
WAVETABLE
instrument):
Be aware, though that
memory for these tables is allocated for each use. This is a potential
small memory leak for algorthmic processes that define a large number of
new tables over a long period of time.