crn
)
is a program for mapping MIDI notes to other MIDI notes and/or
MIDI note sequences. The program performs mapping according to an ordinary
Ascii text file. In the file, a number of maps can be specified; and each
map gives a separate note mapping. At run-time, you can switch between
maps by typing a key from ``a'' to ``z''.
Only MIDI note messages can be mapped.
Simple Example
Here is an example of a file containing some simple maps.
map a chan 1 init prog 2 10 prog 3 11 any nt 2 +12 +0 +0 nt 3 -12 100 +0 pitch A3 go b redoThis example contains 3 maps, labeled ``a'', ``b'', and ``c''. The first two maps apply to incoming notes on channel 1. When map ``a'' is installed by typing ``a'', the commands in themap b chan 1 init prog 2 12 prog 3 13 any dly 100 nt 2 +0 +0 +0 dly 100 nt 3 +0 +0 +0 pitch af3 go a redo
map c init score drums.gio 0 0 100
init
section are run in sequence.
A program change is sent to set channel 2 to program 10 and channel 3 to
program 11. Then, every note (with one exception, described below)
is mapped according to the any
section. There are two commands here:
send a note on channel 2 with the pitch transposed by 12, but with the same
velocity and duration, and send a note on channel 3 with the
pitch transposed by -12, a fixed velocity of 100, and the same duration.
The exception to this rule is that the pitch A3 has a specific set of commands
listed under pitch A3
. The first command, go b
, says to install map
``b''. The second command, redo
, says to process the event again. This
will produce a different effect since map ``b'' will now apply.
Map ``b'' also has an initialization section to change programs on voices
2 and 3. The any
section says that the general response to a note (on
channel 1) is to delay for 100 centiseconds (1 second), play a note on channel
2 with the same pitch, velocity, and duration, delay another 100 centiseconds,
and play a note on channel 3 with the same pitch, velocity, and duration.
An exception in this map is pitch af3
. When an A-flat below middle C is
received, map ``a'' is installed and the incoming note is reprocessed (redo
)
according to the new map.
Map ``c'' is not channel-specific, so installing this map does not override
either map ``a'' or map ``b''. Map ``c'' has only an initialization section,
so it has no effect other than to play a score (drums.gio
) at the normal
transposition, velocity, and rate. Thus, when the ``c'' key is typed, the
score will play.
Some Details
A map can be channel-specific, applying to only one channel, or global,
applying to any MIDI note. Within a map, a response can be pitch-specific,
invoked in response to a certain pitch, or non-pitch-specific, applied
when any note is played. When a note is played, a mapping is looked for
in the corresponding channel-specific map. If none is found, a mapping is
looked for in the global map. Within a map, pitch-specific mappings have
priority over non-pitch-specific mappings. Only the first mapping found
is applied to a note, although one of the actions of a mapping can be to
search for and invoke another mapping as if the current one did not exist.
The global vs. channel-specific property is associated with each map so one
map cannot serve as both.
In the current implementation, only three octaves of pitch-specific mappings are maintained per map, so all lookups are performed mod 36.
Syntax of Map files
The syntax of the map file is more restrictive and more complex than
Adagio, so a formal language syntax will be presented. Each
production will be accompanied by text describing the semantics.
This formal section will be
followed by examples. In the syntax description,
italic means a nonterminal,Even though terminals are in upper case, Cornucopia is case insensitive like Adagio, so files may by typed in lower case.
UPPERCASE
means a terminal,
[ ] means 0 or 1
{ } means 0 or more
| means either
mapfile ::= { map }A mapfile is a sequence of map specifications. Since each map is labeled by the key (``a'' to ``z'') that makes it active, there can be a maximum of 26 maps in a file.
map ::=Each map consists of the keywordMAP
char [CHAN
chan ]
[INIT
{ command } ]
[ANY
{ command } ]
{PITCH
pitch { command } }
{CLASS
pitch { command } }
MAP
followed by a letter A
through Z
.
If the map is channel-specific, the keyword CHAN
introduces the
MIDI channel to which the map applies. The INIT
keyword
introduces a sequence of commands to be performed when the map
is installed. For example, you can send out program changes
in response to selecting a map.
The ANY
keyword introduces the non-pitch-specific mapping and
is followed by a sequence of commands (described below).
Pitch-specific commands can be specified in two ways: the PITCH
keyword introduces a mapping for a particular pitch, and the
CLASS
keyword introduces a mapping for a pitch class (the same
mapping applies in all octaves). Recall that only 3 octaves
are supported. The pitch specification is
described later. Here are the possibilities for command:
command ::=Commands are of various forms.PROG
chan value |
NT
chan key vel dur |
DLY
number |
CTRL
chan number value |
CALL
char |
RET
|
GO
char |
PASS
|
NOOP
|
REDO
|
SCORE
filename transposition loudness rate [GATED
[CYCLE
]]
PROG
, NT
, and CTRL
send MIDI
program, note-on, and control change commands. The note command
(NT
) also specifies a duration until the note-off message. The
chan, key, vel, and dur fields can be transformations of the
input note, and these fields are described in detail below.
Other commands provide various forms of extended control.
The DLY
command delays processing of the next command by some
number of centiseconds (1/100 second). Without a DLY
, commands
are all executed at once.
The CALL
command invokes another map and and remembers the
current one on a stack. There is one stack for each channel
and a stack for global maps. Calling a map that does not have
the same channel (or that is not also global) may give strange
results. The RET
command restores the previous channel-specific
or global map. The GO
command changes the current map to a
different one. The specified map should have the same channel
or also be global. The PASS
command causes the event mapping
to continue as if the current handler did not exist. In other
words, the search continues from channel-specific pitch-specific,
to channel-specific non-pitch-specific, to global pitch-specific,
to global non-pitch-specific. The REDO
command restarts event
handling. This would make sense only after a CALL
or GO
has
changed the current map. The SCORE
command plays an Adagio
score or MIDI file with an optional transposition and velocity
(loudness) shift. The rate is in percent, e.g. 200 means twice
as fast as the nominal tempo. The optional GATED
parameter says
to stop playing the score when the triggering note ends. If
GATED
is selected, then the option CYCLE
may also be added,
indicating that the score should repeat until the key is released.
char ::=a
..z
chan ::=1
..16
A char indicating a map is any letter from a
to z
.
A chan is a number from 1 to 16. The key for an NT
command
is specified in one of several ways:
key ::= pitch | offset | valueA key is either an absolute pitch, an offset, or a numerical value. The pitch form is similar to Adagio, withpitch ::= value | pitchletter [ sharpflat ] [ number ]
pitchletter ::=A
..G
sharpflat := {S
} | {F
}offset ::=
+
number |-
numbervalue ::=
0
..127
S
or F
for sharps and flats and an octave number
(C4B4 is the octave starting at middle C). Pitches and velocities can
also be given as an offset from the pitch
or velocity of the incoming note, e.g. +12
to transpose the pitch
up one octave.
vel ::= value | offsetVelocity is given as either a value, a number from 0 to 127, or an offset, a signed number from 127 to +127. Note that
+10
means ``louder by 10'', while
10
means ``velocity 10 (very soft)''.
dur ::= number |Durations can be specified in three ways. First, a number can be specified, giving centiseconds. Second, an offset can be specified with a leading+
number |-
number |*
number
+
:
+20
means 20 centiseconds longer than the mapped note. For example, if
an incoming note with duration 200 maps to a note with a duration offset of
+30, the generated note will last 200+30 = 230 centiseconds.
Third, an offset can
be specified with a leading ``-
'': -20
means 20 centiseconds shorter than the
mapped note.
As you might imagine, if an incoming note maps immediately to
some note with a duration offset specified as -30
, Cornucopia cannot predict
the future and turn off the generated note 30 centiseconds before the incoming
note-off. However, if the generated note is started after a delay
(see the DLY
command), then a negative offset makes sense. If the offset would
generate a negative duration, a duration of zero (note-on followed immediately
by note-off) is used.
Fourth, a scale factor
can be given, preceded by a ``*
'': *130
means make the duration 130% the
duration of the mapped note. A duration of less than 100% can be given, but
again this only makes sense after a delay.
transposition ::=Scores may be transposed, shifted by a loudness offset, and played faster or slower. The transposition is a signed number (the ``-63
..[+
]63
|P
-
number
loudness ::=-128
..[+
]127
|V +
number |V -
number
rate ::= number
+
'' is
optional), or the transposition may be relative to the incoming mapped note.
This latter case is indicated by a leading ``P-
''. For example,
``P-60
'' means ``take the pitch of the incoming note, subtract 60,
and use the result to transpose the score''. In this example, middle C
(pitch 60) would not transpose at all, D above middle C would transpose up 2
semitones, etc.
Loudness is similar to transposition; it may be expressed as a signed number
or the loudness offset may be relative to the incoming note's velocity.
For example, ``V-100
'' means ``take the incoming velocity, subtract 100, and
use the result to offset loudnesses (velocities) of the score.
The rate can be used to scale the tempo of the score. Note that this is not a tempo in beats per minute but a percentage scale factor, where 100 is the ``normal'' tempo. A rate of 200 means twice the normal tempo.
Examples
These are examples of mapper commands. Imagine that you want to send a
volume control (number 7) to channel 6 at some point. This could be
triggered by playing a particular pitch:
map v pitch D6 CTRL 6 7 70or simply by typing the letter to install the map:
map v init CTRL 6 7 70In this case, installing the map will replace the previous (in this case global) map. One way to avoid this is to dedicate one channel to some maps that have only
init
sections. For example, if you have no input on
channel 15, you might want to say:
map v chan 15 init CTRL 6 7 70That way, any global map will be undisturbed when you type ``
v
'' to send
the control change.
Here are some examples of nt
commands:
map a chan 1 any nt 2 +12 +0 +0 nt 2 -6 -10 +30 nt 2 C4 90 300 nt 2 D4 +0 *120 dly 50 nt 2 60 +0 -50 nt 2 E4 100 *50The first
nt
transposes pitch up by 12 and keeps the same velocity and
duration as the incoming note. The second nt
transposes down by 6
semitones, decreases the velocity by 10, and adds 30 centiseconds to the
duration. The third nt
plays a C4 pitch at velocity 90 with a duration
of 300 centiseconds. The fourth plays a D4 with the same velocity as the incoming note and with
the duration 20 percent longer than that of the incoming note. After the
dly
command, which inserts a delay of 50 centiseconds (1/2 second),
there is a note with pitch 60 (middle C) whose duration is decreased by 50
centiseconds. Since this note started 50 centiseconds later than the
incoming note, this one and the incoming one will end at the same time. The
last note is an E4 with velocity 100. The duration will be half (50
percent) of the duration of the incoming note. A little math will show that
if the incoming note is longer than one second, its duration will not be
known in time to turn off the E4 as specified. In these cases, the E4 will
be held until the incoming note ends. At that time, Cornucopia will compute
that the E4 should have already ended and it is turned off immediately.
The next example illustrates the use of the pass
command. The any
section plays a chord on channel 2
to harmonize each incoming note. In addition, any
E-flat starts playing a score. The score is repeated until the E-flat is
released. The pass
command ``passes'' control to the next less
specific handler for the incoming note. From most specific to least specific,
handlers are:
pitch-specific handler in a channel-specific mapThe
any
section of a channel-specific map
pitch-specific handler of a global map
any
section of a global map
pass
command simply re-handles the note at the next level in the
list. In this case, from a pitch-specific handler to the any
section
of a channel-specific map. Here is the example:
map a chan 1 any nt 2 +5 +0 +0 nt 2 +7 +0 +0 nt 2 +10 +0 +0 nt 2 +12 +0 +0 class Ef score "trill.gio" 0 0 100 GATED CYCLE pass
Applications
The Cornucopia program can serve a number of functions. Here are a few:
go
) to new maps;
go
to new maps;
link maps together in this way to form a complex network that is navigated
by the performer.