Controllers, bends and slides
By the end of this chapter, the pad's filter opens across the build, from dark to bright, and the bass slides up into its top note, in the manner of an acid bass line.
The piece so far
Everything Patterns that listen to each other built, without its render. The pad and bass patterns are left out, because this chapter rewrites them.
import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums
import subsequence.constants.midi_notes as notes
import subsequence.sequence_utils
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)
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)
composition.transition(before="drop", mute=["drums", "hats"])
@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)
@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.conductor.line("swell", start_val=40, end_val=90, duration_beats=32, start_beat=16)
composition.conductor.lfo("breath", shape="sine", cycle_beats=16, min_val=60, max_val=100)
@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=int(p.signal("breath")))
p.groove(swing)
def tempo (section):
if section is not None and section.name == "build":
composition.target_bpm(126, bars=8)
elif section is not None and section.name == "breakdown":
composition.target_bpm(120, bars=4)
composition.on_section(tempo)
@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")
steps = [note.position // 6 for note in p.placed()]
p.data["drum_syncopation"] = subsequence.sequence_utils.syncopation(steps, p.grid)
p.groove(swing)
A controller that moves
MIDI carries more than notes. A control change, CC, sets one of an instrument's controls to a value from 0 to 127. CC 74 is the one the MIDI specification names brightness, and many synths send it to the filter's cutoff, so that is the control this chapter moves. Check which CC your instrument's filter answers to, and use that number if it differs.
A third signal gives the cutoff its path across the build, as the swell does for the pad's velocity:
composition.conductor.line("cutoff", start_val=20, end_val=127, duration_beats=32, start_beat=16)
The pad, rewritten to sweep the filter:
@composition.pattern(channel=3, beats=4, voice_leading=True)
def pad (p, chord):
p.chord(chord, root=notes.E3, sustain=True, velocity=int(p.signal("swell")))
p.cc_ramp(74, int(p.signal("cutoff")), int(p.conductor.get("cutoff", (p.bar + 1) * p.bar_beats)))
p.ccsends one control change at a beat.p.cc_rampsends a stream of them, moving from a start value to an end value across the bar.- The start is the cutoff signal at the start of this bar. The end is the same signal at the start of the next bar:
p.conductor.getreads a signal at any beat, and(p.bar + 1) * p.bar_beatsis the beat the next bar starts on. So each bar carries on from where the last one left off, and the whole build is one smooth sweep.
A bass that slides
A slide bends a note's pitch into the next note instead of jumping, the sound of an acid bass line. The bass, rewritten with one line more:
@composition.pattern(channel=2, beats=4, min_energy=0.5)
def bass (p, chord):
busy = p.data.get("drum_syncopation", 0.0) > 0.5
if busy:
p.arpeggio(chord, root=notes.E1, count=1, spacing=1)
else:
p.arpeggio(chord, root=notes.E1, count=4, spacing=0.25)
p.groove(swing)
if not busy:
p.slide(notes=[3, 7, 11, 15], time=0.5, bend_range=12)
composition.render(bars=24, filename="controllers-bends-and-slides.mid")
In piece.py, put the cutoff line with the other signals, and replace pad and bass with these.
busykeeps the answer to the question theifused to ask,TrueorFalse, so the pattern can ask it twice.p.slideslides into the notes it is given, counted in order from 0. In the arpeggio, notes 3, 7, 11 and 15 are the fourth note of each beat, its top note, so the bass slides up into it every beat. In a bar of rootsbusyisTrue, and nothing slides.- A slide is laid once the pattern is built, against the notes where they finally fall, so it ends as its swung note sounds whether it comes before or after the groove.
time=0.5makes the slide take the last half of the note before.- A slide is made with pitch bend, and how far a full bend reaches is a setting on the instrument.
bend_range=12tells Subsequence the bass instrument's bend range is 12 semitones, so set your bass instrument's pitch bend range to 12 for the slide to land on the right note.
What moved
The pad sends the cutoff in every bar, one control change after another, and the summary reads each stretch as one line: held at 20 through the intro, rising to 127 across the build, and held there after it:
MIDI channel 3
1 1.000 CC 74 value 20, repeated until 5 1.125 in 392 messages
5 1.125 CC 74 value 20, ramping to 127 at 12 4.875 in 770 changes
12 4.875 CC 74 value 127, repeated until 24 4.958 in 1167 messages
In bar 6, where the bass plays its arpeggio, each beat's third note bends up into the swung top note, and the bend returns to 0 as that note starts:
MIDI channel 2
6 1.000 note G1 (31) velocity 100 length 0.250
6 1.292 note B1 (35) velocity 100 length 0.250
6 1.500 note D2 (38) velocity 100 length 0.292
6 1.625 pitch bend 0, ramping to 3413 at 6 1.792 in 5 changes
6 1.792 note G2 (43) velocity 100 length 0.250
6 1.792 pitch bend 0
More expression
p.pitch_bendsends one pitch bend, andp.portamentoglides between every pair of notes rather than chosen ones.p.program_changechooses an instrument's sound by number.p.nrpn,p.rpnandp.sysexreach the controls a CC number cannot: finer values, and messages particular to one instrument.