midish 0.2 - tutorial

# Table of contents

- Introduction
- Devices setup
- Channels
- Filtering/routing
- Time structure
- Tracks
- System exclusive messages
- Obtaining information
- Saving and loading songs
- Import/export standard MIDI files
- The interpreter's language
- Changes
- Project attributes
- Events and event ranges specification
- Language reference
- Function reference
- Sample midishrc-file
- Example sessions

# Introduction

## 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:

- multiple MIDI devices handling
- synchronisation to external midi devices
- real-time MIDI filtering/routing (controller mapping, keyboard splitting, ...)
- track recording, metronome
- basic track editing (insert, copy, delete, ...)
- track quantisation
- import and export of standard MIDI files
- tempo and time-signature changes
- system exclusive messages handling

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

## Installation

Requirements:

- a midi sound module
- a midi keyboard
- a POSIX unix-like operating system with "raw" midi support. Midish works on OpenBSD (its development platform) and Linux.
- the readline(3) library

(without any midi-devices Midish will be probably useless).

To install midish:

1. Untar and gunzip the tar-ball:

               gunzip midish-0.2.tar.gz
               tar -xf midish-0.2.tar
               cd midish-0.2

2. Edit Makefile and change the readline(3) options in Makefile
3. Compile midish, just type '`make all`', this will build midish and rmidish, the readline(3) front-end to midish.
4. Install binaries, documentation and examples by typing '`make install`'. They are copied as follows:
   - binaries in '`PREFIX/bin`'
   - manual pages in '`PREFIX/man/man1`'
   - html documentation in '`PREFIX/share/doc/midish`'
   - example files in '`PREFIX/share/examples/midish`' where '`PREFIX`' is by default '`/usr/local`'. To change this, set the '`PREFIX`' variable in '`Makefile`', for instance:

                 make PREFIX=$HOME install

5. If there isn't a `/etc/midishrc` file, then copy the sample file by typing '`cp midishrc /etc`' in your shell.
6. Read the documentation and modify `/etc/midishrc` in order to choose the default midi device by using the '`devattach`' function (example: '`devattach 0 "/dev/rmidi3"`', see next section)

## 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:

            rmidish

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

## How does it work

Midish uses the following objects to represent a project:

- Midi devices:
  these are actual midi devices (keyboards, sound modules, external sequencers...) from which events are received and/or to which they are sent.
- Filters:
  a filter is a set of rules that says which midi event to discard and how to transform incoming events. The filter also "sanitises" the input midi stream by removing nested notes, duplicate controllers and other anomalies.
- Channels:
  a channel is an a output {device midi-channel} pair that represent an instrument. A channel holds some properties like patch number, volume, reverb depth or other controllers.
- Tracks:
  tracks represent pieces of music: they hold midi voice events (notes, controllers ...). Tracks aren't bound to a particular channel and can contain events from any channel.
- Sysex banks:
  a sysex bank is a set of system exclusive messages. They will be sent to midi devices when performance mode is entered.
- The "meta-track":
  it is a particular hidden track that contains only special events like tempo changes and time signature changes.
- Song parameters:
  these are miscellaneous parameters like the metronome configuration, the current selection...

The above objects are grouped in a ''project'' (a song) and manipulated in prompt mode by issuing interactively commands.

Performance mode is used to play/record the project. When performance mode is entered, midi devices are opened, and all sysex messages and channel configuration events are sent to them. There are three performance modes:

- "Idle" mode
  The midi input passes through the current filter and the result is sent to the midi output. No tracks are played or recorded.

              +---------+             +------------+          +----------+
              |         |             |            |          |          |
              | midi in |------------>|   filter   |--------->| midi out |
              |         |             |            |          |          |
              +---------+             +------------+          +----------+

- Play mode
  The midi input passes through the current filter the result is mixed with the currently played tracks and finally sent to the midi output. No tracks are recorded.

                                      +--------------+
                                      | track_1 play |---\
                                      +--------------+   |
                                                         |
                                           ...        ---+
                                                         |
                                      +--------------+   |
                                      | track_N play |---+
                                      +--------------+   |
                                                         |
              +---------+             +------------+     |    +----------+
              |         |             |            |     \--->|          |
              | midi in |------------>|   filter   |--------->| midi out |
              |         |             |            |          |          |
              +---------+             +------------+          +----------+

- Record mode
  The midi input passes through the current filter and is recorded to the current track. The result is mixed with the currently played tracks and finally sent to the midi output. System exclusive messages are recorded to the current sysex without being passed through the filter.

                                      +--------------+
                                      | track_1 play |---\
                                      +--------------+   |
                                                         |
                                           ...        ---+
                                                         |
                                      +--------------+   |
                                      | track_N play |---+
                                      +--------------+   |
                                                         |
              +---------+             +------------+     |    +----------+
              |         |             |            |     \--->|          |
              | midi in |-----+------>|   filter   |----+---->| midi out |
              |         |     |       |            |    |     |          |
              +---------+     |       +------------+    |     +----------+
                              |                         |
                              |                         |     +----------------+
                              |       +--------------+  \---->| track_X record |
                              \------>| sysex record |        +----------------+
                                      +--------------+

The above performance modes are started respectively with `songidle`, `songplay`, `songrecord`. Performance mode is blocking (no commands can be issued on the prompt). To return again to prompt mode, send a interrupt signal (SIGINT) to midish (hit control-C on the console).

## An example

Suppose that there are two devices:

- `/dev/rmidi4` - a midi sound module
- `/dev/rmidi3` - a midi keyboard

Thus, the `/etc/midishrc` file contains the following lines:

    	devattach 0 "/dev/rmidi4"	# attach the module as dev number 0
    	devattach 1 "/dev/rmidi3"	# 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 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.

# Devices setup

In midish, midi-devices are numbered from 0 to 15. Each midi-device has its "slot 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:

            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.

# Channels

Because midish handles multiple devices, instead of using midi-channels, it uses device/midi-channel pairs to represent instruments. So channel refers the device/midi-channel pair. Channels are handled by two-item lists, like this:

            {0 1}                   # device 0, midi-channel 1

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

## Channel configuration

A channel represents one musical instrument; midish allows to attach to the channel definition basic "configuration" events like program changes and controllers. Such events are sent to the output when performance mode is entered, for instance just before playback is started. This approach avoids flooding midi devices with "slow" events (like program changes).

For instance; to select patch 34 on channel '`mybass`', attach a "program change" event:

            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 }

# Filtering/routing

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:

- route events from a device/channel to another
- map a controller to another
- split the keyboard
- drop unwanted events
- etc...

## About filters

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:

1. First, input events are checked for inconsistencies: nested note-on, orphaned note-off and duplicate controller, bender and aftertouch events are removed. Zero-length notes are lengthened to one "tic". The rate of controller, bender and aftertouch events is normalised in order not to flood output devices.
2. The input event is checked against all voice rules and the resulting events (if any) are sent to the output of the filter (if the event matches more than one voice rule then it is duplicated if needed).
3. If the input didn't match any voice rule then it is checked against all channel rules and the resulting events (if any) are sent to the output of the filter. (if the event matches more than one channel rule then it is duplicated if needed).
4. If the input event didn't match any channel rule then it is checked against all device rules and the resulting events (if any) are sent to the output of the filter (if several device rules match the input event then it is duplicated if needed).
5. Finally, if the input event didn't match any device rule then it is passed to the output of the filter as-is.

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

## Examples

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

            filtnew mydevmap                # define filter 'mydevmap'
            filtdevmap mydevmap 1 0         # make it route device 1 -> device 0

To test the filter, start performance mode:

            songidle

(hit control-C to stop performance mode).

### Channel maps

The following example defines a filter that routes events from device 1, channel 0 (first channel of the keyboard) to device 0, channel 9 (default drum channel of the sound module).

            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.

### Controller maps

The following example add a new rule to the above filter that maps the modulation wheel (controller 1) of the source channel (ie device 1, channel 0) to the expression controller (number 11) of the destination channel (device 0, channel 9).

            filtctlmap mydrums {1 0} {0 9} 1 11

the 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 mydrums

which will print:

            {
                    chanmap {1 0} {0 9}
                    ctlmap {1 0} {0 9} 1 11 id
            }

### Transpose

The following example transposes by 12 half-tones (one octave) notes on device 1, channel 0 (keyboard) and sends them on device 0 channel 2 (sound-module).

            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 12

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

### Keyboard splits

In the same way it is possible to create a keyboard-split with two key-rules and two channel-rules. The following example splits the keyboard in two parts (left and right) on note 64 (note E3, the middle of the keyboard). Notes on the left part will be routed to channel 3 of the sound module and notes on the right part will be routed to channel 2 of the sound module.

            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.

# Time structure

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:

- 24 tics per beat
- 4 beats per measure
- 120 beats per minute

From the musical point of view, a beat often corresponds to a quarter note, to an eight note etc... By default an unit note corresponds to 96 tics, thus by default one beat corresponds to one quarter note, i.e. the time signature is 4/4.

## Metronome

In order to "hear" time, a metronome can be used. It is used only in play and record modes. It produces a click sound on every beat. To start the metronome:

            metroswitch 1           # switch the metronome on
            songplay                # start playback

The metronome has two kind of click-sound:

- high-click: on the first beat of the measure
- low-click: on the other beats.

The click-sound can be configured by giving a couple of note-on events, as follows:

            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.

## Time signature changes

Time signature changes are achieved by inserting or deleting measures. The following starts a song with time signature of 4/4 (at measure 0) and change the time signature to 6/8 at measure 2 during 5 measures:

            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 it

To 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

## Tempo changes

Tempo changes are achieved simply by giving the measure number and the tempo value 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.

            songsettempo 0 100
            songsettempo 2 180

## Moving within the song

The following selects the current position in the song to measure number 3:

            songsetcurpos 3

This will make `songrecord` and `songplay` start at this particular position instead of measure number 0.

# 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; 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.

## Recording a track without a filter

The following defines a track and record events as-is from all midi devices:

            tracknew mytrack
            songrecord

tracks are played as follows:

            songplay

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

## Recording a track with a filter

The following creates a filter and uses it to record to the above track:

            filtnew mypiano
            filtchanmap mypiano {1 0} {0 0}         # dev1/chan0 -> dev0/chan0
            tracknew mytrack
            songrecord

## Basic editing of a track

Most track editing functions in midish take at least the following arguments:

- start measure
- number of measures
- precision/quantum (in tics).

For instance to blank measure number 3 of track `mypiano`:

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

## Track quantisation

A track can be quantised by rounding note-positions to the nearest exact position. The following will quantise 4 measures starting at measure number 3 by rounding notes to the nearest quarter note.

            trackquant mypiano 3 4 (96 / 4) 75

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

## 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:

            trackcheck mytrack

This function can be useful to remove nested notes when a track is recorded twice (or more) without being erased.

# 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 the midi devices. System exclusive ('sysex') messages are stored into named banks. To create a sysex bank named `mybank`:

            sysexnew mybank

Then, 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:

            songidle

Sysex 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
            songrecord

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:

            sysexinfo mybank

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

# Obtaining information

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.

# Saving and loading songs

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.

# 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:

- tempo changes
- time signature changes

all meta-events are removed from the "voice" tracks and are moved into the midish's meta-track. Finally tracks are checked for anomalies. Example:

            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"

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

## Global variables

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.

## Defining simple procedures

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 myfilt

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

# Changes

## Changes from release 0.1 to release 0.2

- added '`device`' parameter to '`sendraw`'
- removed '`filtchangein`' and added `filtchgich`, `filtchgidev`, `filtswapich`, `filtswapidev`, `filtchgoch`, `filtchgodev`, `filtswapoch`, `filtswapodev`.
- added new filter rules: `filtdevdrop`, `filtchandrop`, `filtctldrop` and `filtkeydrop`.
- added functions that remove existing rules: `filtnodevdrop`, `filtnodevmap`, `filtnochandrop`, `filtnochanmap`, `filtnoctldrop`, `filtnoctlmap`, `filtnokeydrop` and `filtnokeymap`.
- added support for external midi synchronisation, added `devsetmaster`, `devgetmaster` and `devsendrt`
- added `trackdelete`, `chandelete`, `filtdelete`
- added `trackrename`, `chanrename`, `filtrename`
- split `changetnum` into `changetdev` and `changetch`, added `chanset`
- added "event ranges", and an event range argument to `trackblank` and `trackcopy`
- removed support for single number channels.
- added `tracksetmute` and `trackgetmute`
- added support for system exclusive messages; added `sysexlist`, `sysexnew`, `sysexdelete`, `sysexrename`, `sysexexists`, `sysexinfo`, `sysexclear`, `sysexsetunit`, `sysexadd`, `songgetcursysex` and `songsetcursysex`.
- removed `tracksave` and `trackload`
- removed support for gnu readline(3) library from midish, and created the `rmidish` utility, a front-end to midish using the readline(3) library.
- added filters the ability to limit midi traffic (one controller, one bender, one aftertouch per tic)
- make the filter drop duplicate controllers, pitch bends and aftertouches
- added global and per filter current channel: `songgetcurchan`, `songsetcurchan`, `filtgetcurchan` and `filtsetcurchan`.
- added global and per channel current input: `songgetcurinput`, `songsetcurinput`, `changetcurinput` and `chansetcurinput`.
- added `trackchanlist` and improved `songinfo`
- added a midi file player and recorder: smfplay(1) and smfrec(1)

# Project attributes

## Device attributes

The following table summarises the device attributes:

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.

## Channel attributes

The following table summarises the channel attributes:

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.

## Filter attributes

The following table summarises the filter attributes:

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.

## Track attributes

The following table summarises the track attributes:

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.

## Sysex attributes

The following table summarises the sysex back attributes:

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.

## Song attributes

The following table summarises the song attributes:

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.

# Events and event ranges specification

## Event specification

Some functions take events as arguments. An event is specified as a list containing:

- a reference from the following list: `noff`, `non`, `kat`, `ctl`, `pc`, `cat`, `bend`.
- a channel
- a number
- a second number (not for `pc` and `cat`)

Event references correspond to the following midi events:

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

## Event ranges specification

Some track editing functions take an event range as argument. The event range is specified as a list containing:

- an optional reference from the following list: `any`, `note`, `ctl`, `pc`, `cat`, `bend`.
- an optional channel range specified as a list containing a device number range and a midi-channel number range. Number ranges are specified as two-number list like this `{12 34}`. Channel name matches an unique channel.
- an optional number range, specified as a list of two number like this `{12 65}`
- an optional second number range, in the same format as the first one (not permitted for `pc`, `bend` and `cat`)

In the above, empty lists can also be used. An empty list means "match everything".

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

# Language reference

## Lexical structure

The input line is split into tokens:

- Identifiers:
  an identifier is a sequence of up to 32 characters, digits and underscores ('`_`'). However, an identifier cannot start with a digit. Examples:

              mytrack _mytrack  my34_track23          # good
              123mytrack                              # bad, starts with digit
              mytrackabcdefghijklmnopqrstuvwxyz       # bad, to long

- Numbers
  numbers should be in decimal or hex format and may not exceed 2^31. Hex numbers are preceded by "0x", as in the C language. Examples:

  `123` | 123 in decimal
  `0x100` | 256 in hex
  `0xF0` | 240 in hex

- String constants
  string constants are sequence of printable characters enclosed in pairs of double quotes. Thus a string cannot contain double quotes, tabs, newlines.
- Keywords
  valid keywords are: `nil`, `let`, `if`, `else`, `for`, and `proc`.
- Operators, separators etc...
  sequences of meta-characters like '`(`', '`)`', '`+`', '`$`', newline character, '`;`', etc...
- Comments
  comments are ignored, they start with '`#`' and end with the new line character.

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

## 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:

- A procedure call, example:

              myproc arg1 arg2

- An assignment using the `let` keyword, examples:

              let x = 123
              let y = 1 + 2 * (3 + x)

  The left-hand side should be the name of a variable and the right hand side an expression (see the expression section).

- An `if..else` statement, example:

              if $i {
                      print "i is not zero";
              } else {
                      print "i is zero";
              }

  the `else` and the second block are not mandatory. Note that since newline character is interpreted as a '`;`', the line cannot be broken in an arbitrary way. If the expression following the `if` keyword is true the first block is executed, otherwise the second one (if any) is executed. The expression is evaluated in the following way:

  non-zero integer | true
  zero integer | false
  non-empty list | true
  empty list | false
  any name | true
  nil | false

- A loop over a list:

              for i in { "bli" "bla" "blu" } {
                      print $i;
              }

  the block is executed for each value of the list to which '`$i`' is set.
- A return statement. The return statement ends the procedure execution and returns a value to the caller. Example:

              return $x * $x;

## Expressions

An expression can be an arithmetic expression of constants, expressions, variable values, return values of function calls. The following constant types are supported:

`"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) + $x

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

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

# Function reference

## Track functions

`tracklist`

> return the list of names of the tracks in the song example:

>             print [tracklist]

`tracknew trackname`

> create an empty track named `trackname`

`trackdelete 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 otherwise

`trackaddev trackname measure beat tic ev`

> put the event `ev` on track `trackname` at the position given by `measure`, `beat` and `tic`

`tracksetcurfilt 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 none

`trackcheck trackname`

> check the whole track for orphaned notes, nested notes and other anomalies; also removes multiple controllers in the same tic

`trackcut 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 `from`

`trackcopy 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`.

## Channel functions

`channew channelname { device midichan }`

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

`changetch channelname`

> return the midi channel number of channel named `channelname`

`changetdev channelname`

> return the device number of channel named `channelname`

`chanconfev 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`.

## Filter functions

`filtnew filtname`

> create an new filter named `filtname`

`filtdelete 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 otherwise

`filtreset filtname`

> remove all rules from the filter `filtname`

`filtinfo filtname`

> list the rules of the given filter

`filtsetcurchan filtname channame`

> set the default channel of filter `filtname` to `channame`.

`filtgetcurchan filtname`

> return the default channel of `filtname`, returns `nil` if none

`filtchgich filtname oldchan newchan`

> change the input channel of all rules with input channel equal to `oldchan` to `newchan`

`filtchgidev filtname olddev newdev`

> change the input device of all rules with input device equal to `olddev` to `newdev`

`filtswapich 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 `olddev`

`filtchgoch filtname oldchan newchan`

> change the output channel of all rules with output channel equal to `oldchan` to `newchan`

`filtchgodev filtname olddev newdev`

> change the output device of all rules with output device equal to `olddev` to `newdev`

`filtswapoch 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 `olddev`

`filtdevdrop filtname inputdev`

> make filter drop events from device `inputdev`

`filtnodevdrop filtname inputdev`

> remove `devdrop` rules that drop events from device `inputdev`

`filtdevmap 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 duplicated

`filtnodevmap filtname outputdev`

> remove `devmap` rules that route events to device number `outputdev`.

`filtchandrop filtname inputchan`

> make filter drop events from channel `inputchan`

`filtnochandrop filtname inputchan`

> remove `chandrop` rules that drop events from channel `inputchan`

`filtchanmap 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 duplicated

`filtnochanmap 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 duplicated

`filtnoctlmap 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 duplicated

`filtnokeymap filtname outchan keystart keyend`

> remove `keymap` rules that route note events in the range `keystart..keyend` to channel `outchan`.

## System exclusive messages functions

`sysexnew sysexname`

> create a new bank of sysex messages named `sysexname`

`sysexdelete 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 otherwise

`sysexclear 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 sent

`sysexinfo sysexname`

> print debug info about sysex bank `sysexname`

## Real-time functions

`songidle`

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

## Song functions

`songsetcurquant tics`

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

`songgetcurtrack`

> return the current track (if any) or `nil`

`songsetcurfilt filtname`

> set the current filter ie the one used (with songplay and songidle).

`songgetcurfilt`

> return the current filter or `'nil'` if none

`songsetcursysex sysexname`

> set the current sysex bank, ie the one that will be recorded

`songgetcursysex`

> return the current sysex bank or `'nil'` if none

`songsetcurchan channame`

> set the current (named) channel.

`songgetcurchan`

> return the name of the current channel or `'nil'` if none

`songsetcurinput {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 note

`songsettempo measure bpm`

> set the tempo to `bpm` beats per minute at measure `measure`

`songtimeins 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 changes

`songinfo`

> display some info about the default values of the song

`songsave 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 failed

`songreset`

> destroy completely the song, useful to start a new song without restarting the program

`songexportsmf 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.

## Device functions

`devlist`

> 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 `devnum`

`devsetmaster 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.

## Misc. functions

`metroswitch number`

> if `number` is equal to zero then the metronome is disabled else it is enabled

`metroconf eventhi eventlo`

> select the notes that the metronome plays. The pair of events must be note-ons

`info`

> display the list of built-in and user-defined procedures and global variables

`print expression`

> display the value of the expression

`exec 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:
> - `rmidi` - show raw midi traffic on stderr
> - `filt` - show events passing through the current filter
> - `song` - show start/stop events

`panic`

> cause the sequencer to core-dump

# Sample midishrc-file

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 input

`ctlmap ictl octl`

> make the current filter route controller number `ictl` on the current input to controller `octl` on the current channel

`transpose 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 track

`r`

> go in performance mode and record the current track using its current filter

`l`

> list tracks, channels and filter and print some additional info

`g measurenum`

> move the current song position to measure number `measurenum`. Play/record and all editing procedures that follow will start at that position

`sel 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 position

`copy 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 current

`unmute 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/denominator`

`timerm num`

> remove `num` measures from the meta-track.

`gmon devnum`

> send "general midi on" system exclusive message to device number `devnum`

`gmp patch`

> configures the current channel to use general midi patch number `patch`. (this will send program change event when performance mode is entered).

`vol value`

> set volume (controller number 7) of the current channel

`reverb value`

> set reverb (controller number 91) of the current channel

`chorus value`

> set chorus (controller number 93) of the current channel

# Example sessions

## Example - midi filtering

The following session show how to configure a keyboard split:

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

## Example - recording a track

The following session show how to record a track. It uses the procedures defined in the default `/etc/midishrc`.

    	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.

Copyright (c) 2003-2005 Alexandre Ratchov
Last updated oct 27, 2005