Chords that move
By the end of this chapter, a pad plays a chord progression that Subsequence chooses as the piece goes, a chord a bar, and the bass line plays the notes of whichever chord is sounding.
The piece so far
Everything An arpeggiated bass line built, without its render. The bass pattern is left out, because this chapter rewrites it.
import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums
import subsequence.constants.midi_notes as notes
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)
A harmony engine
composition.harmony starts Subsequence choosing chords. It walks a chord graph: a map, for one style of music, of which chords tend to follow which, and how strongly. At each change it picks the next chord from the ones that can follow the current one, and by default it changes once a bar.
To hear what it chooses before giving it to the piece, here is a composition of its own, called listening, whose one pattern only prints the chord of each bar:
listening = subsequence.Composition(bpm=120, key="E", scale="minor", seed=1)
listening.harmony(style="aeolian_minor")
@listening.pattern(channel=3, beats=4)
def chord_names (p, chord):
print("bar", p.bar + 1, chord.name())
listening.render(bars=8, filename="listening.mid")
A pattern whose function takes a second parameter called chord is given the chord sounding in its cycle, and chord.name() is that chord's name. p.bar + 1 counts bars from 1, as a musician does.
bar 1 Em
bar 2 D
bar 3 C
bar 4 D
bar 5 C
bar 6 B
bar 7 B7
bar 8 Em
"aeolian_minor" is a style built on the natural minor scale. B and B7, in bars 6 and 7, are the major chord on the scale's fifth note, whose D sharp the natural minor does not have, and they pull back to E minor, which bar 8 goes to. Other styles include "dorian_minor", "phrygian_minor" and "functional_major", and the reference lists them all. The seed makes the choice, so the same seed chooses the same chords every time.
A pad, and a bass that follows
Now the piece gets the harmony, a pad on MIDI channel 3, and the bass rewritten to follow the chords:
composition.harmony(style="aeolian_minor")
@composition.pattern(channel=3, beats=4, voice_leading=True)
def pad (p, chord):
p.chord(chord, root=notes.E3, sustain=True, velocity=70)
@composition.pattern(channel=2, beats=4)
def bass (p, chord):
p.arpeggio(chord, root=notes.E1, count=4, spacing=0.25)
p.groove(swing)
composition.render(bars=4, filename="chords-that-move.mid")
In piece.py, put the harmony line below the composition, replace the bass with this one, and add the pad. Give a pad instrument MIDI channel 3.
p.chordplays the chord's notes together, built on the chord's root nearest toroot.sustain=Trueholds them for the whole bar.voice_leading=Truelets Subsequence choose how each chord is stacked, which note is at the bottom, so that its notes move as little as they can from the chord before. The first four chords move by a step each time, and all four stay in root position.- The bass is the arpeggio from the last chapter, now given the chord rather than a list of notes, so its four notes change with each bar's chord.
The first two bars, the bass's first beat and the pad's chord, as the engine chose them above, E minor and then D:
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
2 1.000 note D1 (26) velocity 100 length 0.250
2 1.292 note F#1 (30) velocity 100 length 0.250
MIDI channel 3
1 1.000 note E3 (52) velocity 70 length 4.000
1 1.000 note G3 (55) velocity 70 length 4.000
1 1.000 note B3 (59) velocity 70 length 4.000
2 1.000 note D3 (50) velocity 70 length 4.000
2 1.000 note F#3 (54) velocity 70 length 4.000
2 1.000 note A3 (57) velocity 70 length 4.000
A progression you write
The engine is one way to have chords. The other is to write them. subsequence.progression takes a list, and a number in it is a chord built on that note of the scale. Given to harmony as progression=, it plays in order, a bar a chord, and starts again when it runs out. In a composition of its own, called as_written:
as_written = subsequence.Composition(bpm=120, key="E", scale="minor", seed=1)
as_written.harmony(progression=subsequence.progression([1, 6, 3, 7]))
@as_written.pattern(channel=3, beats=4)
def chord_names (p, chord):
print("bar", p.bar + 1, chord.name())
as_written.render(bars=4, filename="as-written.mid")
bar 1 Em
bar 2 C
bar 3 G
bar 4 D
In E minor, the first note of the scale is E, the sixth C, the third G and the seventh D. A pattern that takes chord works the same with either, so the pad and the bass above would follow a written progression as they follow the engine.
Also for harmony:
subsequence.Progressionis a progression as a value: it can be generated from a style, joined to another with+, and repeated with*.composition.request_cadenceasks the engine to arrive at a cadence by a given bar.composition.chordsadds a chord part of its own, playing a progression withoutharmony.