An arpeggiated bass line
By the end of this chapter, a bass line climbs through the notes of E minor in sixteenth notes, landing on its root with the kick on every beat and swinging with the kit.
The piece so far
Everything Patterns that change every cycle built, without its render. One line is new: the composition now has a key and a scale, key="E", scale="minor", which this chapter explains. In piece.py, change your composition line to match.
import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums
composition = subsequence.Composition(bpm=120, key="E", scale="minor", seed=1)
swing = subsequence.Groove.swing(percent=57)
@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums (p):
p.hit_steps("kick", [0, 4, 8, 12], velocity=110)
p.hit_steps("snare", [4, 12], velocity=100)
p.ghost_fill("snare", density=0.5, velocity=(20, 40), bias="before")
p.groove(swing)
@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def hats (p):
p.hit_steps("hi_hat_closed", range(16))
p.velocity_shape(low=40, high=90)
p.groove(swing)
@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def euclidean_snare (p):
p.euclidean("snare_2", pulses=5, velocity=60).rotate(2).groove(swing)
@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def fill (p):
if p.bar_cycle(4).last:
p.sequence(steps=[12, 13, 14, 15], pitches=["high_tom", "high_mid_tom", "low_tom", "low_floor_tom"], velocities=[100, 90, 90, 110])
p.groove(swing)
@composition.pattern(channel=10, beats=0.75, reschedule_lookahead=0.75, drum_note_map=gm_drums.GM_DRUM_MAP)
def pedal_hats (p):
p.hit_steps("hi_hat_pedal", [0], velocity=80)
Pitches as numbers and names
Every note Subsequence plays is a MIDI note number, as the drums have been all along. For a pitched part a note reads better by its name, and Subsequence has a name for every MIDI note, standing for its number, so a name goes anywhere a pitch does:
import subsequence.constants.midi_notes as notes
print(notes.E1, notes.E2, notes.C4)
28 40 60
The octave number follows the note: E1 is 28, E2 an octave above it is 40, and middle C is C4, 60. Some instruments and DAWs number octaves one lower, calling 60 C3, and it is the same note.
A key and a scale
key="E" and scale="minor" on the composition say what the piece is in. They make no notes by themselves. What they do is let every pattern ask, through p.key and p.scale, rather than each pattern writing its own key down, so changing the key of the whole piece is one edit.
subsequence.scale_notes turns a key and a scale into the notes, as a list of numbers. Here are eight notes of E minor, from E1 up:
print(subsequence.scale_notes("E", "minor", low=notes.E1, count=8))
[28, 30, 31, 33, 35, 36, 38, 40]
Those are E, F#, G, A, B, C and D, and the E an octave up. Scale names Subsequence knows include major, minor, dorian and minor_pentatonic, and subsequence.register_scale adds your own.
The bass line
The bass is its own pattern, on MIDI channel 2, so give a bass instrument MIDI channel 2:
@composition.pattern(channel=2, beats=4)
def bass (p):
scale = subsequence.scale_notes(p.key, p.scale, low=notes.E1, count=8)
p.arpeggio([scale[0], scale[2], scale[4], scale[7]], spacing=0.25)
p.groove(swing)
composition.render(bars=2, filename="an-arpeggiated-bass-line.mid")
In piece.py, put the notes import beside the other imports, and add bass below the other patterns.
scaleis the list of eight notes, now from the composition's own key and scale.scale[0]is the first note in the list, because Python counts a list's places from 0, as the grid counts steps. Soscale[0],scale[2]andscale[4]are the first, third and fifth notes of the scale, the root, third and fifth of E minor, andscale[7]is the root an octave up.p.arpeggioplays the notes it is given one at a time, in that order,spacingbeats apart, and starts again from the first until the bar is full. Four notes a sixteenth note apart fill a beat, so the root comes back on every beat, with the kick.- The groove swings it with the kit.
MIDI channel 2
1 1.000 note E1 (28) velocity 100 length 0.250
1 1.292 note G1 (31) velocity 100 length 0.250
1 1.500 note B1 (35) velocity 100 length 0.250
1 1.792 note E2 (40) velocity 100 length 0.250
1 2.000 note E1 (28) velocity 100 length 0.250
One vocabulary for chords
p.chord, p.strum and p.arpeggio take their notes the same way: a list of pitches, as above, or a chord by its name and the note to build it on. So the same figure can be written as the chord of E minor, played as an arpeggio, in a composition of its own called as_chord:
as_chord = subsequence.Composition(bpm=120, key="E", scale="minor", seed=1)
@as_chord.pattern(channel=2, beats=4)
def bass (p):
p.arpeggio("Em", root=notes.E1, count=4, spacing=0.25)
as_chord.render(bars=1, filename="the-same-figure-as-a-chord.mid")
count=4 asks for four notes of the chord, and a chord of three notes carries on into the octave above for the fourth. Unswung, the notes are the same:
1 1.000 note E1 (28) velocity 100 length 0.250
1 1.250 note G1 (31) velocity 100 length 0.250
1 1.500 note B1 (35) velocity 100 length 0.250
1 1.750 note E2 (40) velocity 100 length 0.250
Swap arpeggio for chord and take out spacing, which a chord does not take, and the four notes sound together. Swap it for strum and take out spacing there too, and they sound one after another, a moment apart.
Also for pitched parts:
p.broken_chordplays a chord's notes one at a time in an order you give, numbering them by their place in the chord.p.dronestarts a note that holds across cycles untilp.drone_offstops it.p.snap_to_scalemoves every note of a pattern onto the nearest note of a scale.