Skip to content

Instantly share code, notes, and snippets.

@twobob
Last active January 19, 2026 18:58
Show Gist options
  • Select an option

  • Save twobob/4097c3120b8910501eea282619244e82 to your computer and use it in GitHub Desktop.

Select an option

Save twobob/4097c3120b8910501eea282619244e82 to your computer and use it in GitHub Desktop.
I need some breaky beats. Now. no deps
import struct
import math
import random
import os
# --- MIDI CONSTANTS ---
PPQ = 960
# General MIDI Map
KICK = 36
SNARE = 38
SNARE_RIM = 37
CL_HAT = 42
OP_HAT = 46
PEDAL_HAT = 44
CRASH = 49
SPLASH = 55
RIDE = 51
RIDE_BELL = 53
# Toms
TOM_HI = 50
TOM_MID = 47
TOM_LO = 43
def get_variable_length_number(n):
"""Convert integer to MIDI variable length bytes."""
if n < 128:
return bytes([n])
output = []
output.append(n & 0x7F)
n >>= 7
while n > 0:
output.append((n & 0x7F) | 0x80)
n >>= 7
return bytes(reversed(output))
def create_midi_event(delta_time, status, data1, data2=None):
"""Pack MIDI event data."""
output = get_variable_length_number(delta_time)
output += bytes([status])
output += bytes([data1])
if data2 is not None:
output += bytes([data2])
return output
def resolve_directory(target_path):
"""
Attempts to create/access the target path.
If it fails (e.g. drive doesn't exist), falls back to local directory.
"""
try:
os.makedirs(target_path, exist_ok=True)
return target_path
except OSError:
print(f"WARNING: Could not write to '{target_path}'. Defaulting to local folder.")
return "."
class FractalGroove:
def __init__(self, bpm=174, swing_pct=58, quant_pct=95, jazziness=0.3, style_mode="random"):
self.bpm = bpm
self.swing_pct = swing_pct
self.quant_pct = quant_pct
self.jazziness = jazziness
# --- PATTERN STYLES ---
styles = {
"Amen-ish": {
"kick": [0, 10], # 1, 3-and
"snare": [4, 12] # 2, 4
},
"2-Step": {
"kick": [0, 10, 11], # 1, 3-and, 3-a
"snare": [4, 12]
},
"Rolling": {
"kick": [0, 7, 10], # 1, 2-a, 3-and
"snare": [4, 12]
},
"Tech-Step": {
"kick": [0, 2, 10], # 1, 1-and, 3-and
"snare": [4, 12]
}
}
if style_mode == "random":
self.style_name = random.choice(list(styles.keys()))
else:
self.style_name = style_mode if style_mode in styles else "Amen-ish"
self.base_kick = styles[self.style_name]["kick"]
self.base_snare = styles[self.style_name]["snare"]
def get_tick(self, bar, step, current_jitter_profile, is_fill=False):
"""Calculates absolute tick with swing and quantize logic."""
quarter_note = math.floor(step / 4)
step_in_beat = step % 4
ticks_per_8th = PPQ / 2
# Absolute start
bar_offset = bar * 4 * PPQ
beat_offset = quarter_note * PPQ
# Swing Logic (MPC Style)
ratio = self.swing_pct / 100.0
swing_offset = 0
if step_in_beat == 1: # e
swing_offset = ticks_per_8th * ratio
elif step_in_beat == 2: # &
swing_offset = ticks_per_8th
elif step_in_beat == 3: # a
swing_offset = ticks_per_8th + (ticks_per_8th * ratio)
perfect_tick = bar_offset + beat_offset + swing_offset
# Quantize / Jitter Logic
effective_quant = self.quant_pct + 10 if is_fill else self.quant_pct
effective_quant = min(effective_quant, 100)
deviation_amount = (100 - effective_quant) * 2
jitter = random.uniform(-deviation_amount, deviation_amount)
final_tick = int(perfect_tick + jitter)
return max(0, final_tick)
def generate_bar_pattern(self, bar_index):
notes = []
# --- FRACTAL ANALYSIS ---
check = bar_index + 1
is_4_end = (check % 4 == 0)
is_8_end = (check % 8 == 0)
is_16_end = (check % 16 == 0)
is_32_end = (check % 32 == 0)
is_64_end = (check % 64 == 0)
intensity = 0
if is_4_end: intensity = 1
if is_8_end: intensity = 2
if is_16_end: intensity = 3
if is_32_end: intensity = 4
if is_64_end: intensity = 5
# --- BASE GRID (Hats & Rides) ---
for step in range(16):
cymbal = CL_HAT
if intensity >= 4: cymbal = RIDE
if intensity == 5 and step % 4 == 0: cymbal = CRASH
hat_vel = 80 + (intensity * 4) if step % 2 == 0 else 50
if step % 2 != 0 and random.random() > 0.3:
notes.append((cymbal, step, hat_vel, False))
elif step % 2 == 0:
notes.append((cymbal, step, hat_vel, False))
# --- CORE GROOVE (Kick/Snare) ---
for step in range(16):
if step in self.base_kick:
notes.append((KICK, step, 120, False))
if step in self.base_snare:
if not (intensity >= 4 and step == 12):
notes.append((SNARE, step, 127, False))
# --- FILL & GHOST ENGINE ---
max_extras = 2 + int(self.jazziness * 6)
extras_count = 0
# 1. FILL LOGIC
if is_4_end:
fill_type = 'ghosts'
roll = random.random()
if intensity >= 3 and roll < 0.7: fill_type = 'toms'
elif intensity >= 2 and roll < 0.4: fill_type = 'cymbals'
if fill_type == 'toms':
notes = [n for n in notes if n[1] < 12]
pat = [TOM_HI, TOM_MID, TOM_LO, KICK]
if intensity == 5: pat = [TOM_HI, TOM_HI, TOM_MID, TOM_LO]
for i, inst in enumerate(pat):
notes.append((inst, 12 + i, 115, True))
elif fill_type == 'cymbals':
positions = [10, 13, 15]
for p in positions:
if random.random() < 0.8:
inst = SPLASH if random.random() < 0.5 else CRASH
notes.append((inst, p, 100, True))
if intensity >= 3:
notes.append((KICK, p, 90, True))
else: # Ghosts
possible_steps = [x for x in range(16) if x not in self.base_kick and x not in self.base_snare]
random.shuffle(possible_steps)
for s in possible_steps:
if extras_count >= max_extras: break
if intensity < 2 and (s == 0 or s == 15): continue
if random.random() < self.jazziness:
inst = KICK if random.random() > 0.5 else SNARE
vel = 60 if inst == KICK else 40
notes.append((inst, s, vel, True))
extras_count += 1
# 2. STANDARD BAR GHOSTING
else:
ghost_spots = [2, 3, 8, 9, 11, 14, 15]
for s in ghost_spots:
if extras_count >= (max_extras * 0.5): break
occupied = any(n[1] == s and n[0] in [KICK, SNARE] for n in notes)
if not occupied and random.random() < (self.jazziness * 0.4):
notes.append((SNARE, s, 35, True))
extras_count += 1
return notes
def render(self, output_path, filename_prefix, batch_id):
description = f"[{batch_id}] {self.style_name} Sw:{self.swing_pct}% Q:{self.quant_pct}% Jz:{self.jazziness:.1f}"
filename = f"{filename_prefix}_{batch_id:02d}_{self.style_name.replace(' ','')}.mid"
# SAFE PATH RESOLUTION
safe_path = resolve_directory(output_path)
full_path = os.path.join(safe_path, filename)
all_events = []
print(f"Generating -> {full_path}")
print(f" Details: {description}")
for bar in range(128):
bar_notes = self.generate_bar_pattern(bar)
for (note, step, vel, is_fill) in bar_notes:
start_tick = self.get_tick(bar, step, 0, is_fill)
duration = 60
all_events.append({'tick': start_tick, 'type': 'on', 'note': note, 'vel': vel})
all_events.append({'tick': start_tick + duration, 'type': 'off', 'note': note, 'vel': 0})
all_events.sort(key=lambda x: x['tick'])
# Build Track
track_data = bytearray()
# Track Name
track_name_bytes = description.encode('ascii', 'ignore')
track_data += b'\x00\xFF\x03' + get_variable_length_number(len(track_name_bytes)) + track_name_bytes
# Tempo
mspqn = int(60000000 / self.bpm)
track_data += b'\x00\xFF\x51\x03' + mspqn.to_bytes(3, 'big')
last_tick = 0
for e in all_events:
delta = e['tick'] - last_tick
last_tick = e['tick']
status = 0x99 if e['type'] == 'on' else 0x89
track_data += create_midi_event(delta, status, e['note'], e['vel'])
track_data += b'\x00\xFF\x2F\x00'
header = b'MThd' + (6).to_bytes(4, 'big') + (0).to_bytes(2, 'big') + \
(1).to_bytes(2, 'big') + PPQ.to_bytes(2, 'big')
chunk = b'MTrk' + len(track_data).to_bytes(4, 'big') + track_data
with open(full_path, 'wb') as f:
f.write(header)
f.write(chunk)
# --- EXECUTION ---
if __name__ == "__main__":
# Requested default path
target_dir = r"E:\SONGS\code Project"
print("--- STARTING BATCH GENERATION (16 FILES) ---")
for i in range(1, 17):
rnd_bpm = random.randint(160, 178)
rnd_swing = random.randint(50, 66)
rnd_quant = random.randint(75, 98)
rnd_jazz = round(random.uniform(0.2, 0.7), 2)
gen = FractalGroove(
bpm=rnd_bpm,
swing_pct=rnd_swing,
quant_pct=rnd_quant,
jazziness=rnd_jazz,
style_mode="random"
)
gen.render(target_dir, "fractal_batch", i)
print("--- BATCH COMPLETE ---")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment