Notes and MIDI events
Notes and names
A common approach to composition in isobar-ext is building up patterns of notes, which correspond to the standard MIDI note range (0..127, with 60 = middle C) and velocity range (0..127).
timeline.schedule({
"note": iso.PSequence([ 60, 64, 67, 72 ], 1),
"amplitude": 64
})
To get a note from its string representation, use the note_name_to_midi_note
utility function:
>>> print(iso.note_name_to_midi_note("G#2"))
32
Note events support the following properties:
property | type | description |
---|---|---|
note | int | MIDI note value |
amplitude | int | MIDI note velocity |
duration | float | Interval between notes (seconds) |
gate | float | Proportion of note to sustain (where 1.0 = legato) |
key | Key | Key for degree lookup |
scale | Scale | Scale for degree lookup |
degree | int | Degree within key/scale (cannot be used if note is specified) |
transpose | int | MIDI note transpose |
octave | int | MIDI note octave transpose |
active | int | If false, skips the note |
Duration and gate
The duration
specifies the interval between one note and the next. The gate
key specifies what proportion of this interval the note will be sustained for (and defaults to 1.0, for seamless legato).
For instance:
#--------------------------------------------------------------------------------
# Play a note every beat, sustained for half a beat
#--------------------------------------------------------------------------------
timeline.schedule({
"note": 60
"duration": 1,
"gate": 0.5
})
The gate
can also be greater than 1, to hold a note down as the next note begins:
#--------------------------------------------------------------------------------
# Play a sustained, overlapping chord, with each note lasting for 4 beats
#--------------------------------------------------------------------------------
timeline.schedule({
"note": iso.PSequence([ 60, 64, 67, 72 ], 1),
"duration": 0.5,
"gate": 8
})
Keys and Scales
isobar has builtin objects representing musical keys and scales.
- Scale encapsulates an ordered set of semitones. Named scales are defined corresponding to commonly-used scales (
major
,minor
,chromatic
,aeolian
, etc). - Key encapsulates a Scale with a specified tonic.
Warning
isobar currently only supports equal temperament tuning.
Keys and scales can be used in events by using the degree
key to look up a specific degree within the key/scale. transpose
and octave
can be used to transpose by semitones and octaves respectively.
#--------------------------------------------------------------------------------
# Play an F minor arpeggio
#--------------------------------------------------------------------------------
timeline.schedule({
"degree": iso.PSequence([ 0, 2, 4, 7 ], 1),
"key": iso.Key("F", "minor"),
"octave": 4
})
Chords
Chords can be specified by passing tuples of notes to the note
or degree
property.
#--------------------------------------------------------------------------------
# Play a series of 3-note chords.
#--------------------------------------------------------------------------------
timeline.schedule({
"degree": iso.PSequence([
(0, 1, 4),
(1, 4, 6),
(3, 4, 7),
(-1, 0, 4),
]),
"octave": 4,
"duration": 2
})
You can also specify different amplitudes or gates for notes of a chord by passing tuples of the same length:
timeline.schedule({
"note": iso.PSequence([
(60, 62, 67),
]),
"gate": iso.PSequence([
(0.25, 0.5, 1)
]),
"amplitude": iso.PSequence([
(96, 64, 32),
(64, 96, 32),
(64, 32, 96),
]),
"duration": 0.25
})
Rests
Use None
to specify a rest:
timeline.schedule({
"note": iso.PSequence([ 60, 60, None, 60, None, 60, 60, None ]),
"duration": 0.25
})
Many Pattern classes can operate explicitly on rests, or introduce rests.
For example:
PCollapse
takes an input and steps past any rests to remove gapsPSkipIf
replaces notes with rests given a conditionalPSkip
replaces notes with rests randomly given a probabilityPPad
pads a sequence with rests until it reaches a specified length