cmtman

advertisement
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
Download