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