Swing, groove and other transforms
By the end of this chapter, the whole kit swings, the closed hi-hats rise and fall in loudness across the bar instead of playing at one velocity, and you know how to reshape any pattern after its notes are placed.
The piece so far
The setup from A Euclidean snare. Its three patterns are left out, because this chapter rewrites all of them.
import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums
composition = subsequence.Composition(bpm=120, seed=1)
A transform
Everything the piece has done so far places notes. A transform changes notes a pattern has already placed: it moves them, changes their velocity, or takes some away. p.rotate, in the last chapter, was one. Because a transform works on what is already there, it comes after the notes it changes, at the end of the pattern.
Swing, for the whole kit
A swing feel delays every second sixteenth note a little, so the beat leans rather than ticks. In Subsequence a feel like that is a Groove: a value you make once and give to any pattern. Make one, below the composition:
swing = subsequence.Groove.swing(percent=57)
Groove.swing takes the swing as a percentage. At 50 the sixteenth notes are straight. The higher it goes, the later every second sixteenth note lands.
Subsequence places notes on 24 divisions of a beat, so swing moves a sixteenth note a whole division at a time: every percentage from 55 to 62 swings the same, one twenty-fourth of a beat late, and 67 moves it two, onto the eighth-note triplet. That is Subsequence at the version this guide describes.
The kit, rewritten
Now the drum pattern again, with the closed hi-hats taken out to a pattern of their own, and the groove given to each pattern with p.groove:
@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)
The hi-hats have a pattern of their own because a transform changes every note in its pattern. p.velocity_shape gives each note a velocity between low and high, spread so that no two neighbours are alike, and in the drums pattern it would have reshaped the kick and snare too. The velocities follow the same order in every bar, so the shape repeats.
Transforms chain
The Euclidean snare takes the groove as well. This time its three steps are written as one line:
@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.render(bars=2, filename="swing-and-groove.mid")
Each of these methods hands back p itself when it is done, so the next one can be called straight after it with another .. The line does what three lines did, in the same order: place the pulses, rotate them, then swing them.
In piece.py, replace the three patterns with these, and put the swing line below the composition.
What swing did
Here are the first beat and a half of the hi-hats, each a sixteenth note apart, with their shaped velocities:
1 1.000 note F#2 (42) velocity 40 length 0.083
1 1.292 note F#2 (42) velocity 65 length 0.083
1 1.500 note F#2 (42) velocity 52 length 0.083
1 1.792 note F#2 (42) velocity 77 length 0.083
1 2.000 note F#2 (42) velocity 46 length 0.083
The notes on the beat and on the half beat stay where they were. The second and fourth sixteenth notes of each beat now land at .292 and .792, one twenty-fourth of a beat late. The ghost notes and the Euclidean snare move the same way wherever they fall on one of those sixteenth notes:
1 1.792 note D2 (38) velocity 34 length 0.083
1 2.292 note E2 (40) velocity 60 length 0.083
Every transform
As with generators, Subsequence lists its transforms itself, with subsequence.transforms:
for transform in subsequence.transforms():
print(transform["name"])
rotate
swing
dropout
velocity_shape
reverse
Among the others, each with its entry in the reference:
p.reverseplays a pattern backwards, reflected around its first beat.p.dropoutremoves notes at random.p.randomizenudges timing and velocity at random.p.transposeshifts every pitch up or down.p.stretchscales a pattern's notes in time.p.legatolengthens each note to meet the next.p.ratchetturns notes already placed into rapid rolls.