Using RTcmix as a Standalone, Command-line Application

[NOTE: This tutorial is for older, 3.X (or earlier) versions of RTcmix. This version does cover the use of the makegen scorefile command, which has been largely superceded by the maketable and associated pfield-handle routines in RTcmix 4.0. Please use this version of the tutorial for RTcmix 4.0 and later.]
Because of it's Ancient History, RTcmix was developed initially as a command-line program. What does this mean for you? To use RTcmix as a standalone music creation application, you will need to edit a text file (called a scorefile) filled with command directives for RTcmix. You will then need to send this scorefile into RTcmix where it can be properly parsed and executed.

Fortunately, this is a very simple thing to do on most Unix-like systems. Suppose that you create a scorefile called "greatmusic.score". To run RTcmix with the commands you have painstakingly entered and saved in the "greatmusic.score" file, all you need to do is to say (from a Terminal or Shell window):

       CMIX < greatmusic.sco
Of course, this makes several basic assumptions -- we are assuming that you are "in" the same directory where the file "greatmusic.score" is stored, and we are also assuming that the RTcmix CMIX command has been installed and that your command path has been set properly (please see the RTcmix Installation web page for directions on how to do this, or consult a good Unix tutorial for information about directories, command search paths, etc.).

Once the CMIX application parses and executes the commands in your "greatmusic.score" scorefile, you should hear the ASTOUNDING SOUNDS that you have specified by the particular directives you have entered into the scorefile.


Structure of the Scorefile -- a Simple RTcmix Example

Let's take a look at a very basic RTcmix scorefile. Suppose that the very Great Music you wanted to create using your "greatmusic.score" file consisted of a single, 3.5-second long tone at 440 Hz. An RTcmix scorefile that would specify this might contain the following text:
       rtsetparams(44100, 2)
       load("WAVETABLE")

       makegen(1, 24, 1000, 0,1, 3.5,1)
       makegen(2, 10, 1000, 1.0, 0.4, 0.2)

       WAVETABLE(0, 3.5, 20000, 440.0)
Easy as pie, eh? We'll take a closer look at each line in the above. First of all, there is
       rtsetparams(44100, 2)
You will need an rtsetparams command in every RTcmix scorefile you intend to run. It sets up the audio hardware properly for sound creation. In this case we are using it with 2 parameters specified: a sampling rate of 44100 and stereo (2 channels) output. [note: the sampling rate and number of channels specification will be limited by the audio hardware one your computer. A sampling rate of 44100 and 2-channel output should work for nearly all contemporary computers.]

Notice that every RTcmix scorefile command looks like a function in many standard computer-programming langauges. The syntax consists of a command name followed by a set of parameters (in parentheses) for the command. In RTcmix scorefiles, the parameters can be specified as integers (no decimal point) or as floating-point numbers (yes, with a decimal point) -- it doesn't make a difference. What's even more fun, you can also substitute variables, additional functions, or expressions for any parameter in an RTcmix scorefile command. Some RTcmix scorefile commands require literal or string arguments, specified in "quotation_marks". We cover these features in more detail below.

The next line

       load("WAVETABLE")
tells RTcmix that we will be using the WAVETABLE digital synthesis instrument. RTcmix has a fairly rich set of pre-made instruments for use in scorefiles, and you can easily create your own instrument designs. Once you load an instrument, it is available for use throughout the score; you don't have to reload it again.

Each RTcmix instrument has a unique set of parameters required to specify a sound. Many instruments also need auxiliary data, often stored in blocks of computer memory called function tables. For example, the documentation for the WAVETABLE instrument says something like:

       Function table 1 gives amplitude curve.

       Function table 2 gives waveform.
This means that WAVETABLE needs to have some data describing the fade-up and fade-down of the note stored in function table 1 and the waveform (sine wave, square wave, some other wave) used for the WAVETABLE instrument in function table 2.

These function tables are created in the scorefile by the makegen command. makegen is a multi-purpose scorefile command. The first parameter specifies the function table # being created by the makegen. The second parameter determines how makegen will build the function-table data by specifying a particular gen routine that will dictate how later makegen parameters will be interpreted. The third makegen parameter always specifies how large to make the computer memory storage for the function table data (1000 almost always works if you have no idea what to use).

The "greatmusic.score" scorefile contains two makegen commands for use by the WAVETABLE instrument:

       makegen(1, 24, 1000, 0,1, 3.5,1)
       makegen(2, 10, 1000, 1.0, 0.4, 0.2)
The first makegen tells RTcmix to create a function-table in function-table slot #1, using gen routine 24, with 1000 values set aside in computer memory for this function-table. Gen routine 24 causes RTcmix to interpret the remaining parameters as [time, value] pairs -- in this case the WAVETABLE amplitude will have a value of 1 at time 0, and a value of 1 at time 3.5.

The second makegen specifies data for function-table slot #2, using gen routine 10. Again, 1000 values are set aside for the function-table. Gen routine 10 is a little more indirect than gen 24. The parameters determine the relative strength of harmonics in a waveform... in the

       makegen(2, 10, 1000, 1.0, 0.4, 0.2)
case, harmonic #1 (the fundamental) will have an amplitude of 1.0; harmonic #2 will have an amplitude of 0.4, and harmonic #3 will be in the composite waveform with an amplitude of 0.2. The resulting waveform looks like this:



All subsequent invocations of the WAVETABLE instrument in the scorefile will now use the data in the function-tables specified by the two makegens listed above.

Finally, we get to the WAVETABLE command itself. After setting up the two function-tables, the parameters for WAVETABLE are very simple.

       WAVETABLE(0, 3.5, 20000, 440.0)
Consulting the WAVETABLE documentation, the first parameter is the starting time. [note: all times in RTcmix are in seconds unless otherwise stated in the documentation.] The second is the duration, with the third being the amplitude and the fourth the frequency. Saving all this in the "greatmusic.score" scorefile, we have specified precisely what we want -- a note that starts at the beginning of the CMIX command execution, running for 3.5 seconds, with an amplitude of 20000 and a frequency of 440.0 Hz. Typing the command
       CMIX < greatmusic.sco
will give you exactly that, using as a timbre the waveform we specified in the
       makegen(2, 10, 1000, 1.0, 0.4, 0.2)
scorefile command.



Some final comments about this simple scorefile -- you may be wondering where the heck we came up with an amplitude specification of 20000. RTcmix generally treats amplitude in two ways. The method used by instruments doing direct synthesis of sound (like WAVETABLE) is to work on a 16-bit (CD-quality, integer) scale. This means that the absolute amplitude allowed by all combined RTcmix notes is 32768. Any sample-value higher than this will generate distortion, probably something you don't want to hear in your ASTOUNDING SOUNDS. Much lower than 5000 or 10000 will probably be difficult to hear, so we chose 20000 is a good starting-point for our scorefile.

For instruments that signal-process an input sound, the amplitude parameter usually works as a multiplier of the input sound before signal-processing is done. For example, an amplitude specification of 1.0 will pass the sound into the signal-processing instrument without altering the amplitude, a multiplier of 0.5 will cut the amplitude of the input in half, 2.0 will double the amplitude, etc. [note: an example of a signal-processing instrument follows later in this tutorial.]

Often it is a matter of trial and error to find the right amplitude for the best qualty sound. If you prefer, you can direct RTcmix to write a floating-point soundfile, which you will need to convert (and 'rescale') later to a 16-bit format for auditioning. This allows you to use an amplitude scale of your choice, and you will probably not need to be concerned about distortion from exceeding an upper-limit (the upper numerical limit for floating-point numbers is quite large on current machines). You will need to consult the documentation for the rtoutput scorefile command to do this.

Reading the WAVETABLE documentation, you may notice that it mentions that the frequency or pitch of the note may be specified in Hz (cycles per second) or in something called oct.pc notation. oct.pc is a way to use standard "western" keyboard notes without having to look up the pitch-frequency conversion. It works by arbitrarily assigning the octave of middle-C to 8.00. Any semitone above middle-C is added as a "hundredth" to the left of the decimal point, i.e. 8.01 is the C# just above middle-C, 8.02 is the D, 8.03 is the D# (Eb), etc. up to 8.12, which is equivalent to 9.00. 9.01 is then the C# one octave and a semitone abouve midddle-C.

The fun thing about this notation is that you are not limited to keyboard-notes. A pitch specification of 7.07542389 will select a frequency that is somewhere about half-way between the G (7.07) and Ab (7.08) just below middle-C. Different RTcmix instruments will require the pitch or frequency to be specified in different ways, although the scorefile commands cpspch, pchcps or other related commands can do most necessary conversions. The WAVETABLE instrument allows for both Hz and oct.pc by arbitrarily choosing that a value of "15.00" is probably at the lowest end for a Hz-specification and at the highest end for an oct.pc specification. WAVETABLE listings of

       WAVETABLE(0, 3.5, 20000, 8.09)
and
       WAVETABLE(0, 3.5, 20000, 440.0)
will produce identical results.

Also in the WAVETABLE documentation, you will note that the parameters are referred to as "p-fields", and that the numbering of parameters starts at 0. So the start-time parameter for WAVETABLE is given as "p0", and the duration as "p1", amplitude as "p2"... RTcmix instruments are not limited in how they use (or how many they use) p-fields, although for synthesis instruments p0 is usually the start time, and p1 is the duration. For signal-processing instruments, p0 is the output start time, p1 is usually the input start time, and p2 is the duration.

Many RTcmix instruments also have optional parameters, sometimes required when other p-fields have certain values, often optional for use at your discretion. The WAVETABLE instrument has one optional p-field, the "stereo spread" or "stereo location". This is a value between 0.0 and 1.0 determining how much of the synthesized signal will be placed in the channel 0 (usually the left channel) or channel 1 (usually right). A value of 0.5 will split the signal equally between the two channels, a value of 0.2 will place more of the signal in the left, etc. In our simple scorefile above, we just used the default value of 0, which means that all our sound will come from one channel of our output.


A Simple RTcmix Signal-Processing Example

This set of RTcmix scorefile commands will amplitude-modulate an input soundfile:
       rtsetparams(44100, 2)
       load("AM")

       rtinput("/snd/somesoundfile.aiff")
       rtoutput("/snd/amsound.aiff")

       makegen(1, 24, 1000, 0,0, 1,1, 9,1, 10,0)
       makegen(2, 10, 1000, 1)

       AM(0, 0.5, 4.34, 0.7, 478.98, 0, 0.2)
       AM(5.43, 0.5, 4.34, 0.7, 487.98, 1. 0.8)

       makegen(2, 10, 1000, 1, 0.4, 0.7, 1.4, 0, 0, 0.33334)
       AM(2, 0, 21, 0.2, 6.05)
Most of this scorefile is similar to the simple WAVETABLE scorefile above, with a few obvious and more subtle differences. The rtinput and rtoutput commands are new, and relatively simple to understand. On the documentation page for rtinput, it describes how to set up for real-time audio input to enable live signal processing. [note: this will cause the "input skip" parameter to be ignored in RTcmix instruments.] In our example, we are going to process a pre-existing soundfile, "/snd/somesoundfile.aiff".

The presence of the rtoutput command will instruct RTcmix to write a soundfile (in this case an aiff file -- RTcmix will use the suffix of the filename by default to determine the type of soundfile. This can be changed and the data format set "by hand" with optional parameters for rtoutput). At the end of the CMIX execution of the scorefile, the soundfile "/snd/amsound.aiff" will exist with the processed sound in it. RTcmix will play the output sound as it is created, unless the set_option command is used to turn the RTcmix audio output off. rtoutput will also check to see if the file already exists -- if it does it will terminate the application and not overwrite the file. This behavior can also be changed using the set_option command.

The use of the amplitude envelope specified by

       makegen(1, 24, 1000, 0,0, 1,1, 9,1, 10,0)
by the AM commands needs a little explanation. The duration for the AM notes are 4.34, 4.34 and 21 seconds respectively. But the function-table slot #1 (amplitude -- in general function-table slot #1 will contain the amplitude envelope), using makegen with gen routine 24, [time, value] parameters suggests that the duration of the note should be 10 seconds long. RTcmix will 'stretch' or 'compress' the data for an amplitude envelope (as well as many other note-length envelopes) to fit the duration of each individual note. Basically, we have specified an amplitude envelope with a 10% fade-up and a 10% fade-down, no matter what the duration. If you want to have very precise amplitude points on your amplitude envelope, then just be sure that the duration of the note you are generating matches the duration of the [time, value] pairs on your amplitude makegen command.

The function-table slot #2 makegen that creates the AM modulator waveform -- a sine wave initially:

       makegen(2, 10, 1000, 1)
is overwritten by a more harmonically-complex waveform towards the end of the scorefile:
       makegen(2, 10, 1000, 1, 0.4, 0.7, 1.4, 0, 0, 0.33334)
This is perfectly fine. You may rewrite a function-table slot at any time in an RTcmix scorefile. What is unusual is the time-ordering of the AM note commands. The third AM note actually occurs second in time. RTcmix instrument commands do not have to be time-sorted in a scorefile. The ordering of instrument commands relative to the makegen commands they use is important, however. In our AM scorefile, the first two AM commands listed:
       AM(0, 0.5, 4.34, 0.7, 478.98, 0, 0.2)
       AM(5.43, 0.5, 4.34, 0.7, 487.98, 1. 0.8)
will use the simple sine wave as a modulation waveform, but the third command:
       AM(2, 0, 21, 0.2, 6.05)
which will be the second sonic event entrance, will draw upon the harmonically-complex modulation waveform. In an RTcmix scorefile, a makegen specification for a function-table slot will "hold" throughout the rest of the scorefile, affecting all commands "under" it in the scorefile, until it is overwritten by another makegen for the same function-table slot.

There are other subtleties in using RTcmix makegen commands (such as the fact that we confusingly refer to function-table slots in many different ways -- makegen slots, makegen table #s, gen arrays, etc.); please see the makegen documentation for more detailed information.



A note about the output listing

Going back to our the first simple WAVETABLE scorefile above ("greatmusic.sco"), running the

       CMIX < greatmusic.sco
command will result in something like the following output on the Terminal or Shell window:
       --------> RTcmix 3.4.0 (CMIX) <--------
       ============================
       rtsetparams:  44100 2 
       Audio output buffer set to 4096 frames (32768 bytes).
       Audio set:  44100 sampling rate, 2 channels
       ============================
       load:  1.97882e+06 
       Loaded RT functions from shared library '/usr/local/src/RTcmix/shlib/libWAVETABLE.so'.
       ============================
       makegen:  1 24 1000 0 1 3.5 1 
       ============================
       makegen:  2 10 1000 1 0.4 0.2 
       ===============
       WAVETABLE:  0.000000 3.500000 20000.000000 440.000000 
       
       *** WARNING:  No bus_config defined, setting default (in/out).
       
       
       Peak amplitudes of output:
         channel 0:     0.000000 (  -Inf dBFS) at frame 0 (0 seconds)
         channel 1: 19999.998047 ( -4.29 dBFS) at frame 8900 (0.201814 seconds)
This output should be fairly self-explanatory. The main thing to realize is that the p-field (parameters) that are printed in the listing are the actualy numeric values that are passed into RTcmix. Any variables, mathematical operations or functions that are employed in the scorefile will display their calculated value in this listing. The total peak amplitude for the output channel(s) is also printed. This listing is very useful when debugging a score. If you are totally annoyed by this, you can turn off the output printing (or turn it back on again) using the print_off or print_on scorefile commands.

A Simple RTcmix Algorithmic Scorefile Example

Suppose that you wanted to generate a three-octave chromatic scale, starting at the C one octave below middle-C. using the WAVETABLE instrument. The hard way to do this would be to type in all 36 WAVETABLE note commands "by hand". The easy way would be to use the following scorefile:
       rtsetparams(44100, 2)
       load("WAVETABLE")
       reset(44100)

       makegen(1, 24, 1000, 0,0, 1,1, 2,1, 3,0)
       makegen(2, 10, 1000, 1.0, 0.3, 0.2, 0.1, 0.15)

       start = 0.0
       pitch = 7.00
       for (i = 0; i < 36; i = i+1)
       {
              WAVETABLE(start, 0.5, 10000, pitch)
              start = start+0.3
              pitch = pitch+0.01
       }
The basic functioning of this scorefile should be pretty easy to understand, although a few particulars are new. The reset scorefile command sets how often RTcmix will update control functions (like amplitude envelopes) within instruments designed to take advantage of this capability. Saying
       reset(44100)
will guarantee smooth amplitude transitions in our notes. Updating will happen once every sample. The default is to update 1000 times/second, which is normally fine unless you have very fast control changes occuring. Our WAVETABLE notes in the score are 0.5 seconds long, and since we are taking advantage of the 'stretching' and 'compressing' feature of the amplitude function-table slot, we will have fade-up and fade-down times in the hundreths of a second -- a 1000 times/second update may not be enough.

Why not always update at 44100/second? Many times this fine-grained updating is not necessary, and it slows down the computation of notes. In real-time situations where many notes may be scheduled simlutaneously, a more efficient, faster-running instrument may be preferable.

The other obvious feature of this WAVETABLE score is the use of variables and looping control-flow structures (the for (i = 0...) loop). The default RTcmix installation uses a command-parsing language called Minc (Minc is not c). This parsing language, written by Lars Graf, includes all of the original "C" language control-flow constructs (while loops, for loops, if-then-else branches, nesting of these constructs, etc.) as well as allowing for the use of variables anywhere in an RTcmix command. The "is not c" designation comes primarily from three aspects of the parser:

You can declare and type variables if you'd like, but it isn't needed for the language. Same for semicolons.

Minc also allows us to imbed scorefile commands within commands, using them like nested functions. By making use of the random scorefile command (random() returns a pseudo-random number between 0.0 and 1.0), we can generate unique timbres (waveforms) for every note in our chromatic scale scorefile:

       rtsetparams(44100, 2)
       load("WAVETABLE")
       reset(44100)

       makegen(1, 24, 1000, 0,0, 1,1, 2,1, 3,0)

       start = 0.0
       pitch = 7.00
       for (i = 0; i < 36; i = i+1)
       {
              makegen(2, 10, 1000, random(), random(), random(), random(), random())
              WAVETABLE(start, 0.5, 10000, pitch)
              start = start+0.3
              pitch = pitch+0.01
       }
Saving the above information in a scorefile and executing it with the CMIX command should schedule 36 overlapping, 0.5-second-long notes, each with a different timbre corresponding to the makegen(2, ...) command immediately preceding it in the 'unrolled' for-loop of the scorefile.

A Slightly More Complex Algorithmic Scorefile Example

Our intention is not to teach C-like programming in this tutorial, and in fact you may want to choose a different command interface such as the perl or python command-language versions of RTcmix, However, one more algorithmic scorefile example may help demonstrate a few additional features of the RTcmix command parser as well as show how small number of scorefile commands imbedded in an algorthmic structure can yield relatively complex output.

Consider the following scorefile:

       rtsetparams(44100, 2)
       load("STRUM")

       makegen(5, 2, 9, 7.07, 7.09, 7.10, 8.00, 8.02, 8.03, 8.05, 8.07, 8.09)

       st = 0
       for (i = 0; i < 1000; i = i+1)
       {
              pchindex = irand(0, 9)
              pitch = sampfunc(5, pchindex)
              START(st, 1.0, pitch, 1.0, 0.1, 10000, 1, random())
              st = st + irand(0.01, 0.3)
       }
In this scorefile, we are storing pitch values (in oct.pc form) in function-table slot #5 using a makegen command. We chose this arbitrarily; we could easily have chosen a different function-table slot (including slot #1 in this case -- the STRUM instrument creates a 'plucked-string' sound that has no external amplitude envelope). We are using gen routine 2, which allows us to specify data for the function-table directly. In this case, we are specifying 9 paramaters (p-field 2 is set to "9").

We then spin out 1000 notes, but we select the pitch for each note by randomly choosing one of the function-table slot #5 values. We accomplish this with these two lines in the for loop:

              pchindex = irand(0, 9)
              pitch = sampfunc(5, pchindex)
The first line uses the RTcmix scorefile command irand, to set pchindex to a random value between 0 and 9 (the irand command returns a number between it's two parameters). The second line uses the sampfunc command to retrieve a value from function-table slot #5 (the first parameter), using pchindex to determine which value to retrieve (sampfunc ignores any decimal part of the 0.0-9.0 number that may be generated by irand).

We are also using the random command to choose a stereo location for each of the 1000 notes we generate, using the optional p-field 7 in the START command.

And finally, we are randomizing the spacing of the notes with the line:

              st = st + irandom(0.01, 0.3)
This will cause each note to follow the preceding note with a delay varying between 0.01 seconds and 0.31 seconds. [note: RTcmix allows you to specify timing to single-sample accuracy.]

Additional Scorefile Command Hints

There are a few RTcmix scorefile commands with multiple or optional parameters that may be useful to you. Some of these we have mentioned already:
Happy RTcmixing!


Brad Garton
August, 2003