A first beat
By the end of this chapter, the piece has begun: a drum beat with a kick on every beat, a snare on the second and fourth beats, and a closed hi-hat on every sixteenth note.
Everything the piece grows into over the rest of the guide starts from the first two blocks of code in this chapter, so keep them: save them together as piece.py in your music folder.
The composition
A script starts by naming what it uses, and by making the composition:
import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums
composition = subsequence.Composition(bpm=120, seed=1)
import subsequence.constants.instruments.gm_drums as gm_drumsbrings in the General MIDI drum map, which names each drum sound, andas gm_drumsgives it a shorter name to use in the script.seed=1makes this the same take every time. Whenever Subsequence chooses something at random, the seed decides the choice, so the same script with the same seed plays the same notes on every run. A different number is a different take. Nothing in this beat is random, and the seed is there from the start so that nothing later has to change it.
The beat
Now the drum pattern, and a render of two bars of it:
@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)
composition.render(bars=2, filename="a-first-beat.mid")
The decorator makes drums a pattern, as in the last chapter, with three things to say about it:
channel=10plays it on MIDI channel 10, where General MIDI instruments play drums.beats=4makes it four beats long, which is one bar of 4/4, so it comes round once a bar.drum_note_map=gm_drums.GM_DRUM_MAPlets the pattern name its drums."kick","snare"and"hi_hat_closed"each become the MIDI note General MIDI gives that drum.
The pattern places its notes on a grid. Four beats are divided into sixteen steps, one for each sixteenth note, numbered from 0 to 15. Step 0 is the first beat, step 4 the second, step 8 the third and step 12 the fourth, and the steps between are the sixteenth notes in between.
p.hit_steps places a short hit of one drum on each step it is given, at one velocity:
[0, 4, 8, 12]is a list: several values in square brackets, separated by commas. Here it is the four steps that start a beat, so the kick plays on every beat.[4, 12]puts the snare on the second and fourth beats.range(16)counts from 0 up to 15, every step of the grid, so the closed hi-hat plays every sixteenth note. A range always stops one short of the number it is given.
The render writes this, shown here for the first beat and the start of the second:
MIDI channel 10
1 1.000 note C2 (36) velocity 110 length 0.083
1 1.000 note F#2 (42) velocity 70 length 0.083
1 1.250 note F#2 (42) velocity 70 length 0.083
1 1.500 note F#2 (42) velocity 70 length 0.083
1 1.750 note F#2 (42) velocity 70 length 0.083
1 2.000 note C2 (36) velocity 110 length 0.083
1 2.000 note D2 (38) velocity 100 length 0.083
1 2.000 note F#2 (42) velocity 70 length 0.083
The file holds MIDI notes, so the drums appear as the note numbers the map gave them: 36 is the kick, 38 the snare and 42 the closed hi-hat. Beat 1.250 is the second sixteenth note of the first beat. Every hit lasts a small part of a beat.
To hear the beat, set your drum machine or drum instrument to MIDI channel 10 and call composition.play() in place of composition.render(...), as in the last chapter.
The same beat, written as text
A pattern can also be written as a line of text, with p.seq. Here is the same beat in a composition of its own, called as_text, so that composition is still the piece:
as_text = subsequence.Composition(bpm=120, seed=1)
@as_text.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums (p):
p.seq("kick kick kick kick", velocity=110)
p.seq(". snare . snare", velocity=100)
p.seq("x x x x x x x x x x x x x x x x", pitch="hi_hat_closed", velocity=70)
as_text.render(bars=2, filename="a-first-beat-as-text.mid")
The words in the text are spread evenly across the pattern: four words are four beats, and sixteen are sixteen sixteenth notes. A . is a rest. Where pitch= names a drum, every word that is not a rest plays that drum, so the xs are closed hi-hats.
It places the same hits, with one difference: each note lasts as long as its slot, where p.hit_steps makes short hits.
1 1.000 note C2 (36) velocity 110 length 1.000
1 1.000 note F#2 (42) velocity 70 length 0.250
1 2.000 note D2 (38) velocity 100 length 1.000
Use whichever reads better to you. The piece carries on from the first version, with p.hit_steps.
Other ways to place notes
p.hitplaces hits by beat rather than by step:[0, 1, 2, 3]is every beat of the bar.p.noteplaces one note, of any pitch, at a beat and for a length.p.sequenceplaces notes on steps, each with a pitch and velocity of its own.gm_drumsholds every General MIDI drum name the map knows.