Midish user's manual and tutorial

Table of contents

1 Introduction

1.1 What is midish?

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:

Midish is open-source software distributed under a BSD-style license.

1.2 Installation

Requirements:

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:

  1. Untar and gunzip the tar-ball:
    gunzip midish-1.0.tar.gz
    tar -xf midish-1.0.tar
    cd midish-1.0
    
  2. 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
    
  3. Compile midish, just type ``make'', this will build midish.
  4. Install binaries, documentation and examples by typing ``make install'', possibly as root.
  5. If there isn't a ``/etc/midishrc'' file, then copy the sample file by typing in your shell:
    cp midishrc /etc
    
  6. 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 "snd/0" rw
    
    see next section for details.

1.3 Invocation

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:

midish

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:

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.

1.4 How does it work

Midish uses the following objects to represent a project:

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 output configuration events are sent. There are three performance modes:

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.

1.5 An example

Suppose that there are two devices:

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 (the keyboard input) to device 0, channel 5 (the sound module output). 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

2 Devices setup

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.

3 Inputs

Inputs are named ``{device channel}'' pairs on which MIDI events arrive. Inputs are handled by two-item lists, like this:

{1 0}                   # device 1, MIDI channel 0

Inputs are named using the ``inew'' command, as follows:

inew keyboard {1 0}

this defines the ``keyboard'' symbol that may be used instead of the ``{1 0}'' pair.

Note: Unlike simple ``{device channel}'' pairs, inputs are used internally by certain functions. For instance, inputs are used to create default filter rules.

4 Outputs

Outputs are named ``{device channel}'' pairs where go outgoing MIDI events. They are handled by two-item lists, like this:

{0 4}                   # device 0, MIDI channel 4

Outputs are named using the ``onew'' command, as follows:

onew mybass {0 4}

this defines an output named ``mybass'' that may be used instead of the ``{0 4}'' pair.

Outputs have a filter with the same name; it defines how input events are routed to the output. The filter comes with a default rule that routes all named inputs to the given output. See filter section for further details.

5 Output and input configuration events

Inputs and outputs may hold configuration events (like program changes and controllers) to be attached. Such events are sent when performance mode is entered, for instance just before playback is started.

For instance, to set program number 34 on output ``mybass'', attach a ``program change'' event as follows. First make ``mybass'' the current output:

co mybass
then, set its program number:
oaddev {pc mybass 34}

the list argument gives the event to attach to the output. See the event section for more details about events.

To set its volume (controller 7) to 120:

oaddev {ctl mybass 7 120}

If a setting is set multiple times, the last one is kept. For instance, the following will change the volume to 125 by replacing the above event:

oaddev {ctl mybass 7 125}
Events attached to inputs work the same way. When performance mode is entered, they are sent as if they come from the input. Consequently they pass through the current filter and will be recorded on the current track in record mode. This makes them useful only in very specific cases.

6 Events and event sets specification

6.1 Event specification

An event is specified as a list containing:

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} 1234 34}

set controller number 7 to 99 on the ``drums'' output:

{ctl drums 7 99}

6.2 Event set specification

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:

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 "bass" output
{ 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 "bass" output
{ ctl bass 7 }          # match controller 7 on "bass" output
{ bend {} }             # match bender
{ xctl {} 19 }          # match 14-bit controller number 19
{ xpc  {} 1234 21 }     # match patch 21 on bank 1234
{ nrpn {} 21 }          # match NRPN 21 change

6.3 User-defined events

Short system-exclusive patterns can be handled as events. Such events are defined with the evpat function.

7 Filtering/routing

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:

Multiple filters may 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 may be added, modified and removed. The filtering rules may be listed with the finfo function. All rules may be removed with the freset function. See filtering functions section for details.

7.1 Filter rules

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

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 channel 3 are sent to device 2, 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 input ``{0 0}'' by rewriting:

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 input ``{0 0}'' the following rule might be used:

ctl {0 0} 7 -> none

Rules are created and deleted 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
}

7.2 Rules hierarchy

Filters may 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 useful 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 the ``kbd'' input to the ``piano'' 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 the fmap and funmap functions sort them.

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.

7.3 Examples

7.3.1 Device redirections

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

7.3.2 Input to output mappings

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.

7.3.3 Controller mappings

The following example adds a new rule to the above filter that maps the modulation wheel (controller 1) of the input (i.e. device 1, midi channel 0) to the expression controller (number 11) of the output (device 0, midi channel 9).

fmap {ctl {1 0} 1} {ctl {0 9} 11}

Rules of the filter may be listed with the finfo function. It displays them as follows:

{
        evmap any {1 0} > any {0 9}
        evmap xctl {1 0} 1 > xctl {0 9} 11
}

7.3.4 Transpose

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 rule transposes anything the filter generated on output ``{0 2}''. Note that the order of the rules is not important, the ``ftransp'' rule always applies to outgoing events.

7.3.5 Keyboard splits

In the same way it is possible to create a keyboard-split with two 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}

8 Time structure

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:

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.

8.1 Moving within the song

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.

8.2 Metronome

The metronome has the following states:

The ``m'' command changes the mode of the metronome, example:

m on            # switch the metronome on
p               # start playback
s               # stop

The metronome has two kind of click-sounds:

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.

8.3 Time signature changes

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.

8.4 Tempo changes

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

9 Tracks

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}'' pair. A track may contain MIDI data from any device and 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.

9.1 Recording a track without a filter

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).

9.2 Recording a track with a filter

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.

9.3 Basics about editing tracks

Most track editing functions use the project context, i.e. a set of ``current values'':

So editing tracks consists in modifying above parameters and issuing commands to process the current selection.

9.4 Copy, cut, insert

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

9.5 Track quantization

A track can be quantized by rounding event positions to the nearest exact position. The following will quantize the current selection of the current track by rounding event positions to the nearest quarter note.

setq 4          # quarter note = 1/4-th of a whole
tquanta 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 too regular especially on acoustic patches.

There are two quantization algorithms:

9.6 Checking a track

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.

10 Frames, more about filtering and editing

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.

10.1 Note frames

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.

10.2 Pitch bend frames

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.

10.3 Controller frames

The way controller events are packed in a frame depend on the controller type. Currently the following controllers are supported:

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
}

10.4 Other frames

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.

11 System exclusive messages

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}

12 Obtaining information

The following functions gives some information about midish objects:

ls              # summary
minfo           # list tempo and signature  changes
iinfo           # list config events of current input
oinfo           # list config events of current output
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 tracks list
print [ilist]   # print inputs list
print [olist]   # print outputs list
print [flist]   # print filters list
print [xlist]   # print sysex banks 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 input or output may 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.

13 Saving and loading songs

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 inputs, outputs, 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.

14 Import/export standard MIDI files

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:

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 output is exported as a track containing its 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"

15 Undo

Most operations may be undone with the ``u'' command, example:

tquant 90               # quantize current track
u                       # restore current track

The the ``ul'' command lists the previous command calls that may be undone.

Theres no way to redo operations that are undone.

16 The interpreter's language

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.

16.1 Global variables

Variables can be used to store numbers, strings and references to tracks, inputs, outputs 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.

16.2 Defining simple procedures

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 ("$").

If the last argument name is the "..." string, then a variable argument list could be used. In this case, in the code block between braces, the "..." token will is replaced by the variable list of arguments. For instance:

proc gmon ... {
        xnew gmon
	for devnum in ... {
	        xadd $devnum { 0xF0 0x7E 0x7F 0x09 0x01 0xF7 }
	}
}

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.

17 Changes since last release

  • Allow fmap and tevmap to change events types, for instance to map pitch-bend to a controller.
  • Increase max number of events to handle very large MIDI files.
  • Swap parameters of the xpc event: now bank number comes before the program number.
  • Delete the rmidish utility as midish has a built-in line editor.

    18 Project attributes

    18.1 Device attributes

    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
    iev list of compound event types the device transmits; it's a subset of ``xpc'', ``nrpn'', ``rpn''.
    oev list of compound event types the device accepts; it's a subset of ``xpc'', ``nrpn'', ``rpn''.

    18.2 Input and output attributes

    The following table summarizes the input or output attributes:

    attribute description
    name identifier used to reference the input or output
    {dev chan} device and MIDI channel where events are sent to or received to
    conf events that are sent when performance mode is entered

    18.3 Filter attributes

    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

    18.4 Track attributes

    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.

    18.5 Sysex attributes

    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.

    18.6 Song attributes

    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. 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. 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
    tap start mode one of ``off'', ``start'' or ``tempo''. See ``tap'' command.
    tap start events Events used to trigger start and/or measure tempo.

    19 Language reference

    19.1 Lexical structure

    The input line is split into tokens:

    Multiple lines ending with ``\'' are parsed as a single line. Anything else generates a ``bad token'' error.

    19.2 Statements

    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:

    19.3 Expressions

    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}}
    

    19.4 Procedure definition

    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
    }
    

    20 Function reference

    20.1 Track functions

    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
    tquanta rate
    Round event positions to the nearest exact position, as defined by the see setq function. This affects events of the current selection of the current track. Event 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.
    tquantf rate
    Round frame positions (eg. note positions) to the nearest exact position, as defined by the see setq function. Frame length (eg. note lengths) is preserved. 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.
    tvcurve weight
    Adjust velocity of note events, using the given ``weight'' in the -63..63 range. Applies only to note events of current selection of the current track, (see ev function).
    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 outputs 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
    tdump
    Display selected events of the current track.

    20.2 Input functions

    inew name {dev chan}
    create an new input with th given name and assigned the given device and MIDI channel.
    iset {dev chan}
    set the ``{device channel}'' pair of the current input. All filters are updated to use the new setting as if the appropriate fchin function was invoked for each filter.
    idel
    delete current input.
    iren newname
    rename the current input to ``newname''
    iexists name
    return 1 if ``name'' is an input, 0 otherwise
    igetc
    return the MIDI channel number of the current input
    igetd name
    return the device number of the current input
    iaddev event
    add the event to the configuration of the current input, it's not used yet.
    irmev evspec
    remove all events matching ``evspec'' (see event ranges) from the configuration of the current input
    iinfo
    print all events on the config of the current input.

    20.3 Output functions

    onew name {dev chan}
    create an new output with the given name and assigned the given device and MIDI channel numbers. Outputs have a filter with the same name; initially it maps all inputs to the newly created output.
    oset {dev chan}
    set the ``{device channel}'' pair of the current output. All filters are updated to use the new setting as if the appropriate fchout function was invoked for each filter.
    odel
    delete current output.
    oren newname
    rename the current output to ``newname''
    iexists name
    return 1 if ``name'' is an output, 0 otherwise
    ogetc
    return the MIDI channel number of the current output
    ogetd name
    return the device number of the output.
    oaddev event
    add given event to the configuration of the current output.
    ormev evspec
    remove all events matching ``evspec'' (see event ranges) from the configuration of the current output.
    oinfo
    print all events of the config of the current output.

    20.4 Filter functions

    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

    20.5 System exclusive messages functions

    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.
    ximport devnum path
    replace contents of the current sysex bank by contents of the given .syx file; messages are assigned to ``devnum'' device number.
    xexport path
    store contents of the current sysex bank in the given .syx file

    20.6 Real-time functions

    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.

    20.7 Song functions

    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 tquanta and tquantf functions 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.
    loop
    Enable loop mode. When playback or recording is started, the current selection is repeated forever.
    noloop
    Disable loop mode.
    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 name
    set the current (named) input. All input editing functions will act on it.
    geti
    return the name of the current input or ``nil'' if none
    co channame
    set the current (named) output. All output editing functions will act on it.
    geto
    return the name of the current output 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, inputs, outputs, 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.
    u
    undo last operation saved for undo.
    ul
    list operations saved for undo.

    20.8 Device functions

    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).
    diev devnum list
    Configure the device to process as a single event the following patterns of input MIDI messages.
    • ``xpc'' - group bank select controllers (0 and 32) with program changes into a signle ``xpc'' event.
    • ``nrpn'' - group NRPN controllers (98 and 99) with data entry controllers (6 and 38) into a single ``nrpn'' event.
    • ``rpn'' - same as ``nrpn'', but for RPN controllers (100 and 101).
    By default all of the above are enabled, which allows banks, NRPNs and RPNs to be handled by midish the standard way. It makes sense to disable grouping of above messages on rare hardware that maps above-mentioned controller numbers (0, 6, 32, 38, 98, 99, 100, 101) to other parameters than bank number and NRPN/RPN.
    doev devnum list
    Same as diev but for output MIDI messages.

    20.9 Event functions

    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
    evpat name sysex_pattern
    Define a new event type corresponding to the given system exclusive message pattern. The pattern is a list of bytes or event parameter identifiers (aka atoms). The following atoms are supported: v0, v0_lo, v0_hi, v1, v1_lo, v1_hi. They correspond to the full 7-bit value (coarse parameter), the low 7-bit nibble and the high 7-bit nibble (fine grained parameters) of the first and second parameters respectively. Example:
    evpat master {0xf0 0x7f 0x7f 0x04 0x01 v0_lo v0_hi 0xf7}
    
    defines a new event type for the standard master volume system exclusive message.
    evinfo
    Print the list of event patterns.

    20.10 Misc. functions

    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
    tap mode
    Set the way start is triggered by events. The following modes are available:
    • ``off'' - no events trigger start
    • ``start'' - a single event triggers start
    • ``tempo'' - two events trigger start; the time between them corresponds to one beat and is used to determine the initial tempo.
    tapev evspec
    Events set used to trigger start when tap mode is ``start'' or ``tempo''.
    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
    version
    Display midish version.
    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.

    21 Using midish in other programs

    21.1 Creating scripts: batch mode

    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.

    21.2 Creating front-ends: verbose mode

    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:

    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.

    22 Examples

    22.1 Example - MIDI filtering

    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).

    22.2 Example - recording a track

    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
    [0000:00]> onew drums {0 9}                  # create drum output
    [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]> tquanta 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 to ``{1 0}'' (the keyboard). Then, we define the ``drum'' output as 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.

    23 Copyright

    Copyright (c) 2003-2010 Alexandre Ratchov <alex@caoua.org>
    Copyright (c) 2008 Willem van Engen <wvengen@stack.nl>

    Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

    24 Thanks

    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-2019 Alexandre Ratchov
    Last updated jun 5, 2019