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
redo
map 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
This 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 the 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.
UPPERCASEmeans 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 keywordMAPchar [CHANchan ]
[INIT{ command } ]
[ANY{ command } ]
{PITCHpitch { command } }
{CLASSpitch { 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.PROGchan value |
NTchan key vel dur |
DLYnumber |
CTRLchan number value |
CALLchar |
RET|
GOchar |
PASS|
NOOP|
REDO|
SCOREfilename 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 70
or simply by typing the letter to install the map:
map v
init
CTRL 6 7 70
In 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 70
That 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 *50
The 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
anysection of a channel-specific map
pitch-specific handler of a global map
anysection 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.