#!/usr/bin/env python3 """ Tronica - 90s Trance / EBM Driving hypnotic 90s trance with arpeggiated leads, wide choir pads, and propulsive rhythm. 138 BPM. Structure: 40 bars Intro (4 bars) - sparse 16th-notes Build (8 bars) - speed to 32nd-notes Drop (8 bars) - full 32nd-notes Break (8 bars) - back to 16th-notes Drop2 (8 bars) - full 32nd-notes Outro (4 bars) - fade out Instruments: T0: Lead 1 Synth GM 80 - driving arpeggio T1: Pad 4 Choir GM 88 - sustained pads T2: Drum set Ch 10 - trance dance beat T3: Lead 3 Synth GM 84 - accent in drops """ from midiutil import MIDIFile BPM = 138 PPQ = 960 CHORDS = [ {"root": 48, "arp": (0, 3, 7, 12)}, {"root": 45, "arp": (0, 4, 7, 12)}, {"root": 50, "arp": (0, 4, 7, 12)}, {"root": 46, "arp": (0, 2, 7, 12)}, ] total_bars = 40 fill_bars = {3, 11, 19, 27} def sp(v): """Safe pitch: return only if 0-127.""" return v if 0 <= v < 128 else None # ========= Create MIDI File ========= midi = MIDIFile(4, file_format=1, ticks_per_quarternote=PPQ) midi.addTempo(0, 0, BPM) midi.addProgramChange(0, 0, 0, 80) midi.addProgramChange(1, 0, 0, 88) midi.addProgramChange(3, 9, 0, 84) # ========= Track 0: Arpeggio ========= for start, num, spd in [(0,4,4), (4,8,8), (12,8,8), (20,8,4), (28,8,8)]: dt = 1.0 / spd for bar in range(start, start + num): chord = CHORDS[bar % 4] r = chord["root"] ints = chord["arp"] for i in range(4 * spd): p = sp(r + ints[i % 4] + (i // 4) * 12) if p is not None: v = 88 if i % 4 else 93 midi.addNote(0, 0, p, bar * 4 + i * dt, dt * 0.85, v) # Outro fade (4 bars, decrescendo) for bar in range(36, 40): chord = CHORDS[bar % 4] for i in range(4): p = sp(chord["root"] + chord["arp"][i % 4]) if p is not None: midi.addNote(0, 0, p, bar * 4 + i * 0.25, 0.2, max(35, 75 - (bar-36)*15)) # ========= Track 1: Pads ========= midi.addControllerEvent(1, 0, 0, 91, 75) # Reverb midi.addControllerEvent(1, 0, 0, 93, 85) # Chorus midi.addControllerEvent(1, 0, 0, 11, 127) # Expression for bar in range(total_bars): r = CHORDS[bar % 4]["root"] for n in [r+7, r+12, r+19]: midi.addNote(1, 0, n, bar*4, 3.9 if bar%2==0 else 3.7, 50) # ========= Track 2: Drums ========= for bar in range(total_bars): base = bar * 4 for beat in range(4): bt = base + beat midi.addNote(2, 9, 36, bt, 0.2, 115 if beat==0 else 105) if beat in (1, 3): midi.addNote(2, 9, 38, bt, 0.2, 110 if bar in fill_bars else 90) midi.addNote(2, 9, 42, bt + 0.5, 0.1, 70) if bar in fill_bars: midi.addNote(2, 9, 46, base + 3.5, 0.3, 80) midi.addNote(2, 9, 46, base + 3.75, 0.2, 75) # ========= Track 3: Lead Cues ========= for bar in range(8): bt = (12 + bar) * 4 r = CHORDS[(12 + bar) % 4]["root"] for bp, off in [(0,12),(0.5,16),(1.0,19),(2.0,16),(2.5,19),(3.0,24)]: midi.addNote(3, 9, r+off, bt+bp, 0.5, 90) for bar in range(8): bt = (28 + bar) * 4 r = CHORDS[(28 + bar) % 4]["root"] for bp, off in [(0,12),(0.5,16),(1.0,19),(2.0,16),(2.5,19),(3.0,24)]: midi.addNote(3, 9, r+off, bt+bp, 0.5, 90) # Pitch bend for bar in range(8): bt = (4 + bar) * 4 midi.addPitchWheelEvent(0, 0, bt, 3000 + bar*500) midi.addPitchWheelEvent(0, 0, bt + 2, -(3000 + bar*500)) # ========= Write ========= output_file = "/home/paperclip/projects/gitea/pi-midi-zone/midi_output/tronica.mid" with open(output_file, "wb") as f: midi.writeFile(f) print(f"Written: {output_file}") print(f" 4-track, {total_bars} bars at {BPM} BPM") print(f" Style: 90s Trance / EBM")