An omkamra.sequencer target for MIDI applications.
This target cannot be used on its own: its main purpose is to provide a set of common helpers for higher level targets which want to interface with actual MIDI devices.
It provides the following features:
- a dictionary of scales:
:chroma
,:major
,:minor
, etc. - MIDI-specific pattern expressions:
:note
,:degree
,:bank
,:program
,:cc
,:channel-pressure
,:pitch-bend
,:mod-wheel
,:volume
,:balance
,:pan
,:all-sounds-off
,:all-notes-off
- MIDI-specific bind expressions:
:degree->key
- MIDI-specific binding keys:
:channel
,:root
,:scale
,:vel
,:oct
,:mode
,:semi
,:dur
These elements make it possible to encode musical phrases as pattern expressions:
[:bind {:root :f#3
:scale :major
:channel 0
:vel 100
:dur 2
:step 1/2}
[:program 33]
(for [i (range 7)]
[:seq
[:degree i]
[:bind {:oct [:add 1] :vel [:sub 30]} [:degree i]]])
[:degree 7]
[:wait 4]
[:all-sounds-off]]
This pattern would play the seven degrees of the F# major scale starting in octave 3 on MIDI channel 0 using program 33, followed by the root note of the scale one octave higher.
Each note has a duration of 2 beats, i.e. the note-off
MIDI message
is scheduled two beats after the note-on
. The :step
value is 1/2,
this means each note will advance the pattern offset by 1/2 beats.
Each of the seven scale degrees is played twice: first without transposition, then one octave higher and with a velocity decreased by 30.
After the scale and the finishing note the pattern waits 4 steps -
this equals 2 beats as the value of the :step
binding is 1/2 - and
finally turns all sounds off.
To ease the encoding of musical phrases, this target provides a compiler for a text-based music description language which can be embedded into patterns as Clojure strings.
The language provides the following building blocks:
Example input | Resulting pattern expression | Effect |
---|---|---|
b2 |
[:bank 2] |
set bank of current channel to 2 |
p4 |
[:program 4] |
set program of current channel to 4 |
m60 |
[:note 60] |
play MIDI note 60 |
c-5 |
[:note 60] |
play MIDI note 60 |
c#5 |
[:note 61] |
play MIDI note 61 |
b-4 |
[:note 59] |
play MIDI note 59 |
3 |
[:degree 3] |
play 3rd degree of current scale |
-5 |
[:degree -5] |
play 2nd degree of current scale one octave lower (assuming a scale of 7 notes) |
, |
[:wait 1] |
wait 1 step |
,4 |
[:wait 4] |
wait 4 steps |
,1/2 |
[:wait 1/2] |
wait half step |
,/3 |
[:wait 1/3] |
wait 1/3 step |
%4 |
[:wait -4] |
wait until pattern offset reaches next multiple of 4 steps |
These building blocks can be aggregated into larger units using the following syntax:
Example input | Resulting pattern expression | Effect |
---|---|---|
(0 2 4) |
[:seq [:degree 0] [:degree 2] [:degree 4]] |
play the three degrees in succession |
{0 2 4} |
[:mix1 [:degree 0] [:degree 2] [:degree 4]] |
play the three degrees in parallel, advance the pattern by the length of the first one |
Both simple building blocks and aggregates can be ornamented with various binding modifiers:
Example input | Resulting pattern expression | Effect |
---|---|---|
2c5 |
[:bind {:channel 5} [:degree 2]] |
play degree 2 on MIDI channel 5 |
{3 4}~2 |
[:bind {:dur 2} [:mix1 [:degree 3] [:degree 4]]] |
play degrees 3 and 4 simultaneously with duration 2 |
1~ |
[:bind {:dur nil} [:degree 1]] |
play degree 1 with no duration (i.e. without a note-off) |
0./4 |
[:bind {:step [:mul 1/4]} [:degree 0] |
play degree 0 and advance the pattern offset by 1/4th of the current step |
(5^ 5)c5 |
[:bind {:channel 5} [:seq [:bind {:oct [:add 1]} [:degree 5]] [:degree 5]]] |
play degree 5 first one octave higher, then without transposition on channel 5 |
(5_3 5)v70 |
[:bind {:vel 70} [:seq [:bind {:oct [:sub 3]} [:degree 5]] [:degree 5]]] |
play degree 5 first three octaves lower, then without transposition with velocity 70 |
6v-30 |
[:bind {:vel [:sub 30]} [:degree 6]] |
play degree 6 with velocity decreased by 30 |
0#1 |
[:bind {:semi [:add 1]} [:degree 0]] |
play degree 0 raised by one semitone |
0b2 |
[:bind {:semi [:sub 1]} [:degree 0]] |
play degree 0 lowered by two semitones |
{0 2 4}&(minor) |
[:bind {:scale :minor} [:mix1 [:degree 0] [:degree 2] [:degree 4]]] |
play a minor triad |
{0 2 4}>2 |
[:bind {:mode 2} [:mix1 [:degree 0] [:degree 2] [:degree 4]]] |
play a triad in mode 2 (Phrygian if we are in a major scale) |
2@3 |
[:bind {:root [:degree->key 3]} [:degree 2]]] |
play the 2nd degree of the scale starting at the 3rd degree of the current scale |
Aggregate modifiers can be placed either after the aggregate (as shown in the examples above) or interspersed with the aggregate elements:
Alternative 1 | Alternative 2 | Alternative 3 |
---|---|---|
(0 2 4)c3 |
(c3 0 2 4) |
(0 c3 2 4) |
{0 2 4}.2~3 |
{0 2 4 .2 ~3} |
{0 2 .2 4}~3 |
The location of the modifiers does not matter: they are all collected
and put into the map of the :bind
expression which encloses the
group.
Pattern expressions parsed from a string are always wrapped in a
:seq
form, so there is no need to use parentheses around the
top-level group.
Copyright © 2021 Balázs Ruzsa
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.