#!/usr/bin/env python3 """ "The U's Late Equaliser" — fast chiptune sports/fighting theme Style: Game Boy racing / fighting-game stage select. E minor, 152 BPM. Structure: 2-bar intro, A (8), B (8), A' with harmony (8), tag (2). The chiptune fingerprints: - square lead hook in 8th-note runs, nothing sustains - 16th-note synth bass, octave pops - off-beat chord stabs (the "skank" that makes 152 BPM feel faster) - drum fills every 4 bars, crash on the downbeat of every section """ import os, sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib")) from midikit import MidiFile, Track, MPQN, verify, note as N from songkit import (bar, melody, arp, bass_drive, drums, vibrato, crash, chord, parse_score, QUALITIES as QUAL, TPQ) BPM = 152 OUT = os.path.join(os.path.dirname(__file__), "..", "midi", "03_late_equaliser.mid") CHORDS = ( [("E", "m"), ("E", "m")] + # intro [("E", "m"), ("E", "m"), ("C", ""), ("C", ""), ("G", ""), ("G", ""), ("D", ""), ("D", "")] + # A [("C", ""), ("D", ""), ("E", "m"), ("E", "m"), ("C", ""), ("D", ""), ("E", "m"), ("E", "m")] + # B [("E", "m"), ("E", "m"), ("C", ""), ("C", ""), ("G", ""), ("G", ""), ("D", ""), ("D", "")] + # A' [("E", "m"), ("E", "m")] # tag ) HOOK_A = """ E5-8 G5-8 B5-8 A5-8 G5-8 E5-8 G5-4 | B5-8 A5-8 G5-8 E5-8 F#5-4 G5-4 | G5-8 A5-8 G5-8 E5-8 C5-2 | E5-8 G5-8 C6-8 B5-8 A5-4 G5-4 | B5-8 A5-8 B5-8 D6-8 B5-4 G5-4 | D6-8 B5-8 A5-8 G5-8 A5-4 B5-4 | A5-8 B5-8 A5-8 F#5-8 D5-4 F#5-4 | G5-8 A5-8 B5-8 C6-8 B5-2 """ HOOK_B = """ C6-8 B5-8 A5-8 G5-8 A5-4 E5-4 | B5-8 A5-8 G5-8 F#5-8 A5-4 D5-4 | E5-8 G5-8 B5-8 E6-8 D6-4 B5-4 | E6-2 B5-4 G5-4 | C6-8 D6-8 C6-8 A5-8 G5-4 E5-4 | F#5-8 A5-8 D6-8 C6-8 B5-4 A5-4 | G5-8 B5-8 E6-8 G6-8 F#6-4 E6-4 | E6-1 """ INTRO_LICK = "B4-8 E5-8 G5-8 B5-8 A5-8 G5-8 F#5-8 G5-8" def build(): mf = MidiFile(tpq=TPQ) mf.time_sig = (4, 2) mf.key_sig = (-1, True) # E minor mf.tempos = [MPQN.from_bpm(0, BPM)] lead = Track("Square lead", channel=0) lead.program(0, "square lead") lead.cc(0, 7, 90); lead.cc(0, 10, 52) harm = Track("Harmony 3rd", channel=1) harm.program(0, "square lead") harm.cc(0, 7, 62); harm.cc(0, 10, 78) stabs = Track("Stabs", channel=2) stabs.program(0, "electric piano 1") stabs.cc(0, 7, 62); stabs.cc(0, 10, 24) bass = Track("Bass", channel=3) bass.program(0, "synth bass 2") bass.cc(0, 7, 84); bass.cc(0, 10, 36) d = Track("Drums", channel=9) d.cc(0, 7, 88) def stab_chart(start, count): """Off-beat chord stabs: hit on the 'and' of every beat.""" for i in range(count): root, qual = CHORDS[start + i] for b in (0.5, 1.5, 2.5, 3.5): for iv in QUAL[qual]: stabs.note(bar(start + i, b), N(root + "4") + iv, TPQ // 2 - 40, 78) for start, count in ((0, 2), (2, 8), (10, 8), (18, 8), (26, 2)): stab_chart(start, count) # intro bass_drive(bass, 0, CHORDS[0:2], subdivision=2, velocity=90) melody(lead, 0, INTRO_LICK) melody(lead, 1, INTRO_LICK) drums(d, 1, 1, "dos_roll", velocity_scale=0.8) # A bass_drive(bass, 2, CHORDS[2:10], subdivision=2) melody(lead, 2, HOOK_A) vibrato(lead, bar(2), bar(10), tempo_bpm=BPM, depth=0.4, rate=6.5) drums(d, 2, 8, "drive") drums(d, 5, 1, "dos_roll", velocity_scale=0.7) drums(d, 9, 1, "break") crash(d, bar(10)) # B bass_drive(bass, 10, CHORDS[10:18], subdivision=2) melody(lead, 10, HOOK_B) vibrato(lead, bar(10), bar(18), tempo_bpm=BPM, depth=0.45, rate=6.5) drums(d, 10, 8, "drive") drums(d, 13, 1, "dos_roll", velocity_scale=0.7) drums(d, 17, 1, "dos_roll") crash(d, bar(18)) # A' with third-harmony bass_drive(bass, 18, CHORDS[18:26], subdivision=2) melody(lead, 18, HOOK_A) vibrato(lead, bar(18), bar(26), tempo_bpm=BPM, depth=0.5, rate=6.5) # harmony a diatonic third below, velocity down for t, p, dur, v in parse_score(HOOK_A): harm.note(bar(18) + t, p - 4 if p % 12 in (0, 1) else p - 3, dur, max(40, v - 25)) drums(d, 18, 8, "drive") drums(d, 21, 1, "dos_roll", velocity_scale=0.7) drums(d, 25, 1, "dos_roll") crash(d, bar(26)) # tag: cadence hit bass_drive(bass, 26, CHORDS[26:28], subdivision=2, velocity=100) for p in ("E2", "E3", "B3"): bass.note(bar(27), N(p), TPQ * 4, 100) for p in ("E4", "G4", "B4", "E5"): stabs.note(bar(27), N(p), TPQ * 4, 85) lead.note(bar(27), N("E6"), TPQ * 4, 95) crash(d, bar(26)) drums(d, 26, 1, "break") mf.add_track(lead); mf.add_track(harm); mf.add_track(stabs) mf.add_track(bass); mf.add_track(d) mf.set_loop(bar(2), bar(26)) return mf if __name__ == "__main__": os.makedirs(os.path.dirname(OUT), exist_ok=True) mf = build() mf.write(OUT) dd = verify(OUT, min_sec=30, max_sec=120) print("OK:", OUT) print(" duration:", dd["duration_sec"], "s | notes:", sum(len(t["notes"]) for t in dd["tracks"]), "| tracks:", len(dd["tracks"]))