File size: 2,265 Bytes
b2e4883 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | #!/usr/bin/env python3
"""Build a clean half-time drum MIDI for Hextermination's opening.
Opening section (per Carlos):
3 bars of 4/4, then 2 bars of 3/4.
Always starts with kick on the downbeat.
Half-time feel.
Groove per bar:
- kick on beat 1 (downbeat)
- snare on beat 3 (half-time backbeat)
- hi-hat on every 8th note
"""
import argparse
import mido
KICK, SNARE, HIHAT = 36, 38, 42
Q = 960 # quarter-note ticks
def build(bpm, q=Q):
mid = mido.MidiFile(ticks_per_beat=q)
tempo_us = int(round(60e6 / bpm))
eighth = q // 2
# tempo + time-signature track
tt = mido.MidiTrack()
mid.tracks.append(tt)
tt.append(mido.MetaMessage("set_tempo", tempo=tempo_us, time=0))
# 3 bars 4/4, then 2 bars 3/4
sigs = [(4, 4)] * 3 + [(3, 4)] * 2
# absolute start tick of each bar
starts = []
acc = 0
for num, _ in sigs:
starts.append(acc)
acc += num * q
# time signatures, as deltas
prev_tick = 0
for (num, den), st in zip(sigs, starts):
tt.append(mido.MetaMessage("time_signature", numerator=num,
denominator=den, time=st - prev_tick))
prev_tick = st
# percussion track, one continuous stream of (abs_tick, note)
pt = mido.MidiTrack()
mid.tracks.append(pt)
all_hits = []
for bar_idx, ((num, _), st) in enumerate(zip(sigs, starts)):
all_hits.append((st, KICK)) # downbeat kick
all_hits.append((st + 2 * q, SNARE)) # snare on beat 3
for e in range(num * 2): # 8th notes
all_hits.append((st + e * eighth, HIHAT))
all_hits.sort()
prev = 0
for tick, note in all_hits:
pt.append(mido.Message("note_on", note=note, velocity=100,
time=tick - prev))
pt.append(mido.Message("note_off", note=note, velocity=0, time=0))
prev = tick
return mid
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", required=True)
ap.add_argument("--bpm", type=float, default=117)
args = ap.parse_args()
build(args.bpm).save(args.out)
print(f"Wrote {args.out}: 3x4/4 + 2x3/4, half-time, {args.bpm} BPM")
if __name__ == "__main__":
main()
|