Subsystem

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: