Subsystem

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.

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: