Subsystem

A Euclidean snare

By the end of this chapter, a second snare plays five quiet hits spread as evenly as they will go across every bar, turned so that they pull against the beat.

The piece so far

Everything Ghost fills built, without its render. This chapter adds a pattern and changes none.

import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums

composition = subsequence.Composition(bpm=120, seed=1)

@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.hit_steps("hi_hat_closed", range(16), velocity=70)
	p.ghost_fill("snare", density=0.5, velocity=(20, 40), bias="before")

A generator

So far the drum pattern has said exactly which steps to play, and p.ghost_fill has chosen steps at random. A generator works the steps out for itself, from a rule and a few numbers. p.euclidean is one of the simplest: give it a number of hits, which it calls pulses, and it spreads them as evenly as they will go across the pattern's steps.

The rhythms it makes are old. Godfried Toussaint showed in a 2005 paper, The Euclidean Algorithm Generates Traditional Musical Rhythms, that spreading pulses this way produces rhythms heard in music around the world: three pulses over eight steps, x..x..x., is the Cuban tresillo.

Here are five pulses over a bar of sixteen steps, in a composition of its own, called on_its_own, so they can be heard alone:

on_its_own = subsequence.Composition(bpm=120, seed=1)

@on_its_own.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)

on_its_own.render(bars=1, filename="five-pulses.mid")

"snare_2" is the second snare General MIDI names, an electric snare, note 40, so it sounds apart from the snare on the backbeat. The five hits land three steps apart, with one step more before the bar comes round:

    1  1.000  note E2 (40)  velocity 60  length 0.083
    1  1.750  note E2 (40)  velocity 60  length 0.083
    1  2.500  note E2 (40)  velocity 60  length 0.083
    1  3.250  note E2 (40)  velocity 60  length 0.083
    1  4.000  note E2 (40)  velocity 60  length 0.083

Turning it against the beat

Two of those hits double what the piece already plays: the first lands with the kick on the first beat, and the last with the snare on the fourth. p.rotate moves every note of a pattern along by a number of steps, and a note pushed past the end of the bar comes round to its start. Two steps later, none of the hits meets the snare, and only one meets the kick. Add the pattern to the piece, rotated:

@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)
	p.rotate(2)

composition.render(bars=2, filename="a-euclidean-snare.mid")

In piece.py, add this pattern below drums. Rotating it moves only its own notes, which is why it is a pattern of its own rather than two more lines in drums. Its hits now fall here in every bar, one of them with the kick on the third beat:

    1  1.500  note E2 (40)  velocity 60  length 0.083
    1  2.250  note E2 (40)  velocity 60  length 0.083
    1  3.000  note E2 (40)  velocity 60  length 0.083
    1  3.750  note E2 (40)  velocity 60  length 0.083
    1  4.500  note E2 (40)  velocity 60  length 0.083

Try other numbers of pulses and other rotations. Five hits spread evenly cannot fall once a beat in a bar of four, so however they are turned, most of them land off the beat and pull against the kick.

Every generator

Subsequence can list its generators itself. subsequence.generators returns a description of each, and this prints their names:

for generator in subsequence.generators():
	print(generator["name"])

for runs the indented line once for each item in a list, calling the item generator each time. Each description holds a generator's name, a one-line summary and its parameters, and generator["name"] picks out the name. The list takes in the methods that place notes where you say, such as p.note and p.hit_steps, as well as the generators that decide for themselves:

bresenham
cellular_1d
euclidean
ghost_fill
golden
reaction_diffusion
thue_morse

Other rhythm generators

Each has its entry in the reference: