Creating and Using MIDI Patterns

In this example we will show how to group together a number of notes into a pattern.

Audio Setup

The first few lines are the same as the scheduling notes example: create an LV2 sampler, set its volume, and connect its stereo outputs to the system outputs.



local sampler = Lv2.Plugin("http://www.openavproductions.com/fabla", "fabla808")
local mainOutput = Audio.StereoOutput("main", "system:playback_1", "system:playback_2")
mainOutput.connect(sampler)


Also as seen in the scheduling notes example we will create our notes:



local kickNote = Midi.Note(36, 127, 1, 4)
local snareNote = Midi.Note(40, 127, 1, 4)


Adding Notes to a Pattern

Now instead of scheduling the notes directly on the sampler, we will create a pattern object and add the notes to the pattern.

First create a new, empty, MIDI pattern to hold a drum beat:



local simpleBeat = Midi.Pattern()


Now add the kick + snare to the pattern to define a simple beat with the snare on the 2nd and 4th beat and the kick on the 1st beat and the 2nd + 3rd offbeat:



simpleBeat.add(kickNote, 1, 0, 4)
simpleBeat.add(snareNote, 1, 1, 4)
simpleBeat.add(kickNote, 1, 3, 8) // 4th eight note position = 2nd beat offbeat
simpleBeat.add(kickNote, 1, 5, 8) // 6th eight note position = 3rd beat offbeat
simpleBeat.add(snareNote, 1, 3, 4)


Copying a Pattern

Now we want to create another version of this same pattern that has additional elements. We do this by copying the existing pattern with the clone keyword:



local fullBeat = clone simpleBeat


Now at this point the new pattern is a direct copy of the pattern from which it was created, we can go ahead and add addional notes to the new pattern.

Adding each note line by line sometimes makes sense but things can easily become cluttered, one way to save space is to add our notes within a for loop, for example adding a note on every eighth note:



for(local i = 0; i < 8; i++) {
    fullBeat.add(Midi.Note(47, 127, 1, 4), 1, i, 8)
}


Notice we have also saved a line of code by creating the note in place rather than predefining a variable as we did with "kickNote" and "snareNote", but it makes it less clear what the note represents (in this case, maracas).

So using loops and creating note inline can save us lines of code but it still doesn't make for very readable scripts.

One thing we can do to fix that is to use a shorthand notation for entering the notes. For example, one classic shorthand musical notation is ABC notation, ABC support means we can write music in ABC and then use a Midi.ABCReader to convert it into a Midi.Pattern.

Using Drum Tabs

Another shorthand notation for representing notes is drum tablature. The Midi.DrumTabReader converts a string containing drum tab into a MIDI pattern that we can use as any other pattern.

The first two characters of each line are a symbol representing the drum to be played or alternatively the MIDI pitch value for this line.

Following that are the measures which are divided by the number of characters in each, each character represents a duration of that division. A dash ("-") character represents a rest and other characters represent notes of that duration.

Here is a drum fill notated in drum tab and then converted into a MIDI pattern, notice the use of the @" notation to create a multi-line string:



local fillPattern = Midi.DrumTabReader().read(@"
40|o-o-o-o-oooooooo|
36|o-------o---o---|
")


In this tab we have one measure subdivided into sixteenth notes. The pattern will play 12 sixteenth notes of MIDI note 40 according to the pattern on the top line and 3 sixteenth notes of MIDI note 36 according to the bottom line.

Scheduling Patterns

Now we have made three patterns: a simple beat, a copy of the simple beat with added eighth notes, and a drum fill. But the patterns are just containers for the notes, we still have not scheduled anything to play, so the final step is to schedule the patterns to play on the sampler plugin.

We'll create a simple arrangement that starts with the simple beat and progresses to the full beat after 8 bars, with the fill every 8 bars, using the sampler's schedule method to schedule the patterns:



for(local measure = 1; measure <= 16; measure++) {
    if(measure % 8 == 0) {
        sampler.schedule(fillPattern, measure)
    }
    else if(measure <= 8) {
        sampler.schedule(simpleBeat, measure)
    }
    else {
        sampler.schedule(fullBeat, measure)
    }
}

Transport.Master(120)


We can schedule a pattern to play at any point by including a position and division within the measure, in the same way as we schedule MIDI notes. However these parameters are optional and if we are scheduling the pattern at the beginning of the measure then we can omit them as we have done above.



Complete Script

local sampler = Lv2.Plugin("http://www.openavproductions.com/fabla", "fabla808")
local mainOutput = Audio.StereoOutput("main", "system:playback_1", "system:playback_2")
mainOutput.connect(sampler)

local kickNote = Midi.Note(36, 127, 1, 4)
local snareNote = Midi.Note(40, 127, 1, 4)

local simpleBeat = Midi.Pattern()

simpleBeat.add(kickNote, 1, 0, 4)
simpleBeat.add(snareNote, 1, 1, 4)
simpleBeat.add(kickNote, 1, 3, 8) // 4th eight note position = 2nd beat offbeat
simpleBeat.add(kickNote, 1, 5, 8) // 6th eight note position = 3rd beat offbeat
simpleBeat.add(snareNote, 1, 3, 4)

local fullBeat = clone simpleBeat

for(local i = 0; i < 8; i++) {
    fullBeat.add(Midi.Note(47, 127, 1, 4), 1, i, 8)
}

local fillPattern = Midi.DrumTabReader().read(@"
40|o-o-o-o-oooooooo|
36|o-------o---o---|
")

for(local measure = 1; measure <= 16; measure++) {
    if(measure % 8 == 0) {
        sampler.schedule(fillPattern, measure)
    }
    else if(measure <= 8) {
        sampler.schedule(simpleBeat, measure)
    }
    else {
        sampler.schedule(fullBeat, measure)
    }
}

Transport.Master(120)


Tutorial IndexList of All Examples


Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.