diff --git a/tracks/acid_reign.py b/tracks/acid_reign.py index 0c8f19b..4273741 100644 --- a/tracks/acid_reign.py +++ b/tracks/acid_reign.py @@ -1,13 +1,23 @@ """ -ACID REIGN — 303 acid bass. Simple, raw, relentless. +ACID REIGN — 303 acid bass. Groovy, squelchy, raw. """ -from pytheory import Key, Duration, Score, play_score +from pytheory import Key, Duration, Score, Tone, play_score from pytheory.rhythm import DrumSound key = Key("A", "minor") s = key.scale -prog = [c.transpose(-24) for c in key.progression("i", "VII", "VI", "v")] + +# Notes in the bass range (octave 2-3) +A = Tone.from_string("A2") +B = Tone.from_string("B2") +C = Tone.from_string("C3") +D = Tone.from_string("D3") +E = Tone.from_string("E3") +F = Tone.from_string("F3") +G = Tone.from_string("G3") +Ab = Tone.from_string("Ab2") # chromatic approach +Bb = Tone.from_string("Bb2") score = Score("4/4", bpm=135) @@ -16,45 +26,88 @@ CL = DrumSound.CLAP CH = DrumSound.CLOSED_HAT OH = DrumSound.OPEN_HAT -# ── 303 — arpeggiated chords, legato, squelchy ───────────────── +# ── 303 — the groove, hand-written, legato slides ────────────── acid = score.part("303", synth="saw", volume=0.7, lowpass=800, lowpass_q=4.0, - distortion=0.4, distortion_drive=5.0, - saturation=0.3, legato=True, glide=0.04, - sub_osc=0.5) -acid.lfo("lowpass", rate=0.015, min=400, max=8000, bars=64, shape="triangle") + distortion=0.3, distortion_drive=4.0, + saturation=0.25, legato=True, glide=0.05, + sub_osc=0.4, sidechain=0.3) +acid.lfo("lowpass", rate=0.02, min=500, max=6000, bars=64, shape="triangle") -# Bars 1-8: 303 alone — arped progression, one octave down +# Four groove patterns — the rests make the groove +# Pattern A: pumping root — DUM-rest-dum-DUM rest-dum-dum-DUM +pA = [(A, 110), (None, 0), (A, 80), (E, 100), + (None, 0), (A, 85), (G, 75), (A, 105)] + +# Pattern B: climbing — slides up chromatically +pB = [(A, 105), (Bb, 80), (C, 95), (None, 0), + (C, 85), (D, 90), (E, 100), (None, 0)] + +# Pattern C: menacing — big gaps, low hits +pC = [(A, 110), (None, 0), (None, 0), (E, 95), + (None, 0), (A, 100), (Ab, 85), (A, 108)] + +# Pattern D: funky — syncopated, all over the place +pD = [(None, 0), (A, 100), (C, 90), (None, 0), + (E, 105), (D, 85), (None, 0), (A, 110)] + +# Bars 1-8: 303 alone — patterns A and B for _ in range(2): - for chord in prog: - acid.arpeggio(chord, bars=1, pattern="updown", - division=Duration.SIXTEENTH, octaves=2) + for pat in [pA, pB]: + for _ in range(2): + for note, vel in pat: + if note is None: + acid.rest(Duration.EIGHTH) + else: + acid.add(note, Duration.EIGHTH, velocity=vel) -# Bars 9-32: same but louder patterns kick in around it -for _ in range(6): - for chord in prog: - acid.arpeggio(chord, bars=1, pattern="updown", - division=Duration.SIXTEENTH, octaves=2) +# Bars 9-24: cycling all patterns +for _ in range(2): + for pat in [pA, pB, pC, pD]: + for _ in range(2): + for note, vel in pat: + if note is None: + acid.rest(Duration.EIGHTH) + else: + acid.add(note, Duration.EIGHTH, velocity=vel) -# Bars 33-40: breakdown — sustained chords, filter wide -for chord in prog: - acid.add(chord, Duration.WHOLE) -for chord in prog: - acid.add(chord, Duration.WHOLE) +# Bars 25-32: just A and C patterns, harder +for _ in range(2): + for pat in [pA, pC]: + for _ in range(2): + for note, vel in pat: + if note is None: + acid.rest(Duration.EIGHTH) + else: + acid.add(note, Duration.EIGHTH, velocity=min(127, vel + 10)) -# Bars 41-56: back to arps, peak energy +# Bars 33-40: breakdown — sustained notes, filter wide open +for _ in range(2): + acid.add(A, Duration.WHOLE, velocity=90) + acid.add(E, Duration.WHOLE, velocity=85) + acid.add(C, Duration.WHOLE, velocity=88) + acid.add(A, Duration.WHOLE, velocity=92) + +# Bars 41-56: peak — all patterns cycling, max energy +for _ in range(2): + for pat in [pA, pB, pC, pD]: + for _ in range(2): + for note, vel in pat: + if note is None: + acid.rest(Duration.EIGHTH) + else: + acid.add(note, Duration.EIGHTH, velocity=min(127, vel + 12)) + +# Bars 57-64: patterns A only, filtering down for _ in range(4): - for chord in prog: - acid.arpeggio(chord, bars=1, pattern="updown", - division=Duration.SIXTEENTH, octaves=2) + for _ in range(2): + for note, vel in pA: + if note is None: + acid.rest(Duration.EIGHTH) + else: + acid.add(note, Duration.EIGHTH, velocity=max(40, vel - 15)) -# Bars 57-64: fading arps -for _ in range(2): - for chord in prog: - acid.arpeggio(chord, bars=1, pattern="up", - division=Duration.EIGHTH, octaves=1) - -# ── KICK — four on the floor, enters bar 9 ───────────────────── +# ── KICK — four on the floor, enters bar 9, sidechained ──────── kick = score.part("kick", volume=0.5, humanize=0.03) for _ in range(8): @@ -62,10 +115,10 @@ for _ in range(8): for _ in range(48): for beat in range(4): - kick.hit(K, Duration.QUARTER, velocity=115) + kick.hit(K, Duration.QUARTER, velocity=118) for bar in range(8): - vel = max(30, 110 - bar * 10) + vel = max(30, 112 - bar * 10) for beat in range(4): kick.hit(K, Duration.QUARTER, velocity=vel) @@ -77,37 +130,52 @@ for _ in range(8): for _ in range(48): clap.rest(Duration.QUARTER) - clap.hit(CL, Duration.QUARTER, velocity=95) + clap.hit(CL, Duration.QUARTER, velocity=100) clap.rest(Duration.QUARTER) - clap.hit(CL, Duration.QUARTER, velocity=98) + clap.hit(CL, Duration.QUARTER, velocity=102) for bar in range(8): - vel = max(25, 90 - bar * 8) + vel = max(25, 95 - bar * 9) clap.rest(Duration.QUARTER) clap.hit(CL, Duration.QUARTER, velocity=vel) clap.rest(Duration.QUARTER) clap.hit(CL, Duration.QUARTER, velocity=vel) -# ── HATS — 16ths, enters bar 17 ──────────────────────────────── -hats = score.part("hats", volume=0.25, humanize=0.04) +# ── HATS — 16ths with open hat accents, enters bar 17 ────────── +hats = score.part("hats", volume=0.25, humanize=0.04, sidechain=0.15) for _ in range(16): hats.rest(Duration.WHOLE) for _ in range(40): for beat in range(4): - hats.hit(CH, Duration.SIXTEENTH, velocity=72) + hats.hit(CH, Duration.SIXTEENTH, velocity=75) hats.hit(CH, Duration.SIXTEENTH, velocity=42) - hats.hit(OH if beat % 2 == 1 else CH, Duration.SIXTEENTH, velocity=58) + hats.hit(OH if beat % 2 == 1 else CH, Duration.SIXTEENTH, velocity=60) hats.hit(CH, Duration.SIXTEENTH, velocity=40) for bar in range(8): - vel = max(20, 65 - bar * 6) + vel = max(20, 68 - bar * 6) for beat in range(4): hats.hit(CH, Duration.SIXTEENTH, velocity=vel) - hats.hit(CH, Duration.SIXTEENTH, velocity=max(15, vel - 25)) - hats.hit(CH, Duration.SIXTEENTH, velocity=max(15, vel - 12)) hats.hit(CH, Duration.SIXTEENTH, velocity=max(15, vel - 28)) + hats.hit(CH, Duration.SIXTEENTH, velocity=max(15, vel - 15)) + hats.hit(CH, Duration.SIXTEENTH, velocity=max(15, vel - 30)) + +# ── PAD — dark atmosphere, sidechained ────────────────────────── +pad = score.part("pad", synth="supersaw", envelope="pad", volume=0.15, + reverb=0.5, chorus=0.3, chorus_rate=0.2, + chorus_depth=0.008, lowpass=1200, + sidechain=0.4) + +prog = key.progression("i", "VII", "VI", "v") + +for _ in range(8): + pad.rest(Duration.WHOLE) + +for _ in range(14): + for chord in prog: + pad.add(chord, Duration.WHOLE, velocity=60) # ═════════════════════════════════════════════════════════════════ import sys