Miscellaneous — mididings documentation

# Miscellaneous

## Event Types

Every event has one of the following types:

Symbolic constant | MIDI event type
--- | ---
`NOTEON` | Note-on event
`NOTEOFF` | Note-off event
`CTRL` | Control change event
`PROGRAM` | Program change event
`PITCHBEND` | Pitchbend event
`AFTERTOUCH` | Channel aftertouch event
`POLY_AFTERTOUCH` | Polyphonic aftertouch event
`SYSEX` | System exclusive event
`SYSCM_QFRAME` | MTC quarter frame
`SYSCM_SONGPOS` | Song position pointer
`SYSCM_SONGSEL` | Song select
`SYSCM_TUNEREQ` | Tune request
`SYSRT_CLOCK` | Timing clock
`SYSRT_START` | Start sequence
`SYSRT_CONTINUE` | Continue sequence
`SYSRT_STOP` | Stop sequence
`SYSRT_SENSING` | Active sensing
`SYSRT_RESET` | System reset

For use in filters, the following constants are also defined:

Symbolic constant | Matched MIDI event types
--- | ---
`NOTE` | Any note event (`NOTEON` | `NOTEOFF`)
`SYSCM` | Any system common event (`SYSCM_QFRAME` | `SYSCM_SONGPOS` | `SYSCM_SONGSEL` | `SYSCM_TUNEREQ`)
`SYSRT` | Any system realtime event (`SYSRT_CLOCK` | `SYSRT_START` | `SYSRT_CONTINUE` | `SYSRT_STOP` | `SYSRT_SENSING` | `SYSRT_RESET`)
`SYSTEM` | Any system (non-channel) event (`SYSEX` | `SYSCM` | `SYSRT`)
`NONE` | No event
`ANY` | Any event

Event types are bit masks, so when building filters, they can be combined using operators `|` (bitwise or) and `~` (bitwise negation).

## Event Attributes

These constants are used by Generators and the `SceneSwitch()` unit to refer to an event’s data attributes:

Symbolic constant
`EVENT_PORT`
`EVENT_CHANNEL`
`EVENT_DATA1`
`EVENT_DATA2`
`EVENT_NOTE`
`EVENT_VELOCITY`
`EVENT_CTRL`
`EVENT_VALUE`
`EVENT_PROGRAM`

## Note Names and Ranges

Many mididings units accept notes and/or note ranges as parameters. Notes can be specified either as a MIDI note number or by their name, consisting of one letter, optionally `'b'` or `'#'`, and an octave number. Examples of valid note names are `'c3'`, `'g#4'`, or `'eb2'`.

Note ranges can be specified either as a 2-tuple of note numbers or names, e.g. `(48, 60)` or `('c2', 'c3')`, or as two note names separated by a colon, e.g. `'g#3:c6'`.

Like all ranges in Python, note ranges are semi-open (do not include their upper limit), so `'g#3:c6'` matches notes from g#3 up to b5, but not c6. It’s also possible to omit either the upper or the lower limit, for example `'c4:'` matches all notes above (and including) c4, while `':a2'` matches all note up to (but not including) a2.

## Port Names

Internally, ports are always referred to by their number. For instance, when an event is received on the second input port, and is not explicitly routed to another port, it will be sent to the second output port, regardless of port names.

If you named your input and output ports using the `in_ports` and `out_ports` parameters to `config()`, you can also refer to them by their names in all units that accept ports as parameters.

To avoid ambiguities, port names should be unique (with the JACK backend they must be).

## mididings and Python

mididings is a little peculiar in the way it uses Python, so here’s a couple of things you might need to know…

### Overloaded Functions

Python does not support overloaded functions per se, but mididings implements a mechanism that allows functions to have behave differently depending not only on the number of arguments, but also on the parameters’ names.

For example, while `Velocity(offset=32)` increases MIDI note velocities by 32, `Velocity(fixed=32)` changes velocities to a fixed value of 32.

When multiple versions of a function accept the same number of parameters, it’s necessary to explicitly name the parameters of the version you’d like to use. Cases where parameter names are mandatory are indicated in this documentation by an equal sign followed by an ellipsis, as in Velocity(fixed=…). Most functions also have a “default” version that does not require specifying the name of any parameter. In mididings it is usually possible to explicitly name parameters though, regardless of whether it’s actually required for a particular function invocation.

Other than that, the documentation follows common Python conventions for the notation of positional arguments, variable arguments and keyword arguments.

### Everything is an Object

Everything in mididings is a Python object, and can be assigned to variables, returned from functions, etc.:

    # add a fifth (7 semitones) above each note, and route all events
    # to channel 2 (of course there are easier ways to do this)
    def add_interval(n):
        return Pass() // Transpose(n)

    route = Channel(2)
    mypatch = add_interval(7) >> route

    run(mypatch)

### Line Break and Indentation

As in any other Python code, line breaks delimit statements, and indentation delimits blocks. However, as a rule of thumb, both line breaks and indentation are irrelevant as long as at least one parenthesis or bracket remains open.

Many mididings patches consist of lists and dictionaries, so line breaks and indentation may not be an issue, but often it is convenient to put patches in parentheses in order to allow them to span multiple lines:

    mypatch = (
        Filter(~PROGRAM) >> Print("this line is getting kind of long") >>
        Transpose(12) >> Velocity(curve=1.0)
    )

### Operator Precedence

Overloading operators in Python can not change their precedence. This is a list of all operators relevant to mididings, in order of their precedence (highest to lowest):

Operator | Meaning in mididings
--- | ---
(…) [A, B, …] | Binding (parentheses) Connection in parallel (list)
~F -F +F | Filter inversion Filter negation Apply to duplicate
A // B S % A | Connection in parallel Selector “then”
A >> B | Connection in series
S1 & S2 | Selector “and”
S1 | S2 | Selector “or”

In short, remember that…

- parallel connection binds stronger than serial.

- selectors (of more than one filter) must be in parentheses.

- when in doubt, you can always use additional parentheses.

Note

Operator overloading doesn’t apply if both sides of the operator are builtin Python types like `list` or `dict`. In some cases it may be necessary to wrap those in `Fork()` or `Split()`.