Midish 1.0
user's manual and tutorial
Table of contents
Midish is an open-source MIDI sequencer/filter for
Unix-like operating systems (tested on OpenBSD and Linux).
Implemented as a simple command-line interpreter (like a shell)
it's intended to be lightweight,
fast and reliable for
real-time performance.
Important features:
-
real-time MIDI filtering/routing (controller mapping, keyboard splitting, ...)
-
track recording, metronome
-
track editing (insert, copy, delete, ...)
-
progressive track quantization
-
multiple MIDI devices handling
-
synchronization to external audio and MIDI software/hardware
-
import and export of standard MIDI files
-
tempo and time-signature changes
-
system exclusive messages handling
Midish is open-source software distributed under
a 2-clause BSD-style license.
Requirements:
-
A MIDI sound module and a MIDI keyboard
(without any MIDI devices midish will be probably useless).
-
A POSIX unix-like operating system with "raw" MIDI support.
Works on OpenBSD (its development platform)
and Linux.
-
A ``C'' complier (gcc), the ``make'' utility, and other common
unix tools
-
The readline library (which depends on the ncurses library on most
systems)
-
The ALSA library (on Linux only).
Certain Linux distributions split libraries in two packages: one with run-time
files only and one with development files.
Both are necessary to build midish.
Usually development packages have the ``-dev'' or ``-devel'' suffix.
For instance, on Debian, package names are ``libreadline-dev'' and
``libasound2-dev''.
To install midish:
-
Untar and gunzip the tar-ball:
gunzip midish-1.0.tar.gz
tar -xf midish-1.0.tar
cd midish-1.0
-
Configure midish, type ``./configure'', it will select reasonable
defaults.
On Linux systems it will use ALSA devices, on OpenBSD
it will use sndio(7) devices and on other systems
it will use raw MIDI devices.
Binaries, scripts, examples and documentation will be installed
in the /usr/local directory subtree; this can be overridden
with the ``--prefix'' option.
Example:
./configure --prefix=$HOME
-
Compile midish, just type ``make'', this will build
midish and rmidish, the readline(3) front-end to midish.
-
Install binaries, documentation and examples by
typing ``make install'', possibly as root.
-
If there isn't a ``/etc/midishrc'' file,
then copy the sample file by
typing in your shell:
cp midishrc /etc
-
Read the documentation and modify ``/etc/midishrc'' in order
to choose the default MIDI device by using the
``dnew''
function, example (if you're using raw devices):
dnew 0 "/dev/rmidi3" rw
or if you're using ALSA (default on Linux),
the following formats are accepted:
dnew 0 "28:0" rw
dnew 0 "FLUID Synth (qsynth)" wo
on OpenBSD, the following formats are accepted:
dnew 0 "rmidi:3" rw
dnew 0 "midithru:0" rw
dnew 0 "aucat:0" rw
see next section for details.
Midish is a MIDI sequencer/filter driven by a command-line
interpreter (like a shell).
Once midish started, the interpreter prompts
for commands.
Then, it can be used to configure MIDI devices,
create tracks, define channel/controller mappings,
route events from one device to another, play/record a song etc.
To start midish, just type:
rmidish
Then, commands are issued interactively on the midish prompt,
example:
print "hello world"
Once MIDI devices are set up, one of the performance modes
can be started or stopped with one of the following
single letter commands:
-
``i'' -
idle, open MIDI devices but do nothing,
just process input and send it to the output.
-
``p'' -
play, open MIDI devices and start playback
-
``r'' -
record, open MIDI devices and start playback and
recording
-
``s'' -
stop one of the above and close MIDI devices.
In performance mode certain features are not available (like
most editing functions).
Thus performance mode should be disabled in order to
be able to edit the song; furthermore MIDI
devices are closed and thus are available to other
applications.
Midish uses the following objects to represent a project:
-
MIDI devices:
these are actual MIDI devices (keyboards, sound modules,
external sequencers...) from which events are received and/or to which
they are sent.
-
Input channels:
they represent ``{device midi-channel}'' pair on which MIDI events
arrive, like a MIDI keyboard, a MIDI control surface etc...
-
Output channels:
they represent ``{device midi-channel}'' pair on which MIDI events
can be sent, like synths. Output channel objects hold
properties like patch number, volume, reverb depth or other
controllers.
-
Filters:
a filter is a set of rules that determines which MIDI event to discard
and how to transform incoming events. The filter also "sanitizes" the
input MIDI stream by removing nested notes, duplicate controllers and
other anomalies.
-
Tracks:
tracks represent pieces of music: they hold MIDI voice events
(notes, controllers ...). Tracks aren't bound to a particular
channel and can contain events from any channel.
-
Sysex banks:
a sysex bank is a set of system exclusive messages. They
will be sent to MIDI devices when performance mode is entered.
-
The "meta-track":
it is a particular hidden track that contains
only special events like tempo changes and time signature changes.
-
Song parameters:
these are miscellaneous parameters like the
metronome configuration, the current position, current selection...
Above objects are grouped in a project (a song) and manipulated
in prompt mode by issuing interactively commands.
Performance mode is used to play/record the project. When performance
mode is entered, MIDI devices are opened, and all sysex messages and
channel configuration events are sent. There are three
performance modes:
-
"Idle" mode
The MIDI input passes through the current filter
and the result is sent to the MIDI output. No tracks are
played or recorded.
+---------+ +------------+ +----------+
| | | | | |
| MIDI in |--------->| filter |--------->| MIDI out |
| | | | | |
+---------+ +------------+ +----------+
-
Play mode
The MIDI input passes through the current filter
the result is mixed with the currently played tracks
and finally sent to the MIDI output. No tracks are recorded.
+--------------+
| track_1 play |---\
+--------------+ |
|
... ---+
|
+--------------+ |
| track_N play |---+
+--------------+ |
|
+---------+ +------------+ | +----------+
| | | | \--->| |
| MIDI in |------------>| filter |--------->| MIDI out |
| | | | | |
+---------+ +------------+ +----------+
-
Record mode
The MIDI input passes through the current filter
and is recorded on the current track.
The result is mixed with the currently played tracks
and finally sent to the MIDI output. System exclusive
messages are recorded to the current sysex without
being passed through the filter.
+--------------+
| track_1 play |---\
+--------------+ |
|
... ---+
|
+--------------+ |
| track_N play |---+
+--------------+ |
|
+---------+ +------------+ | +----------+
| | | | \--->| |
| MIDI in |-----+------>| filter |----+---->| MIDI out |
| | | | | | | |
+---------+ | +------------+ | +----------+
| |
| | +----------------+
| +--------------+ \---->| track_X record |
\------>| sysex record | +----------------+
+--------------+
The above performance modes are started with
the single letter commands
``i'',
``p'' and
``r'' respectively.
Certain functions are not available during performance mode; to stop
it, use the
``s'' function.
Suppose that there are two devices:
-
``/dev/rmidi4'' - a MIDI sound module
-
``/dev/rmidi3'' - a MIDI keyboard
In this case, the ``/etc/midishrc'' file probably should contain
the following lines:
dnew 0 "/dev/rmidi4" wo # attach the module as dev number 0
dnew 1 "/dev/rmidi3" ro # attach the keyboard as dev number 1
the ``wo'' parameter means that the device will be opened in
write-only mode.
If you're using ALSA, instead of /dev/rmidi3 and /dev/rmidi4,
use the ALSA sequencer ports, as listed by
``aseqdump -l'', example:
dnew 0 "28:0" wo # attach the module as dev number 0
dnew 1 "32:0" ro # attach the keyboard as dev number 1
Alsa also accepts client names, instead of client numbers,
ex:
dnew 0 "FLUID Synth (qsynth)" wo
If you're using OpenBSD, insted of /dev/rmidi3 and /dev/rmidi4,
use the sndio(7) port names:
dnew 0 "rmidi:3" wo # attach the module as dev number 0
dnew 1 "rmidi:4" ro # attach the keyboard as dev number 1
The following session shows how to record a simple
track. First, we define a filter named ``piano'' that routes any
event from device 1, channel 0 (input channel of the keyboard) to
device 0, channel 5 (output channel of the sound module). Then we
create a new track ``pi1'', we start recording and we save the
song into a file.
send EOF character (control-D) to quit
[0000:00]> fnew piano # create filter "piano"
[0000:00]> fmap {any {1 0}} {any {0 5}} # dev=1,ch=0 -> dev=0,ch=5
[0000:00]> tnew pi1 # create track "pi1"
[0000:00]> r # start recording
[0006:02]> s # stop recording
[0000:00]> save "mysong" # save the song into a file
[0000:00]> # EOF (control-D) to quit
In midish, MIDI devices are numbered from 0 to 15.
Each MIDI device has its device number.
For instance, suppose that there is a MIDI sound module known as
``/dev/rmidi3'' and a MIDI keyboard known as
``/dev/rmidi4''.
The following commands will configure the module as device number 0
and the keyboard as device number 1:
dnew 0 "/dev/rmidi4" rw
dnew 1 "/dev/rmidi3" rw
If you're using ALSA, then use ALSA port names (as listed by
``aseqdump -l'' command) instead of the device node paths.
If ``nil'' is given instead of the path, then the port is not
connected to any existing port; this allows other ALSA sequencer
clients to subscribe to it and to provide events to midish or to
consume events midish sends to it.
If you're using OpenBSD, then use sndio(7) port names (hardware ports,
software MIDI thru boxes, aucat(1) control devices).
Note:
To make easier the import/export procedure from systems
with different configurations, it's strongly recommended to
attach the main sound module (the mostly used one)
as device number 0.
In order to check that the sound module is properly configured
play the demo song:
send EOF character (control-D) to quit
[0000:00]> load "sample.sng" # load the file
[0000:00]> p # start playback
[0004:02]> s # stop playback
When devices are set up, put the corresponding dnew
commands in the user's ``$HOME/.midishrc''. It will be
automatically executed the next time midish is run.
Since midish handles multiple input sources, instead of
using MIDI channels, it uses device / MIDI channel pairs
to represent inputs.
So in this document input channel refers the
``{device MIDI-channel}'' pair on which MIDI events
are incoming.
Channels are handled by two-item lists, like this:
{1 0} # device 1, MIDI channel 0
Channels can also be named using the
``inew'' command,
as follows:
inew keyboard {1 0}
this defines a named input channel ``keyboard'' that can
be used instead of the ``{1 0}'' pair.
Note:
input channels are used as default values by certain functions,
so it's better to register inputs using the
``inew'' command.
The easier is to add a ``inew'' statement in your ``$HOME/.midishrc''
file, to avoid typing it every time midish is started.
As for input channels, midish uses device / MIDI channel pairs
to represent outputs.
So in this document output channel refers the
``{device MIDI-channel}'' pair to which MIDI events will
be sent.
Output channels are handled by two-item lists, like this:
{0 4} # device 0, MIDI channel 4
they can also be named using the
``onew'' command, as follows:
onew mybass {0 4}
this defines a named channel ``mybass'' that can
be used instead of the ``{0 4}'' pair.
Output channels expose a built-in filter with the same name; it
defines how input events are routed to the output channel.
The filter comes with a default rule that routes all input channels to
the given output channel.
See filter section for further details.
An output channel represents one musical instrument; midish allows
basic channel configuration events (like program changes and
controllers) to be attached to the channel definition.
Such events are sent to the output when performance mode is entered,
for instance just before playback is started.
This approach avoids flooding MIDI devices with slow events (like
program changes).
For instance,
to select patch 34 on channel ``mybass'', attach
a ``program change'' event:
oaddev mybass {pc mybass 32}
the list argument gives the event to attach to the channel.
See the event section for
more details about events.
To set the volume (controller 7) of this channel to 120:
oaddev mybass {ctl mybass 7 120}
If several events of the same type are attached
then just the last one is kept. So, the following
will change the volume to 125 by replacing the above event:
oaddev mybass {ctl mybass 7 125}
An event is specified as a list containing:
-
a reference from the following list: noff, non,
kat, xctl, xpc, cat, bend
rpn, nrpn.
-
a channel
-
a number
-
a second number (not for ``cat'' and ``bend'')
Event references correspond to the following
MIDI events:
ref. name | MIDI event
|
noff | note off
|
non | note off
|
kat | key after-touch (poly)
|
ctl | 7-bit controller
|
xctl | 14-bit controller
|
xpc | bank and program change
|
cat | channel after-touch (mono)
|
bend | pitch bend
|
rpn | RPN change
|
nrpn | NRPN change
|
Examples:
note-on event on device 2, channel 9, note 64 with velocity 100:
{non {2 9} 64 100}
program change device 1, channel 3, patch 34, bank 1234
{xpc {1 3} 34 1234}
set controller number 7 to 99
on device/channel drums:
{ctl drums 7 99}
Filter and track editing functions use event sets to select the
set of events they will affect.
For instance a filter may check if incoming events match an user
supplied event set in order to decide whether to drop or to keep the
incoming event.
The user must specify event sets as lists of parameter ranges.
An event set is a list containing:
-
A keyword specifying the type of the selected events:
any, note, ctl, pc,
cat, bend, rpn, nrpn
xpc, xctl, none
-
an optional channel set.
If a name is given, its interpreted as an input or output created with
inew or onew.
If a number is given, it matches all MIDI channels of the
given device number.
Alternatively, a list containing a device number range and a MIDI channel
number range can be used (see examples below).
-
an optional number range, specified as a list of two number separated
by the ``..'' operator, like ``12..65''.
-
an optional second number range, in the same
format as the first one (not permitted for ``pc'',
``bend'' and ``cat'')
In the above, empty lists can also be used. An empty list means "match
everything". If the ``none'' keyword is used as event set type then the
event set is the empty set, i.e. it matches no events.
Examples:
{} # match everything
{ any } # match everything
{ none } # match nothing
{ any 1 } # match anything on device 1
{ any bass } # match anything on channel "bass"
{ any {1 4} } # match anything on device 1, channel 4
{ note } # match note events
{ note {1 9} } # match notes on dev 1, channel 9
{ note {0 3..5} } # match notes on device 0, channel 3, 4, 5
{ note {} 0..64 } # match notes between 0 an 64
{ ctl bass } # match controllers on channel "bass"
{ ctl bass 7 } # match controller 7 on channel "bass"
{ bend {} } # match bender
{ xctl {} 19 } # match 14-bit controller number 19
{ xpc {} 21 1234 } # match patch 21 on bank 1234
{ nrpn {} 21 } # match NRPN 21 change
A filter transforms incoming MIDI events and send them to the
output.
The filter also sanitizes the input MIDI stream by removing nested
notes, duplicate controllers and other anomalies.
Filters are in general used to:
-
route events from a device/channel to another
-
map a controller to another
-
split the keyboard
-
drop unwanted events
-
etc...
Multiple filters can be defined, however only the
current filter will run in performance
mode.
If no filters are defined or if there is no current filter
then, in performance mode, input is sent to the output as-is.
In any cases input events are checked for inconsistencies: nested
note-on, orphaned note-off and duplicate controller, bender and
aftertouch events are removed. The rate of controller, bender and
aftertouch events is normalized in order not to flood output devices.
Filters are defined as follows:
fnew myfilt # create filter "myfilt"
initially the filter is empty and will send input to output
as-is.
Once the filter is created, filtering rules can be added, modified and
removed.
The filtering rules can be listed with the
finfo function.
All rules can be removed with the
freset function.
See filtering functions
section for details.
The filter is configured through a list of filtering rules provided
by the user.
Each rule teaches the filter which incoming events to transform on
which outgoing events.
A rule is defined as
- a source event set
- a destination event set
When the filter is running if an incoming event is contained in the
source set, then it is rewritten to match the destination set.
With the following rule:
note {1 3} -> note {2 8}
note events arriving on device 1 midi-channel 3 are sent
to device 2, midi-channel 8.
The source and destination event sets must be of the same type
and must contain ranges of the same size.
If the source and destination event sets contain
ranges, then the incoming event will be rewritten
to match the destination by being shifted.
For instance, the following rule:
note {0 0} 40..49 -> note {0 0} 60..69
will map notes on channel ``{0 0}'' by rewriting:
- note 40 into note 60
- note 41 into note 61
- note 42 into note 62
- ...
- note 49 into note 69
In order to discard a set of input events, it's possible to create
rules with the empty set as destination set. For instance to
discard the volume controller (number 7) on channel
``{0 0}'' the following rule might be used:
ctl {0 0} 7 -> none
Rules
are created and delete with the
fmap and
funmap functions respectively.
For instance, to create a filter with the
above rules:
send EOF character (control-D) to quit
[0000:00]> fnew myfilt
[0000:00]> fmap {note {1 3}} {note {2 8}}
[0000:00]> fmap {note {0 0} 40..49} {note {0 0} 60..69}
[0000:00]> fmap {ctl {0 0} 7} {none}
[0000:00]> finfo
{
evmap xctl {0 0} 7 > none
evmap note {0 0} 40..49 > note {0 0} 60..69
evmap note {1 3} 0..127 > note {2 8} 0..127
}
In real life, the filter will probably have to contain multiple rules.
If the input event matches the source set of two rules, then the most
specific rule wins, i.e. the event is run only through the rule with the
smaller source set.
If both rules have the same source set, then the event is duplicated
and run through both rules.
This mechanism is very useful because it allows to define a default
rule with a large source set and then to refine it by defining
exceptions to it.
For instance consider the following filter:
fnew myfilt
fmap {any kbd} {any piano}
fmap {ctl kbd 1} {ctl piano 11}
the first rule will map all events coming from channel ``kbd'' (the
input) to channel ``piano'' (the output); the second rule maps
controller 1 (modulation) to controller 11 (expression).
Controller 1 events match source sets of both rules, but they will be
run through the second rule only, because it has the smaller set.
The order in which rules are created is not important because
fmap and funmap
functions will always sort them.
That last point to retain is that in order to avoid inconsistencies
there's a constraint on the source sets of rules of the same filter:
if two source sets overlap, then one of them must contain the other
one.
The user doesn't need to care about this constraint since
fmap and funmap
functions will do the right thing in all cases.
The following example defines a filter that routes events from
device number 1 (the MIDI keyboard) to device number 0
(the sound module).
fnew mydevmap # define filter "mydevmap"
fmap {any 1} {any 0} # make it route device 1 -> device 0
To test the filter, start performance mode using the
``i'' command and
then type
``s'' to stop performance mode
The following example defines a filter that routes events from device
1, midi channel 0 (first channel of the keyboard) to device 0, midi
channel 9 (default drum channel of the sound module).
fnew mydrums # define filter "mydrums"
fmap {any {1 0}} {any {0 9}} # route dev/chan {1 0} to {0 9}
To test the filter, start performance mode using the
``i'' command.
Playing on channel 0 of the keyboard will make sound channel 9 of the
sound-module.
To stop performance mode, use
``s'' command.
The following example add a new rule to the above filter that maps the
modulation wheel (controller 1) of the source channel (i.e. device 1,
midi channel 0) to the expression controller (number 11) of the
destination channel (device 0, midi channel 9).
fmap {ctl {1 0} 1} {ctl {0 9} 11}
Rules of the filter can be listed with the
finfo
functions, which should display the list
of rules as follows:
{
evmap any {1 0} > any {0 9}
evmap xctl {1 0} 1 > xctl {0 9} 11
}
The following will transpose notes on ``{0 2}'' by 12 halftones:
fnew mypiano # define filter ``mypiano''
fmap {any {1 0}} {any {0 2}} # route {1 0} -> {0 9}
ftransp {any {0 2}} 12 # transpose by 12 halftones
First we create the filter, and we add a ``default'' rule to map
anything coming from ``{1 0}'' (i.e. the MIDI keyboard) to ``{0 2}''
(the second channel of the synth).
The second rule transposes anything the filter generated
on channel ``{0 2}''.
Note that the order of the rules is not important, the
``ftransp''
rule always applies to events outgoing from the filter.
In the same way it is possible to create a keyboard-split with two
key-rules and two channel-rules.
The following example splits the
keyboard in two parts (left and right) on note 64 (note E3, the
middle of the keyboard).
Notes on the left part will be routed to
channel 3 of the sound module and notes on the right
part will be routed to channel 2 of the sound module.
fnew mysplit
fmap {any {1 0}} {any {0 2}}
fmap {any {1 0}} {any {0 3}}
fmap {note {1 0} 0..63} {note {0 2} 0..63}
fmap {note {1 0} 64..127} {note {0 3} 64..127}
Defining filters seems quite tedious, however it's possible
to define procedures that do the same in a very simpler way.
See the interpreter language
for more details.
In midish, time is split in measures. Each measure
is split in beats and each beat is split in
ticks. The tick is the fundamental
time unit in midish. Duration of ticks is fixed
by the tempo. By default midish uses:
-
24 ticks per beat
-
4 beats per measure
-
120 beats per minute
From the musical point of view, a beat often corresponds to
a quarter note, to an eighth note etc...
By default
a whole note corresponds to 96 ticks, thus by default
one beat corresponds to one quarter note, i.e.
the time signature is 4/4.
The following selects the current position in the song to
measure number 3:
g 3
This will make
``p''
and
``r''
commands start at this particular position instead of measure
number 0.
Furthermore all track editing function will process
the track starting at this position.
In order to "hear" time, a metronome can be used.
It has three modes:
- off - metronome is disabled
- on - metronome is enabled
- rec - metronome is enabled only for recording (default)
The
``m''
command can be used to
change the mode of the metronome:
m on # switch the metronome on
p # start playback
s # stop
The metronome has two kind of click-sound:
-
high-click: on the first beat of the measure
-
low-click: on the other beats.
The click-sound can be configured by giving a couple
of note-on events, as follows:
metrocf {non {0 9} 48 127} {non {0 9} 64 100}
this configures the high-click with note 48, velocity 127 on
device 0, channel 9 and the low-click with note 64, velocity 100
on device 0, channel 9.
Time signature changes are achieved by inserting or
deleting measures.
The following starts a song with time signature of 6/8 (at measure 0)
and change the time signature to 4/4 at measure 2 during 3 measures:
g 0 # go to measure 0
mins 4 {6 8} # insert 4 measures at 8/6
g 2 # move to measure 2
mins 3 {4 4} # insert 3 measure at 4/4
m on # turn metronome on
p # test it, i.e. start playback
s # stop playback
To suppress measure number 2 (the first 4/4 measure)
g 2 # go to measure 2
sel 1 # select 1 measure
mcut # remove it
m on # switch the metronome on
p # test it
s #
To get the time signature at any given measure number, the
msig function can be used.
It returns the denominator and the numerator in a two integer list.
For instance, to print the time signature at measure number 17:
g 17 # go to measure 17
print [msig] # print what msig returned
A handy way to duplicate the time structure of a portion
of the song is given by the mdup
function. It copies the current selection at another
place of the song:
g 17 # go to measure 17
sel 16 # select 16 measures
mdup 0 # copy them behind the selection
The parameter to mdup is the number
of measures to leave between the the original and the point
where the replica is inserted.
Tempo changes are achieved simply by moving within
the song and using the
``t'' command to change
the tempo at the current position.
The tempo must be provided in beats per minute.
For instance, the following changes tempo on measure 0 to 100 beats
per minute and on measure 2 to 180 beats per minute.
g 0 # go to measure 0
t 100 # set tempo to 100 bpm
g 2 # go to measure 2
t 180 # set tempo to 180 bpm
To get the tempo at any given measure number, the mtempo function can be used.
It returns the tempo in beats per minute.
For instance, to print the tempo at measure number 17:
g 17 # go to measure 17
print [msig] # print what mtempo returned
A track is a piece of music, namely an ordered in time list
of MIDI events. In play mode, midish play simultaneously
all defined tracks, in record-mode it plays all defined tracks
and records the current track.
Tracks aren't assigned to any particular device/channel; a track
can contain MIDI data from any device/channel.
A track can have its
current filter; in this case, MIDI events
are passed through that filter before being recorded.
If the track has no current filter, then the song current filter is
used instead.
If there is neither track current filter nor song current filter, then
MIDI events from all devices are recorded as-is.
The following defines a track and record events
as-is from all MIDI devices:
[0000:00]> tnew mytrack # create a new track
[0000:00]> r # start recording
[0003:02]> s # stop
tracks are played as follows:
[0000:00]> p # start playback
[0001:01]> s # stop playback
However, with the above configuration this will not
work as expected because events from the input
keyboard (device number 1) will be recorded as-is and then
sent back to the device number 1 instead of being sent
to the sound module (device number 0).
The following creates a filter and uses it to
record to the above track:
[0000:00]> fnew mypiano # create the filter
[0000:00]> fmap {any {1 0}} {any {0 0}} # dev1/chan0 -> dev0/chan0
[0000:00]> tnew mytrack # create the track
[0000:00]> r # start recording
[0001:03]> s # stop
This setup, is more suitable for multitracking.
The correct approach for multitracking is to create a filter for each
musical instrument, and then to use this filter to record one or more
tracks per instrument.
Most track editing functions use the project context, i.e.
a set of ``current values'':
-
current track - the track that will be processed, it's selected
with the ``ct'' command.
-
current position - the measure number where processing will start.
It's set using the ``g'' command
-
current selection - number of measures to process.
It's set using the ``sel'' command
-
current event set - events type that will be processed.
It's set using the ``ev'' command.
Most of the time all events types need to be processed, but sometimes
one might want to process only a subset (eg. only controller number
7), leaving the rest as-is.
-
current quantization step - this defines how notes at the selection
boundaries are handled.
It's set using the ``setq'' command.
Using a quantization step allows to properly process events at the
selection boundaries, by slightly enlarging the selection in order to
select for processing events that would be outside the selection.
So editing tracks consists in modifying above parameters and issuing
commands to process the current selection.
To clear the current selection of the current track:
tclr
above command will clear only events matching the current
event type, which by defaults is set to ``any event''.
To clear only controller number 7 of the current selection:
ev {ctl {} 7}
tclr
see the event set section for more details.
If you don't plan to continue working only on controller number 7
events, then don't forget to revert the current event selection to the
default value (which is ``{}'', all events).
To cut a piece of a track, for instance to cut 2 measures starting at
measure number 5:
g 5 # move to measure 5
sel 2 # select 2 measures
tcut # cut them
this command removes 2 measures of ``time'' from the current track;
thus this will shift all measures following the current position by 2
measures.
The following inserts 2 blank measures at
measure number 3:
g 3
tins 2
similarly, since this commands insert time, this will shift all
measures following the current position by 2 measures.
Note: the tcut and
tins functions cause a part
of the track to be shifted.
If there are signature changes in the project, the track contents
may no more correspond to the project's signature.
The correct way of cutting or inserting a portion of the project is to
use mcut and mins,
which preserve the signature.
The current selection of a track could be copied
into another track.
For instance the following will copy the current selection
at measure 5 of track ``mypiano2''.
tcopy # copy current selection
ct mypiano2 # change current track to ``mypiano2''
g 5 # go to measure 5
tpaste # paste the copy
A complete portion of the project (all tracks and time structure
included) can be copied with the mdup
function. It copies the current selection and inserts it
at the position given as argument to mdup.
The argument is relative to the end of the current selection (if
positive) or to the beginning of the current selection (if
negative). Example:
g 17 # go to measure 17
sel 4 # select 4 measures
mdup 0 # create 3 copies
mdup 0
mdup 0
A track can be quantized by rounding note-positions
to the nearest exact position. The following
will quantize the current selection of the current track
by rounding notes to the nearest quarter note.
setq 4 # quarter note = 1/4-th of a whole
tquant 75
The last arguments gives the percent of quantization.
100% means full quantization and 0% leans no
quantization at all. This is useful because full quantization
often sounds to regular especially on acoustic patches.
It is possible that a MIDI device transmits
bogus MIDI data. The following scans the track
and removes bogus notes
and unused controller events:
ct badtrack
tcheck
This function can be useful to remove
nested notes when a track is recorded twice (or more)
without being erased.
In midish, MIDI events are packed into frames. For
instance a note-on event followed by a note-off with the same
note number constitute a frame. All filtering and
editing functions work on frames, not on events. That means
that all events within a frame are processed consistently. For
instance, deleting a note-on event will also delete related
note-off and key-aftertouch events. This ensures full
consistency of tracks and MIDI I/O streams.
Note frames are made of a starting "non" event any optional
"kat" events and the stopping "noff" event. In editing functions
note frames are copied/moved/deleted as a whole; they are never
truncated. The starting event (note on) determines whether
the frame is selected. For instance, in
tclr function, only
note frames whose "non" events are in the selected region
are erased.
Conflicting note frames (i.e. with the same note number)
are never merged. An attempt to copy a note frame on
the top of a second one will erase the second one. This avoids
having nested notes.
A pitch bend frame is made of "bend" events. It starts with
a "bend" event whose value is different from the default value
(i.e. different from 0x3FFF). It stops when the value reaches
the default value of 0x3FFF. In editing functions a "bend"
frame may be truncated or split into multiple frames, however
resulting frames always terminate with the default value. For
instance, tclr may erase
the middle of "bend" frame resulting in two new "bend" frames
(the beginning and the ending of the old one).
Conflicting "bend" frames are merged. An attempt to copy a
frame on top of another one will overwrite conflicting
regions of the second one and "glue" the rest; this will
result in a single frame, containing chunks of both original
frames.
The way controller events are packed in a frame depend
on the controller type. Currently the following
controllers are supported:
-
Parameters: this controller change a parameter
of the channel but it doesn't have a special default
value. For instance volume controller, reverb depth
etc... These controllers are not packed together, they
form single event frames. This is the default behavior
for most controllers.
-
Frames: these are controllers that have special
default value and that are packed in frames, similar
to "bend" frames. For instance modulation wheel,
sustain pedal. For those frames special care is taken
in editing functions.
The user can specify for each controller number the desired
behavior.
The ctlconf function
configures the controller. As arguments, it takes the name of
the controller (an identifier) the controller number and the
default value of the controller.
If the default value is ``nil'' (i.e. no default value),
then the controller is considered as of type parameter.
For instance, following command:
ctlconf expr 11 127
configures controller number 11 of type frame and with
default value of 127. The controller name can be
any identifier, it can be used for other functions to
reference a controller.
The ctlinfo function
can be used to display the current configuration
of all controllers:
ctltab {
#
# name number defval
#
mod 1 0
vol 7 nil
sustain 64 0
}
Channel aftertouch events are packed in channel aftertouch
frames. They behave exactly as pitch bender frames, except
that the default value is zero.
Program/bank change, NRPN and RPN events are not packed
together, they are single event frames.
Midish can send system exclusive messages
to MIDI devices before starting performance mode.
Typically, this feature can be used to change
the configuration of MIDI devices. System
exclusive (aka "sysex") messages are stored into
named banks. To create a sysex bank named ``mybank'':
xnew mybank
Then, messages can be added:
xadd 0 {0xF0 0x7E 0x7F 0x09 0x01 0xF7}
This will store the "General-MIDI ON" messages into the
bank. The second argument (here "0") is the device number
to which the message will be sent when performance mode
is entered.
To send the latter messages to
the corresponding device, just enter performance mode
using the
``i'' command.
Sysex messages can be recorded from
MIDI devices, this is useful to save "bulk dumps"
from synthesizers.
Sysex messages are automatically recorded on the current
bank. So, to record a sysex:
cx mybank # set current sysex bank
r # start recording
The next time performance mode is entered, recorded
sysex messages will be sent back to the device.
Information about the recorded sysex messages
can be obtained as follows:
xinfo
A bank can be cleared by:
xrm {}
the second argument is a (empty) pattern, that matches
any sysex message in the bank. The following will remove
only sysex messages starting with 0xF0 0x7E 0x7F:
xrm {0xF0 0x7E 0x7F}
Sysex messages recorded from any device can be configured
to be sent to other devices. To change the device number
of all messages to 1:
xsetd 1 {}
the second argument is an empty pattern, thus it matches
any sysex message in the bank.
The following will change the device number of only sysex messages
starting with 0xF0 0x7E 0x7F:
xsetd 1 {0xF0 0x7E 0x7F}
The following functions gives some information
about midish objects:
ls # summary
minfo # list tempo and signature changes
iinfo # list config events of current input channel
oinfo # list config events of current output channel
finfo # list rules of current filter
tinfo # list events distribution of current track
dinfo 0 # list device 0 properties
Objects can be listed as follows:
print [tlist] # print track list
print [ilist] # print named input channel list
print [olist] # print named output channel list
print [flist] # print filter list
print [xlist] # print sysex back list
Current values can be obtained as follows:
print [getunit] # ticks per whole note
print [getpos] # print current position
print [getlen] # print current selection length
print [getf] # current filter
print [gett] # current track
print [getx] # current sysex bank
print [tgetf] # default filter of current track
The device and the MIDI channel of a channel definition
can be obtained as follows:
print [igetc] # print midi chan number of current input
print [igetd] # print device number of current input
print [ogetc] # print midi chan number of current output
print [ogetd] # print device number of current output
To check if object exists:
print [iexists myinput]
print [oexists myouput]
print [fexists myfilt]
print [texists mytrack]
print [xexists mysx]
this will print 1 if the corresponding object exists
and 0 otherwise.
A song can be saved into a file as follows:
save "myfile"
In a similar way, the song can be load from a file
as follows:
load "myfile"
All channel definitions, filters, tracks, their properties, and values
of the current track, current filter are saved and restored. However,
note that the local settings (like device configuration, metronome
settings) are not saved.
Standard MIDI files type 0 or 1 can be imported. Each
track in the standard MIDI file corresponds to a track in midish.
Tracks are named ``trk00'', ``trk01'', ...
All MIDI events are assigned to device number 0. Only the following
meta events are handled:
-
tempo changes
-
time signature changes
all meta-events are removed from the "voice" tracks and
are moved into the midish's meta-track. Finally tracks
are checked for anomalies. Example:
import "mysong.mid"
Midish songs can be exported into standard MIDI files.
Tempo changes and time signature changes are exported
to a meta-track (first track of the MIDI file). Each
channel definition is exported as a track containing
the channel configuration events. Voice tracks
are exported as is in separate tracks. Note that device
numbers of MIDI events are not stored in the MIDI file
because the file format does not allow this.
Example:
export "mysong.mid"
Even to achieve some simple tasks with midish, it's
sometimes necessary to write several long statements. To
make midish more usable, it suggested to use variables
and/or to define procedures, as follows.
Variables can be used to store numbers, strings and
references to tracks, channels and filters, like:
let x = 53 # store 53 into "x"
print $x # prints "53"
The ``let'' keyword is used to assign values to
variables and the dollar sign (``$'') is used
to obtain variable values.
For instance, let
us create a procedure named ``gmon'' that creates
a sysex bank and stores a the standard sysex message
to turn on General MIDI mode on device number 0:
proc gmon {
xnew gm
xadd 0 { 0xF0 0x7E 0x7F 0x09 0x01 0xF7 }
}
The ``proc'' keyword is followed by the procedure
name ``gmon'' and then follows a list of statements between
braces.
Procedures can take arguments. For instance, to improve
above procedure to take the device number as argument:
proc gmon devnum {
xnew gmon
xadd $devnum { 0xF0 0x7E 0x7F 0x09 0x01 0xF7 }
}
After the name of the procedure follows the argument names list
that can be arbitrary identifiers. The value of an argument
is obtained by preceding the variable name by the dollar
sign ("$").
A lot of similar procedures are defined in the
sample ``midishrc'' file, shipped in the source
tar-ball.
Procedure and variables definitions can be
stored in the ``~/.midishrc'' file (or ``/etc/midishrc'').
It will be automatically executed the next time you run midish.
-
added ``device'' parameter to ``sendraw''
-
removed ``filtchangein'' and added: filtchgich,
filtchgidev, filtswapich,
filtswapidev, filtchgoch,
filtchgodev, filtswapoch,
filtswapodev.
-
added new filter rules:
filtdevdrop,
filtchandrop,
filtctldrop,
filtkeydrop.
-
added functions that remove existing rules:
filtnodevdrop,
filtnodevmap,
filtnochandrop,
filtnochanmap,
filtnoctldrop,
filtnoctlmap,
filtnokeydrop,
filtnokeymap.
-
added support for external MIDI synchronization,
added: devsetmaster, devgetmaster, devsendrt
-
added
trackdelete,
chandelete,
filtdelete
-
added
trackrename,
chanrename,
filtrename
-
split ``changetnum'' into ``changetdev'' and ``changetch''
-
added ``chanset''
-
added event ranges, and an event range argument to
``trackblank'' and
``trackcopy''
-
removed support for single number channels.
-
added ``tracksetmute'' and ``trackgetmute''
-
added support for system exclusive messages; added
sysexlist,
sysexnew,
sysexdelete,
sysexrename,
sysexexists,
sysexinfo,
sysexclear,
sysexsetunit,
sysexadd,
songgetcursysex,
songsetcursysex.
-
removed ``tracksave'' and ``trackload''
-
removed support for gnu readline(3) library from midish,
and created the ``rmidish'' utility, a front-end
to midish using the readline(3) library.
-
added filters the ability to limit MIDI traffic
(one controller, one bender, one aftertouch per tick)
-
make the filter drop duplicate controllers, pitch bends and
aftertouches
-
added global and per filter current channel:
songgetcurchan,
songsetcurchan,
filtgetcurchan,
filtsetcurchan.
-
added global and per channel current input:
songgetcurinput,
songsetcurinput,
changetcurinput,
chansetcurinput.
-
added ``trackchanlist'' and improved ``songinfo''
-
added a MIDI file player and recorder: smfplay(1) and smfrec(1)
-
added ``tracktransp'' to transpose tracks
-
added ``trackmerge'' to merge two tracks
-
make rmidish run midish from the same directory where rmidish is
located.
-
now multiple bender events are gathered together. So track editing
functions (``trackcopy'' & friends) always leave the bender
in a neutral state.
-
improved ``trackcheck''
-
added ``exit'' keyword to exit midish
-
Added a ``mode'' argument to ``devattach'' function. Now
devices can be used in read-only or write-only mode (allowing to use
named pipes as MIDI devices).
-
added basic support for the MIDI "Active Sensing" feature
-
make metronome click audible with non-percussive patches
-
save metronome settings
-
added tempo factor: ``songsetfactor'' and ``songgetfactor''
-
changed all track functions to gracefully handle nested notes,
overlapping controllers/benders.
-
when entering real-time mode, midish restores states of all
controllers/benders corresponding to the current position
-
when leaving real-time mode midish release only notes that are
sounding and reset only controllers that are touched instead of
resetting all notes/controllers. This allows midish to better share
MIDI devices with other sequencers.
-
added support for 14-bit controllers
-
added support for RPN/NRPN MIDI events.
-
split format 0 MIDI files into 16 tracks (one per MIDI channel)
-
made controller interpretation in editing functions configurable:
ctlconf, ctlconfx, ctlunconf, ctlinfo.
-
added per-device controller precision selection (7-bit or 14-bit):
devixctl, devoxctl.
-
added ``chanunconfev'' function to remove an event from a channel
-
added output MIDI merger, to avoid conflicts
between tracks and MIDI input
-
simplifications: removed current input and filter's current channel
attributes.
-
continuous controllers are merged more
intelligently in performance mode, avoiding clipping
-
changed all functions to use internal context instead of taking
dozens of parameters, while we're at it give them shorter names.
Old functions are deprecated, they are still available for
compatibility.
Below is the list of new functions:
ci, co, geti, geto, cx, getx, setunit, getunit, goto, getpos, sel,
getlen, setq, getq, fac, getfac, ct, gett, cf, getf, mute, unmute,
getmute, ls, save, load, reset, export, import, idle, play, rec, stop,
tempo, mins, mcut, mdup, minfo, mtempo, msig, mend, ctlconf, ctlconfx, ctlunconf,
ctlinfo, metro, metrocf, tlist, tnew, tdel, tren, texists, taddev,
tsetf, tgetf, tcheck, tcut, tins, tclr, tpaste, tcopy, tmerge, tquant,
ttransp, tclist, tinfo, ilist, olist, iexists, oexists, inew, onew,
idel, odel, iren, oren, iset, oset, igetc, ogetc, igetd, ogetd,
iaddev, oaddev, irmev, ormev, iinfo, oinfo, flist, fnew, fdel, fren,
fexists, finfo, freset, fmap, funmap, ftransp, fvcurve,
fchgin, fchgout, fswapin,
fswapout, xlist, xexists, xnew, xdel, xren, xinfo, xrm,
xsetd, xadd, dnew, ddel, dclkrx, dclktx, dclkrate, dinfo, dixctl,
doxctl
-
output channels are no more the only to be named and/or configured. Now
input channels can be named too.
-
added
``msig'' and
``mtempo''
functions returning time signature and
tempo respectively at the current position
-
added
``err''
function to print an error message and stop the
interpreter execution flow.
-
added ``current event set'' in the context and associated
``ev''
function. All track editing functions use it.
-
greatly simplified the filter: more features with less code and
concepts. The filter can map any event set to any other set. So all
mapping can be created with a single
fmap function.
-
added a new ``integer range'' data type, useful to define event sets.
-
added a new metronome mode ``rec'' to turn on the metronome only in
record mode.
-
switched to multithreaded performance mode, meaning that the prompt
is available during performance mode.
-
added new messages for front ends allowing to get asynchronously the
current position. Updated rmidish to use it to display the current
position in the prompt.
-
make tcopy copy selection into a
hidden temporary track and add
tpaste
to paste it at the current position.
-
added basic readline completion of built-in functions to rmidish.
-
add ftransp command to
transpose output easily.
-
add fvcurve command to
adjust note velocity.
-
add mend function to return the end of the
song (i.e. the first unused measure).
-
make mins and mcut
modify all tracks, not only the time structure.
-
add mdup function to copy portions of
the project preserving its time structure.
-
added built-in filter to output channels, so there's no more need
to create filter for every channel.
-
removed all legacy functions
-
added tevmap function to transform
a set of events of a track into another set (eg. to change the
device number).
-
allow midish to synchronize to a MTC source, meaning that it can
be used with audio applications.
Added dmtcrx function.
-
send MMC start, stop and relocate messages allowing
MMC-capable audio applications to be controlled from
midish.
Added dmmctx function.
The following table summarizes the device attributes:
attribute
| description
|
device number
| integer that is used to reference the device
|
clkrate
| number of ticks per whole note, default is 96, which corresponds
to the MIDI standard
|
clock ``tx'' flag
|
boolean; if it is set, the real-time MIDI clock events
(like start, stop, ticks) are transmitted to the MIDI device.
|
ixctlset
|
list of continuous controllers that are expected to be
received with 14-bit precision.
|
oxctlset
| list of continuous controllers that will be transmitted
with 14-bit precision
|
The following table summarizes the channel attributes:
attribute
| description
|
name
| identifier used to reference the channel
|
{dev chan}
| device and MIDI channel where events are sent to or
received to
|
conf
| events that are sent when performance mode is entered
|
The following table summarizes the filter attributes:
attribute
| description
|
name
| identifier used to reference the filter
|
rules set
| set of rules that handle MIDI events
|
The following table summarizes the track attributes:
attribute
| description
|
name
| identifier used to reference the track
|
mute flag
| if true then the track is silent during playback
|
current filter
|
default filter. The track is recorded with this filter. If there is no
current filter, then is is recorded with the song's default filter.
|
The following table summarizes the sysex back attributes:
attribute
| description
|
name
| identifier used to reference the sysex back
|
list of messages
|
each message in the list contains the actual message and the device
number to which the message has to be sent.
|
The following table summarizes the song attributes:
attribute
| description
|
meta track
| a track containing tempo changes and time signature changes
|
ticks_per_unit
|
number of MIDI ticks per whole note, the default value is 96 which
corresponds to the MIDI standard.
|
tempo_factor
| number by which the tempo is multiplied on play and record.
|
metronome mode
|
can be ``on'', ``off'' or ``rec'',
see ``m'' command.
|
metro_hi
|
a note-on event that is sent on the beginning of every measure if the
metronome is enabled
|
metro_lo
|
a note-on event that is sent on the beginning of every beat if the
metronome is enabled
|
current track
| default track: the track that will be recorded in record mode
|
current filter
|
default filter. The filter with which the default track is recorded if
it hasn't its default filter.
|
current input
|
default input channel. This value isn't used in real-time, however it can be
used as default value when adding new rules to the filter.
|
current output
|
default output channel. This value isn't used in real-time, however it can be
used as default value when adding new rules to the filter.
|
current position
|
current position (in measures) within the song. Playback and record
start from this positions. It is also user as the beginning of the
current selection
|
current selection
| length (in measures) of the current selection.
This value isn't used in real-time,
however it is used as default value in track
editing functions.
|
current quant step
|
current quantization step. This value isn't used in
real-time, however it's used as default value for the track
editing functions
|
current event set
| current event selection
This value isn't used in real-time,
however it is used as default value for
the track editing functions
|
The input line is split into tokens:
Multiple lines ending with ``\'' are parsed as a
single line. Anything else generates a ``bad token'' error.
Any input line can be ether a function definition or a
statement.
Most statements end with the ``;''
character. However, in order to improve
interactivity, the newline character can be used
instead. Thus, the newline character cannot be
used as a space.
A statement can be:
-
A procedure call, example:
myproc arg1 arg2
-
An assignment using the ``let'' keyword,
examples:
let x = 123
let y = 1 + 2 * (3 + x)
The left-hand side should be the name of
a variable and the right hand side an
expression (see the expression section).
-
An ``if..else'' statement, example:
if $i {
print "i is not zero";
} else {
print "i is zero";
}
the ``else'' and the second block are
not mandatory. Note that since newline
character is interpreted as a ``;'', the
line cannot be broken in an arbitrary
way. If the expression following the ``if''
keyword is true the first block is executed,
otherwise the second one (if any) is executed. The
expression is evaluated in the following way:
expression | bool. value
|
non-zero integer | true
|
zero integer | false
|
non-empty list | true
|
empty list | false
|
non-empty string | true
|
empty string | false
|
any name | true
|
nil | false
|
-
A loop over a list:
for i in {"bli" "bla" "blu"} {
print $i;
}
the block is executed for each value
of the list to which ``$i'' is set.
-
A return statement. The return statement ends
the procedure execution and returns a
value to the caller. Example:
return $x * $x;
-
An exit statement. The exit statement ends
the execution of the current script.
It takes no arguments. Example:
exit
An expression can be an arithmetic expression of constants,
expressions, variable values, return values of
function calls.
The following constant types are supported:
token | type
|
``"this is a string"'' | a string
|
``12345'' | a number
|
``mytrack'' | a reference
|
``nil'' | has no value
|
Variable are referenced by their identifier.
Value of a variable is obtained with the ``$''
character.
let i = 123 # puts 123 in i
print $i # prints the value of i
The following operators are recognized:
oper. | usage | associativity
|
{} | list definition | left to right
|
() | grouping
|
[] | function call
|
! | logical NOT | right to left
|
~ | bitwise NOT
|
- | unary minus
|
* | multiplication | left to right
|
/ | division
|
% | reminder
|
+ | addition | left to right
|
- | subtraction
|
<< | left shift | left to right
|
>> | right shift
|
< | less | left to right
|
<= | less or equal
|
> | greater
|
>= | greater or equal
|
== | equal | left to right
|
!= | not equal
|
& | bitwise AND | left to right
|
^ | bitwise XOR
|
| | bitwise OR
|
&& | logical AND | left to right
|
|| | logical OR
|
.. | range definition | left to right
|
Examples:
2 * (3 + 4) + $x
is an usual integer arithmetic expression.
[tlist]
is the returned value of the procedure
``tlist''.
12..56
is the range of integer between 12 and 65 (included).
{"bla" 3 zer}
is a list containing the string ``"bla"'' the integer
3 and the name ``zer''. A list is a set of
expressions separated by spaces and enclosed between
braces, a more complicated example is:
{"hello" 1+2*3 mytrack $i [myproc] {a b c}}
A procedure is defined with the keyword
``proc'' followed by the name of the procedure,
the names of its arguments and a block containing
its body, example:
proc doubleprint x y {
print $x
print $y
}
Arguments and variables defined within a procedure
are local to that procedure
and may shadow a global variable with the same
name. The return value is given to the caller
with a ``return'' statement:
proc square x {
return $x * $x
}
- tlist
-
return the list of names of the tracks in the song
example:
print [tlist]
- tnew trackname
-
create an empty track named ``trackname''
- tdel
-
delete the current track.
- tren newname
-
change the name of the current track to ``newname''
- texists trackname
-
return 1 if ``trackname'' is a track, 0 otherwise
- taddev measure beat tick ev
-
put the event ``ev'' on the current track
at the position given by ``measure'',
``beat'' and ``tick''
- tsetf filtname
-
set the default filter (for recording) of
the current track to ``filtname''. It will
be used in performace mode if there is no current filter.
- tgetf
-
return the default filter (for recording)
of the current track, returns ``nil'' if none
- tcheck
-
check the current track for orphaned notes,
nested notes and other anomalies; also
removes multiple controllers in the same tick
- tcut
-
cut the current selection of the current track.
- tclr
-
clear the current selection of the current track.
only events matching the current event selection
(see ev function) are
removed.
- tins amount
-
insert ``amount'' empty measures in the
current track, at the current position.
- tpaste
-
copy the hidden temporary track (filled by
tcopy)
on the current position of the current track.
the current event selection
(see ev function)
are copied
- tcopy
-
copy the current selection of the current track
into a hidden temporary track.
Only events matching
the current event selection
(see ev function)
are copied
- tquant rate
-
quantize the current selection of the current track using
the current quantization step
(see setq function).
Note positions
are rounded to the nearest tick multiple of the quantization step;
Rate must be between 0 and 100: 0 means no
quantization and 100 means full quantization.
- ttransp halftones
-
transpose note events of current selection of the current track,
by ``halftones'' half tones.
Only events matching the current event selection
(see ev function)
are transposed.
- tevmap evspec1 evspec2
-
convert events matching evspec1 (source) into events matching
evspec2 (destination) in the current selection of the current
track.
Both evspec1 and evspec2 must have the same number of devices,
channels, notes, controllers etc..
- trackmerge sourcetrack
-
merge the ``sourcetrack'' into
the current track
- mute trackname
-
Mute the given track, i.e. events from ``trackname''
will not be played during record/playback.
- unmute trackname
-
Unmute the given track, i.e. events from ``trackname''
will be played during record/playback.
- getmute trackname
-
Return 1 if the given track is muted and 0 otherwise.
- tclist
-
Return the list of channels used by events stored in the
current track.
- tinfo
-
scan the current selection of the current track, an for each
measure display the number of events that match the current
event selection
- inew channelname {dev midichan}
- create an new channel named ``channelname''
and assigned the given device and MIDI channel.
- iset {dev midichan}
-
set the device/channel pair of the current channel.
- idel
-
delete current channel.
- iren newname
-
rename the current channel to ``newname''
- iexists channelname
-
return 1 if ``channelname'' is a channel, 0 otherwise
- igetc
-
return the MIDI channel number of the current channel
- igetd channelname
-
return the device number of the current channel
- iaddev event
-
add the event to the configuration of the current
channel, it's not used yet.
- irmev evspec
-
remove all events matching ``evspec''
(see event ranges)
from the configuration of the current channel
- iinfo
-
print all events on the config
of the current channel.
- onew channelname {dev midichan}
- create an new channel named ``channelname''
and assigned the given device and MIDI channel.
Output channels contain a built-in filter having the same name; by
defaut it maps all inputs to the newly created output channel.
- oset {dev midichan}
-
set the device/channel pair of the current channel.
- odel
-
delete current channel.
- oren newname
-
rename the current channel to ``newname''
- iexists channelname
-
return 1 if ``channelname'' is a channel, 0 otherwise
- ogetc
-
return the MIDI channel number of the current channel
- ogetd channelname
-
return the device number of the current channel
- oaddev event
-
add the event to the configuration of the current
channel, it's not used yet.
- ormev evspec
-
remove all events matching ``evspec''
(see event ranges)
from the configuration of the current channel
- oinfo
-
print all events on the config
of the current channel.
- fnew filtname
-
create an new filter named ``filtname''
- fdel filtname
-
delete the current filter.
- fren newname
-
rename the current filter to ``newname''
- fexists filtname
-
return 1 if ``filtname'' is a filter, 0 otherwise
- freset
-
remove all rules from the current filter.
- finfo
-
list all fitering rules of the current filter
- fchgin old_evspec new_evspec
-
rewrite all filtering rules of the current
filter to consume ``new_evspec'' events instead of
``old_evspec'' events.
This means that each rule that would consume ``old_evspec''
on the input will start consuming ``new_evspec'' instead.
- fswapin evspec1 evspec2
-
Similar to fchgin but swap
``evspec1'' and ``evspec2'' in the source events
set of each rule.
- fchgout old_evspec new_evspec
-
rewrite all filtering rules of the current
filter to produce ``new_evspec'' events instead of
``old_evspec'' events.
This means that each rule that would produce ``old_evspec''
on the output will start producing ``new_evspec'' instead.
- fswapout evspec1 evspec2
-
Similar to fchgout but swap
``evspec1'' and ``evspec2'' in the destination events
set of each rule.
- fmap evspec1 evspec2
-
add a new rule to the current filter, to make it
convert events matching evspec1 (source) into events matching
evspec2 (destination).
Both evspec1 and evspec2 must have the same number of devices,
channels, notes, controllers etc..
- funmap evspec1 evspec2
-
remove event maps from the current filter. Any
mapping with source included in evspec1 and destination
inluded in evspec2 is deleted.
- ftransp evspec halftones
-
transpose events generated by the filter and matching ``evspec''
by the give number of halftones
- fvcurve evspec weight
-
adjusts velocity of note events produced by the filter, using
the given ``weight'' in the -63..63 range. If ``weight''
is:
- negative - sensitivity is decreased
- positive - sensitivity is increased
- zero - the velocity is unchanged
- xnew sysexname
-
create a new bank of sysex messages named ``sysexname''
- xdel
-
delete the current bank of sysex messages.
- xren newname
-
rename the current sysex bank to ``newname''
- xexists sysexname
-
return 1 if ``sysexname'' is a sysex bank, 0 otherwise
- xrm pattern
-
remove all sysex messages starting with ``pattern''
from the current sysex bank. The given pattern
is a list of bytes; an empty pattern matches any
sysex message.
- xsetd newdev pattern
-
set device number to ``newdev''
on all sysex messages starting with ``pattern''
in the current sysex bank. The given pattern
is a list of bytes; an empty pattern matches any
sysex message.
- xadd devnum data
-
add to the current sysex bank
a new sysex message. ``data'' is a list containing the
MIDI system exclusive message and ``devname'' is the device number to which
the message will be sent when performance mode is entered
- xinfo
-
print all sysex messages of the current sysex bank. Messages that are
too long to be desplayed on a single line are truncated and
the ``...'' string is displayed.
- i
-
enter ``idle'' performance mode. Start
processing MIDI input and generating MIDI output.
data passes through the current filter (if any)
or through the current track's filter (if any).
- p
-
play the song from the current position.
Input passes through the current filter (if any)
or through the current track's filter (if any).
- r
-
play the song and record the input.
Input passes through the current filter (if any)
or through the current track's filter (if any). On startup,
this function play one measure of countdown before
the data start being recorded.
- s
-
stop performance and release MIDI devices. I.e. stop the effect
``i'',
``p'' or
``r'' functions;
- sendraw device arrayofbytes
-
send raw MIDI data to device number ``device'',
for debugging purposes only.
- ev evspec
-
set the current event selection. Most track editing
functions will act only on events matching "evspec",
ignoring all other events.
- setq step
-
set the current quantization step to the given note
value, as follow:
- 4 - quarter note
- 6 - quarter note triplet
- 8 - eighth note
- 12 - eighth note triplet
- 16 - sixteenth note
- 24 - sixteenth note triplet
- etc...
The quantization step will be used by tquant
function and also by all editing functions to optimize event
selection. If the special ``nil'' value is specified as quantization
step, then quatization is disabled.
- getq
-
return the current quatization step
- g measure
-
set the current song position pointer to
the given measure number. Record and playback
will start a that position. This also defines
the beginning of the current selection used
by most track editing functions.
- getpos
-
return the current song position pointer which
is also the start position of the current selection.
- sel length
-
set the length of the current selection to
``length'' measures. The current selection
start at the current position set with the
``g'' function.
- getlen
-
return the length (in measures) of the current selection.
- ct trackname
-
set the current track. The current track
is the one that will be recorded. Most track editing
functions act on it.
- gett
-
return the current track (if any) or ``nil''
- cf filtname
-
set the current filter to ``filtname''.
The current filter
is the one used to process
input MIDI events in performance mode.
It's also the one affected
by all filter editing functions.
- getf
-
return the current filter
or ``nil'' if none
- cx sysexname
-
set the current sysex bank,
i.e. the one that will be recorded. The current
sysex back is the one affected by all sysex
editing functions.
- getx
-
return the current sysex bank
or ``nil'' if none
- ci channame
-
set the current (named) input channel. All input channel
editing functions will act on it.
- geti
-
return the name of the current input channel
or ``nil'' if none
- co channame
-
set the current (named) output channel. All output channel
editing functions will act on it.
- geto
-
return the name of the current output channel
or ``nil'' if none
- setunit ticks_per_unit
-
set the time resolution of the sequencer to ``tpu''
ticks in a whole note (1 unit note = 4 quarter notes).
The default is 96 ticks, which is the
default of the MIDI standard.
- getunit
-
return the number of ticks in a whole note
- fac tempo_factor
-
set the tempo factor for play and record to the given
integer value. The tempo factor must be between 50 (play
half of the real tempo) and 200 (play at twice the real
tempo).
- getfac
-
return the current tempo factor
- t beats_per_minute
-
set the tempo at the current song position
- mins amount {num denom}
-
insert ``amount'' blank measures at the
current song position.
The time signature used is num/denom.
If the time signature is an empty list (i.e. ``{}'')
then the time signature at the current position
is used.
- mcut
-
cut the current selection of all tracks, including the
time structure.
- mdup where
-
duplicate the current selection inserting a copy
of it at the position given by the ``where'' parameter.
The target position is a measure number relative to the
current selection to be copied.
If ``where'' is positive it's relative to the end of the current
selection; if it's negative it's relative to the beginning of the
current selection.
- minfo
-
print the meta-track (tempo changes, time signature
changes.
- mtempo
-
Return the tempo at the current song position. The unit
is beats per minute.
- msig
-
Return the time signature at the current song position. The result
is a two number list: numerator and denominator.
- mend
-
Return the ending measure of the song (i.e. its size in measures).
- ls
-
list all tracks, channels, filters and
various default values
- save filename
-
save the song into the given file. The ``filename''
is a quoted string.
- load filename
-
load the song from a file named ``filename''.
the current song is destroyed, even if
the load command fails.
- reset
-
destroy completely the song, useful to
start a new song without restarting the
program
- export filename
-
save the song into a standard MIDI file, ``filename''
is a quoted string.
- import filename
-
load the song from a standard MIDI file, ``filename''
is a quoted string. Only MIDI file ``type 1'' and
``type 0'' are supported.
- dlist
-
return the list of attached devices
(list of numbers)
- dnew devnum filename mode
-
attach MIDI device ``filename'' as
device number ``devnum''; ``filename''
is a quoted string. The ``mode'' argument
is the name of the mode, it can be on if the following:
-
``ro'' - read-only, for input only devices
-
``wo'' - write-only, for output only devices
-
``rw'' - read and write.
If midish is configured to use ALSA (default on Linux systems) then
``filename'' should contain the ALSA sequencer port, as listed by
``aseqdump -l'', (eg. ``28:0'', ``FLUID Synth (qsynth)'').
If ``nil'' is given instead of the path, then the port is not
connected to any existing port; this allows other ALSA sequencer
clients to subscribe to it and to provide events to midish or to
consume events midish sends to it.
- ddel devnum
-
detach device number ``devnum''
- dmtcrx devnum
-
use device number ``devnum'' as MTC source.
In this case, midish will relocate, start and stop according to
incoming MTC messages.
Midish will generate its clock ticks from MTC, meaning that it will
run at the same speed as the MTC device.
This is useful to synchronize midish to an audio multi-tracker or any
MTC capable audio application.
If ``devnum'' is ``nil'', then MTC messages are ignored and the
internal timer will be used instead.
- dmmctx { devnum1 devnum2 ... }
-
Configure the given devices to transmit MMC start, stop
and relocate messages.
Useful to control MMC-capable audio applications
from midish.
By default, devices transmit MMC.
- dclktx { devnum1 devnum2 ... }
-
Configure the given devices to transmit MIDI clock information
(MIDI ticks, MIDI start and MIDI stop events). Useful
to synchronize an external sequencer to midish.
- dclkrx devnum
-
set device number ``devnum'' to be the master MIDI clock source.
It will give midish MIDI ticks, MIDI start and MIDI stop events. This
useful to synchronize midish to an external sequencer.
If ``devnum'' is
``nil'', then the internal clock will be used and midish
will act as master device.
- dclkrate devnum ticrate
-
set the number of ticks in a whole note that are transmitted
to the MIDI device (if dclktx was called
for it). Default value is 96 ticks. This is the standard MIDI value and
its not recommended to change it.
- dinfo devnum
-
Print some information about the MIDI device.
- dixctl devnum list
-
Setup the list of controllers that are expected to
be received as 14-bit numbers (i.e. both coarse and fine
MIDI controller messages will be expected). By default
only coarse values are used, if unsure
let this list empty.
- devoxctl devnum list
-
Setup the list of controllers that will be transmitted
as 14-bit numbers (both coarse and fine MIDI controller messages).
- ctlconf ctlname ctlnumber defval
-
Configure controller number ``ctlnumber'' with name
``ctlname'', and default value ``defval''.
If defval is ``nil''
then there is no default value and corresponding
controller events are not grouped into frames.
See sec. Controller frames.
- ctlconfx ctlname ctlnumber defval
-
Same as ctlconf function,
but for 14-bit controllers. Thus defval is in the
range 0..16383.
- ctlconf ctlname
-
Unconfigure the given controller. ``ctlname'' is
the identifier that was used with
ctlconf
- ctlinfo
-
Print the list of configured controllers
- m mode
-
Set the mode of the metronome. The following modes
are available:
-
``on'' - turned on for both playback and record
-
``rec'' - turned on for record only
-
``off'' - turned off
- metrocf eventhi eventlo
-
select the notes that the metronome plays. The
pair of events must be note-ons
- info
-
display the list of built-in and user-defined
procedures and global variables
- print expression
-
display the value of the expression
- err string
-
display the given string and abort the
statement being executed.
- h funcname
-
display list of arguments function ``funcname''
- exec filename
-
read and executes the script from a file, ``filename''
is a quoted string. The execution of the script is aborted
on error. If the script executes an exit statement, only
the script is terminated.
- debug flag val
-
set debug-flag ``flag'' to (integer) value ``val''.
It's a developer knob.
If ``val=0'' the corresponding debug-info are
turned off. ``flag'' can be:
-
``filt'' - show events passing through the current filter
-
``mididev'' - show raw MIDI traffic on stderr
-
``mixout'' - show conflicts in the output MIDI merger
-
``norm'' - show events in the input normalizer
-
``pool'' - show pool usage on exit
-
``song'' - show start/stop events
-
``timo'' - show timer internal errors
-
``mem'' - show memory usage
- panic
-
Cause the sequencer to core-dump,
useful to developpers.
- proclist
-
Return the list of all user defined procs.
- builtinlist
-
Return a list of all builtin commands.
Midish could be used from general purpose scripting languages to do
MIDI-related tasks.
This is accomplished by starting the ``midish'' binary and writing
commands to it's standard input.
To ease this process, midish should be started in
batch mode, with the -b flag.
In batch mode the ``~/.midishrc'' and ``/etc/midishrc''
files are not parsed, errors cause midish to exit, and
``p'',
``r'' and
``i'' commands are blocking.
For instance the following simple shell script will play, on the
``/dev/rmidi1'' device, standard midi files enumerated on the command
line:
#!/bin/sh
trap : 2
for arg; do
midish -b <<END
dnew 0 "/dev/rmidi1" wo
import "$arg"
p
END
done
The ``smfplay'' and ``smfrec'' files shipped in the source tar-balls
are examples of such scripts.
A program that wants to use a midish feature, may start midish
and issue commands on its standard input. Then, the standard
output of midish could be parsed so the program can obtain the
desired information (if any).
To ease this process, the midish binary can be
started with the -v flag; in this case it will write
additional information on its standard output, allowing the
caller to be notified of changes of the state of midish. The
information is written on a single line starting with the
+ sign, as follows:
-
+ready
means that midish is ready to
parse and execute a new line. A front-end should
always wait for this string before issuing any command.
-
+pos measure beat tick
is written
during performace mode on every beat, it gives the current
song position.
No midish function (like print) can
generate a line starting with the + sign, so it is safe
to assume that such lines are synchronization lines and not
the output of a function. Furthermore, such lines will never
appear in the middle of the output of a function. Additional
information may be available in the same format in future
versions of midish; thus front-ends should be able to ignore
unknown lines starting with +.
Generally, any front-end should use a loop similar
to the following:
while (!eof) {
command = get_command_from_user();
wait_for("+ready");
write_to_midish_stdin(command);
result = parse_midish_stdout();
do_something(result);
}
The ``rmidish'' program shipped in the source tar-ball is
and example of such front-end.
The sample midishrc file shipped in the source tar-balls
contains a lot of examples of procedure definitions.
- ctldrop ictl
-
make the current filter drop controller number ``ictl''
on the current input
- ctlmap ictl octl
-
make the current filter route controller number
``ictl'' on the current input
to controller ``octl'' on the current channel
- solo
-
mute all tracks but current
- nomute
-
unmute all tracks.
- gmon devnum
-
create a new gmon sysex back and
generate a ``general midi on'' system exclusive message
for device number ``devnum'' and store it
to the current sysex bank.
- gmp patch
-
configures the current channel
to use general MIDI patch number ``patch''.
(this will send program change event when
performance mode is entered).
- vol value
-
set volume (controller number 7) of
the current channel
- reverb value
-
set reverb (controller number 91) of
the current channel
- chorus value
-
set chorus (controller number 93) of
the current channel
- rpn addr val
-
set registered parameter number (RPN) ``addr''
for the current channel to ``val''. Both ``addr''
and ``val'' are integers in the range 0..16383.
- nrpn addr val
-
set not registered parameter number (NRPN) ``addr''
for the current channel to ``val''. Both ``addr''
and ``val'' are integers in the range 0..16383.
The following session show how to
configure a keyboard split:
send EOF character (control-D) to quit
[0000:00]> inew kbd {1 0}
[0000:00]> onew bass {0 5}
[0000:00]> oaddev {pc bass 33}
[0000:00]> onew piano {0 6}
[0000:00]> oaddev {pc piano 2}
[0000:00]> fnew split
[0000:00]> fmap {any kbd} {any bass}
[0000:00]> fmap {any kbd} {any piano}
[0000:00]> fmap {note kbd 12..62} {note bass 0..50}
[0000:00]> fmap {note kbd 63..127} {note piano 63..127}
[0000:00]> finfo
{
evmap any {1 0} > any {0 5}
evmap any {1 0} > any {0 6}
evmap note {1 0} 12..62 > note {0 5} 0..50
evmap note {1 0} 63..127 > note {0 6} 63..127
}
[0000:00]> i
[0000:00]> s
[0000:00]> save "piano-bass"
First we set the default input to device 1, channel 6, on
which the keyboard is available. Then we define 2
named-channels ``bass'' on device 0, channel 5 and
``piano'' on device 0 channel 6. Then we assign patches
to the respective channels. After this, we define a new filter
``split'' and we add rules corresponding to the
keyboard-split on note number 62 (note D3), the bass is
transposed by -12 half-tones (one octave).
The following session show how to
record a track.
send EOF character (control-D) to quit
[0000:00]> inew kbd {1 0} # select default input channel
[0000:00]> onew drums {0 9} # create drum channel
[0000:00]> tnew dr1 # create track ``dr1''
[0000:00]> t 90 # tempo to 90 bpm
[0000:00]> r # start recording
[0003:03]> s # stop
[0000:00]> setq 16 # set quantization step
[0000:00]> sel 32 # select 32 measures
[0000:00]> tquant 75 # quantize to 75%
[0000:00]> p # play
[0001:02]> s # stop playing
[0000:00]> save "myrythm" # save to a file
[0000:00]> # hit ^D to quit
first, we set the default input channel to ``{1 0}'' (default channel
of the keyboard).
Then, we define the ``drum'' channel on device 0, channel 9, this
creates a default filter that maps ``kbd'' to ``drums''.
Then we define a new track named ``dr1'' an we start recording.
Then, we set the quantization step to 16 (sixteenth note), we select
the first 32 measures of the track and we quantize them. Finally, we
start playback and we save the song into a file.
Many thanks to all who contributed (new features, bug fixes, bug
reports, packaging efforts and other improvements):
Julien Claassen,
Karim Saddem,
Marcell Mars,
Richard L. Hamilton,
Samuel Mimram,
Will Woodruff,
and
Willem van Engen.
Copyright (c) 2003-2010 Alexandre Ratchov
Last updated apr 19, 2010