Subsystem

A melody that remembers

By the end of this chapter, a melody moves over the chords in eighth notes, leaping and then turning back, stepping and then carrying on, as a singer's line does, with a breath here and there.

The piece so far

Everything Chords that move built, without its render. This chapter adds a pattern and changes none.

import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums
import subsequence.constants.midi_notes as notes

composition = subsequence.Composition(bpm=120, key="E", scale="minor", seed=1)
swing = subsequence.Groove.swing(percent=57)

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

@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.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def fill (p):
	if p.bar_cycle(4).last:
		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)
def pedal_hats (p):
	p.hit_steps("hi_hat_pedal", [0], velocity=80)

composition.harmony(style="aeolian_minor")

@composition.pattern(channel=3, beats=4, voice_leading=True)
def pad (p, chord):
	p.chord(chord, root=notes.E3, sustain=True, velocity=70)

@composition.pattern(channel=2, beats=4)
def bass (p, chord):
	p.arpeggio(chord, root=notes.E1, count=4, spacing=0.25)
	p.groove(swing)

A line that remembers

Every pattern so far has decided its notes knowing only the moment it runs in: the bar, the cycle, the chord. A melody needs more than that. Whether its next note should rise or fall depends on the notes before it, including the notes at the end of the last bar, and the function that builds each bar starts fresh every time.

So the memory is kept outside the function, in a MelodicState, made once:

melody_state = subsequence.MelodicState(low=notes.E4, high=notes.E5, rest_probability=0.2)

@composition.pattern(channel=4, beats=4)
def melody (p, chord):
	p.melody(melody_state, spacing=0.5, chord_tones=chord.tones(notes.E4))

composition.render(bars=4, filename="a-melody-that-remembers.mid")

In piece.py, put melody_state above the patterns and add melody below them. Give a lead instrument MIDI channel 4.

How it chooses

The state scores every note it could play next, using a model of how listeners expect a melody to go: Eugene Narmour's implication-realisation model, set out in his 1990 book The Analysis and Cognition of Basic Melodic Structures. Two of its expectations do most of the work. After a large leap, a listener expects the line to turn back. After a small step, they expect it to carry on the same way. The state also avoids the same note too many times in a row, and favours the notes of the chord.

The first bar leaps and turns back:

MIDI channel 4
    1  1.000  note E5 (76)  velocity 90  length 0.167
    1  2.000  note G4 (67)  velocity 90  length 0.167
    1  2.500  note E5 (76)  velocity 90  length 0.167
    1  3.500  note B4 (71)  velocity 90  length 0.167
    1  4.000  note C5 (72)  velocity 90  length 0.167

E5 falls a major sixth to G4, and the line turns straight back up. The rests fall where no note is listed: after the first E5, and after the second. By the third bar the line has settled into steps, rising and falling a note or two at a time:

    3  1.000  note G4 (67)  velocity 90  length 0.167
    3  1.500  note A4 (69)  velocity 90  length 0.167
    3  2.000  note G4 (67)  velocity 90  length 0.167
    3  2.500  note E4 (64)  velocity 90  length 0.167
    3  3.000  note F#4 (66)  velocity 90  length 0.167

Other melodic generators

Each has its entry in the reference: