Subsystem

Sections and form

By the end of this chapter, the piece has a shape: a four-bar intro of pad and hi-hats, a build where the kit, bass and melody come in, a drop with everything, and a breakdown that pulls back, with the drums falling out for the bar before the drop and a tom fill at the end of each section.

The piece so far

The setup from Motifs and phrases, without its render. Its patterns are left out, because this chapter rewrites every one of them.

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.harmony(style="aeolian_minor")
melody_state = subsequence.MelodicState(low=notes.E4, high=notes.E5, rest_probability=0.2)
hook = subsequence.motif([5, 6, 5, 3, None, 1, 2, 3])
answered = subsequence.period(hook)

A form

So far every pattern has played in every bar. composition.form gives the piece sections, in order:

composition.form(subsequence.Form([
	subsequence.Section("intro", 4, energy=0.2),
	subsequence.Section("build", 8, energy=0.5),
	subsequence.Section("drop", 8, energy=1.0),
	subsequence.Section("breakdown", 4, energy=0.3),
]), loop=True)

Out for a bar before the drop

A drop hits harder after a moment without the beat. composition.transition mutes patterns, by name, for the bar before a section starts, and brings them back when it does:

composition.transition(before="drop", mute=["drums", "hats"])

Patterns that know where they are

Every pattern now takes min_energy=: it plays only in sections whose energy is at least that much, and in the rest Subsequence does not run it at all. This is the kit, rewritten with it:

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP, min_energy=0.5)
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, min_energy=0.2)
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, min_energy=1.0)
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, min_energy=0.5)
def fill (p):
	if p.section.last_bar:
		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, min_energy=0.5)
def pedal_hats (p):
	p.hit_steps("hi_hat_pedal", [0], velocity=80)

The fill has changed too. It used to play on the last of every four bars. Now it asks p.section, which describes the section the bar is in: its name, its bars, which bar of them this is, counted from 0, and its energy. p.section.last_bar is True on a section's last bar, so the fill ends each section, whatever its length.

The pitched parts, and a render of the whole form:

@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, min_energy=0.5)
def bass (p, chord):
	p.arpeggio(chord, root=notes.E1, count=4, spacing=0.25)
	p.groove(swing)

@composition.pattern(channel=4, beats=4, min_energy=0.3)
def melody (p, chord):
	p.melody(melody_state, spacing=0.5, chord_tones=chord.tones(notes.E4))

@composition.pattern(channel=5, bars=4, min_energy=1.0)
def hook_line (p):
	p.phrase(answered, root=notes.E5)

composition.render(bars=24, filename="sections-and-form.mid")

In piece.py, put the form and the transition below answered, and replace every pattern with these.

The pad has no min_energy, so it plays everywhere. Together, the thresholds and the energies decide the arrangement:

Pattern min_energy Intro, 0.2 Build, 0.5 Drop, 1.0 Breakdown, 0.3
pad none plays plays plays plays
hats 0.2 plays plays plays plays
melody 0.3 plays plays plays
drums, fill, pedal_hats, bass 0.5 plays plays
euclidean_snare, hook_line 1.0 plays

What the form made

The first bar of the intro has closed hi-hats and no kick, and the kick arrives with the build, in bar 5:

MIDI channel 10
    1  1.000  note F#2 (42)  velocity 40  length 0.083
    5  1.000  note C2 (36)  velocity 110  length 0.083

In bar 12, the last of the build, the drums and closed hi-hats are muted, and the kit is down to the pedal hi-hat and the tom fill:

   12  2.000  note G#2 (44)  velocity 80  length 0.083
   12  4.000  note D3 (50)  velocity 100  length 0.083
   12  4.292  note C3 (48)  velocity 90  length 0.083

The drop starts in bar 13 with everything, and the hook comes in on MIDI channel 5:

MIDI channel 5
   13  1.000  note B5 (83)  velocity 100  length 1.000

More on form