an Introduction to CMIX

Christopher Bailey

CMIX is a package of sound-processing, synthesizing, modification and mixing programs that you can use to do virtually anything with any pre-recorded sound, or with which you can synthesize any sound you can imagine. Its workings are very flexible and relatively simple, and, in the right combinations, any musical project can be realized.

Although in our studio we have many friendly "GUI" graphical interfaces for dealing with, modifying, mixing, and playing with sound, which can make these processes very intuitive, sometimes almost gestural; the way in which this user-friendliness and visceral interaction is accomplished can be very task-specific as a result: GUI applications are often very good for writing one kind of music, but very cumbersome or even impossible to use for writing another kind. Sometimes you will have a sonic/musical problem you know exactly how to solve, but whose solution is not served by any of the current graphical interfaces. In that case you can turn to the simple text-based building-blocks of CMIX to pursue your project.

In the old days, CMIX was not a real-time sound processor: you typed in commands, gave it a "score" to play; and then let the computer (take hours to. . . ) crunch the commands, resulting in the sound you were hoping to get (hopefully.) Now, (thanks to Brad Garton's development of RT-CMIX), you can hear, correct, and experiment with your results in a quick, non-discouraging manner. With RT-CMIX (Real Time--) the machine plays the sound as it crunches your "score." This quick turnover allows you to experiment, tweak and perfect your sound in reasonable amounts of time. Thus, you get flexibility that might be lacking with GUI apps, plus speed that almost matches the latter's.

RT-CMIX is much easier to use, but good-old-fashioned "DiskSpace CMIX" (non-real time), though slightly more involved, is not that much more difficult. A fair (though ever-decreasing) number of CMIX instruments still only exist as non-real-time, disk-based. Thus, I shall introduce patient reader to both types, side by side.

 

THE BASIC IDEA

You can think of CMIX, at first, as being set up as a kind virtual musician, or virtual "player" to realize your musical ideas. There are a set of available "instruments," which are actually sound-making computer programs. (You can also create your own). You write a companion program, a very simple kind of program, which tells these "instruments" what notes to play, how and when to play them. This program is called, naturally, a "score," or "score file," and it is written in a very simple "language" (I don't want to scare anyone, it's really too simple to be called a "computer language") called MINC, which tells CMIX, the third program, how to use the "score" to create sound.

 

So, when you run CMIX, basically four things are involved:


                      goes into        which makes  
     Your Score file  ----------->  The  ---------------->   the sound
       in MINC                ^     Cmix                     that gets
                              |     program                  created
                              |     itself
                              |
                              |
                              |
      "Instrument"------------/
       program

At this point, all you need to worry about is the "score" and the output "sound;" that is, your program to make music, and the resulting music (respectively). At first, we will use pre-constructed instrument programs. (Though it is too complex to explore in this introductory document, it is also possible to make one's own "instruments;" thus fully utilizing the flexibility of the CMIX package to tailor all tools to one's own particular musical needs.)

In other words, you write a program, (the .sco (score) file), which gets interpreted by other programs (the CMIX package , the specific instrument program), all of which then work together to create a sound--which is piped directly to speaker output (in the case of RT-CMIX), and/or, which is put into a "soundfile." Thus, to synthesize a sound in Cmix, you have in general to follow these steps:

     
     create/edit    a       ----------->         type  the command:
     score  (.sco)   file                        CMIX < <scorefilename> 
     using  jot   or                             (explained later)
     similar text editor                              |
                                                      |
                                                      |
                                                      V
                                              listen to  your sound

 

 

SOME DIFFERENCES ‘TWIXT REAL-TIME- AND DISKSPACE- CMIX

 

The procedures for running the two different kinds of CMIX are very similar but differ in the following ways:

1) RT-’s output is directly to the speakers (you can also send it to a soundfile with the command rtoutput.) DiskSpace -CMIX’s output goes only to a soundfile on a hard-disk. Disk-space CMIX's output involves some extra procedures that I’ll cover shortly.

2) The command for running RT- is written in CAPS (i.e.--CMIX). The DiskSpace- command is in small letters (cmix).

So to run a wavetable score designed for DiskSpace- CMIX, you type:

cmix < scorefile

To run a score for real-time, one types:

CMIX < scorefile

3) The other differences ‘twixt the 2 CMIXes involve the syntax of the score-file--these will be discussed now as we move on to cover :


THE SCORE FILE

Let's look at a simple score file. Before you write a score file, you must decide what instrument you're going to be using. We'll start with one of the simplest of the instruments: WAVETABLE. In the score for this instrument, you can

1) define the timbre, and envelope

2) define a "note" by describing when, where (in stereo space), how long, and how loud it is, and, of course, the pitch.

You can have as many notes and timbre and envelope changes as you like.

Here's an example, written for RT-CMIX:

/*what soundfile: */

rtsetparams(44100, 2)

rtoutput("/sndgr/chris/CLASS/bleep.snd")

/*what instrument(s) to use: */

load("WAVETABLE")

/*what timbre & envelope: */

makegen(1, 10, 10000, 1, .5, .2, 0, .1, .2)

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

/*two notes: both are the pitch a4 */

WAVETABLE(0, 2, 10000, 440, .1)

WAVETABLE(2, 3, 20000, 8.09, .9)

Here’s the same score, but written for DiskSpace-CMIX:

/*what soundfile: */

output("/sndgr/chris/CLASS/bleep.snd")

/*what instrument(s) to use: */

load("wavetable")

/*what timbre & envelope: */

makegen(1, 10, 10000, 1, .5, .2, 0, .1, .2)

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

/*two notes: both are the pitch a4 */

wavetable(0, 2, 10000, 440, .1)

wavetable(2, 3, 20000, 8.09, .9)

 

 

(Where can you make text files like this? For the beginner, I reccomend simply using the "jot" program, which you can start by simply typing "jot" in any UNIX shell. It works like a very simplified Microsoft Word.)

 

Note the comment symbol: /* Anything in between a /* and a */ is a comment and will be ignored by CMIX. Use comments so you can remember what means what in your score-file; that way, if you want to modify or correct it later, you can find the sources of errors or places to make changes quickly.

First, in RT-CMIX, (and this command appears only in RT-CMIX, not in DS-CMIX) we see the rtsetparams() command. This is simply a basic system set-up command, telling the machine what sampling rate (in this case, 44100 samples/second) and how many channels (in this case, 2, meaning a stereo sound). It should always appear at the top of the score, before anything else.

Second, there is the rtoutput(), or, in DS-CMIX, the corresponding output() command. This tells CMIX the path & filename of the soundfile you are making. (Remember, pathname refers to the "path" the computer takes from the "root" directory through the directory tree to find your file.) In RT-CMIX, this command is optional for your score, in either case, sound will come out the speakers, whether or not you want to write to a file as well is up to you. For DS-CMIX, however, things are a bit more involved, and always involve writing sound to a soundfile on disk--which you then have to play from your UNIX shell. (The disparity in complexity of running DS- versus RT- means you’re probably going to end up using RT-CMIX most of the time.) Nonetheless, reading the section below is important, as it will give you information on how CMIX as a whole really works.

You will also see the load() directive. This tells CMIX/cmix which instruments to load for this particular scorefile. You may use and load in as many different instruments as you want in a single score-file ... just be sure to keep all the parameters straight!

THE FOLLOWING IS FOR DS-CMIX ONLY, BUT READ IT ANYWAY:

Now, notice the name CMIX. It likes to mix things rather than create them from scratch. In other words, it would rather mix your new sound with an old sound than create it from scratch. In fact, it always assumes that the sound you are writing to already exists, and that you're adding to it.

Therefore, for DS-CMIX, you have to pre-create an empty soundfile to fill with your beautiful sounds. Any soundfile, as a string of bytes (numbers) in memory, has a "header" at the beginning of this string, giving the info like sampling rate, size, # of channels (mono or stereo), etc. about the sound. Then comes the actual sound data itself. When you pre-create a soundfile you are just creating the "header" part of the soundfile.

PRE-CREATING A SOUNDFILE

There are four basic pre-creation commands to choose from. (You type one of these in your UNIX shell (not in your score file.)):

make integer soundfile (unnecessary these days thanks to RT-CMIX):

sfc1 <path/soundfilename>

sfc2 <path/soundfilename>

make floating-point soundfile (only possible in DS-CMIX):

sffc1 <path/soundfilename>

sffc2 <path/soundfilename>

The number part is simple: this just says how many channels there are in the sound. Mono is 1, stereo is 2. The channels, for your information, are known as 0 and 1, so a mono sound has only a channel 0, a stereo, channels 0 and 1.

The difference between sfc and sffc is slightly more complex, but very important to understand, even for RT-CMIX users. There are two formats for soundfile storage: one is called "integer", the other "floating-point." As you know, a sound is stored as a string of, basically, loudnesses, or amplitude levels, (literally, distances that the speaker cone sticks out at a given instant). A string of many of these numbers, racing by at high speed, will cause a speaker cone to vibrate back and forth, producing---sound. These numbers can either be "integers"--which, for the computer, means whole numbers from -32767 to +32767; or "floating-point" numbers--any real numbers: floats can go from 0 to 1, or 1000 to 10000000, the scale is completely arbitrary. ("floating-point" refers to the fact that these numbers can have any number of decimal places.)

The advantage of floating-point type numbers is accuracy, and the fact that you can't "clip"--no sound is too loud. The disadvantage is that these numbers take up more space, and, more importantly, the computer can't really play them. The computer (or more specifically, the Digital-Analog Converter, which changes the numbers into electric energy waves that drive the speaker) is built only to play sounds in "integer" format, so normally, one must convert sounds to "integer" format eventually. Since RT-CMIX plays sound directly from your score, it uses integers, (and therefore, again, all of your volume/amplitude levels must be less than 32768), and only integers. If you want to use "floating-point" numbers, for their advantages (i.e.--increased accuracy, not having to worry about clipping), then you’ll have to use DiskSpace-CMIX. That's the reason why, despite its cumbersome old-fashioned nature, some folks still use DiskSpace -CMIX occasionally.

Thus, the old-fashioned, DiskSpace- method of working was/is to create a floating-point sound, using the sffc1 or sffc2 commands in your UNIX shell, then run the score, without the hassle of having to worry about "clipping"; (which is when the volume gets above 32768, creating extremely obnoxious noise); with "floating-point" numbers, your sounds can get as loud as you want--you can mix lots of notes together and be unconcerned that their addition might go over the 32768 limit; then, you convert that "float" soundfile into "integer" format (so the computer can play, and you can hear, your result) with a command called rshuf, which "normalizes" the sound (finds the loudest point in the sound and multiplies all of the samples in the sound so that that particular loudest sample value is 32768; the sound is now as loud as it can possibly be without clipping anywhere).

So to review: to use DS-CMIX, we pre-create a sound as a floating-point type, using sffc, run our score in CMIX, making the sound, and then rshuf the sound to make it playable by the machine.

Here are some examples of pre-creation:

sffc2 bang.snd

Create a 2-channel (stereo) sound called bang.snd in floating-point format.

sffc1 /sndgr/chris/CLASS/dwich.snd

create a 1-channel (mono) sound called dwich.snd, in the directory CLASS, which is in the directory chris, which is in the directory sndgr; in floating-point format.

(Interesting note about sfc: This command writes a "soundfile header" onto a file. As described above, you use it to create a new, empty soundfile. But you can also use it to write a header on any file, even files that aren't sounds. The latter will now be treated by the computer as sounds. Thus, if you're bored some time, write an sfc onto one of your theory papers or the like. It might just provide you with that inspiring bit of sound material you were looking for. :) )

 

THE SCORE CONT.

Let's continue with our example score.

/*what soundfileto write to: */

rtsetparams(44100, 2)

rtoutput("/sndgr/chris/CLASS/bleep.snd")

/*what instrument(s) to load: */

load("WAVETABLE")

/*what timbre & envelope: */

makegen(1, 10, 10000, 1, .5, .2, 0, .1, .2)

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

 

/*two notes: both are the pitch a4 */

WAVETABLE(0, 2, 10000, 440, .1)

WAVETABLE(2, 3, 20000, 8.09, .9)

 

or, for DS-CMIX:

 

/*what soundfileto write to: */

output("/sndgr/chris/CLASS/bleep.snd")

/*what instruments to load: */

load("wavetable")

/*what timbre & envelope: */

makegen(1, 10, 10000, 1, .5, .2, 0, .1, .2)

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

/*two notes: */

wavetable(0, 2, 10000, 440, .1)

wavetable(2, 3, 20000, 8.09, .9)

Now we look into the makegen commands, and the WAVETABLE (wavetable) commands. For now, let us be simple and say that the makegen defines the timbre of the sounds you are making, and WAVETABLE tells that timbre what notes to play, when, how long, and where. Comparing this situation to that of writing an acoustic score, it's like saying, "The clarinet (timbre: makegens) will play Mary Had a Little Lamb" (a series of WAVETABLE commands, one for each note in the tune.) Writing your makegen commands is like pressing the "Wizard Piano" or other cheezily-titled instrument- or "patch"- button on a synthesizer. The WAVETABLE commands are then the equivalent of playing the keys on the synth to get a tune.

The makegen command

A makegen command graphs a function in memory. This wave-drawing might be used to define timbre, envelope (pattern of attack, loud and soft, etc.), or something else.

Makegens work like this: There are 3 initial numbers, which always have the same meaning, these are followed by more numbers whose meaning depends on the function of the makegen.

  • 1st number in makegen command:

    The first number works like this: As we mentioned earlier, CMIX instruments use a makegen function for a variety of purposes: to tell what timbre to use, what attack envelope, etc. These different purposes of each makegen are each associated with a different "slot" in memory. WAVETABLE, for example, requires two makegen functions: one for timbre, one for envelope. It expects the timbre function in slot 1, and the envelope function in slot 2.

    The first number in a makegen tells the machine, "put this function graph in slot number # x"

     

  • 2nd number in makegen command:

    The second number tells CMIX what kind of function you're trying to draw. It also tells CMIX what the meaning of the string of numbers from the fourth number on will be. There are only a few standard functions that we use. Two of them are used in our WAVETABLE score example :

    TYPES OF MAKEGENS

    10: Draw a harmonic wave. This means a wave made of lots of little waves whose frequencies are all related by integer ratios, and therefore follow the harmonic series. You define the relative strength of each harmonic later in the makegen command, starting with parameter #4. The strengths of the harmonics normally range from 0 to 1. Hence, to get a sound with fundamental of strength 1, 2nd harmonic of strength .5, 3rd hamonic of strength .2, 4th 0, (not present), 5th of strength .1, and sixth, .2, you would use the makegen command given in our example above:

    makegen(1, 10, 3000, 1, .5, .2, 0, .1, .2)

    Example problem: The woodgeezoo, a sound you analyzed, has a harmonic spectrum as follows:

    fundamental: 1

    2nd: .2
    3rd: .8
    4th: .6
    6th: .2
    7th: .4
    8th: .3
    9th: .2
    10th: .14
    17th: .098

    You want to resynthesize it. What would be the makegen command you'd use?

    answer: makegen(1, 10, 3000, 1, .2, .8, .6, 0, .2, .4, .3, .2, .14, 0, 0, 0, 0, 0, 0, .098)

    (Notice that in the analysis of the woodgeezoo, the 5th harmonic was not listed--so we had to insert a 0 in the makegen command in the 5th harmonic's slot.)

    The other makegen in our example : (makegen(2, 24, 2000, 0, 0, 2, 1, 3, 0). OK, it takes slot #2, but what does the 24, and the numbers from the 4th on, signify?

    24: a 24-type makegen defines a set of x-y pairs (from the 4th parameter on) that can define an envelope, or other kind of waveshape. Looking at our example: if you plot 0,0 on an x-y graph, then 2,1, then draw a line between; then, plot 3, 0, draw a line to that from 2,1; you will end up with a kind of triangular shape. If we use this as an "amplitude envelope," this means that each note we write after writing this makegen will: start at niente, crescendo to the note’s volume level, and then decrescendo back to niente. Notice that the decrescendo will be twice as fast as the crescendo. (2-3 is half the time of 0-2). In your score, any note your write after this makegen command, no matter what its duration, will have this envelope "fitted" onto it. The envelope is an abstract shape that stretches or shrinks to fit any sound. You can define this abstract shape with as many points as you like, and it will always do the stretch-shrink thang to fit over the duration of whatever notes you write. You will find this stretch-shrink graph technique in many places in computer music, so learn to use it. (Note: you can, of course, issue amakegen command, write a few notes, and then issue another makegen command, then write a few more notes which will utilize this new graph, and so on.)

  • 3rd number in makegen command:

    Finally, the third number in the makegen command refers to the graph you're making in memory: how many points do you want to be in this graph? You should generally have a lot, to make your graph as "smooth" as possible (imagine trying to graph a sine wave with 8 points, it would be pretty darn rough. Generally, you want to use about 10000 points in each graph. That will avoid problems such as "qunatization noise" and other nastiness.)

  • Other makegens:

    There are other types of makegens available for your use, which graph different kinds of functions in memory slots. To see them all, check out the Makegen WWW page. Makegens in general are used in almost all CMIX instruments.

     

    WAVETABLE, THE INSTRUMENT, THE COMMAND, THE LOVE OF MY LIFE . . . .

    makegen is a generalized command you’ll find in scores for just about all CMIX instruments. WAVETABLE, however, is a specific CMIX instrument. You can only use the WAVETABLE command when you have loaded the WAVETABLE instrument into your score with the load command mentioned above. (The same will go for any other CMIX instruments.) Let's discuss how WAVETABLE works.

    With any CMIX instrument, making a "note" consists of defining the parameters of that particular note. These usually include attack-time, duration, loudness, pitch, stereo position. In fact, with the WAVETABLE instrument those are the only parameters that are necessary. With other, more complex CMIX instruments , other parameters will be added to this basic group. The parameters are usually referred to as p0, p1, p2, etc.

    Parameter 0

    The first number signifies attack- or start-time. Sometimes this is confusingly called outskip by strange computer hacker-people like Brad Garton or Paul Lansky. (I guess the amount you skip into your output soundfile before you start writing a particular note, is the same thing as that note's start-time. So don't be FREAKED OUT by hacker jargon.)

    Parameter 1

    The second parameter value or number signifies duration. Both duration and start-time are given in seconds.

    Parameter 2

    The third parameter is loudness or amplitude . Remember that loudness can be measured in two ways, Floating-Point, which can be any value range you want, but which you can only use in DS-CMIX; or, what the computer ultimately uses when it plays sound, and what RT-CMIX uses, integers, from 0 to +/-32768. If we've been using a range of values from 0 to 1, and the computer wants 0 to 32768, conversion is necessary. That's what the rshuf command will be for--we’ll get to that in a moment. (Again, if you’re using RT-CMIX, you don’t have to worry about this, just make sure that your output amp levels are less than 32768. (Generally, (for RT-CMIX), if you're just writing notes that will appear alone, in series, then amp levels of, say, 25000, are good. If you're going to be writing algorithms that write lots of notes on top of each other, then amp levels have to be much less, like 1000 or 2000, so that the addition of all of the amplitudes of all of the notes playing at any one instant will never add up to more than 32768.)

    Parameter 3

    The fourth parameter of WAVETABLE (p3, since we're counting from 0) is frequency or pitch. If you specify a number over 17, the computer assumes you mean hertz, or cycles per second.

    Thus, our first example WAVETABLE command,

    WAVETABLE(0, 2, 1000, 440, .1)

    Will create a pitch of 440 herz, or cycles per second. (Also note: starting at time 0, duration 2 seconds, loudness 1000)

    If you specify a number less than 17, and follow that whole number with a fraction, a different system of pitch signification is invoked, known as the octave-point-pitch-class or 8va.pc system. It works fairly simply: the whole number signifies the octave that you're in. Where 8 = (the standard) 4 (middle c octave). The fraction part of the number indicates pitch-class, starting at .00 is C, .01, C#, .02, D, etc. and you can have numbers like .02345 for a microtone 'twixt D and Eb. So 8.00 is C4, middle C, 8.11 is B above that, 9.01 is Db above that, and so on. To avoid confusing yourself, always specify two decimal digits, because, for example, 8.1 = 8.10 = Pitch-class 10 = Bb, not pitchclass 1 = D. Likewise, 8.25 = pitch-class 25 , mod 12 = 1 =Db, not pitchclass 2.5 equals D quarter-sharp. Note too, that 8.25 = Db5 = 9.01. Thus a given pitch can have several names.

    Almost all CMIX instruments can take pitch arguments in these two fashions, in herz, or in 8va.pc format.

    Thus, the following is another way of stating the command above:

    WAVETABLE(0, 2, 1000, 8.09, .1)

     

    Parameter 4

    The final parameter tells stereo position. From 0 to 1, where 0 is hard left, and 1 is hard right, decimals in between are relative positions in between. (i.e.--.5 is center.)

    
    
    

    WHAT CMIX ACTUALLY DOES

    So what does CMIX actually do with all this info?

    Well, to write your sounds, it needs some templates, it needs to know what waves look like. The makegens do this. They say, "Here's an example of one cycle of a sine wave. Here {makegen(x, 10, x, x, . . .) } is an example of one cycle of a harmonic sound. Here {makegen(x, 24, x, x, . . .) } is an example of a amplitude envelope. What CMIX actually does is read through the makegen templates in memory, over and over, at just the right speed, according to the frequency you specified in your WAVETABLE command, multiplying or dividing the values of each point in the template according to the amplitude you specified in your WAVETABLE command, and multiplying that by numbers derived from the amplitude envelope specified in makegens. By this process it calculates each and every sample of the 44100 samples that make up each second of each note of your music. That's all it's doing, just copying the makegen waves over and over, modifying them in this way and that; it's that simple. Now, of course, things get complicated when you start modifying waves by waves, and so on, but the basic principle is just this one of copying with modifications.

    GETTING SOUND FROM AN RT-CMIX SCORE:

    Again, you will probably be using RT-CMIX most often to make your sound. To actually get sound from your RT-CMIX score, you need merely to save your score on disk somewhere, (probably either in your /snd or in your /Users directory). Then, in your UNIX shell, you cd to that directory, and type

    CMIX < scorename

    In other words, if I save my score as beeps.sco, in /sndgr/chris, then to run it, I simply type:

    cd /sndgr/chris

    and then type:

    CMIX < beeps.sco.

    
    

    GETTING SOUND FROM A DS-CMIX SCORE:

    OK, but, let's say you've written a DS-CMIX score. How do you go about actually getting sound from it? Follow the following 6-step process:

    
    
    

    CHRIS BAILEY'S HANDY WORKING LOOP FOR DISK-SPACE CMIX

  • 1) make your score file, or edit one you've already made.

  • 2) pre-create your soundfile. Use the sffc1 or sffc2 commands explained above. Make sure the filename in the command is the exact same name as the one in your "output" command. That is, the name should be the same and, you have to make sure the path (the directory location) is the same as well. Be careful with this matter--91% of all CMIX problems are caused by a "naming" mistake somewhere--pathnames, filenames, etc.

  • 3) run your score. You do this by typing

    cmix < score

    For example:

    cmix < bleeps.sco

    (note small-case letters for DS-CMIX)

    Remember to use a back-arrow < character. Think of it as meaning, "Into (<) instrument, put score."

    Note on directories: Remember that sound takes up a lot of space, so it is forbidden (at the Columbia CMC, at least) to put sound in your /Users directory. However, text-files, such as scores, are relatively quite small, so they can go anywhere. Therefore, to make your life easier, you can put scores and sounds both in your /snd directory. Then you can just cd into the /snd directory, and do all of your work in there, without having to worry about pathnames and other paraphernalia.

  • 4) rshuf the sound to make it playable:

    rshuf soundfilename

    To repeat, you must use sffc to pre-create the sound, and then, after synthesis, rshuf to set it up for playing.

  • 5) play the soundfile using play, SndEditor, etc.

  • 6) if you want to go back and modify your sound, you should delete the soundfile using rm, and then go back to step 1. If you don't remove your soundfile, then, when you re-write it, it will MIX in the new, better sound, with the old, worse sound, and better + worse = even worse, probably. (Remember, that's why it's called CMIX, it likes to MIX things.)

    WHERE TO PUT STUFF---IMPORTANT!!!!!!!!!!!!!

    Just remember, your soundfiles should always be stored in one of your /snd directories; your score files can be stored either your /snd directory or in your /Users directory. Make sure, when loading files, or doing UNIX commands, you specify the full pathname when it's necessary. When in doubt, just specify the full pathname, it can't hurt. Storing huge files like sounds in the wrong place slows everyone down, creates more work for staff people, and causes machine crashes. To avoid complexity, do all of your work in your sound directory.