THE CMU MIDI TOOLKIT ``Charles Ives'' Edition Roger B. Dannenberg 14 January 1988 Carnegie Mellon University Pittsburgh, PA 15213, U.S.A. The CMU MIDI Toolkit (CMT), consisting of software and documentation, is provided ``as is'' without warranty of any kind, either expressed or implied. Should CMT prove defective, you (and not Carnegie-Mellon University) assume the entire cost of all necessary servicing, repair or correction. Further, Carnegie-Mellon University does not warrant, guarantee or make any representation regarding your requirements or that CMT is free of errors. Neither Carnegie-Mellon University nor anyone else who has been involved in the creation, production, or delivery of CMT program shall be liable for any direct, indirect, consequential or incidental damages arising out of use or inability to use CMT. Preface This manual is a guide for users of the Carnegie Mellon University MIDI Toolkit, also known as CMT, a collection of software for experimental computer music using standard Musical Instrument Digital Interface (MIDI) equipment. This manual corresponds to CMT Version 2.00. This is the Charles Ives edition, the first CMT manual. This manual is updated periodically as new programs are contributed to CMT and as bugs are discovered and fixed. I solicit your help in making this manual more accurate and complete. PLEASE help us by noting any errors, omissions, or suggestions you may have. You can send your suggestions to Dannenberg@A.CS.CMU.EDU (ARPAnet) via computer mail, or by campus mail to Roger B. Dannenberg, Center for Art and Technology, CFA, or by ordinary mail to Roger B. Dannenberg, Center for Art and Technology, Carnegie Mellon University, Pittsburgh, PA 15213-3890, USA. A bug report form can be found at the end of this manual. Many people have contributed to CMT which grew out of earlier computer music systems used in the CMU Computer and Electronic Music Studio. Dean Rubine wrote the first MPU-401 interface routines with help from Dale Amon. Joe Newcomer ported this first version to his IBM-AT and helped clear up many of our interrupt problems. Aaron Wohl wrote routines to use the PC timer and made many good suggestions for improvements. The Tuning program and the initial tuning software were written by Michael Cressman. Most of the development work was done on the IBM-XT in the CMU Computer and Electronic Music Studio. This machine was part of a grant from IBM. I also wish to acknowledge support from CMU through the Music Department, the Computer Science Department, the Center for the Design of Educational Computing, and especially the Center for Art and Technology. 1. Introduction and Overview The CMU MIDI Toolkit (CMT) is a software package designed for experimental computer music education, composition, performance, and research. CMT includes a compiler for a simple, text-based music language, and software for recording, playing, and computing MIDI data in real time. CMT has three major attractions: the flexibility of an open-ended design, the availability of source code, and low system cost. What does CMT do? The major components and their functions are: - Adagio is a language and an associated compiler. In Adagio, a note is represented by a line of text that specifies attributes such as pitch, duration, and loudness. Adagio is quite flexible and is compatible with several different ways of thinking about scores. For example, ``Q'' stands for a quarter note, but duration can also be indicated by ``U87'', which means 0.87 seconds. Adagio also supports arbitrary tuning systems. - Transcribe reads input from a MIDI keyboard or other MIDI controller and produces an Adagio score. The score can be played back using Adagio, or it can be edited with a text editor to make alterations. Since Transcribe output is at least somewhat readable by people, it can also be used for performance analysis. - Record is a combination of Adagio and Transcribe. It plays an existing Adagio score while transcribing a performance. Thus, complex compositions can be created one part at a time as if using a multi-track tape recorder. - DXGet and DXPut are programs for recording and replaying MIDI system exclusive messages. These programs are typically used to save and restore synthesis parameters for a digital synthesizer. - Moxc is a real-time programming environment that is ideal for writing interactive music programs. Moxc is an extension of the C programming language and is based on Douglas Collinge's Moxie language. - Also provided are routines (in C) that allow direct production of MIDI output. Other routines are available to read MIDI data from a circular input second resolution. buffer and to get the current time with 0.01 Required hardware and software. CMT runs on an IBM-XT or IBM-AT with a Roland MPU-401 MIDI interface and IBM-PC interface adapter. You should also have a screen-based text editor. To use Moxc, you will need a Lattice C compiler. Other details. CMT is distributed by the CMU Center for Art and Technology, Carnegie-Mellon University, Pittsburgh, PA, 15213. We hope that users will contribute new software to the system and enhance the existing software in various ways. We will encourage this by helping to integrate and document new software and by distributing software updates to CMT users. 1.1. Installing CMT CMT comes on a set of two floppy disks. Disk #1 contains executable programs and some example files. Disk #2 contains source code and files you need in order to recompile CMT programs. If you have a hard disk, you should copy the contents of the floppy disks that come with CMT onto the hard disk. Put Disk #1 in the floppy disk drive and type: c: cd \ mkdir cmt cd cmt copy a:*.* There are some test files in the directory test on Disk #1. copy them as well by typing: You can mkdir test cd test copy a:\test\*.* Some useful non-music programs are included with CMT. grep, and ls. To install these programs, type: They are make, cd \bin copy a:\bin\*.* If you want to use CMT to write programs, you should copy the contents of Disk #2 into your cmt directory. Put Disk #2 into the floppy disk drive and type: cd \cmt copy a:*.* Finally, you add c:\cmt to the path in your autoexec.bat file so that DOS can find CMT programs. Assuming you have installed an MPU-401 MIDI interface, you should now connect your synthesizer(s). At Carnegie Mellon, we generally use a Yamaha DX7 as input and a TX816 as output. Thus, we connect the DX7 MIDI OUT to the MPU-401 MIDI IN and the MPU-401 MIDI OUT to the TX816 MIDI IN. You should not connect the MPU-401 OUT to the DX7 IN. Doing so will cause outputs from the DX7 to be returned as inputs. This can cause problems in the DX7. If you want to use the same synthesizer for both input and output, a slightly different approach is necessary. Here, you should go ahead and connect both MPU-401 IN and OUT to your synthesizer OUT and IN, respectively. To avoid the problem of having outputs returned as inputs, you should use the ``block'' option on all CMT software. When the software is not running, it may be necessary to disconnect either of the two MIDI cables because the MPU-401 default is to resend all inputs as outputs. In some cases, the DX7 cannot handle having its output data returned to its input. 2. DOS Utilities Before describing music software that comes with the CMU MIDI Toolkit, some DOS commands and utility programs are described. Three programs are provided with CMT to help make your life easier. These are make, ls, and grep. A section on backing up files, intended for novice users, is also included in this chapter. 2.1. make The make program is based on the Unix(Unix is a trademark of A.T.& T. Bell Laboratories) program of the same name, and is documented in the file make.man. The purpose of make is to figure out what has to be recompiled when you edit a file. A file called makefile contains a list of file dependencies and a command that specifies how to recompile a dependent file. The make program figures out what files have changed by examining their ``last modified'' times and dates. Therefore, if you use make, you must always tell DOS the time and date when you turn on your machine. An example of an entry in makefile describing a dependency is ADOBJ = adagio.obj phase1.obj phase2.obj adaspace.obj noteoff.obj MUSICLIB = cmt.lib adagio.exe : #(ADOBJ) #(MUSICLIB) link #(COBJ) #(ADOBJ),adagio,,#(LIBS) In this example, ADOBJ is a symbol that is defined in the first line to be a list of files. The line with the colon (:) says that adagio.exe depends upon everything on the right of the colon, that is, all of the object files represented by ADOBJ and also the file cmt.lib, represented by MUSICLIB. If adagio.exe does not exist or is older than one of the files it depends upon, it should be remade by executing the lines after the line stating the dependency. In this case, there is only one command (starting with the word link) needed. The file makefile, from which this example was taken, is provided with CMT and contains a complete list of CMT file dependencies. It is a good reference if you have questions about how to write a makefile. To use make, date. For example, simply give it the file you want to make up-to- make adagio.exe will make adagio.exe up to date. to-date, you can type To make sure the entire CMT is up- make everything Note: the makefile provided with CMT assumes you are in the directory with all the CMT source files when you run make. To clean up leftover files, type: make clean This will not remove .OBJ files. files by typing: You can get rid of unneeded .OBJ make cleanobjs 2.2. ls The ls program is like the DOS dir command, except it lists file names only (no size and date information), and the names are formatted into columns in alphabetical order. The current version of ls does not work with large directories, although this may be fixed by the time you read this. 2.3. grep The grep program searches one or more files for a text string. If the string is found, the line containing the string is printed. grep is indispensable for reading someone else's code. For example, to find out where fileopen is declared (as well as where it is used), type grep fileopen *.c in the CMT source code directory. 2.4. Backing up files Note: This section is intended Music Studio, but it is worth reading if personal computers. for new users of the CMU Computer you are inexperienced with The IBM XT system has a built-in disk that can store about 10 million characters of information. Unfortunately, that may not be enough to meet the demands of all of the studio users. Fortunately, there is an alternative way to store information called the floppy disk. Floppy disks are also a good way to make copies of your work for safe keeping. There is always a small chance that all of your data will be lost from the Studio computer. To protect yourself, you should keep a backup copy of any critical files on a floppy disk. Floppy disks are thin magnetic disks that come in a protective sleeve and insert into the slot in front of the IBM XT. To make a backup copy of your directory, 1. put a floppy disk into the disk drive slot and push down the door 2. if you have not used the disk before, type format a: This will completely erase your disk, so only do this if the disk is new or there is nothing on the disk you want to keep. 3. type copy *.* a: This will copy all files from your directory to the floppy disk. If there is a file on the floppy disk whose name is the same as a file in your directory, then it will be erased and replaced. For example, if you copied a file called tune.gio to your floppy disk last week, then edited tune.gio, and now you back up you directory, then you will lose last week's version of tune.gio from your floppy disk. It will be replaced by the current version. To copy individual files, type copy filename a: where filename is the file you want to save on floppy. recover an individual file from floppy disk to your directory, type To copy a:filename 3. The Adagio Language Adagio is an easy-to-use, non-procedural notation for scores. In Adagio, text commands are used to specify each note. If you are new to Adagio, you may want to glance at the examples in Section 3.3 starting on page 5 before reading any further. A note is described in Adagio by a set of attributes, and any attribute not specified is ``inherited'' from the previous line. Attributes may appear in any order and must be separated by one or more blanks. An attribute may not contain any blanks. The attributes are: time, pitch, loudness, voice number, duration, and timbre. Adagio has been used to program a variety of hardware and software synthesizers, and the Adagio compiler can be easily adapted to new environments. Although not originally intended for MIDI, Adagio works quite well as a representation for MIDI scores. The MIDI version of Adagio currently uses the timbre attribute to select a MIDI ``program'' (synthesizer preset). Adagio has been extended to allow MIDI controller data such as modulation wheels, pitch bend, and volume. A note command in Adagio must be separated from other notes. Usually, notes are distinguished by writing each one on a separate line. Notes can also be separated by using a comma or semicolon as will be described below. Besides notes, there are several other types of commands: 1. An asterisk (*) in column one (or immediately after a comma or semicolon) indicates that the rest of the line is a comment. The line is ignored by Adagio, and is therefore a good way to insert text to be read by people. 2. An empty command (a blank line, for example) is ignored as if it were a comment(To be consistent, a blank line ought to generate a note that inherits all of its attributes from the previous one. Adagio is intentionally inconsistent in this respect. ). 3. An exclamation point (!) in column one (or immediately after a comma or semicolon) indicates a special command. A special command does not generate a note, and currently, the only special commands are the Rate and Tempo commands, described below. Special commands extend to the end of the line. 4. Control change commands are used to control parameters like pitch bend that can change as a note is playing. Control change commands can be specified along with notes or by themselves. A command that specifies only control changes will not produce a note. Adagio is insensitive to case, thus ``A'' is equivalent to ``a'', and you can mix upper and lower case letters freely. 3.1. Specifying Attributes 3.1.1. Time The time attribute specifies when to start the note. A time is specified by th a ``T'' followed by a number representing 100 's of a second or by a duration (durations are described below). Examples: T150 --1.5 sec TQ3 --3 quarter note's duration If no time is specified, the default time is the sum of the time and duration attributes of the previous note. (But see Section 3.1.4.) Time is measured relative to the time of the most recent Tempo or Rate command. (See the examples in Section 3.3 for some clarification of this point.) 3.1.2. Pitch The pitch attribute specifies what frequency to produce. Standard scale pitches are named by name, using "S" for sharp, "F" for flat, and (optionally) "N" for natural. For example, "C" and "CN" represent the same pitch, as do "FS" and "GF" (F sharp and G flat). Octaves are specified by number. C4 is middle C, and B3 is a half step lower. F5 is the top line of the treble clef, etc. Accidentals can go before or after the octave number, so FS3 and F3S have the same meaning. An alternate notation for pitch is Pn, where n is an integer representing the pitch. Middle C (C4) is equivalent to P48, CS4 is P49, etc. [If you know MIDI codes, you will notice that P48 in Adagio results in a MIDI pitch code of 60. The ``middle C = 48'' convention was borrowed from Gary Nelson's MPL and it is consistent with MUSIC V, cmusic, and many other computer music languages. I decided to stick with the convention on the assumption that if you know enough about MIDI to know that middle C is 60, then you know enough not to be confused by Adagio and other CMT software.] If you do not specify an octave, Adagio will choose one for you. This is done by picking the octave that will make the current pitch as close to the previous pitch as possible. In the case of augmented fourths or diminished fifths, there are two equally good choices. Adagio chooses the lower octave. 3.1.3. Duration Duration is specified by a letter indicating a number of beats, followed by one or several modifiers. The basic duration codes are: W (whole, 4 beats), H (half, 2 beats), Q (quarter, 1 beat), I (eighth, 1/2 beat), and S (sixteenth, 1/4 beat). The default tempo is 100 beats per minute (see Section 3.1.9). These codes may be followed by a "T" (triplet), indicting a duration of 2/3 the normal. A dot (".") after a duration code extends it by half to 3/2 the normal. Finally, an integer after a note multiplies its duration by the indicated value (the result is still just one note). Examples: Q QT W. ST6 H5 ------ 1 2/3 6 1 10 beat (quarter note) beat (quarter triplet) beats(dotted whole note) beat (6 sixteenth triplets) beats(5 half notes) Finally, a duration may be noted by Un, where n is an integer indicating th 100 's of a second. For example, U25 is one quarter of one second. 3.1.4. Next Time The time of the next command (the next command in the Adagio program text) is normally the time of the current note command plus the duration of the current note. This can be overridden by a field consisting of the letter ``N'' followed by a number indicating hundredths of seconds, or followed by a duration as described above. The next note will then start at the time of the current note plus the duration specified after ``N''. If the next note has an explicit time attribute (``T''), then the specified time will override the one based on the previous note. Examples: N0 N50 NQT 3.1.5. Rest -- start the next note at the same time as the current one -- start the next note 0.5 seconds after the current one -- start the next note 2/3 beat after the current one Rests are obtained by including the field ``R'' in a note command. The effect of an ``R'' field is to omit the note that would otherwise occur as the result of the current note command. In all other respects, the command is processed just like any other line. This means that attributes such as duration, loudness, and pitch can be specified, and anything specified will be inherited by the note in the next command. Normally, a rest will include just ``R'' and a duration. The fact that a note command specifies a rest is not inherited. For example: R H RH -- a half (two beat) rest -- illegal, R must be separated from other fields by space(s) Because some synthesizers (e.g. a DX7) cannot change programs (presets) rapidly, it may be desirable to change programs at the beginning of a rest so that the synthesizer will be ready to play by the end of the rest. See Section 3.1.8 for an example. 3.1.6. Loudness Loudness is indicated by an ``L'' followed by a dynamic marking from the following: PPP, PP, P, MP, MF, F, FF, FFF. Alternatively, a number from 0 to 127 may be used. The loudness attribute is the MIDI note velocity. The dynamic markings are translated into numbers as follows: Lppp Lpp Lp Lmp 20 26 34 44 Lmf Lf Lff Lfff 58 75 98 127 3.1.7. Voice The voice attribute tells which of the 16 MIDI channels to use for the note. The voice attribute consists of a ``V'' followed by an integer from 1 (the default) to 16. There is a limit to how many notes can be played at the same time on a given voice (MIDI channel). Since the limit depends upon the synthesizer, Adagio cannot tell you when you exceed the limit. Similarly, Adagio cannot tell whether your synthesizer is set up to respond to a given channel, so there is no guarantee that what you write will actually be heard. 3.1.8. Timbre (MIDI program) A MIDI program (synthesizer preset) can be selected using the attribute ``Zn'', where n is the program number. Notice that in MIDI, changing the program on a given channel will effect all notes on that channel. Also, for many synthesizers, you will not be able to change programs at the start of a note or during a note. Change the program during a rest instead. For example: R I Z23 V4 A4 -- change MIDI channel 4 to program 23 during rest -- play a note on channel 4 The cartridge programs on a DX7 can be accessed by adding 32 to the cartridge program number. For example, cartridge program number 10 is specified by Z42. 3.1.9. Tempo The length of a beat may be changed using a Tempo command: !TEMPO n where n indicates beats per minute. The exclamation mark tells Adagio that this is a special command line rather than a note definition. A special command takes the place of a note specification. No other attributes should be written on a line with a special command. The new tempo affects all succeeding notes. The time attribute of all succeeding notes is now measured relative to the time of the Tempo command. Durations specified in hundredths of seconds (for example U58, N15, or T821) are not affected by the Tempo command. The Tempo command is fairly clever about default durations. If the last duration specified before the Tempo command is symbolic (using one of S, I, Q, H, or W), then the default duration for the node after the Tempo command will be modified according to the tempo change. Consider the following tempo change: !TEMPO 60 A4 H !TEMPO 120 G In this example, the first note will last 2 seconds (2 beats at 60 beats per minute). The second note inherits the duration (H) from the first note, but at 120 beats per minute, the second note will last only 1 second. If the duration had been specified U200 (also a duration of 2 seconds), the second note would also last 2 seconds because the Tempo command does not affect times or durations specified in hundredths of seconds. 3.1.10. Rate The rate command scales all times including those specified in hundredths of seconds. A rate of 100 means no change, 200 means twice as fast, and 50 means half as fast. For example, to make a piece play 10% faster, you can add the following command at the beginning of the score: !RATE 110 Rate and Tempo commands combine, so !RATE 200 !TEMPO 70 will play 70 beats per minute at double the normal speed, or 140 beats per minute. Like Tempo, the time of the Rate command is added to the time attribute of all following notes up to the next Time or Rate command. 3.2. Default Attributes If an attribute is omitted, the previous one is used by default (with the exception of the time attribute). The default values for the first note, which are inherited by succeeding notes until something else is specified, are given below in Adagio notation: Time Pitch Duration Loudness Voice Timbre Tempo Rate T0 C4 Q LFFF V1 Z1 !TEMPO 100 !RATE 100 Pitch Bend After Touch Porta Rate Porta Switch Mod Wheel Foot Pedal Y128 O0 J99 K0 M0 X0 3.3. Examples The following plays the first two bars of ``Happy Birthday''. Note that Adagio knows nothing of bar lines, so the fact that the first note occurs on beat 3 or that the meter is three-four is of no consequence: *Example 1 -- Happy Birthday tune (C major) !TEMPO 120 G4 I. LF G4 S A4 Q G4 C5 B4 H The time attribute for the first note is zero (0). The second note will occur a dotted eighth later, etc. Notice that no timbre or rate was specified. Adagio will provide reasonable default values of 1 and 100, respectively. The following example plays the first four bars of an exercise from Bartok's Mikrokosmos (Vol. 1, No. 12). An extra quarter note is inserted at the beginning of each voice in order to allow time to change MIDI programs. The right hand part is played on voice (MIDI channel) 1 and the left hand part on voice 2. Notice the specification of the time attribute to indicate that voice 2 starts at time 0. Also, I have taken advantage of default octaves to reduce typing. *Example 2 -- Bartok *voice 1, right hand R Q Z10 V1 A4 H B Q C D H C D Q C B A B C D R *voice 2, left hand T0 R Q Z15 V2 G3 H F Q E D H E D Q E F G F E D R The next example is the same piece expressed in a different manner, illustrating the interaction between the Tempo command and the time attribute. Recall that the time attribute is measured relative to the time of the last Tempo command: *Example 3 -- 4 measures in 2 sections !Tempo 100 *Voice 1, Measures 1 & 2 R Q Z10 V1 A4 H B Q C D H C *Voice 2, Measures 1 & 2 T0 R Q Z15 V2 G3 H F Q E D H E H !TEMPO 100 *Voice 1, Measures 3 & 4 T0 V1 D4 Q C B A B C D R *Voice 2, Measures 3 & 4 T0 V2 D3 Q E F G F E D R Notice that the piece is written in 4 sections. The first plays a rest followed by two measures, starting at time 0. The next section changes the time back to zero and plays two measures of the left hand part (voice 2). The next command (!TEMPO 100) sets the tempo to 100 (it already is) and sets the reference time to be two measures into the piece. Therefore, the next note (D4) will begin measure 3. The D3 that begins the last group of notes has a T0 attribute, so it will also start at measure 3. Notice how the !TEMPO command can serve to divide a piece into sections. The last example will show yet another way to express the same piece of music using the ``Next'' attribute. Only the first bar of music is given. *Example 4 -- use of the Next attribute !Tempo 100 R Q Z10 V1 N0 R Q Z15 V2 A4 H V1 N0 G3 V2 B4 Q V1 N0 F3 V2 C4 Q V1 N0 E3 V2 Here, each pair of lines represents two simultaneous notes. The ``N0'' attribute forces the second line to start at the same time as the first line of each pair. 3.4. Advanced features Adagio allows you to write multiple commands on one line and to program MIDI control functions. It is also possible to write music with multiple tempi. 3.4.1. Multiple notes per line Notes can be separated by commas or semicolons as well as by starting a new line. A comma is equivalent to typing N0 and starting a new line. A semicolon is equivalent to starting a new line. Here is yet another rendition of the Bartok: *Example 5 -- use of semicolons !Tempo 100 R Q Z10 V1 A4 H; B Q; C; D H; C; D Q; C; B; A; B; C; D; R T0 R Q Z15 V2 G3 H; F Q; E; D H; E; D Q; E; F; G; F; E; D; R This example is similar to Example 2, except semicolons are used. The next example is similar to Example 4, except commas are used and four bars are notated: *Example 6 -- use of commas !Tempo 100 R Q Z10 V1, R Q Z15 V2 A4 H V1, G3 V2 B4 Q V1, F3 V2 C4 V1, E3 V2 D4 H V1, D3 V2 C4 V1, E3 V2 D4 Q V1, D3 V2 C4 V1, E3 V2 B4 V1, F3 V2 A4 B4 C4 D4 R V1, V1, V1, V1, G3 F3 E3 D3 V2 V2 V2 V2 3.4.2. Control change commands The current version of Adagio can control the following functions: Portamento Portamento Modulation Aftertouch Foot pedal Pitch bend rate (J) switch (K) wheel (M) (O) (X) (Y) The letter listed beside each control function is the Adagio command letter. For example, M23 is the command for setting the modulation wheel to 23. Except for pitch bend and the portamento switch, all values range from 0 to 127. Pitch bend is ``off'' or centered at 128, and has a range from 0 to 255. Turn on portamento with K127 and off with K0. There is no convenient way to get smoothly varying functions. You can either type in many control changes to achieve the effect you want or generate a file with the transcribe program containing the data you want. Control changes can be part of a note specification or independent. In the following example, a middle C is played with a modulation wheel setting of 50 and a pitch bend of 120. Then, at 0.1 second intervals, the pitch bend is decreased by 10: *Example 7 -C4 LMF M50 Y120 U100 N10 Y110 N10; Y100 N10; Y90 N10; Y80 N10; Y70 N10; Y60 N10; Y50 N10 3.4.3. Multiple Tempi Writing a piece with multiple tempi requires no new commands; you just have to be clever in the use of Tempo and Time. The following plays a note diatonic scale on voice 1, and a 12 note chromatic scale on voice 2: 7 *Example 8 -- multiple tempi !TEMPO 70 V1 C4; D; E; F; G; A; B T0 R N0 !TEMPO 120 V2 C4; CS; D; DS; E; F; FS; G; GS; A; AS; B !TEMPO 100 V1 C5, V2 C5 The third line plays the 7-note diatonic scale on voice 1. The next line contains the tricky part: notice that the time is set back to zero, there is a rest, and a next (N) attribute is used to specify that the next default time will be at the same time as the current one. Thus, the time of the !TEMPO 120 command is zero. After the 12 note scale, the tempo is changed to 100 and a final note is played on each voice. A little arithmetic will show that 7 notes at tempo 70 and 12 notes at tempo 120 each take 6 seconds, so the final notes (C5) of each scale will happen at the same time. 3.5. Running Adagio To use Adagio, create an Adagio score using a text editor. By convention, Adagio scores are named something.gio, where something is any name you like, and ``.gio'' is a hint that this is an Adagio score. To listen to the score, type adagio something Notice that typing the ``.gio'' is optional with Adagio (but you must type the full file name when you use the editor). If all goes well, you will get the following prompt: Type <RETURN> to play notes, q<RETURN> to quit, ?<RETURN> for help: If you type ``?RETURN'', you get a complete list of commands: <return> q<return> Play music Quit Adagio (Exit) r<return> m<return> Read tuning file Turn on MIDI byte trace -m<return> s<return> t<return> -t<return> ?<return> Turn off MIDI byte trace Report state Turn on music operation trace Turn off music operation trace This message If you type RETURN to start your piece, Adagio first sends a MIDI program change to program 1 on each of the 16 MIDI channels. Exactly 1 second after typing RETURN, Adagio starts playing your score. If you type ``qRETURN'', Adagio will exit. If you type ``rRETURN'', you will get a prompt for a tuning file (see Chapter 4). This command is useful for playing a single score with different tunings without exiting Adagio. The ``m'' command causes Adagio to print a trace of all MIDI data. This is not useful for anything other than debugging changes to Adagio. The ``-m'' command turns tracing off. The ``s'' command reports what data will be traced. The ``t'' command causes Adagio to print a trace of all music operations. The information printed in a fairly readable form as opposed to the numbers printed using the ``m'' switch. The ``-t'' switch turns off the music operation trace. 3.5.1. Debugging Adagio Scores While the score is playing, you can type the space bar to stop the performance. The computer screen will then tell you where it was at the moment you hit the space bar. This is very handy for finding the source of bad notes. For example, if you hear a bad note, play the score again and type a space when you hear the mistake. Suppose the screen says that you were playing a note from line 123 and another from line 259. Remember these numbers, quit Adagio (type ``Q''), and type edit something.gio Now type (to the editor(I assume you have an emacs-like editor.)) CTRL-U 1 2 2 CTRL-N. This moves the cursor down 122 lines to line number 123. If the mistake is not found, put the cursor back at the top of the file (ESC <) and go down 258 lines (CTRL-U 2 5 8 CTRL-N) to get to line 259. 3.5.2. Command Line Options The following options can be specified in the command line: - -debug (or -d): turn on debugging code. - -help: print a list of command line options. - -miditrace (or -m): turn on MIDI byte trace. - -trace (or -t): turn on music operation trace. - -at: tell Adagio that you are running on an AT. (This option is redundant information for debugging. There is no advantage to using it because Adagio and other CMT software can determine the machine type automatically.) - -metronome (or -met): turn on the MPU-401 metronome. This is only useful for debugging because there is no way to set the metronome speed. - -simulated (or -s): simulate the MPU-401 but do not use it. This is only useful for debugging and may not be implemented. - -xt: tell Adagio that you are running on an XT. for debugging _ see -at.) (This option is - -print: tells Adagio to print details of each note on the console. - -block: turns off the MPU-401 MIDI THRU function so that the input is not passed to the output. - -tune filename: tells Adagio to use filename to specify tuning. 3.5.3. Examples Run Adagio with score.gio as the score: adagio score Run Adagio with score.gio as the score specify tuning: and using meantone.tun to adagio score -tune meantone 4. Defining Nonstandard Tunings Tuning in MIDI is normally twelve-tone equal temperment. MIDI has no provisions to change this except by using the pitch bend control. In general, a different setting of pitch bend is needed for each pitch in a scale. Needless to say, this can be very tedious to program explicitly; however CMT has a way to automate the use of pitch bend to obtain the desired scale. Notice that pitch bend affects every note on a given channel; therefore, it is generally not possible to play several notes on one channel whenever alternate tunings are desired. (Ignoring this limitation can lead to some very interesting effects, however.) The tuning mechanism in CMT is quite simple: whenever a program (Adagio, Record, Transcribe, Moxc, etc.) goes to play a note, the note's pitch is used as an index into a table of (pitch, pitch bend) pairs. The pitch bend is sent, followed immediately by the pitch from the table. Using the table, it is possible to translate every specified pitch into an arbitrary pitch and pitch bend. Important: CMT assumes that you have set up your synthesizer so that the pitch bend range is one semitone (100 cents) and that the synthesizer changes frequency exponentially with respect to the pitch bend value. If your synthesizer is different, it will be necessary to modify CMT to make its tuning mechanism more general. The current system seems to work fine with a DX7. The formula used to calculate the MIDI pitch bend data is PitchBend = (8192 * c) + 8192, where c is the pitch bend in cents (not the velocity of light). A scale is defined by a ``tuning'' file (``.tun'' is the default suffix), which can be created with the help of a program called tuning, described below. The format of the file is simple. Each line of text in the file consists of three numbers. The first is a note number (48 is middle C) between 12 and 115. The second is a pitch, also between -12 and 115, and the third is the pitch bend, between -100, and 100. Any pitches that are not mentioned in the file are given their normal equal-temperment interpretation. To use a tuning file, say option anywhere in the command line: meantone.tun, you type the following -tune meantone If no tuning is specified, then notes are not bend commands are sent. translated and no pitch 4.1. The Tuning Program The tuning program lets you define scales in terms of ratios or cents. You run Tuning by typing tuning myscale where myscale.tun is the name of the tuning file you want to create. (An extension of ``.tun'' will automatically be appended.) The program then prints: You will now create a pitch file. r - enter a range of values o - enter one octave (that will be generalized) p - enter one pitch (you choose) q - quit Command >> To which you should respond with one of r, o, p, or q. associated with each command are described below: The actions 4.1.1. Entering a range of pitches. The r command prompts you for a range of pitches and then prompts you for a pitch and pitch bend for each pitch in the range. For example, The following is a transcript of what you would do to make C4 through D4 sound 1 step and 10 cents (110 cents) higher (user typein is in italics): Command >>r Adagio note range is -12..115 What range of notes would you like to enter ? From >> 48 To >> 50 For the given Adagio note number, enter the desired pitch and the amount of pitchbend in cents Bend range = -100 .. 100, '0' for no pitch bend (100 cents = 1 semitone) Pitch range = 1 .. 128, 48 is middle C Adagio Note 48 pitch >>49 bend >>10 Adagio Note 49 pitch >>50 bend >>10 Adagio Note 50 pitch >>51 bend >>10 4.1.2. Entering an octave The o command lets you enter information for one octave and then automatically transpose that information (by octaves) to provide information for all possible pitches. Here is an abbreviated sample transcript for a tuning in which every other note is 50 cents sharp: Command >>o You will input the information for one octave and that will be generalized for all octaves. Would you like to enter the information as (1) frequency ratios of notes to a tonic or (2) semitones (integer) + pitchbend (in cents) >>2 For the given note number, enter the desired pitch and the amount of pitchbend in cents based on given tonic 1 unit of pitch = 1 semitone -- the tonic (note 0) has pitch 0 and bend 0. Bend range = -100 .. 100 cents, 0 for no pitch bend (100 cents = 1 semitone) Octave note 1 pitch >>1 bend >>50 Octave note 2 pitch >>2 bend >>0 Octave note 3 pitch >>3 bend >>50 -- six entries are omitted here -Octave note 10 pitch >>10 bend >>0 Octave note 11 pitch >>11 bend >>50 What note would you like your octave to start on? C C# D D# E F F# G G# A A# B 0 1 2 3 4 5 6 7 8 9 10 11 >>0 4.1.3. Entering one pitch You can use the p command to modify just one pitch. This is useful to go back and change information you have entered with the r or o commands. Here is an example that makes A4 (pitch number 57) play an octave higher: Command >>p Adagio note range is -12..115. (type return to quit) Note >>57 Which note would you like to enter ? For the given pitch number, enter pitch parameters Bend range = -100 .. 100 cents, 0 for no pitch bend (100 cents = 1 semitone) Pitch range = 1 .. 128, 48 is middle C Adagio note 57 pitch >>69 bend >>0 Adagio note range is -12..115. (type return to quit) Note >> Which note would you like to enter ? 4.1.4. Saving a tuning file. When you are done, use the q command to save the tuning file: Command >>q As discussed above, you can use the resulting file with Adagio, Record, and Moxc to control tuning. 4.2. The Retune Program A simple demonstration program is included with CMT to allow you to ``retune'' a MIDI controller. To use it, you should connect a MIDI controller (e.g. a DX7) to MIDI IN or your computer interface, and connect MIDI OUT of the interface to a synthesizer (e.g. a TX816). Run Retune by typing: retune -tune myscale where myscale.tun is a tuning file previously created. Now, as you play on the controller, the Retune program will use pitch bend in real time to adjust the pitch of each note. Note: The Retune program is \test\retune.exe on CMT DISK 1. You may have to copy it to your \cmt directory to use it from another directory. Also notice that since pitch bends apply to the whole keyboard, polyphonic notes interfere with one another. 5. The Transcribe Program 5.1. Overview The Transcribe and to program is used to record performances via MIDI translate them into Adagio. Rather than attempt to guess musical durations (like a dotted-quarter), Transcribe records all times and durations in hundredths of seconds. This program, together with Record makes it fairly simple to build up complex Adagio scores one part at a time. Transcribed music can be copied, edited, or repeated using an ordinary text editor. The output from Transcribe might also be useful in the study or evaluation of performances. 5.2. Tutorial To use Transcribe, type transcri filename where filename is the name of the file you want to create. If you just type transcri, you will be prompted for a file name. In either case, a default extension of ``.gio'' will be assumed. Transcribe will then ask you if you want to record control information: Pitch bend, etc. on? [n] Type ``y'', ``n'', or just RETURN for the default (n). Generally, you should type RETURN. At present, recording control information tends to use up the available recording memory very quickly, so it is of limited use. When you have finished what you want to play, type the space bar on the IBM. This signals the program to stop and write out the notes. This file can now be played by Adagio and what comes out will be very similar to what you just heard yourself play. 5.3. Timbre When you start Transcribe, it sends MIDI program change commands to program 1 on all MIDI channels. When you make a program change on the synthesizer, Transcribe records the change and translates it into an Adagio Z attribute. This corresponds to the default Z1 attribute in Adagio and to the fact that Adagio also initializes all MIDI channels to program 1. Note: Transcribe (and Record) record Z attributes whenever they receive a MIDI Program Change command. Synthesizers normally send this when you change a preset sound using the controls on the synthesizer. One noteable exception is that if a Yamaha synthesizer is in the ``Sys Info Avail'' mode, then pressing a button to change the program causes the synthesizer to dump all of the internal data comprising the program. No MIDI Program Change command is sent, so Transcribe records nothing. You can find out what is happening by running the MM (Midi Monitor) program and pushing a program change button on your synthesizer. For Yamaha synthesizers, the mode should be ``Sys Info Unavail'', which is a fairly cryptic way to say ``send program numbers instead of program data''. 5.4. Command Line Options The following options can be specified in the command line: - -debug (or -d): turn on debugging code. - -help: print a list of command line options. - -miditrace (or -m): turn on MIDI byte trace. - -trace (or -t): turn on music operation trace. - -block: turns off the MPU-401 MIDI THRU function so that the input is not passed to the output. - -control (or -c) on: record control information, do not the ``Pitch bend, etc. on?'' query. present - -control (or -c) off: do not record control information, do not present the ``Pitch bend, etc. on?'' query. 5.4.1. Examples Print information about command line options: transcri -help Record data into file foo.gio, do not record control turn information, and off the MIDI THRU function of the MPU-401: transcri foo -c off -block 6. The Record Program 6.1. Overview The Record program is a combination of Adagio and Transcribe. It is used to record performances via MIDI and to translate them into Adagio while simultaneously playing an Adagio score. This allows you to enter scores one part at a time. Section 6.3 discusses how you can combine several Adagio scores into one. 6.2. Instructions Before using Record, you should first create an Adagio score, either with an editor or with Transcribe. This will be the score that Record plays while recording a new score. Let us assume that the existing score is called old.gio and you want to record another part called new.gio. Run Record by typing: record old Record uses the Adagio compiler to get old.gio ready to play. prompts you with: It then Type <RETURN> to record, q<RETURN> to quit, ?<RETURN> for help: The possible responses are described in detail in Section 3.5. Normally, you should just type RETURN to start playing and recording. Next, you will get a prompt for the file you want to create: Name of output file: Type new. The extension .gio will be added automatically if you do not provide any extension. After a delay of one second, Record begins to play the score in old. The delay is not precise, so you would normally put something into old to ``count off'' the start play. Recording does finishes playing. of not the piece. stop until Record also records anything you you type the space bar, even if old After you type the space bar, you will again get the prompt: Type <RETURN> to record, q<RETURN> to quit, ?<RETURN> for help: You can erase and re-record new by repeating the instructions given above. (This time, you will have to confirm that you want to erase new.gio.) Otherwise, just type q to quit. You now have two files, old.gio and new.gio. The next section describes how to merge files to create a single file for Adagio. 6.3. Merging Adagio Scores A few tricks are helpful in merging two scores into one. In this section, I will describe the basics of making two scores play at the same time as well as getting two scores to play in sequence. It should be remembered that the goal is simply to create an Adagio score. If you try something that does not work, you should be able to look at your scores and figure out where the problem is. 6.3.1. Playing two scores in sequence To play two scores in sequence, you should edit the first score file so that the last line contains the last event of that score. Since Adagio scores are not necessarily written in time order, this may or may not be the case. Transcribe and Record always write scores in the order of note start times. The last note will specify a next (N) attribute that corresponds to the time you typed the space bar. Once you get the that if you insert some after the score (unless they conduct an experiment to find timing of the last note right, it should be apparent notes at the end of the score, they will be played specify a time (T) attribute). out where your score ends. You might want to If you insert the line C6 LFFF U20 at the end of your file, you should here a distinctively loud and high note at the end of your score. If the ``end'' comes too early or too late, you should edit your file accordingly. You can DOS copy command: insert an entire file at the end of another file using the copy score1.gio+score2.gio DOS knows nothing about Adagio, so the .gio extensions must be specified. This command will modify score1.gio by appending the contents of score2.gio, which is not modified. An alternative form is copy score1.gio+score2.gio score3.gio which puts the score2.gio intact. result into score3.gio, leaving score1.gio and If score2.gio does not specify (T) attributes, then appending the scores should be adequate. On the other hand, if score2.gio specifies times relative to the beginning of the score, you can insert a !TEMPO command at the beginning. Then, when score2.gio is appended to score1.gio, the !TEMPO command will occur at the end of score1.gio, and everything in score2.gio will be measured relative to the !TEMPO command. 6.3.2. Playing two scores at the same time To get two scores to play at the same time, you should make sure that the second score includes a time (T) attribute on or before its first note. Otherwise, the second score will be played after the first one. (Record automatically inserts a time attribute on the first note of each file it creates. The time is the correct one assuming you want to merge the recorded file (e.g. new.gio with the played file (e.g. old.gio). The DOS copy command is used to merge the two files as described above. A typical problem is trying to play two scores on the same voice (MIDI channel). This is only a problem if both scores try to play the same note at the same time. MIDI assumes that notes are turned on by pressing keys, and it is impossible to press the same key twice without first releasing it. This problem usually manifests itself as notes that get cut off early. If you are trying to merge two files with the same voice, you might want to change the voice specified for the second file to make sure the two scores do not interfere. (This of course assumes you have several synthesizers so that you can play several channels at once.) 6.4. Command Line Options The following options can be specified in the command line: - -debug (or -d): turn on debugging code. - -help: print a list of command line options. - -miditrace (or -m): turn on MIDI byte trace. - -trace (or -t): turn on music operation trace. - -at: tell Record that you are running on an AT. (This option is redundant information for debugging. There is no advantage to using it because Record and other CMT software can determine the machine type automatically.) - -metronome (or -met): turn on the MPU-401 metronome. This is only useful for debugging because there is no way to set the metronome speed. - -simulated (or -s): simulate the MPU-401 but do not use it. This is only useful for debugging and may not be implemented. - -xt: tell Record that you are running on an XT. for debugging -- see -at.) (This option is - -print: tells the console. Record to print details of each compiled note on - -block: turns off the MPU-401 MIDI THRU function so that the input is not passed to the output. - -control (or -c) on: record control information, do not present the ``Pitch bend, etc. on?'' query. - -control (or -c) off: do not record control not present the ``Pitch bend, etc. on?'' query. information, do 6.4.1. Examples Print information about command line options: record -help Play score in file foo.gio, do not record control information, and turn off the MIDI THRU function of the MPU-401: record foo -c off -block 7. DXGet and DXPut DXGet and DXPut are used to store and recall programs for synthesizers. These are generally voice programs, but in general they can be any sequence of MIDI system exclusive messages. 7.1. DXGet The DXGet program is used to record MIDI system exclusive messages and store them in files. The program is designed to work with DX7 and TX816 synthesizers, although it should work with any others. To run DXGet, type dxget filename where filename is the name of a file you want to create. If you leave off the extension, DXGet will use the extension ``.dx7''. If you do not specify a file, you will be prompted for one. After DXGet starts, you will get the following prompt: Do you want instructions? [y] to which you can respond with "y," "n," or just RETURN to indicate yes. If your response is yes, the following instructions are printed: This program will let you save DX7 and TX816 voice programs. To save DX7 programs, you must connect DX7 MIDI OUT to MPU 401 MIDI IN. For TX816 programs, connect TX816 MIDI OUT to MPU 401 MIDI IN, and set the OUT SLOT (leftmost display) to the module from which you want data. For DX7: push the orange FUNCTION key, then the green 8. Push 8 repeatedly until the display says SYS INFO AVAIL or SYS INFO UNAVAIL. If the display says SYS INFO UNAVAIL, push YES to change the display to SYS INFO AVAIL. Now, select a voice with the INTERNAL or CARTRIDGE button followed by a (green) number. For ALL DX7 voices: after getting the SYS INFO AVAIL display, push 8 again to get MIDI TRANSMIT ?. Push YES. For TX816: Hold down SELECT until you see UT in the display. Let up on SELECT and then push it once more to get the flashing dU. Push YES to dump all data from this module. Hold down SELECT until you see digits in the display. After data is dumped, dxget will tell you what it got and write data to a file. Use dxput to copy a file to a dx module. Next, you are prompted as follows: Ready for your data. Type space when you are done... and you should cause your synthesizer to send whatever system exclusive messages you want to save. One or more messages can be saved. Notice that DXGet will not request information automatically. This would require DXGet to know about types of synthesizers. To be more general, DXGet is very simple. Consequently, you must manually cause your synthesizer to send the desired information to DXGet. After one or more messages have been sent, type the space bar and the messages will be saved in the file you specified. The file format is simply bytes in hexadecimal separated by spaces. As the data is output, DXGet will check to make sure the data has not been garbled. A warning will be printed if any errors are detected. DXGet will also try to print a description of the exclusive messages it received. You can send the data back to your synthesizer using the DXPut program described below. NOTE: If you find that you are sending two messages instead of one when you use DXGet, here is what is probably happening: When you push the ``yes'' button to send data, the DX7 sends a MIDI message that means ``push the `yes' button'', analogous to sending a MIDI note-on message whey you press a key. Next, the DX7 sends voice data as requested. Meanwhile, the first MIDI message has gone into the MPU-401 MIDI IN port and has been forwarded to the MIDI OUT port. If the MPU-401 MIDI OUT is connected to the DX7 MIDI IN, then the DX7 gets a second request (via MIDI) to send data! You can avoid this by disconnecting the DX7 MIDI IN cable or by using the ``-block'' switch when you run DXGet. 7.2. DXPut The DXPut program takes files created by DXGet and sends them as MIDI output. To run DXPut, type dxput filename where filename is the file created by DXGet. A default extension of ``dx7'' is assumed if you do not specify one. After starting, DXPut will ask: Do you want instructions? [y] The instructions are as follows: This program will let you send voice programs to a DX7 or TX816. You must send a file previously created by the dxget program. To send DX7 programs, you must connect MPU 401 MIDI OUT to DX7 MIDI IN. For TX816 programs, connect MPU 401 MIDI OUT to TX816 MIDI IN. For a DX7 voice: push the orange FUNCTION key, then the green 8. Push 8 repeatedly until the display says SYS INFO AVAIL or SYS INFO UNAVAIL. If the display says SYS INFO UNAVAIL, push YES to change to SYS INFO AVAIL. Next, push MEMORY PROTECT/INTERNAL, then NO, and then then MEMORY SELECT/INTERNAL. Now, type the name of a file created by dxget and then the space bar to send data. To store the voice program on the DX7, hold down STORE while you push a (green) number. The current voice at this number will be erased and replaced by the new voice. For ALL DX7 voices: Same as for a single DX7 voice, but all 32 voice programs on the DX7 will be replaced when you type Y in response to `Are you sure?' on the computer. For all TX816 voices: To store into one module, push NO until the MEMORY PROTECT light goes out. Make sure other MEMORY PROTECT lights are ON. Type Y in response to 'Are you sure?' on the computer. Assuming the file you specified contains valid data, you will see the prompt: next Ready with your data. Type space when you are ready... Type space to send the data. If a message is one which DXPut knows will cause a major change, you will be asked for confirmation before the data is sent. At present DXPut only asks you to confirm Yamaha 32 voice and 64 performance data messages. After the data in the file is sent, you are prompted by: Type Q to quit, RETURN to send another file: As the computer says, you can quit or continue to send more Typing a data. file name gets you back to the prompt: ``Do you want instructions? [y]'', which even if you asked for them earlier have by now have scrolled off the screen. 8. Programming in C 8.1. Introduction This chapter is not about how to write in C Major, nor is it about how to reallize a piece by Terry Riley with a computer. Rather, it is about a programming language called ``C''. Specifically, this chapter is about how to use a somewhat twisted subset of the C language to make music with CMT. I hope to expand this chapter in the future, but for now, it's going to look more like lecture notes than a complete guide to programming. 8.2. Writing a Program Let's say you want to write a program called ``foo'' that plays a couple of notes. First use the editor to make a file called ``foo.c'': edit foo.c When you get into the editor, type the following program: /* a simple program */ #include "cmtprog.h" mainscore() begin note(48, note(50, note(52, note(53, end 100); 50); 50); 100) This program will play 4 notes in sequence by ``calling'' note 4 times. You indicate what kind of note you want by giving two parameters in parentheses after the word ``note''. The parameters for note specify pitch and duration. The pitch parameter is 48 for middle C (C4), and goes up by one for each semitone. Thus, the pitches in this program are C4, D4, E4, and F4. The second parameter is the duration parameter, expressed in hundredths of a second. Thus, the first note will be 1 second long, the next two will be 0.5 seconds, and the last will be 1 second. A few more observations: notice that the ``score'' consists of calls to note preceded by a name, ``mainscore()'', and is bracketed by the words ``begin'' and ``end''. Also notice that calls to note are separated by semicolons (;). All of these details are essential, and C is not very forgiving of even trivial mistakes. The first line of the program is called a comment. Any text between /* and */ is ignored by C. You will find it useful to use comments to remind yourself what your program does, when it was written, and so on. The next line, beginning with ``#include'' tells the C compiler to read the file ``musiprog.h'' from the system library. This file contains important definitions for music, so every program you write should contain this line. Unlike Adagio, C is a case sensitive language. This means that mainscore, MainScore, MAINSCORE, and MaInScOrE are all distinct identifiers. Because of this, I suggest that you never use upper case letters in C programs, except in strings and comments. To hear the results of this program, you have to compile your program, then link it, and then execute it. The Lattice C compiler (invoked by the command cmtc) translates your program into a machine language form which is stored in a file. This file is then linked, or combined with other programs to produce an executable program file. This file can then be executed, meaning that the machine instructions in the file are loaded into working memory, and the computer then carries them out. To compile your program (called foo.c), type the following: cmtc foo.c If your program has errors, the line number where the error was discovered will be printed along with a brief description of the error. You must fix these errors by editing your program and then compiling again. When the compile phase and then the link phase completes without errors, you can link your program by typing: cmtl foo The command cmtl is short for cmt link. Be sure the last character is the letter l, not the numeral 1. If no errors are reported by the linker, your program is ready to run. Otherwise, you will have to fix your program with the editor, recompile, and relink. To run your program, type foo To run your program again, change your program with the editor. you need not recompile unless you 8.3. Stopping a Program Sometimes you need to stop programs in progress before they finish on their own. You can type CTRL-C (hold down the CTRL key and type ``C'' or CTRL-BREAK to stop your program in most cases. It is possible to write programs that cannot be stopped except by turning off the computer, but these are unusual[Typing CTRL-C or CTRL-BREAK sets a flag but does not terminate your program immediately, because doing so might leave the MPU-401 interface in a state that would cause problems later on. Instead, the gettime function checks the flag and carefully exits your program if the flag is set. Since gettime is called by almost every procedure that takes any significant time to execute (e.g. note, rest, and rest until), it is a pretty safe bet that typing CTRL-C will terminate any program.] 8.4. Writing a Procedure Procedures allow you to give a name to a musical phrase. The phrase can then be called from several places. This saves your having to retype all of the calls to note every time you want to hear the phrase. In the following example, there called from mainscore: are two phrases, or procedures, up and down, which are #include "cmtprog.h" mainscore() begin up(); down(); up(); note(48, 100) end up() begin note(48, note(50, note(52, note(53, end down() begin note(55, note(53, note(52, note(50, end 20); 20); 20); 20) 20); 20); 20); 20) Notice that up and down obey the same rules as mainscore: procedure names are followed by a pair of parentheses and the word begin. Then there is a list of calls to note or other procedures separated by semicolons. While note has two parameters, up and down each have zero parameters. You indicate the absence of parameters when you call up or down by not putting anything between the open and close parentheses. However, you must always type the parentheses after the name of any procedure you call. Finally, the sequence of calls is ended by the word end. When this program is run, the computer will do what mainscore says to do. The first call is to up, so the computer finds the definition of up and does what up says to do. In this case, up plays four notes (C, D, E, F). Now that up is finished, mainscore continues by calling down, which in turn plays (G, F, E, D). When down returns, mainscore continues with another call to up, and again up will play C, D, E, F. Finally, up returns and mainscore plays a C. At this point the mainscore program is finished. 8.5. Repeats Repetition is important in both programming and music. To repeat a phrase, a special ``repeat'' construct is provided. Consider the following example: #include "cmtprog.h" mainscore() begin repeat(i, 5) note(48, 30); note(60, 30) endrep end Look at the two calls to note above. By themselves, these calls would play the phrase C4 C5. The repeat construct consists of the form repeat(counter, howmany) phrase endrep where counter is just the name you want the computer to give to a variable number that keeps track of which repeat is being played (more about this later), howmany is the number of repeats to take, and phrase is a sequence of calls to note or any other procedure. The example above will play the phrase C4 C5 C4 C5 C4 C5 C4 C5 C4 C5 because we wrote 5 for the number of repeats. If you like, you can repeat up to about 30,000 times. The phrase could just as well have been calls to other procedures. The next example uses repeat to play C4 D4 E4 F4 three times, using the up procedure used earlier. #include "cmtprog.h" mainscore() begin repeat(i, 3) up() endrep end up() begin note(48, note(50, note(52, note(53, end 20); 20); 20); 20) 8.6. Conditions Computer programs are made more flexible by the use of conditionals, that is, the ability to test a condition and act upon the result of the test. For example, suppose you want to repeat a phrase, but you want the phrase to have a first and second ending. In other words, the first time through you want the end of the phrase to be played one way, and the second time through, you want the ending to be played another way. How would you do this using just repeats and procedures? The following program uses a new construct (the when construct) that makes this programming task easy: #include "cmtprog.h" mainscore() begin repeat(i, 2) note(60, 30); note(59, 15); note(57, 15); note(55, 15); note(53, 15); note(52, 15); when i == 1 then note(53, 15); note(55, 120) otherwise note(50, 15); note(48, 120) endwhen endrep end This is a program with a repeat construct, but notice that the last part of the repeated phrase is of the form: when condition then phrase1 otherwise phrase2 endwhen The computer interprets this construct as follows: whenever a when is encountered, the computer evaluates the following condition. In this case the condition is ``i == 1'', which is true when the repeat counter (i) is equal to one (the first time through) and false when the repeat counter is not equal to one[The double equal symbol ``=='' is interpreted by C to mean ``is equal to''. A single equal symbol ``='' is called the assignment operator, and should never be used unless you know what you are doing. Even experienced programmers occasionally slip and write ``='' when they mean to write ``==''. It is a good idea to use the editor to search for all occurrences of ``='' to make sure your program is correct.]. If the condition is true (which happens the first time through in this example), the phrase following then is performed up to the word otherwise, and the phrase between otherwise and endwhen is skipped. If the condition is false, the phrase between then and otherwise is skipped, and the phrase between otherwise and endwhen is performed. As usual, these phrases can be calls to other procedures like up and down. The when construct is used to select between two alternatives where the selection is based on a condition. Useful conditions are: a a a a a a == b > b < b >= b <= b != b ------- true true true true true true if if if if if if a a a a a a is is is is is is equal to b greater than less than b greater than less than or not equal to b or equal to b equal to b b Here, a and b stand for repeat counters (like i) or numbers (like 0, 1, etc.). Like the repeat construct, the when construct can be used anywhere in a phrase. The following example plays a phrase three times. On the second time through, the middle of the phrase is extended. The procedures p1, p2, and p3 are not shown. #include "cmtprog.h" mainscore() begin repeat(i, 3) p1(); when i == 2 then p2() otherwise endwhen p3() endrep end In this example, the phrase between otherwise and endwhen is nonexistent. That means that on the first and third times through, the complete phrase will be equivalent to p1(); p3(). On the second time through, the phrase will be equivalent to p1(); p2(); p3(). 8.7. Parameters It's time to confess: there is really no difference between mainscore, note, up, and down. They are all procedures, and every procedure has a list of parameters, which can serve to modify its behavior. For example, the parameters to note tell the note procedure what pitch and duration to use. You can easily write your own procedures that have (more than zero) parameters. Study the newup procedure below, which plays C4 D4 E4 F4. The procedure has one parameter, called dur, which determines the duration of each note: #include "cmtprog.h" mainscore() begin newup(25); newup(50) end; newup(dur) begin note(48, note(50, note(52, note(53, end dur); dur); dur); dur); When mainscore is played, it first calls newup with the parameter 25. The computer will then find the definition of newup and notice that the parameter is to be named dur. Now, whenever the computer finds the name within newup, it will substitute dur's value (25). Thus, the duration specified for all of the calls to note will be 25, or one quarter second. dur After the fourth note is played, the computer returns to mainscore, where the next thing in the sequence is another call to newup. But this time, the parameter is 50. The computer goes through the same steps as before: it finds the definition of newup, associates the value of 50 with dur, and substitutes 50 for dur throughout the definition of newup. The result is that the four notes (C4 D4 E4 F4) are now played with durations of 50, or one half second. Parameters may be named with any string of letters, just like procedures. It is a good idea to use mnemonic names, that is, names that remind you of their purpose. It makes no difference to the computer, but when you start writing large programs, you will find that it is important to make programs readable and understandable. Important: When you use parameters, the number of parameters in the definition must match the number of parameters in the call. The order of parameters in the call determines which parameter gets which value. Neither of these properties are checked by the C compiler, so very strange things will happen if you make a mistake here. 8.8. Producing Chords The C language is called a sequential language because programs are executed in sequence, performing only one operation at a time. This is a big limitation for music, and the next chapter will present some solutions to the problems of using C. In the meantime, there is a fairly simple way to get two notes sounding at once. The procedure pnote is just like note except that pnote does not wait for the specified duration. Instead, it schedules the end of the note to happen in the future but returns immediately without waiting. The following procedure plays a minor chord with the specified tonic and duration: minor(tonic, duration) begin pnote(tonic, duration); pnote(tonic + 3, duration); note(tonic + 7, duration) end The first two notes of the chord are played using pnote. Since pnote returns immediately, all three notes start very close to the same time. The third note uses the note routine, which will delay for duration before returning. Thus the minor procedure will also take duration before returning. 8.9. Low-level Procedures Up until now, we have concentrated on writing programs that control the high level structure of your music. By now, I hope you are beginning to enjoy some of the power available to you as a computer programmer. Now, it is time to learn about the lower levels of control, which concern direct control over the synthesizer. I mentioned earlier that note is just a procedure. Here it is: note(pitch, duration) begin midi note(1, pitch, 100); rest(duration); midi note(1, pitch, 0); end The note procedure first uses the procedure midi note to start a note. The parameters are the MIDI channel number (1), the pitch, and the key velocity (100). The rest procedure stops the specified by duration. This sustains the note. program for the length of time The third procedure call is another call on midi note. The key velocity of zero (the third parameter) indicates to turn the note off. Incidentally, the Adagio program also uses the same midi note procedure. You can find the C programs that make up Adagio in the directory ``\cmt''. 8.9.1. Other Procedures and Functions A complete list of music functions and procedures are listed in the file \cmt\mpu.c and in Appendix I. A summary of some useful ones, as well as some useful C procedures are given below. Italicized parameters stand for numbers that you supply when you call the function. printf("this is a string\n"); writes "this is a string" on the screen when called. The two characters \n should be included after the text you want written. This tells the computer to ``write'' a newline after writing the line of text. rest(duration); does nothing for duration (expressed in hundredths of seconds). getkey(waitflag); gets a keyboard event. Returns a the key number (numbered starting at 0) when a key is pressed, and the key number plus 128 when a key is released. If waitflag is not zero, getkey will wait for a key to be pressed. If waitflag is 0, getkey may return -1 to indicate no key has been pressed. Example return values and their meaning are -1: no key was pressed, 48: middle C was pressed, 176: middle C was released (176 = 128 + 48). midi note(channel, pitch, velocity); sends a MIDI note-on command. The parameters are the MIDI channel (from 1 to 16), the key number (from -12 to 115), and the key velocity (from 0 to 127). If the velocity is 0, then the note is turned off. midi program(channel, program); changes the synthesizer program (preset) to the one indicated in the second parameter. The first parameter is the MIDI channel. random(low, high); a function whose value is a random number between low and high inclusive. 8.10. Command Line Options - -debug (or -d): turn on debugging code. - -help: print a list of command line options. - -miditrace (or -m): turn on MIDI byte trace. - -trace (or -t): turn on music operation trace. - -block: turns off the MPU-401 MIDI THRU function so that the input is not passed to the output. - -tune filename: tells Adagio to use filename to specify tuning. 8.10.1. Examples Assuming you have compiled a program named foo, foo -help will print a list of command line options. To run foo with a tuning specified in myscale.tun, with a printout of music operations as they are performed: foo -tune myscale -t 8.11. Conclusions Becoming a master programmer is like mastering an instrument; it takes years of hard work. Fortunately, you can make a lot of good computer music without a degree in computer science[Of course, you can make a lot of bad computer music with a degree in computer science.]. If you are confused about some point or about how to implement an idea, ask someone for help. It is often useful to write small programs to test out ideas before you write a magnum opus. Write small procedures and test them before putting them into a large confusing program. If you want to learn more about programming, consider taking a computer science course if you haven't already. Your instructors can suggest textbooks if you want to study more independently. There are many programming concepts that I have not even touched in this quick introduction. I designed this system so that students could have all the flexibility offered by programming in order to create new music. Phil Miller showed me how to distill the important concepts to the point where basic programming skills can be developed in a few weeks. Now it's up to you: let there be music! 9. Moxc: real-time programming package It is the cause, it is the cause, my soul: Let me not name it to you, you chaste stars! It is the cause. Othello V.2 9.1. What is Moxc? Moxc is a set of C procedures to help the programmer write programs that perform many tasks simultaneously. The most important procedure is called cause, which allows you to make things happen not now, but in the future. Moxc is a compatible extension of all the programming constructs you learned in the last chapter. Moxc is derived from a system called Moxie, written by Douglas Collinge of the University of Victoria. The original Moxie runs on a Synclavier I, and another version exists as an extension to the Forth language. 9.2. An example The following example uses Moxc to implement a simple performance instrument. A blow-by-blow commentary follows the example: /* an example program using moxc */ #include <\cmt\musiprog.h> keydown(c, k, v) begin pnote(k+12, 100); end asciievent(c) begin when c == 'q' then quit() endwhen end hello() begin printf("hello\n"); cause(300, hello); end mainscore() begin hello(); end The first line is an optional comment which is ignored by the compiler. The next line of text tells the compiler to include some definitions from a file called \cmt\musiprog.h as described in the last chapter. The procedure keydown is called automatically by Moxc keyboard key is pressed. Three parameters, the MIDI channel, the the key velocity are passed to keydown. When a key is pressed, value where 48 corresponds to middle C, 49 is C-sharp, and so whenever a key number, and k will have a on. The procedure pnote takes two parameters: pitch and duration, and it is the same procedure described in the last chapter (see Section 8.8. Notice how I added 12 to k in the first parameter position to make the pitch always one octave higher than the key that was pressed. The second parameter is always 100, for a duration of one second. The next procedure definition is for asciievent. This procedure is called whenever a character is typed at the computer. The character typed is passed as a parameter to asciievent. To denote a character value in C, you enclose the character in single quotes as shown in the example. The asciievent in the example only responds to one letter, 'q', which results in a call to quit, a built-in procedure that tells Moxc to halt execution. Notice that 'q' is written in single quotes to denote the letter ``q''. Without quotes, q would refer to a parameter or procedure name. The exact same asciievent procedure is provided by default if you do not provide your own. With just these two procedures, a simple instrument has been implemented which doubles every note up one octave, and holds the octave for one second. When using the DX7 and TX816, everything you play on the DX7 will be sent directly to the TX816 as well as to Moxc. Thus, you always hear what is being played on the DX7 combined with any notes played by your Moxc program. (Unless you use the -block option in the command line.) Moxc provides a few more features. Look at the next procedure definition, for hello. The hello procedure prints ``hello\n'' on the terminal. (The ``\n'' code at the end means type a newline, putting the cursor at the beginning of the next line.) The next line is the interesting one: cause(300, hello); means that Moxc should, after 300 time units, cause the procedure hello to be called. In other words, travel 3 seconds into the future, call hello, and return to the present. In effect, we have realized time travel in the computer! If this makes you uncomfortable, what really happens is that Moxc saves a record of the procedure to be called, the time to call it, and any parameters that should be passed to it. When the time comes, the call is made automatically by Moxc. In the case of hello, the effect is that every 3 seconds, ``hello'' is printed on the screen, and the performer may be simultaneously playing the keyboard. Why does hello print ``hello'' repeatedly? Because after each printing, hello uses cause to schedule another call to hello in the future. This call will schedule yet another call, and so on. How does hello ever get started? It won't, unless someone calls it for the first time. This is done in mainscore, which is always called once at the beginning of the program, and therefore must be present in your program. 9.3. Compiling There are three simple steps to take you from a text file (created with the editor) to a running program. Let us assume your file is called test.c: 1. Compile your program by typing cmtc test.c. Any detected errors will be listed on the screen and must be corrected before the next step. 2. Link your program by typing cmtl test, without the ``.c''! 3. Run your program by typing test. That's all there is to it. time you edit your file. You must repeat all three steps each 9.4. More about cause What would have happened if mainscore (above) would have called hello twice? Would the characters ``h e l l o'' get scrambled together? Would ``hello'' be printed once or twice every three seconds? Would the computer get confused and blow up? In fact, what happens in Moxc is that every procedure runs to completion before any other procedure is called. If a procedure calls another directly, such as mainscore calling hello, then the caller is suspended as usual until the callee completes. Thus, if there were two calls to hello, they would execute in sequence, and letters would not be scrambled. Now, each of the calls to hello would also issue a call to cause, so Moxc would have two records telling it to call hello again at time 300. The Moxc system picks one of the two records at time 300 and calls hello. When this instance of hello completes, Moxc notices that there is another record for a call to hello at time 300 and makes that call too. By this time, Moxc has probably fallen behind schedule. In general, Moxc will call procedures as soon after the indicated time as possible. This time will be quite accurate unless something is keeping the computer very busy. The current version of Moxc will quit automatically when it has nothing scheduled to do. It will then prompt you to see if you want to run your program again. This can be rather annoying if you plan to give it something to do in the future, say by playing a note on a keyboard. The program example shows how a hello procedure can be used to make sure that Moxc is always waiting to do something (call hello), thus being content to run forever. Also, I find it helpful when debugging to be able to look at the screen and know Moxc is running normally, as indicated by the hello messages. The other way to halt Moxc is to call the quit procedure[You can also type CONTROL-C or CONTROL-BREAK to kill almost any process.]. This will abort any requests to cause procedure calls. You can pass parameters through cause to a future procedure call by listing up to 8 of them after the procedure name. Here is a version of hello that prints the number of times it has been called: hello(i) begin printf("hello: %d\n", i); cause(300, hello, i+1); end On each call to hello, i is created anew with a value that is one greater than the value of i in the previous call to hello. To print the value of i, the characters "%d" are placed anywhere within the first (string) argument to printf, and the number to be printed is passed as the second argument. The procedure printf will replace %d by a decimal representation of the number as it prints the string. If you do not understand this last example (it is not simple!), you will probably have trouble writing Moxc programs, so please ask your instructor for a more detailed explanation. The cause procedure does not use a fast algorithm for scheduling future events. In fact, cause does not really schedule anything; it merely puts events on a list of events to be scheduled at some later time. For every waiting event Moxc runs, Moxc takes at least one event from the list of events created by cause and inserts it into another list of events that are waiting to run. This waiting list is ordered by event time. If there are hundreds of events in the list and Moxc has to insert an event at the end (because the event happens later than the others), Moxc may delay a noticeable amount of time. So far, this behavior has been rather interesting, so no attempts have been made to make Moxc faster or more accurate under heavy loads. 9.5. Event Handlers Event handling procedures were described in the example above, including keydown and asciievent. A complete list of these is given below. your program does not provide an event handler, the cmtl (link) step of preparing your program will provide a default handler which does nothing except for asciievent, which is given in the example above. asciievent(k) action for terminal input, k is the ascii key code. bendchange(ch, val) pitch bend handler; ch is the the new value. channel, and ctrlchange(ch, c, val) control change handler; ch is the channel, control number, and val is the new value. c keydown(ch, p, v) MIDI note on handler; ch is the channel, p is code (48 = middle C), and v is the velocity. keyup(ch, p) pitch MIDI note off val is is the the pitch handler; ch is the channel, and p is the code.. mainscore() at the If first action(s). This ``event'' is called once beginning of execution. peddown(ch) sustain pedal down handler; ch is the channel. pedup(ch) sustain pedal up handler; ch is the channel. touchchange(ch, val) aftertouch handler; ch is the channel, and val is the value. 9.6. Command Line Options The following options can be specified in the command line: - -debug (or -d): turn on debugging code. - -help: print a list of command line options. - -miditrace (or -m): turn on MIDI byte trace. - -trace (or -t): turn on music operation trace. - -block: turns off the MPU-401 MIDI THRU function so that the input is not passed to the output. - -tune filename: tells Adagio to use filename to specify tuning. 9.6.1. Examples Run Moxc program named echoes[Note that echo is a special DOS command, so that is why I named this program echoes. The echoes program and its source code are included on the CMT disks.]: echoes Run the echoes program with tuning file scale23.tun: echoes -tune scale23 10. System Details For simple applications, you do not have to understand much about the structure of the CMU MIDI Toolkit in order to get useful work done. The preceding chapters have described the existing application programs as well as Moxc, a system that helps you write real-time music programs of your own. Details of Appendix I. the available calls to send and receive MIDI are given in However, you may wish to write more ambitious programs or modify existing programs. To do this effectively, you must understand how the CMT programs are put together. This chapter describes the various components of the CMU MIDI Toolkit from a system programmer's perspective. 10.1. Introduction In general, CMT programs are assembled from a collection of modules, where a module is a piece of software that performs a set of related functions. For example, there is a module for parsing command lines and another module for recording MIDI data. Modules usually consist of a single ``.c'' file and a related ``.h'' file. In the following sections, each module is described, and references to example uses of the modules are given. 10.2. Basic MIDI interface All programs in CMT that send or receive MIDI use the module mpu.c, which provides an interface between C programs and the MPU-401 MIDI Processing Unit. In theory, mpu.c could be replaced by another software module designed for another hardware MIDI interface. 10.2.1. Interface design issues A few words about the overall design of this interface are in order. To begin with, mpu.c is neither a complete interface to the MPU-401 nor to MIDI. Instead, mpu.c is an attempt at providing the intended community of users with a rational interface that supports experimental, real-time computer music functions. One of the reasons CMT comes with source code is so that if you disagree with these design decisions, you are free to modify or extend the system to meet your requirements. The main are the areas in which mpu.c deviates from the ``conventional'' absence of ``tracks'', the way in which time is handled, pitch specification, and the lack of external synchronization. Tracks are a concept implemented in the MPU-401 whereby several sequences of MIDI data can be merged in real-time. In some commercial sequencers, the use of tracks allows the sequencer to avoid a lot of bookkeeping and sorting when several sequences are to be played at the same time. In CMT, the Adagio compiler sorts its data, so tracks are not needed to play multiple sequences together. For example, to play two Adagio scores simultaneously, one can normally just concatenate the files together and run Adagio on the new file. This approach has the advantages that an arbitrary number of sequences can be merged, and the resulting system is more portable (not all MIDI interfaces implement the notion of tracks). Timing in CMT is probably the most radical departure from MIDI. Whereas MIDI sequencers normally tend to talk about time in terms of beats, CMT measures time in units of 0.01 seconds. This is roughly the smallest rhythmic time deviation we can perceive. The rationale behind this decision is that not all music is measured in beats, and some music has several tempi going simultaneously. If everything is converted to time in seconds, then one can freely combine scores with different tempi and meters as well as scores with timing notated directly in seconds. Another timing issue is that the MPU-401 was designed to allow the host computer to send data in advance of its actual use. MIDI commands can be sent to the MPU-401 with a ``time tag'' that tells the MPU-401 when to send the data to MIDI OUT. This is a nice feature in that it can help a slow computer achieve tighter real-time performance. On the other hand, it is not very suitable for interactive real-time programs in which one normally wants output to occur immediately after data is sent to the MPU-401. Time tags also have the problem that it is hard to stop a performance immediately, because several seconds of data buffered in the MPU401 will continue to play after the host computer stops sending data. not use time tags; MIDI commands are send immediately. CMT does Pitch in CMT is based on earlier computer music systems in which middle C is represented by the number 48. Therefore, CMT pitch numbers are 12 less than the corresponding MIDI pitch numbers. CMT also allows users to redefine the interpretation of pitch numbers. Within mpu.c, there is a table with two entries for each pitch number. One of these entries specifies a pitch and the other specifies pitch bend. When the midi note routine is called, it uses the table to translate the pitch parameter into a MIDI pitch number and a pitch bend. This translation is normally only enabled if the ``-tune'' option followed by a file name is specified in the command line. Finally, CMT at present has no means for external synchronization and cannot now be used with other sequencers or drum machines to achieve a synchronized performance. This is partly a consequence of the fact that CMT does not measure time in beats, while sequencers synchronize by sending MIDI messages to mark beats and their subdivisions. 10.2.2. Interface implementation The mpu.c module is primarily responsible for MIDI output. For example, the midi note routine sends commands and data to the MPU-401 to cause it to send a MIDI note-on command. In contrast, mpu.c never reads data directly from the MPU-401. Input is handled by the interrupt handler in aintr.asm. The interrupt handler (aintr.asm) is invoked whenever the MPU-401 has data ready. The data is read and decoded to determine what kind of data it is. If the data is a MIDI message (e.g. MIDI Note-on), the message is inserted into a circular buffer, which can be read by either the getbuf or getkey routines in mpu.c. The buffer provides a communication path between the interrupt handler and the non-interrupt level of CMT programs; it also helps prevent lost messages when a burst of MIDI events arrives. The aintr.asm module also receives acknowledgments for commands sent by mpu.c. Since mpu.c cannot read these acknowledgments directly (it might read a MIDI message), mpu.c waits for a global variable to be changed by aintr.asm when it wants to get an acknowledgment. A final function of aintr.asm is maintaining the time. In the current implementation, time is maintained in the MPU-401 by chosing a tempo that results in 400 ticks per second. Whenever the 8-bit timer overflows in the MPU-401, a timer overflow message is sent to aintr.asm, which increments a 32-bit time accumulator. The MPU-401 timer value is sent with each MIDI input message and the timer is then cleared. Therefore, aintr.asm must strip off the time from each MIDI input message and add it to the time accumulator as well. At all times, the current time is the sum of the 32-bit time accumulator and the 8-bit timer inside the MPU-401. Therefore, to get an accurate time reading, one must force the MPU-401 to send the value of its counter. Fortunately, there is an MPU-401 command for this. When a user calls gettime in mpu.c, the command is sent to the MPU-401, which responds by sending its counter value. This value is added to the time accumulator by aintr.asm. The gettime routine can then read the accumulator, which is now up to date, shift it right 2 bits to divide by 4, and return the result, which is absolute time in hundredths of seconds. A new timer has been implemented using the real-time clock on the IBM-PC. The new timer code is not compatible with an IBM-AT. This new code should reduce some of the load on the MPU-401 when the host computer needs to poll gettime. This timer is included in the first release of the system and can be enabled by defining TIMDIF in mpu.c and recompiling it. You will find the definition of TIMDIF commented out in the file mpu.c. with a little coding, one could get the MPU-401 to synchronize clock to an external tape machine. In the hopes that someone will extend CMT to implement tape synchronization, the code that uses the will be left in the system even if it is disabled. Notice that its internal actually MPU-401 timer 10.2.3. System exclusive messages In an earlier implementation of CMT, incoming system exclusive messages were put into the buffer just like other MIDI messages. I then discovered that an IBM XT could not empty the buffer as fast as the messages came in. This is fine if messages are short, but some system exclusive messages are much longer than the buffer. Making the buffer bigger would penalize programs that are not interested in system exclusive messages by taking up lots of space. Consequently, the current system does not put system exclusive messages in the buffer. Instead, there is a call (midi buffer) through which a user of mpu.c can provide a special buffer to be used by system exclusive messages. Most programs use the MPU-401 to filter out these messages, so what to do with them is not usually an issue. If exclusive messages are enabled and no buffer is supplied for them, mpu.c throws them away. 10.3. Command line parsing The module cmdline.c implements an interface to the command line, that is, the line that the user types to the operating system to start a program. There are several conventions enforced by cmdline.c. A command line must consist of three types of entities which must be separated by one or more blanks. In order to describe them, I have given the entities names, although they are not terribly mnemonic: 1. A switch is a sequence of non-space characters that begins with a dash some (``-''). Normally, a switch is used to enable or disable function in the program. An example is the ``-help'' switch, which causes most CMT programs to print a help message and quit. 2. An option looks just like a switch except it is followed by another sequence of non-space characters that do not begin with a dash. Options are used to give additional information to the program. An example is ``-tune myscale.tun'', which tells mpu.c to load the tuning definition file myscale.tun. 3. An argument is a sequence of non-space characters that do not begin with a dash. Arguments are usually pieces of information the program expects. An example is in the command line ``adagio bach'': ``Adagio'' is the command name, and ``bach'' is an argument, which in this case specifies a score file. Both switches and options are referred to in earlier chapters as ``command line options''. Switches, options, and arguments can be placed in the command line in any order, with the exception that arguments are numbered in the order in which they appear from left to right. An example of a command with several switches and arguments is: transcri -tune myscale -control off -block foo Taken one at a time, the entities in this command line are: - transcri is the name of the command. - -tune myscale is an option specifying a tuning definition file. - -control change data. off is an option telling transcri to ignore control - -block is a switch which turns off the the MPU-401. internal MIDI thru of - foo is an argument specifying a score file. Notice that ``block foo'' has the same syntax as an option, but since -block is a switch, foo must be an argument. This module provides the following functions: - Syntax checking. token is recognized. The entire line is checked to make sure every - Testing for switches and options. Functions test whether the user typed a switch or option. are provided to - Argument access. Command line arguments can be retrieved by number. (The first argument after the command is argument number 1.) - Consistency checking. All legal switches or options must be declared when the cmdline.c module is initialized. Attempts to look up a switch or option that was not part of the initial declaration are detected and cause an error message. The only aspect of cmdline.c that is likely to be confusing is the initialization and consistency checking. Notice that a switch followed by an argument is syntactically identical to an option. Thus, cmdline.c must know in advance the complete list of switches and options in order to parse the command line. It is illegal for a switch to have the same name as an option, because this could lead to an ambiguous parse. Therefore, cmdlne.c must be initialized with a list of switches and a separate list of options. This is a somewhat error-prone operation because cmdline.c can be (and is) used by many modules, but there is no convenient way in C for these modules to make known what switches and options they look for. For example, suppose you write a program that uses cmdline.c and mpu.c. You initialize cmdline.c, passing it several switches and the ``-aardvark'' option, but you forget that mpu.c looks for a ``-tune'' option. Consequently, cmdline.c does not have enough information to properly parse the command line. Even worse, your program is booby-trapped! As soon as some poor sole tries to use the ``tune'' option, cmdline.c will report a syntax error. tune'' is not a valid switch or option. As far as it knows, ``- Fortunately, the situation is not as bad as it looks, because cmdline.c performs some internal consistency checks automatically. What will happen in this example is that as soon as mpu.c is initialized, it will go out and look for the ``-tune'' option by calling the appropriate routine in cmdline.c (cl option(), for example). Before searching the command line, cmdline.c checks to see if ``-tune'' is in its list of valid options. It is not, so cmdline.c prints an error message: internal error detected by cmdline module: '-tune' should be in valid lists Thus you, the implementor, will get a clear message that you made a mistake, and you will get the message whether or not ``-tune'' appears on the command line. If it is not obvious where the message came from, you can use grep to search the source code of all the modules you use for the string ``tune''. That would tell you that ``-tune'' is an option used by the mpu.c module. In the best of all worlds, it should not be necessary for the implementor to keep track of options and switches used in other modules, but this is the best compromise I could come up with. 10.4. The record module Two programs in CMT record data from the keyboard and write Adagio scores. These are the transcri and record programs. Both programs use the record.c module to do most of the work. The interface to record.c is fairly simple. The routine rec init is called to initialize the module. Then rec poll is called (frequently) for the duration of the recording ``session''. This routine reads data from the input buffer and stores it in a data structure along with timing information. When the recording is finished, rec final is called to write the data to a file. Several tricks are used to improve the performance of record.c. First, the program does not try to record a time for every input event. This would waste processor time when bursts of data arrive. Instead, the current time is recorded only when something is found in the input buffer. Then the buffer is read until it is empty and all buffered events are assigned the same time. Thus, buffer reading occurs at maximum speed when bursts arrive. A second optimization deals with the form of the output file. It has been found that a DX7 will send large amounts of control information, particularly aftertouch data. The rec final routine filters the recorded data so that a given control will not change more than once per 0.01 second time period. This reduces the size of the output file, reduces the amount of data to be stored by the Adagio program, and has a minimal impact on the accuracy of reproduction. 11. What next? A number of improvements remain to be made to the CMU MIDI Toolkit. To encourage others to make these improvements, CMT is distributed with source code and documentation. In addition, the Center for Art and Technology at Carnegie Mellon University will help coordinate and integrate changes to this software. If you develop a new tool or enhance an existing one, you can incorporate it into CMT by sending it to me (Roger B. Dannenberg) at the Center for Art and Technology. Provided the changes or additions are consistent with the goals of CMT, I will integrate them into the next release and redistribute your code to other users. Thus others will benefit from your efforts as I hope you have benefited from mine. The following sections outline changes I would like to see in the CMU MIDI Toolkit. If you decide to tackle one of these, it might be a good idea to call me at (412) 268-3827 or write to find out if anyone else is working on the same thing. 11.1. Microsoft C conversion The current version of CMT uses the Lattice C compiler. progress to make CMT compatible with both Lattice and Microsoft C. Work is in 11.2. Large memory model conversion The most serious problem with CMT at the moment is that it was designed for use with the ``small memory model''. This means that only 16 bits are used to address code and data, so not all of the available memory can be used. This is a problem that limits Adagio to less than 4 thousand notes. To use another memory model, one would have to recode some of the assembler routines (particularly aintr.asm) to get the right values into the segment registers. 11.3. Multiple Midi Interfaces Another limitation of CMT is that it supports only one MPU-401. There are two reasons for using more than one. First, you might want to have more than one MIDI input to the system. A keyboard like the KX88 will ``mix'' the data it generates with its MIDI Input to form the MIDI Output. Thus, you can get input from a KX88 and at least one other device by connecting them in series. However, most MIDI devices cannot ``mix'' MIDI data, so one might want to have several MIDI interfaces to get input from several controllers. Notice that input data can be ``mixed'' internally by the IBM PC, and there is no reason to distinguish which physical input is the source of a given piece of data. The second reason for multiple interfaces is to overcome the maximum of 16 MIDI channels or to obtain higher speed. A reasonable thing to do in this case is to implement ``logical'' MIDI channels. Logical channels 1 through 16 would correspond to MIDI channels 1 through 16 on the first MPU-401. Logical channels 17 through 32 would correspond to MIDI channels 1 through 16 on the second MPU-401, and so on. This presents no problems for Adagio, but there is a lot of CMT software that assumes only 4 bits are needed to store a channel number. Implementing logical channels idea described in the preceding paragraph. is more work than the mixing 11.4. Improve Adagio compilation (sorting) speed Currently, Adagio keeps a linked list of notes and control changes sorted by start time. If scores are roughly in time order, the performance is good. However, if a score is constructed by concatenating several files created using transcri or record, then notes are not in order and Adagio can spend several minutes loading a score. I believe this can be solved by two transformations. First, change the insert routines to insert notes in forward rather than reverse time order and take out the code that reverses the final score. Second, keep a pointer to the last point at which a note was inserted. When you go to insert a note, try searching forward from the saved pointer. Only if this fails should you search from the beginning of the list. The resulting program will still perform badly on perverse scores, but the expected performance should be quite good on normal scores. 11.5. Multi-track recording Not everyone has enough equipment to play an entire score, and not all scores will fit into the available memory. A solution to these problems is to use a multi-track recorder and to record a composition one track at a time. To do this, one must be able to enable one Adagio voice at a time. I suggest adding an option to the Adagio (and Record) command line of the form -v 1,5 which would mean play only voices (MIDI channels) 1 and 5. Notes and control events on other channels would not be saved in memory for playing, so this facility would help get around memory restrictions. For long works and pieces where close synchronization of parts is important, it is essential to have some means of synchronizing Adagio output with a tape recorder. The MPU-401 has a tape-sync input and output which could be used for this purpose. Probably, a command-line switch (``-sync'') should be read by mpu.c. When the switch is present, mpu.c should tell the MPU401 to synchronize to the tape input. Notice that CMT has optional code to derive time from the internal IBM-PC timer rather than the MPU-401. If that is the case, then the ``-sync'' switch should either cause mpu.c to revert back to the MPU-401 or there should be some way to cause MPU-401 clock input data to resynchronize the internal IBM-PC timer. 11.6. MIDI Continuous Controls Adagio supports the notion of continuous controls through its control change commands, but these are presently very limited. Three enhancements should be made. First, it should be possible to program how control change commands in Adagio are mapped into MIDI control change commands. For example, one might want to use the X command to send breath-controller values. This could be implemented by having an optional control specification file, just as there is now an optional tuning specification file. The same file format should be used by transcri and record to determine how Adagio scores are produced from MIDI input. One could use this same facility to tell record and transcri to ignore certain control information. For example, if no command is assigned to control aftertouch, then aftertouch data would be ignored in the recording (transcription) process. A second change would be to add new commands to Adagio to allow users to specify smooth, continuous changes without a huge amount of typing. This could be done by implementing a new special command. For example, !ramp x0 x100 q. would mean change the X control smoothly from 0 to 100 in the time of a dotted quarter note. This command would be translated into a single Adagio event and the stream of control change data would be computed from the event data in real time. Thus, in addition to save typing, the scheme would save on memory space wherever these special commands could be used in place of many control change commands. Finally, it would be very interesting to look into using control changes to modify MIDI programs in real-time. For example, you can put a DX7 in a mode where the inharmonicity of a tone can be controlled by a slider on the DX7 control panel. By using system exclusive messages, one can control the inharmonicity by computer. The missing link here is to make the control specification file flexible enough that one can use Adagio to send system exclusive messages as well as conventional control change commands. It might be helpful to add yet another command to Adagio to send system exclusive messages directly. 11.7. Interacting with Moxc programs Every Moxc program I write has a big asciievent routine that calls different routines and sets values depending upon characters that get typed at run time. It should be possible to write a routine that could read the map file produced by the linker and figure out where variables and procedures are located. Then, a user interface could be designed such that you could type seti foo 2006 call bar 100 572 in order to set the bar with parameters 100 and 572. integer foo to 2006 and then call procedure 11.8. Code to play sequences from Moxc It is hard to write Moxc programs that play sequences of notes, and it would be nice to be able to write sequences in Adagio and trigger them in real time Moxc programs. I designed a module for this and wrote most of the code, but I have not tested anything yet. The idea is to use a modified Adagio to read scores and transform them into data files that can be input easily without resorting to a full-blown Adagio translator. Routines are then provided to read and play these sequences. Several parameters including transposition and rate can be changed in real-time to modify the performance of the sequence. The interface is as follows: seq read(name, filename); reads the sequence in file named internally by small integers, integer to associate with this sequence. filename. and name Sequences are is the seq play(name); plays sequence associated with the integer name. seq transpose(name, transposition); sets the transposition of the sequence associated with name to transposition. seq rate(name, rate); sets the rate of the sequence associated with name to rate. A rate of 100 gives the normal rate as specified in the Adagio file that generated the sequence. seq cycle(name, flag); sets the cycle flag of the sequence associated with name to flag. If the flag is non-zero, then the sequence will repeat (cycle) indefinitely. seq pause(name, flag); sets the pause flag of the sequence associated name to flag. If the flag is non-zero, the sequence performance will be suspended until the flag is set to zero. with seq at end(name, procedure); will cause the routine named by procedure to be called at the end of the performance of the sequence associated with name. This implementation of this interface is quite compact. A drawback is that there is no provision for recording sequences in real-time within the context of Moxc, so some more thinking and experimentation is probably in order. It has also been suggested that one should be able to combine rhythm from one sequence with pitches from another. 11.9. A player module Currently, Adagio and Record are very similar. The main difference is that in phase 2, in which Adagio plays a score, Record plays a score and calls on record.c to record MIDI input. It would be nice to design a module player.c that both Adagio and Record could call on to play a score. This would eliminate a lot of redundant code and would insure consistency between Adagio and Record when changes are made. For example, one might want to allow users to transpose scores, change the playback speed, and make other changes without editing the score. At present these sorts of changes would have to be made in both phase2.c for Adagio and trans2.c for Record. 11.10. User-defined note routines for Adagio Not only would it be nice to be able to invoke Adagio-defined sequences from Moxc, it would also be nice to be able to invoke Moxc routines from Adagio scores. One way to do this would be to separate the Adagio noteplaying routines into a separate module. Then users could write their own note routines that would get parameters from Adagio scores, but that might have some complex behavior. For example, one could write a routine that produced drum rolls consisting of many MIDI notes, but which could be invoked by a single line in Adagio. One would have to be careful to smoothly integrate Adagio and Moxc; perhaps Adagio should be modified to use Moxc for its own scheduling. 11.11. DX7 Program Librarian In Adagio, one specifies timbre by numbers that correspond to MIDI programs. It is up to the user to make sure the proper programs are loaded into the synthesizer in the right locations. It would be nice to be able to write a file like Channels 1-8 1 2 3-8 ProgramNumber 1 2 2 2 ProgramName marimba harp celeste strings which would specify a correspondence between program numbers and program names. One could write a Program Librarian that could read such a file and use it to load up a TX816 or whatever with exactly the right programs for a particular piece. The program names would correspond to file names. 11.12. An Adagio editor It might be interesting to be able to extract sections of Adagio scores, stretch them, transpose them, copy them, merge them with other files, and so on. This can be done with a text editor, but a specialized editor, particularly one that could play the files as it edits them, might be more effective. Another idea for an editor is to try to use a combination of typed text and MIDI input to speed up the process of entering scores. This is a largely unexplored area that has applications in music typography systems as well as CMT. 11.13. Interface to XLISP An interpretive language like LISP can be an excellent teaching and composing tool. XLISP is a version of LISP implemented in C and in the public domain. It should be fairly simple to provide a LISP interface to mpu.c, allowing XLISP programs to make music. 11.14. A Sequencer Everyone else writes sequencers. Why not build one for CMT? This would be the first MIDI sequencer for small machines that users could modify to suit their special needs. 11.15. Ports There is very little in CMT that is specific to the IBM PC. It should not be too difficult to get CMT running on the Macintosh, Amiga, Atari, and other small machines. Work is under way at Brown University and Carnegie Mellon to port CMT to an IBM RT as well. I. The MIDI Interface This appendix contains documentation for calls in the module mpu.c. Type boolean is an int that takes on values of true or false, and type byte is an unsigned char. exclusive(onflag) boolean onflag; This procedure tells the MPU-401 to read exclusive messages into the buffer provided by a previous call to midi buffer. onflag is set to true to receive MIDI exclusive data, otherwise it is set to false. boolean getbuf(waitflag, p) boolean waitflag; byte * p; getbuf copies 4 bytes of data from buffer (the MIDI input buffer filled by the interrupt handler in aintr.asm) to *p. It will wait for buffer to become nonempty if and only if waitflag is true. It returns true if data was written to *p, and false if data was not written to *p. int getkey(waitflag) boolean waitflag; getkey tries to read a key. It returns the key number of the key which has been depressed, or the key number plus 128 of a key which has been released. It returns -1 if waitflag is false and no key has been pressed. If waitflag is true this routine will block until a key is pressed. Since getkey uses getbuf (see above), non-key events (e.g. pitch bend) may be read and discarded by getkey. long gettime() Return the musicinit or timereset. time in 100ths of seconds since the last call to l rest(time) long time; l rest waits until the amount of time specified by time has elapsed. l restuntil(time) long time; l restuntil waits until the specified time has been reached (absolute time as returned by gettime). metronome(onflag) boolean onflag; metronome enables (true) or disables (false) MPU-401 metronome function, as indicated by onflag. This procedure must be called before musicinit. The metronome is not started until musicinit is called. midi bend(channel, value) int channel; int value; Sends a MIDI pitch bend message; channel is the MIDI channel on which to send data, and value is the pitch bend value from 0 to 16383. The value 8192 is the value representing no pitch bend; smaller values bend pitch down, and higher values bend pitch up. int xbuffhead, xbufftail; int midi buffer(buffer, size) byte * buffer; int size; midi buffer tells interrupt routine to store system exclusive messages in buffer. The largest power of 2 bytes less than size will be used (a power of 2 is used so that masking can be used to cause wrap-around). xbuffhead and xbufftail will be initialized to zero, and xbufftail will be one greater than the index of the last system exclusive byte read from mpu401. midi buffer returns false if size is less than 16 or buffer is NULL, otherwise true. midi cont(onflag) boolean onflag; midi cont enables (true) or disables (false) continuous control info from the MPU-401 to the host, depending on the value of onflag. midi ctrl(channel, control, value) int channel; int control; int value; Sends a MIDI control change message; channel is the MIDI channel on which to send data, control is the control number, and value is the control value. midi exclusive(msg) byte * msg; Sends a MIDI exclusive message; exclusive message, terminated by 0xF7. msg is a pointer to a MIDI midi note(channel, pitch, velocity) int channel; int pitch; int velocity; Sends a MIDI note-on request out; channel is the MIDI channel on which to send data, pitch is the MIDI pitch code, and velocity is the velocity with which to sound it (0 means release). If read tuning has been called to define a tuning and velocity is not zero, then this procedure sends a pitch bend followed by a note-on message. The pitch bend and the note-on pitch are found by table lookup, using pitch as the table index. For zero velocity, the table lookup is done to get the pitch for a note-on message with zero velocity. No pitch bend message is sent since zero velocity means note-off. midi program(channel, program) int channel; int program; Sends a program change request; channel is the channel on send which to the MIDI program change request, and program is the program number to send (this number is decremented by 1 before being sent as MIDI data, so values should be in the range of 1 to 128.) midi thru(onflag) boolean onflag; Enables (true) or disables (false) MIDI thru info from MPU-401 to host, depending on the value of onflag. The default is enabled, but the command line switch -block will cause it to be disabled initially. midi touch(channel, value) int channel; int value; Sends a MIDI after touch message; channel is the MIDI channel on which to send data, and value is the after touch value. mpu error check() Reports any errors originating in the interrupt handler by printing them on stdout. about the errors. If you do not call this routine, you do not find out mpuexists(flag) boolean flag; If argument is false, this indicates no MPU-401 machine, so simulate the mpu-401 (for debugging only). is on the read tuning(filename) char *filename; Reads tuning information from filename and enables pitch bend to be sent by midi note to effect detuning. musicinit() Initializes the MPU-401 device driver and resets the MPU-401. clock (see gettime) is reset to zero. musicterm() The Cleans up; disables MPU-401 interrupts; resets MIDI devices. Call this upon finishing execution. int random(lo, hi) int lo, hi; Returns a pseudo-random number between lo and hi inclusive (lo <= result <= hi). This routine is reasonably fast, but it should not be trusted to be very ``random''. timereset() Resets the time on the MPU-401. reset to 0. trace(flag) boolean flag; Turns tracing on Calls to mpu.c are traced. The time returned by gettime is (if flag is true) or off (if flag is false). tracemidi(flag) boolean flag; Turns MIDI tracing on (if flag is true) or off (if flag is false). MIDI output bytes are printed. II. List of Files in CMT The following files are included in CMT. (The number following the file is its size in bytes. This data was obtained from the DOS directory command.) Volume in drive A is CMT DISK 1 Directory of A:\ README TUNING TRANSCRI MM RECORD DXPUT DXGET ADAGIO EXPORT TEST EXE EXE EXE EXE EXE EXE EXE BAT 508 21072 27616 24568 36674 25110 25834 33320 709 <DIR> CMTL CMTC BIN BAT BAT 61 18 <DIR> 13 File(s) 19456 bytes free Volume in drive A is CMT DISK 1 Directory of A:\bin . .. MAKEEXE MAKE MAKE GREP LS <DIR> <DIR> EXE 19200 MAN 7595 BAT 128 EXE 22400 EXE 18176 7 File(s) 19456 bytes free Volume in drive A is CMT DISK 1 Directory of A:\test . <DIR> .. <DIR> WIDE TUN 1276 BACH GIO 5154 STTHOMAS GIO 5143 CONTIN GIO 172 SCALE GIO 128 ALL GIO 110 RETUNE EXE 25974 ECHOES EXE 25974 10 File(s) 19456 bytes free Volume in drive A is CMT DISK 2 Directory of A:\ MAKEFILE MOXCPRGM ADASPACE RECMAIN RECORD TRANS2 USERIO MOXCTOUC MOXCPDUP MOXCPDDN MOXCKYUP MOXCKYDN MOXCCTRL MOXCASCI MOXCBEND CINTR NOTEOFF TRASPACE MPU C C C C C C C C C C C C C C C C C C 5612 95 417 4259 18538 10610 4804 97 75 79 79 86 102 151 95 9897 4504 415 33966 ADAGIO C 4236 PHASE1 C 34033 PHASE2 C 10380 MM C 4485 CMDLINE C 10779 MOXC C 15736 TUNING C 10005 TRANSCRI C 5071 RECSPACE C 420 EXCLDESC C 1437 DXGET C 6766 DXPUT C 7536 TIMDIF H 18 CEXT H 141 MOXC H 80 TRANS2 H 16 ADAGIO H 1192 ATXT H 85 CMDLINE H 121 MAXSPACE H 280 MIDICODE H 553 MOXCPROG H 656 MPU H 703 MUSIPROG H 1039 PHASE1 H 77 PHASE2 H 71 PITCH H 112 RECORD H 62 USERIO H 51 CINTR H 149 CMTPROG H 1060 TIMDIF ASM 13318 AINTR ASM 14949 BREAK ASM 8292 ATXT ASM 877 CMT LNK 177 MOXC OBJ 4471 TIMDIF LIB 18 MACSYM LIB 5006 PRCSYM LIB 8399 CMT LIB 27648 DOS MAC 2059 TEST <DIR> 62 File(s) 15360 bytes free Volume in drive A is CMT DISK 2 Directory of A:\test . .. MOXCTEST ECHOES PROG RETUNE C C C C <DIR> <DIR> 1919 356 758 491 6 File(s) 15360 bytes free Index Accidentals 4 Adagio 4 Aftertouch 6 Alternatives 14 Amiga 21 Append 11 Asciievent 16, 20 Asterisk 4 Atari 21 Attributes 4 Backing up files Backup 3 Bartok 5 Begin 13 Bendchange 16 Blank 4 Brown University 3 21 Case 4 Cause 16 Character value 16 Chord 14 Clean 3 Cleanobjs 3 Cmtc 13 Cmtl 13 Cmusic 4 Collinge, Douglas 16 Commas 6 Comment 4, 13 Compiler 13 Compiling 16 Conditionals 13 Copy 3, 11 Ctrlchange 16 Debug 20 Debugging 6, 16 Default 5 Default durations Default time 4 Dot 4 Drum rolls 21 Duration 4 DX7 4, 21 DXGet 12 DXPut 12 Dynamic markings 5 4 Editor 21 Eighth note 4 End 13 Endwhen 14 Equal temperment 8 Equal to 14 Errors 1, 13 Event handler 16 Everything 3 Exclamation point 4 Exclusive 22 F 4 FF 4 FFF 4 Fileopen 3 Files 3 First ending 14 Flat 4 Flexible disks 3 Floppy disks 3 Foot pedal 6 Future 16 Getbuf 18, 22 Getkey 14, 18, 22 Gettime 18, 22 Greater than 14 Grep 3 H 4 Halt 16 I 4 IBM RT 21 Include 13 Insert 11 Interrupt handler J 18 6 K 6 Keydown 16 Keyup 17 L 4 L rest 22 L restuntil 22 Large memory model Lattice C 20 Less than 14 Link 13 LISP 21 20 Loudness 4 Loudness, velocity Ls 3 4 M 6 Macintosh 21 Mainscore 13, 17 Make 3 Makefile 3 Memory model 20 Merging two scores 11 Metronome 22 MF 4 Microsoft C 20 MIDI 1 Midi Monitor 10 MIDI program 4, 10 Midi bend 22 Midi buffer 18, 22 Midi cont 22 Midi ctrl 22 Midi exclusive 22 Midi note 15, 22 Midi program 15, 22 Midi thru 22 Midi touch 22 Mikrokosmos 5 Miller, Phil 15 Mistake 6 MM 10 Modulation wheel 6 Moxc 16 Moxie 16 MP 4 MPL 4 Mpu error check 22 Mpuexists 22 Multi-track recording 20 Multiple commands 6 Multiple Midi Interfaces 20 Multiple tempi 6 MUSIC V 4 Musicinit 22 Musicterm 22 Musiprog.h 13 N 4 Natural 4 Nelson, Gary 4 Next 4 Not equal 14 Note 13 O 6 Octave specification Omissions 1 Otherwise 14 P 4 Parameters 14, 16 Peddown 17 Pedup 17 Pitch 4 Pitch bend 6, 8 Pnote 14 Polyrhythm 6 Portamento rate 6 Portamento switch 6 PP 4 PPP 4 Preset 4, 22 Printf 14 Procedure 13 Program change 4, 22 Program Change Command Program Librarian 21 Programming 13 4 Q 4 R 4 Random 15, 22 Rate 4, 5 Read tuning 22 Real-time clock Record 10, 11 Recording 20 Repeats 13 Repetition 13 Rest 14 Rests 4 18 S 4 Saving files 3 Second ending 14 Sections 5 Semicolon 6 Semicolons 13 Sequence 13 Sequencer 21 Sharp 4 Small memory model 20 Space bar 6, 11 Special command 4 Stopping programs 13 Suggestions 1 Synchronization 20 Sys Info Avail 10 10 System exclusive messages 12, 18 T 4 Tape-sync 20 Tempo 4 Terry Riley 13 Then 14 Timbre 4, 10 Time 4, 5, 18 Time travel 16 Timereset 22 Touchchange 17 Trace 22 Tracemidi 22 Tracks 18 Transcribe 10 Triplet 4 Tuning 8 U 4 V 4 Voice 4 W 4 When 14 X 6 XLISP 21 Y 6 Z 4, 10 Table of Contents 1. Introduction and Overview 2 1.1. Installing CMT 2 2. DOS Utilities 3 2.1. make 3 2.2. ls 3 2.3. grep 3 2.4. Backing up files 3 3. The Adagio Language 4 3.1. Specifying Attributes 4 3.1.1. Time 4 3.1.2. Pitch 4 3.1.3. Duration 4 3.1.4. Next Time 4 3.1.5. Rest 4 3.1.6. Loudness 4 3.1.7. Voice 4 3.1.8. Timbre (MIDI program) 4 3.1.9. Tempo 4 3.1.10. Rate 5 3.2. Default Attributes 5 3.3. Examples 5 3.4. Advanced features 6 3.4.1. Multiple notes per line 6 3.4.2. Control change commands 6 3.4.3. Multiple Tempi 6 3.5. Running Adagio 6 3.5.1. Debugging Adagio Scores 6 3.5.2. Command Line Options 6 3.5.3. Examples 7 4. Defining Nonstandard Tunings 8 4.1. The Tuning Program 8 4.1.1. Entering a range of pitches. 8 4.1.2. Entering an octave 8 4.1.3. Entering one pitch 8 4.1.4. Saving a tuning file. 8 4.2. The Retune Program 8 5. The Transcribe Program 10 5.1. Overview 10 5.2. Tutorial 10 5.3. Timbre 10 5.4. Command Line Options 10 5.4.1. Examples 10 6. The Record Program 11 6.1. Overview 11 6.2. Instructions 11 6.3. Merging Adagio Scores 11 6.3.1. Playing two scores in sequence 11 6.3.2. Playing two scores at the same time 11 6.4. Command Line Options 11 6.4.1. Examples 11 7. DXGet and DXPut 12 7.1. DXGet 12 7.2. DXPut 12 8. Programming in C 13 8.1. Introduction 13 8.2. Writing a Program 13 8.3. Stopping a Program 13 8.4. Writing a Procedure 13 8.5. Repeats 13 8.6. Conditions 13 8.7. Parameters 14 8.8. Producing Chords 14 8.9. Low-level Procedures 14 8.9.1. Other Procedures and Functions 14 8.10. Command Line Options 15 8.10.1. Examples 15 8.11. Conclusions 15 9. Moxc: real-time programming package 16 9.1. What is Moxc? 16 9.2. An example 16 9.3. Compiling 16 9.4. More about cause 16 9.5. Event Handlers 16 9.6. Command Line Options 17 9.6.1. Examples 17 10. System Details 18 10.1. Introduction 18 10.2. Basic MIDI interface 18 10.2.1. Interface design issues 18 10.2.2. Interface implementation 18 10.2.3. System exclusive messages 18 10.3. Command line parsing 18 10.4. The record module 19 11. What next? 20 11.1. Microsoft C conversion 20 11.2. Large memory model conversion 20 11.3. Multiple Midi Interfaces 20 11.4. Improve Adagio compilation (sorting) speed 20 11.5. Multi-track recording 20 11.6. MIDI Continuous Controls 20 11.7. Interacting with Moxc programs 20 11.8. Code to play sequences from Moxc 20 11.9. A player module 20 11.10. User-defined note routines for Adagio 21 11.11. DX7 Program Librarian 21 11.12. An Adagio editor 21 11.13. Interface to XLISP 21 11.14. A Sequencer 21 11.15. Ports 21 I. The MIDI Interface 22 II. List of Files in CMT 23 Index 24