# Mixing basic drum patterns

Note

Let's say that we have some basic drum patterns that we want to be able to combine freely at random combinations. For example,

where the note E means right hand, while A means left hand. These patterns can be easily combined to fill a bar, for example 5b -> 5b -> 3 -> 3 or 5a -> 4 -> 4 -> 3, etc. They can also be combined to fill two bars and so on. Notice that some sequences, like e.g. the 5a -> 4 -> 4 -> 3, result in alternating hands: each time the sequence is played the hand that "leads" is swapped. This will be important later on.

The goal is to be able to play arbitrary sequences of them for arbitrary lengths. How does one practice that? We will use random_notes_sequence to create longer 8-bar sequences faster with the help of Julia.

## Defining the Notes

We first have to define the Notes instances that will correspond to those four basic patterns.

using MusicManipulations, MusicVisualizations

tpq = 960 # ticks per quarter note
sixt = 240 # duration of sixteenth note
left = name_to_pitch("A5")
right = name_to_pitch("E6")
88

Reminder: Note(pitch, intensity, start, duration)

motif1 = [ # motif 5a
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(right, 100, 2sixt, sixt),
Note(left, 50, 3sixt, sixt),
Note(left, 50, 4sixt, sixt)
]

motif2 = [ # motif 5b
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(left, 50, 2sixt, sixt),
Note(right, 50, 3sixt, sixt),
Note(right, 50, 4sixt, sixt)
]

motif3 = [ # motif 3
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(left, 50, 2sixt, sixt),
]

motif4 = [ # motif 4
Note(right, 100, 0, sixt),
Note(left, 50, sixt, sixt),
Note(right, 50, 2sixt, sixt),
Note(right, 50, 3sixt, sixt),
]

motifs = Notes.([motif1, motif2, motif3, motif4], tpq)
4-element Vector{Notes{Note}}:
Notes{Note} with 5 notes
Notes{Note} with 5 notes
Notes{Note} with 3 notes
Notes{Note} with 4 notes

Now motifs stands for a pool of note sequences we can draw random samples from. Let's generate sequences that are 8-bars long (i.e. 32 quarter notes)

q = tpq*32

notes, seq = random_notes_sequence(motifs, q)
notes
128 Notes with tpq=960
Note E6  | vel = 100 | pos = 0, dur = 240
Note A5  | vel = 50  | pos = 240, dur = 240
Note A5  | vel = 50  | pos = 480, dur = 240
Note E6  | vel = 50  | pos = 720, dur = 240
Note E6  | vel = 50  | pos = 960, dur = 240
Note E6  | vel = 100 | pos = 1200, dur = 240
Note A5  | vel = 50  | pos = 1440, dur = 240
⋮
Note A5  | vel = 50  | pos = 29040, dur = 240
Note A5  | vel = 50  | pos = 29280, dur = 240
Note E6  | vel = 50  | pos = 29520, dur = 240
Note E6  | vel = 50  | pos = 29760, dur = 240
Note E6  | vel = 100 | pos = 30000, dur = 240
Note A5  | vel = 50  | pos = 30240, dur = 240
Note A5  | vel = 50  | pos = 30480, dur = 240

and now we can write these to a MIDI file simply by doing save("drums_patterns.mid", notes) if we want to. We can also use MuseScore, to visualize and print the result. The function musescore provides this interface.

musescore("drums_patterns.png", notes)

this is a pre-made figure - your random sequence will probably differ

This worked nicely, but there is a problem: The sequence does not respect the fact that some specific patterns (5b and 4) swap the leading hand. This is what we tackle in the next section.

## Adding alternating hands and Lyrics

Notice that random_note_sequence also returns the indices of the motifs that were used to create the sequence:

seq
31-element Vector{Int64}:
2
2
1
1
3
4
1
2
1
2
⋮
1
3
3
3
3
2
4
2
3

We can use this information to put the correct "stickings". To alternate hands we simply need to replace the necessary E notes with A and vice versa. Let's define some "meta-data" like structures

accent1 = ("5a", false)
accent2 = ("5b", true)
accent3 = ("3", false)
accent4 = ("4", true)
accents = [accent1, accent2, accent3, accent4]
4-element Vector{Tuple{String, Bool}}:
("5a", 0)
("5b", 1)
("3", 0)
("4", 1)

The first entry of each tuple is simply the name of the pattern which we will also show in our music score as "lyrics". The second entry of the tuple simply denotes whether the pattern swaps the leading hand.

The function that will "inverse" a note sticking is:

inverse!(n::Note) = (n.pitch = (n.pitch == left ? right : left));

The function that will "count" how long is each pattern, so that we put the lyrics on the correct positions in the score, will be:

note_length(s::String) = parse(Int, s[1]);

(remember: sixt is the duration of one sixteenth note). We now initialize an empty MIDITrack and add all events to it!

track = MIDITrack()
ℓ = 0

for i in 1:length(seq)

s = accents[seq[i]][1]
le = MIDI.LyricEvent(0, MIDI.LYRICEV, s)

for j in ℓ+1:ℓ+note_length(s)
inverse!(notes[j])
end
end

global ℓ += note_length(s)

change = accents[seq[i]][2]
end

notes
128 Notes with tpq=960
Note E6  | vel = 100 | pos = 0, dur = 240
Note A5  | vel = 50  | pos = 240, dur = 240
Note A5  | vel = 50  | pos = 480, dur = 240
Note E6  | vel = 50  | pos = 720, dur = 240
Note E6  | vel = 50  | pos = 960, dur = 240
Note A5  | vel = 100 | pos = 1200, dur = 240
Note E6  | vel = 50  | pos = 1440, dur = 240
⋮
Note E6  | vel = 50  | pos = 29040, dur = 240
Note E6  | vel = 50  | pos = 29280, dur = 240
Note A5  | vel = 50  | pos = 29520, dur = 240
Note A5  | vel = 50  | pos = 29760, dur = 240
Note E6  | vel = 100 | pos = 30000, dur = 240
Note A5  | vel = 50  | pos = 30240, dur = 240
Note A5  | vel = 50  | pos = 30480, dur = 240

Finally, to visualize, we use musescore again, using a midi file as an input

musescore("drums_patterns_with_names.png", MIDIFile(1, 960, [track]))

Isn't it cool that even the lyrics text was displayed so seamlessly?