Important features:
To install midish:
gunzip midish-0.2.tar.gz tar -xf midish-0.2.tar cd midish-0.2
make PREFIX=$HOME install
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:
rmidishThen, commands are issued interactively on the midish prompt, example:
print "hello world"Midish has two modes: prompt mode and performance mode.
In prompt mode, midish interactively waits for commands from the command-line. This mode is used to configure the sequencing engine: create and manage tracks, define filtering rules start/stop recording, etc... Midi devices aren't used, they are let closed in order to let other applications to use them.
In performance mode, midish processes midi input/output in real-time. Commands cannot be issued on the command-line. This mode is typically used for playback and record. To stop performance mode, hit control-C (or send an interrupt signal (SIGINT) to midish).
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 to them. There are three performance modes:
+---------+ +------------+ +----------+ | | | | | | | midi in |------------>| filter |--------->| midi out | | | | | | | +---------+ +------------+ +----------+
+--------------+ | track_1 play |---\ +--------------+ | | ... ---+ | +--------------+ | | track_N play |---+ +--------------+ | | +---------+ +------------+ | +----------+ | | | | \--->| | | midi in |------------>| filter |--------->| midi out | | | | | | | +---------+ +------------+ +----------+
+--------------+ | track_1 play |---\ +--------------+ | | ... ---+ | +--------------+ | | track_N play |---+ +--------------+ | | +---------+ +------------+ | +----------+ | | | | \--->| | | midi in |-----+------>| filter |----+---->| midi out | | | | | | | | | +---------+ | +------------+ | +----------+ | | | | +----------------+ | +--------------+ \---->| track_X record | \------>| sysex record | +----------------+ +--------------+
devattach 0 "/dev/rmidi4" # attach the module as dev number 0 devattach 1 "/dev/rmidi3" # attach the keyboard as dev number 1The following session shows how to record a simple track. First, we define a filter named 'piano' that routes events 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.
1> filtnew piano # create filter 'piano' 2> filtchanmap piano {1 0} {0 5} # dev=1,ch=0 -> dev=0,ch=5 3> tracknew pi1 # create track 'pi1' 4> songrecord # start recording press control-C to finish --interrupt-- 5> songsave "mysong" # save the song into a file 6> # EOF (control-D) to quit
The same task can be achieved in a much easier way by using the simple procedures (or macros) defined in the default /etc/midisrc.
1> ci {1 0} # select default input {dev, chan} 2> ni piano {0 5} # create piano on dev=0, chan=5 3> nt pi1 # new track named "pi1" 4> r # start recording press control-C to finish --interrupt-- 5> save "mysong" # save to file "mysong" 6> # EOF (control-D) to quit
Note: It is strongly recommended to define simple procedures and to use them instead of directly using the built-in functions of midish.
devattach 0 "/dev/rmidi4" devattach 1 "/dev/rmidi3"
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:
songload "sample.sng" songplay(hit control-C to stop playback). When the configuration is setup, put the "devattach" commands in the user's $HOME/.midishrc. It will be automatically executed the next time midish is run.
{0 1} # device 0, midi-channel 1Channels can also be named, as follows:
channew mybass {0 1}this defines a named-channel mybass that can be used instead of the {0 1} pair.
For instance; to select patch 34 on channel 'mybass', attach a "program change" event:
chanconfev 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:
chanconfev 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:
chanconfev mybass { ctl mybass 7 125 }
Midish supports midi filtering: a filter transforms incoming midi events and send them to the output. The filter also "sanitises" the input midi stream by removing nested notes, duplicate controllers and other anomalies. Filters are in general used to:
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. The current filter processes input midi events in the following order:
The following diagram summarises the event path through the filter:
+------------+ | voice | match in ---->| |-----------------------------------------> out | rules | +------------+ doesn't | +------------+ match | | channel | match \-------->| |-------------------------> out | rules | +------------+ doesn't | +------------+ match | | device | match \-------->| |---------> out | rules | +------------+ doesn't | match | \----------------> out
Filters are defined as follows:
filtnew myfilt # define 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. See filtering functions section for details.
Rules can be listed with filtinfo. All filtering rules can be removed with filtreset.
filtnew mydevmap # define filter 'mydevmap' filtdevmap mydevmap 1 0 # make it route device 1 -> device 0To test the filter, start performance mode:
songidle(hit control-C to stop performance mode).
filtnew mydrums # define filter 'mydrums' filtchanmap mydrums {1 0} {0 9} # route dev/chan {1 0} to {0 9}The device/channel pair is in braces. The first {1 0} is the input device/channel and {0 9} is the output channel. To test the filter, start performance mode:
songidle(hit control-C to stop performance mode). Playing on channel 0 of the keyboard will make sound channel 9 of the sound-module.
filtctlmap mydrums {1 0} {0 9} 1 11the first three arguments are the name of the filter, the input and the output device/channel pair. The 4-th argument is the controller number on the input (1 = modulation) and the 5-th argument is the controller number on the output (11 = expression). Rules of the filter can be listed as follows:
filtinfo mydrumswhich will print:
{ chanmap {1 0} {0 9} ctlmap {1 0} {0 9} 1 11 id }
filtnew mypiano # define filter 'mypiano' filtchanmap mypiano {1 0} {0 2} # route dev/chan {1 0} to {0 9} filtkeymap mypiano {1 0} {0 2} 0 127 12both rules are necessary. Note events are handled by the key-rule and other events (controllers) fall trough the channel-rule. Arguments 4 and 5 to filtkeymap give the note range that will be handled (from 0 to 127, i.e. the whole keyboard) and the 6-th argument gives the number of half-tones (12, one octave) to transpose.
filtnew mysplit filtchanmap mysplit {1 0} {0 2} filtkeymap mysplit {1 0} {0 2} 0 63 0 filtchanmap mysplit {1 0} {0 3} filtkeymap mysplit {1 0} {0 3} 64 127 0
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 tics. The tic is the fundamental time unit in midish. Duration of tics is fixed by the tempo. By default midish uses:
metroswitch 1 # switch the metronome on songplay # start playbackThe metronome has two kind of click-sound:
metroconf {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.
songtimeins 0 2 4 4 # 4/4 at measure 0 during 2 measures songtimeins 2 5 6 8 # 8/6 at measure 2 during 5 measures metroswitch 1 # switch the metronome on songplay # test itTo suppress measure number 2 (the first 6/8 measure)
songtimerm 2 1 # remove 1 measure starting a measure 2 metroswitch 1 # switch the metronome on songplay # test it
songsettempo 0 100 songsettempo 2 180
songsetcurpos 3This will make songrecord and songplay start at this particular position instead of measure number 0.
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:
tracknew mytrack songrecordtracks are played as follows:
songplayHowever, 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).
filtnew mypiano filtchanmap mypiano {1 0} {0 0} # dev1/chan0 -> dev0/chan0 tracknew mytrack songrecord
Most track editing functions in midish take at least the following arguments:
trackblank mypiano 3 1 (96 / 16) {}the 3-rd argument set the precision to sixteenth note (assuming 96 tics per unit note). This means that notes position is rounded to the nearest 16-th note before being removed. This is useful, because often recorded notes doesn't start exactly on the measure boundary. The precision argument makes possible to edit a track that is not quantised. The latest argument (empty list) selects the events to be deleted (see section event ranges).
In a similar way, one can cut a piece of a track, for instance to cut 2 measures starting at measure number 5:
trackcut mypiano 5 2 (96 / 16)
The following inserts 2 blank measures at measure number 3:
trackinsert mypiano 3 2 (96 / 16)
A track portion can be copied into another track as follows:
trackcopy mypiano 3 2 mypiano2 5 (96 / 16) {}this will copy 2 measures starting from measure number 3 into (the already existing) track mypiano2 at measure 5. The latest argument (empty list) selects the events to be copied (see section event ranges).
trackquant mypiano 3 4 (96 / 4) 75The last arguments gives the percent of quantisation. 100% means full quantisation and 0% leans no quantisation at all. This is useful because full quantisation often sound to regular especially on acoustic patches.
trackcheck mytrackThis function can be useful to remove nested notes when a track is recorded twice (or more) without being erased.
sysexnew mybankThen, messages can be added:
sysexadd mybank 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, for instance:
songidleSysex messages can be recorded from midi devices, this is useful to save "bulk dumps" from synthesisers. Sysex messages are automatically recorded on the current bank. So, to record a sysex:
songsetcursysex mybank songrecordThe 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:
sysexinfo mybankA bank can be cleared by:
sysexclear mybank {}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:
sysexclear mybank {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:
sysexsetunit mybank 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:
sysexsetunit mybank 1 {0xF0 0x7E 0x7F}
The following functions gives some information about midish objects:
songinfo # summary songtimeinfo # tempo changes chaninfo mydrums # list config. events in 'mydrums' filtinfo myfilt # list rules in 'myfilt' trackinfo mytrack (96 / 16) {} # list number of events per measure devinfo 0 # device properties
Objects can be listed as follows:
print [tracklist] print [chanlist] print [filtlist] print [devlist]
Current values can be obtained as follows:
print [songgetunit] # tics per unit note print [songgetcurpos] # print current position print [songgetcurlen] # print current selection length print [songgetcurfilt] # current filter print [songgetcurtrack] # current track print [songgetcursysex] print [trackgetcurfilt mypiano] # current filter of track 'mypiano' print [filtgetcurchan mysplit] # current channel of filter 'mysplit'
The device and the midi channel of a channel definition can be obtained as follows:
print [changetch mydrums] # print midi chan number print [changetdev mydrums] # print device number
To check if object exists:
print [chanexists] print [filtexists] print [trackexists] print [sysexexists]this will print 1 if the corresponding object exists and 0 otherwise.
A song can be saved into a file. All channel definitions, filters, tracks, their properties, and values of the current track, current filter will be saved by:
songsave "myfile"In a similar way, the song can be load from a file as follows:
songload "myfile"
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:
songimportsmf "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:
songexportsmf "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 "i" that just replaces songidle in order to avoid typing its name.
proc i { songidle; }The proc keyword is followed by the procedure name and then follows a list of statements between braces. In a similar way can define the following procedures:
proc p { metroswitch 0 # turn off metronome songplay # start playback } proc r { metroswitch 1 # turn on metronome songrecord # start recording }
Procedures can take arguments. For instance, to define a procedure named nt that creates a new track:
proc nt name { tracknew $name }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 ("$"). We can use the above procedure to create a track:
nt myfiltA lot of similar procedures are defined in the sample midishrc file, shipped in the source tar-ball. To make midish easy to use, most of the usual tasks can be performed with only 2 or three character statements.
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.
attrubute | description |
---|---|
unit number | integer that is used to reference the device |
ticrate | number of tic per unit note, default is 96, which corresponds to the midi standard |
sendrt flag | boolean; if sendrt = true, the real-time events (like start, stop, tics) are transmitted to the midi device. |
attrubute | description |
---|---|
name | identifier used to reference the channel |
{dev chan} | device and midi channel to which midi events are sent |
conf | events that are sent when performance mode is entered |
curinput | default input {dev chan} pair. This value isn't used in real-time, however it can be used as default value when adding new rules to a filter that uses this channel. |
attrubute | description |
---|---|
name | identifier used to reference the fitler |
rules set | set of rules that handle midi events |
current channel | default channel. This value isn't used in real-time, however it can be used as default value when adding new rules to the filter. |
attrubute | description |
---|---|
name | identifier used to reference the track |
mute flag | if the mute = true, the the track is not played on 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 fitler. |
attrubute | description |
---|---|
name | identifier used to reference the sysex back |
list of messages | each message in the list contains the actual message and the unit number of the device to which the message has to be sent. |
attrubute | description |
---|---|
meta track | a track containing tempo changes and time signature changes |
tics_per_unit | number of midi tics per unit note, the default value is 96 which corresponds to the midi standard. |
metronome flag | if the metronome = true, then the metronome is audible |
metro_hi | a note-on event that is sent on the begining of every measure if the metronome is enabled |
metro_lo | a note-on event that is sent on the begining of every beat if the metronome is enabled |
curtrack | default track: the track that will be recorded in record mode |
curfilt | default filter. The filter with which the default trach is recorded if it hasn't its default filter. |
curchan | default channel. This value isn't used in real-time, however it can be used as default value when adding new rules to the filter. |
curpos | 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 |
curlen | length (in measures) of the current selection. This value isn't used in real-time, however it can be used as default value in track editing functions. |
curquant | current quatisation step in tics. This value isn't used in real-time, however it can be used as default value for the track editing functions |
curinput | default input {dev chan} pair. This value isn't used in real-time, however it can be uses as default value when adding new values to a filter that uses this channel. |
Some functions take events as arguments. An event is specified as a list containing:
noff | note off |
non | note off |
kat | key after-touch (poly) |
ctl | controller |
pc | program change |
cat | channel after-touch (mono) |
bend | pitch bend |
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
{ pc {1 3} 34 }set controller number 7 to 99 on device/channel drums:
{ ctl drums 7 99 }
Examples:
{} # match everything { any } # match everything { any bass } # match anything on channel 'bass' { any {1 4} } # match anything on device 1, channel 4 { any {1 {}} } # match anything on device 1, any channel { any {{} 9} } # match anything on any device and channel 9 { note } # match note events { note {1 9} } # match notes on dev 1, channel 9 { note {0 {}} } # match notes on device 0 { note {0 {3 5}} } # match notes on device 0, channel 3, 4, 5 { note {} {0 64} } # match notes between 0 an 64 { note {} 60 {80 127} } # match note 60 with velocity between 80 an 127 { ctl bass } # match controllers on channel 'bass' { ctl bass 7 } # match controller 7 on channel 'bass' { ctl bass 7 {0 64} } # match ctl 7 on chan 'bass' with value 0->64 { bend {} {0 0x1fff} } # match "lower" bender
mytrack _mytrack my34_track23 # good 123mytrack # bad, starts with digit mytrackabcdefghijklmnopqrstuvwxyz # bad, to long
123 | 123 in decimal |
0x100 | 256 in hex |
0xF0 | 240 in hex |
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:
myproc arg1 arg2
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).
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:
non-zero integer | true |
zero integer | false |
non-empty list | true |
empty list | false |
any name | true |
nil | false |
for i in { "bli" "bla" "blu" } { print $i; }the block is executed for each value of the list to which '$i' is set.
return $x * $x;
"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 recognised:
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 righ |
!= | not equal | |
& | bitwise AND | left to right |
^ | bitwise XOR | left to right |
| | bitwise OR | left to right |
&& | logical AND | left to right |
|| | logical OR | left to right |
Examples:
2 * (3 + 4) + $xis an usual integer arithmetic expression.
[tracklen mytrack]is the returned value of the procedure tracklen called with a single argument mytrack.
{ "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 } }
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 }
return the list of names of the tracks in the song example:tracknew tracknameprint [tracklist]
create an empty track named tracknametrackdelete trackname
delete existing track 'trackname'. Current track cannot be deleted.trackrename trackname newname
rename track 'trackname' to 'newname'trackexists trackname
return 1 if trackname is a track, 0 otherwisetrackaddev trackname measure beat tic ev
put the event ev on track trackname at the position given by measure, beat and tictracksetcurfilt trackname filtname
set the default filter (for recording) of trackname to filtname. It will be user if there is no current filter.trackgetcurfilt trackname
return the default filter (for recording) of trackname, returns nil if nonetrackcheck trackname
check the whole track for orphaned notes, nested notes and other anomalies; also removes multiple controllers in the same tictrackcut trackname from amount quantum
cut amount measures of the track trackname from measure from.trackblank trackname from amount quantum evspec
clear amount measures of the track trackname from the measure from. Only events matching the evspec argument are removed (see event ranges)trackinsert trackname from amount
insert amount blank measures in track trackname just before the measure fromtrackcopy trackname1 from amount trackname2 where quantum evspec
copy amount measures starting at from from track trackname1 into trackname2 at position where. Only events matching the evspec argument are copied (see event ranges)trackquant trackname from amount rate quantum
quantise amount measures of the track trackname from measure from ; the quantum is the round in tics (see songsetunit). Rate must be between 0 and 100. 0 means no quantisation and 100 means full quantisation.tracksetmute trackname muteflag
If muteflag is equal to 1 the the track is muted ie it will not be played during record/playback. If muteflag is equal to 0 the the track is no more muted ie it will be played during record/playback.trackgetmute trackname
Return 1 if the give track is muted and 0 otherwise.trackchanlist trackname
Return the list of channels used by events stored in track trackname.trackinfo trackname quantum evspec
display the number of events that match evspec for each measure of track trackname.
create an new channel named channelname and assigned the given device and midi channel.chanset channelname { dev, midichan }
set the device/channel pair of an existing channel named channelname.chandelete channame
delete existing channel 'channame'.chanrename channame newname
rename channel 'channame' to 'newname'chanexists channelname
return 1 if channelname is a channel, 0 otherwisechangetch channelname
return the midi channel number of channel named channelnamechangetdev channelname
return the device number of channel named channelnamechanconfev channelname event
add the event to the configuration of channel channelname, typically used set the program, volume, depth etc... The channel of the event is not used.chaninfo channame
print all events on the config of the channel.chansetcurinput channame {dev chan}
set the default input {dev chan} pair of channel channame. These values are currently not used in realtime.changetcurinput channame
return the default input {dev chan} pair of channel channame.
create an new filter named filtnamefiltdelete filtname
delete existing filter 'filtname'. Current filter and filters used by tracks cannot be deleted.filtrename filtname newname
rename filter 'filtname' to 'newname'filtexists filtname
return 1 if filtname is a filter, 0 otherwisefiltreset filtname
remove all rules from the filter filtnamefiltinfo filtname
list the rules of the given filterfiltsetcurchan filtname channame
set the default channel of filter filtname to channame.filtgetcurchan filtname
return the default channel of filtname, returns nil if nonefiltchgich filtname oldchan newchan
change the input channel of all rules with input channel equal to oldchan to newchanfiltchgidev filtname olddev newdev
change the input device of all rules with input device equal to olddev to newdevfiltswapich filtname oldchan newchan
change the input channel of all rules with input channel equal to oldchan to newchan, and the input channel of all rules with input channel equal to newchan to oldchan,filtswapidev filtname olddev newdev
change the input device of all rules with input device equal to olddev to newdev, and the input device of all rules with input device equal to newdev to olddevfiltchgoch filtname oldchan newchan
change the output channel of all rules with output channel equal to oldchan to newchanfiltchgodev filtname olddev newdev
change the output device of all rules with output device equal to olddev to newdevfiltswapoch filtname oldchan newchan
change the output channel of all rules with output channel equal to oldchan to newchan, and the output channel of all rules with output channel equal to newchan to oldchan,filtswapodev filtname olddev newdev
change the output device of all rules with output device equal to olddev to newdev, and the output device of all rules with output device equal to newdev to olddevfiltdevdrop filtname inputdev
make filter drop events from device inputdevfiltnodevdrop filtname inputdev
remove devdrop rules that drop events from device inputdevfiltdevmap filtname inputdev outputdev
route all events from device inputdev to device outputdev. If multiple dev maps are defined for the same input device then events are duplicatedfiltnodevmap filtname outputdev
remove devmap rules that route events to device number outputdev.filtchandrop filtname inputchan
make filter drop events from channel inputchanfiltnochandrop filtname inputchan
remove chandrop rules that drop events from channel inputchanfiltchanmap filtname inputchan outputchan
route all events from channel inputchan (ie device/midi-channel pair) to channel outputchan. If multiple channel maps are defined for the same input channel then events are duplicatedfiltnochanmap filtname outputchan
remove channel map rules that route events to channel outputchan.filtctldrop filtname inchan inctl
make the filter drop controller number inctl on channel inctl.filtnoctldrop filtname inchan inctl
remove ctldrop rules that drop controller number inctl on channel inctl.filtctlmap filtname inchan outchan inctl outctl
route controller inctl from inchan to controller outctl on outchan. If multiple ctlmaps are defined for the same input channel and the same input controller then events are duplicatedfiltnoctlmap filtname outchan outctl
remove ctlmap rules that route controllers to controller outctl, channel outchan.filtkeydrop filtname inchan keystart keyend
drop notes between keystart and keyend on channel inchan.filtnokeydrop filtname inchan keystart keyend
remove keydrop rule that drop notes between keystart and keyend on channel inchan.filtkeymap filtname inchan outchan keystart keyend keyplus
route note events from channel inchan and in the range keystart..keyend to outchan. Routed notes are transposed by keyplus half-tones If multiple keymaps are defined for the same input channel and key-range then events are duplicatedfiltnokeymap filtname outchan keystart keyend
remove keymap rules that route note events in the range keystart..keyend to channel outchan.
create a new bank of sysex messages named sysexnamesysexdelete sysexname
delete the bank of sysex messages named sysexname. Current 'sysex' cannot be deleted.sysexrename sysexname newname
rename sysex bank 'sysexname' to 'newname'sysexexists sysexname
return 1 if sysexname is a sysex bank, 0 otherwisesysexclear sysexname pattern
remove all sysex messages starting with pattern from sysex bank sysexname. The given pattern is a list of bytes; an empty pattern matches any sysex message.sysexsetunit sysexname newunit pattern
set device number to newunit on all sysex messages starting with pattern from sysex bank sysexname. The given pattern is a list of bytes; an empty pattern matches any sysex message.sysexadd sysexname unit data
add to sysex bank sysexname an new sysex message. data is a list containing the midi system exclusive message and unit is the device number to which the message will be sentsysexinfo sysexname
print debug info about sysex bank sysexname
put midi input to the midi output, data passes through the current filter (if any) or through the current track's filter (if any).songplay
play the song from the current position. Input passes through the current filter (if any) or through the current track's filter (if any).songrecord
play the song and record the input. Input passes through the current filter (if any) or through the current track's filter (if any). songrecord always tries to play one measure before the actual position on which recording starts.sendraw device arrayofbytes
send raw midi data to device number 'device', can be used to send system exclusive messages, example:sendraw 0 { 0xF0 0x7E 0x7F 0x09 0x01 0xF7 }
set the number of tics in the time scale (for quantisation, track editing...). Currently this value is not used in real-time.songgetcurquant tics
get the number of tics in the time scale.songsetcurpos measure
set the current song position pointer to the measure measure. Record and playback will start a that position. This corresponds also to the start postition of the current selection.songgetcurpos
return the current measure ie the current song position pointer. This corresponds to the start postition of the current selection.songsetcurpos length
set the length of the current selection to 'length' measures.songgetcurpos
return the length (in measures) of the current selection.songsetcurtrack trackname
set the current track ie the track to be recordedsonggetcurtrack
return the current track (if any) or nilsongsetcurfilt filtname
set the current filter ie the one used (with songplay and songidle).songgetcurfilt
return the current filter or 'nil' if nonesongsetcursysex sysexname
set the current sysex bank, ie the one that will be recordedsonggetcursysex
return the current sysex bank or 'nil' if nonesongsetcurchan channame
set the current (named) channel.songgetcurchan
return the name of the current channel or 'nil' if nonesongsetcurinput {dev chan}
set the (global) default input {dev chan} pair. These values are currently not used in realtime.changetcurinput channame
return the (global) default input {dev chan} pair.songsetunit tpu
set the time resolution of the sequencer to tpu tics per unit (1 unit = 4 quarter notes). the unit shall be changed before creating any tracks. The default is 96 tics per unit, which is the default of the midi standard.songgetunit
return the number of tics per unit notesongsettempo measure bpm
set the tempo to bpm beats per minute at measure measuresongtimeins from amount numerator denominator
insert amount blank measures at measure from. The used time signature is given by numerator/denominator.songtimerm from amount
delete amount measures starting at measure from. The time signature is restored with the value preceding the from measure.songtimeinfo
print the meta-track (tempo changes, time signature changessonginfo
display some info about the default values of the songsongsave filename
save the song in a file, filename is a quoted string.songload filename
load the song from a file named filename. the current song is destroyed, even if the load command failedsongreset
destroy completely the song, useful to start a new song without restarting the programsongexportsmf filename
save the song into a standard midi file, filename is a quoted string.songimportsmf filename
load the song from a standard midi file, filename is a quoted string. Currently only midi file "type 1" is supported.
return the list of attached devices (list of numbers)devattach devnum filename
attach midi device filename as device number devnum; filename is a quoted string.devdetach devnum
detach device number devnumdevsetmaster devnum
set device number devnum to be the master clock source. It will give tempo, start event and stop event. If devnum is nil, then the internal clock will be used and midish will act as master device.devgetmaster devnum
return the current master device. If non, nil is returned.devsendrt devnum bool
If bool is true, the real-time information (midi tics, midi start and midi trop events) will be transmitted to device number devnum. Otherwise no real-time midi events are transmitted.devticrate devnum ticrate
set the number of tics per unit note that are transmitted to the midi device if "sendrt" feature is active. Default value is 96 tics. This is the standard MIDI value and its not recommended to change it.devinfo devnum
Print some information about the midi device.
if number is equal to zero then the metronome is disabled else it is enabledmetroconf eventhi eventlo
select the notes that the metronome plays. The pair of events must be note-onsinfo
display the list of built-in and user-defined procedures and global variablesprint expression
display the value of the expressionexec filename
read and executes the script from a file, filename is a quoted string.debug flag val
set debug-flag flag to (integer) value val. If val=0 the corresponding debug-info are turned off. flag can be:panic
- rmidi - show raw midi traffic on stderr
- filt - show events passing through the current filter
- song - show start/stop events
cause the sequencer to core-dump
The sample midishrc file shipped in the source tar-balls contains a lot of examples of procedure definitions.
ci { dev chan }
set the current input device/channel pair. It will be used as default value when tracks, filters and channels are created.cc channame
set the current output channel. It will be used as default value when tracks and filters are created. The current input that was used the last time this channel was the current one is restored.cf filtname
set the default filter, that will be used in performance mode. It will be used as default value when tracks are created. The value of the default track is reset to 'nil' (no default track). The current channel and the current input that were used the last time this filter was the current one are restored.ct trackname
set the current track, that will be recorded in record mode. The current filter, the current channel and the current input that were used the last time this track was the current one are restored.ni instrname {device channel}
create a new channel and a new filter with the same name in such a way that the current input is routed to this channel.ctldrop ictl
make the current filter drop controller number ictl on the current inputctlmap ictl octl
make the current filter route controller number ictl on the current input to controller octl on the current channeltranspose halftones
make the current filter to transpose all notes from the current input and to route them to the current channel.nt trackname
create a new track.i
go into performance mode and run the current filter of the current track.p
go in performance mode and play the all defined tracks and run the current filter of the current trackr
go in performance mode and record the current track using its current filterl
list tracks, channels and filter and print some additional infog measurenum
move the current song position to measure number measurenum. Play/record and all editing procedures that follow will start at that positionsel nummeasures
select measures from the current position. Track editing procedures (q, copy, cut, clr ...) will use the current selection.n denominator
set the current note length for quantisation to denominator, 4 means quarter-note, 8 means eighth-note, 16 means sixteenth-note etc... This value will be used by track editing functions.q rate
quantise the current selection of the current track (rate=100 means full quantisation and rate=0 means no quantisation).cut amount
cut the given number of measures from the current position on the current track.clr num
clear (removes events but not blank space) the current selection on the current track.ins num
insert num empty measures into the current track at the current positioncopy pos
copy the current selection pos measures forward from the current position.gcut
same as cut but acts on all tracks simultaneously ("g" like global).gins num
same as ins but acts on all tracks simultaneously ("g" like global).gcopy num
same as copy but acts on all tracks simultaneously ("g" like global).mute trackname
mute track trackname. The track will be no more audible during playback.solo
mute all tracks but currentunmute trackname
unmute track trackname. The track will be audible during playback.nomute
unmute all tracks.save filename
save the whole project (filters, tracks, channels, and current settings) in file filename.load filename
load the whole project (filters, tracks, channels, and current settings) from file filename.tempo bpm
change the tempo at the current position to bpm beats per measure.timeins num numerator denominator
insert num measures in the meta-track with the time signature numerator/denominatortimerm num
remove num measures from the meta-track.gmon devnum
send "general midi on" system exclusive message to device number devnumgmp 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 channelreverb value
set reverb (controller number 91) of the current channelchorus value
set chorus (controller number 93) of the current channel
1> channew bass {0 5} 2> chanconfev bass {pc bass 33} 3> channew piano {0 6} 4> chanconfev piano {pc piano 2} 5> filtnew split 6> filtchanmap split {1 0} bass 7> filtchanmap split {1 0} piano 8> filtkeymap split {1 0} bass 0 63 (-12) 9> filtkeymap split {1 0} piano 64 127 0 10> filtinfo split { keymap {1 0} {0 2} 65 127 0 id keymap {1 0} {0 1} 0 64 116 id chanmap {1 0} {0 2} chanmap {1 0} {0 1} } 11> songidle press enter to finish ^C -- interrupt -- 12> songsave "piano-bass" 13>First 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 64 (note E3), the bass is transposed by -12 half-tones (one octave).
1> ci {1 0} 2> ni drums {0 9} 3> nt dr1 4> tempo 90 5> r press control-C to finish --interrupt-- 6> n 16 7> sel 32 8> q 75 9> p press control-C to finish --interrupt-- 10> save "myrhythm" 11>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 and the corresponding filter that will route events from the keyboard to the drum channel. Then we define a new track named dr1 an we start recording. Then, we set the quantisation step to 16 (sixteenth note), we select the first 32 measures of the track and we quantise them. Finally, we start playback and we save the song into a file.