""" Build a PROFESSIONAL reggaeton FLP with REAL SAMPLES from the user's library. Key facts: - Only Ch10-19 are sampler channels in the reference FLP (Ch0-9 are VST/plugin) - Each sampler channel loads a real WAV from libreria/reggaeton/ - MIDI notes trigger those real samples - 10 channels = kick, snare, hihat, 808, bell, lead, pad, clap, perc, rim Sample selection (professional reggaeton): Ch10: kick nes 1 — classic reggaeton kick Ch11: snare nes 1 — clean reggaeton snare Ch12: hi-hat 1 — tight hihat Ch13: Bass Reventado — deep 808 bass (dastin.prod) Ch14: bell 4 — bell tone for chords Ch15: lead 3 — melodic lead Ch16: pad 1 — sustained pad Ch17: clap — reggaeton clap (using snap from perc loop) Ch18: perc 1 — perc one shot Ch19: rim — rim/rimshot Output: output/reggaeton_fuego.flp """ import struct import sys import os # ── Paths ────────────────────────────────────────────────────────────────────── BASE = r"C:\Users\Administrator\Documents\fl_control" CH11_TMPL = os.path.join(BASE, "output", "ch11_kick_template.bin") REF_FLP = os.path.join(BASE, r"my space ryt\my space ryt.flp") FLP_OUT = os.path.join(BASE, "output", "reggaeton_fuego.flp") # All samples copied here — clean names, no special chars SAMPLES_DIR = os.path.join(BASE, "output", "fuego_samples") sys.path.insert(0, BASE) from src.flp_builder.events import ( EventID, encode_text_event, encode_word_event, encode_data_event, encode_notes_block, ) from src.flp_builder.skeleton import ChannelSkeletonLoader from src.flp_builder.arrangement import ( ArrangementItem, build_arrangement_section, build_track_data_template, ) # ── Constants ────────────────────────────────────────────────────────────────── BPM = 95 PPQ = 96 # Channel indices — ALL sampler channels (10-19) CH_KICK = 10 CH_SNARE = 11 CH_HH = 12 CH_808 = 13 CH_BELL = 14 CH_LEAD = 15 CH_PAD = 16 CH_CLAP = 17 CH_PERC = 18 CH_RIM = 19 # Sample assignment: ch_idx → (samples_dir, wav_filename) # All samples in fuego_samples/ with clean names SAMPLE_ASSIGNMENT = { CH_KICK: (SAMPLES_DIR, "kick.wav"), CH_SNARE: (SAMPLES_DIR, "snare.wav"), CH_HH: (SAMPLES_DIR, "hihat.wav"), CH_808: (SAMPLES_DIR, "bass_808.wav"), CH_BELL: (SAMPLES_DIR, "bell.wav"), CH_LEAD: (SAMPLES_DIR, "lead.wav"), CH_PAD: (SAMPLES_DIR, "pad.wav"), CH_CLAP: (SAMPLES_DIR, "clap.wav"), CH_PERC: (SAMPLES_DIR, "perc.wav"), CH_RIM: (SAMPLES_DIR, "rim.wav"), } # ══════════════════════════════════════════════════════════════════════════════ # FUEGO CHORD PROGRESSION: Am → Dm → F → E # ══════════════════════════════════════════════════════════════════════════════ PROGRESSION = [ {"name": "Am", "bass": 33, "chord": [45,48,52,57], "triad": [57,60,64], "root": 69}, {"name": "Dm", "bass": 38, "chord": [50,53,57,62], "triad": [62,65,69], "root": 74}, {"name": "F", "bass": 41, "chord": [53,57,60,65], "triad": [65,69,72], "root": 77}, {"name": "E", "bass": 40, "chord": [52,56,59,64], "triad": [64,68,71], "root": 76}, ] BEATS_PER_CHORD = 8 # ══════════════════════════════════════════════════════════════════════════════ # DRUM GENERATORS — using correct channel indices # ══════════════════════════════════════════════════════════════════════════════ def _n(pos, length, ch, vel): return {"pos": pos, "len": length, "key": 60, "vel": max(1, min(127, vel))} def dembow_kick(bars, vel_mult=1.0): """REAL dembow: 0.0, 2.0, 3.25""" notes = [] for b in range(bars): o = b * 4.0 notes.append(_n(o, 0.25, CH_KICK, int(120 * vel_mult))) notes.append(_n(o + 2.0, 0.25, CH_KICK, int(110 * vel_mult))) notes.append(_n(o + 3.25, 0.15, CH_KICK, int(90 * vel_mult))) return {CH_KICK: notes} def perreador_kick(bars, vel_mult=1.0): """Perreador: every beat + offbeat ghosts.""" notes = [] for b in range(bars): o = b * 4.0 for beat in range(4): notes.append(_n(o + beat, 0.25, CH_KICK, int(115 * vel_mult))) notes.append(_n(o + beat + 0.5, 0.15, CH_KICK, int(80 * vel_mult))) return {CH_KICK: notes} def sparse_kick(bars, vel_mult=1.0): notes = [] for b in range(bars): notes.append(_n(b * 4.0, 0.25, CH_KICK, int(100 * vel_mult))) return {CH_KICK: notes} def snare_standard(bars, vel_mult=1.0): """Snare: beats 2, 3-and (positions 1.25, 3.0).""" notes = [] for b in range(bars): o = b * 4.0 notes.append(_n(o + 1.25, 0.15, CH_SNARE, int(105 * vel_mult))) notes.append(_n(o + 3.0, 0.15, CH_SNARE, int(100 * vel_mult))) return {CH_SNARE: notes} def snare_intense(bars, vel_mult=1.0): """Intense snare with ghost hits.""" notes = [] for b in range(bars): o = b * 4.0 notes.append(_n(o + 1.25, 0.15, CH_SNARE, int(110 * vel_mult))) notes.append(_n(o + 1.75, 0.10, CH_SNARE, int(70 * vel_mult))) notes.append(_n(o + 3.0, 0.15, CH_SNARE, int(105 * vel_mult))) notes.append(_n(o + 3.5, 0.10, CH_SNARE, int(65 * vel_mult))) return {CH_SNARE: notes} def hihat_offbeat(bars, vel_mult=1.0): notes = [] for b in range(bars): o = b * 4.0 for i in range(4): notes.append(_n(o + i + 0.5, 0.1, CH_HH, int(55 * vel_mult))) return {CH_HH: notes} def hihat_8th(bars, vel_mult=1.0): notes = [] for b in range(bars): o = b * 4.0 for i in range(8): v = 70 if i % 2 == 0 else 50 notes.append(_n(o + i * 0.5, 0.1, CH_HH, int(v * vel_mult))) return {CH_HH: notes} def hihat_16th(bars, vel_mult=1.0): """Full 16ths with accents and open hats.""" notes = [] for b in range(bars): o = b * 4.0 for i in range(16): p = i * 0.25 if p % 1.0 == 0.0: v, l = 90, 0.1 elif p % 0.5 == 0.0: v, l = 65, 0.1 else: v, l = 40, 0.08 if i in [5, 10]: l = 0.2; v = int(v * 1.2) notes.append(_n(o + p, l, CH_HH, int(v * vel_mult))) return {CH_HH: notes} def clap_standard(bars, vel_mult=1.0): notes = [] for b in range(bars): o = b * 4.0 notes.append(_n(o + 1.0, 0.15, CH_CLAP, int(120 * vel_mult))) notes.append(_n(o + 3.0, 0.15, CH_CLAP, int(115 * vel_mult))) return {CH_CLAP: notes} def clap_soft(bars, vel_mult=1.0): notes = [] for b in range(bars): o = b * 4.0 notes.append(_n(o + 1.0, 0.15, CH_CLAP, int(80 * vel_mult))) notes.append(_n(o + 3.0, 0.15, CH_CLAP, int(75 * vel_mult))) return {CH_CLAP: notes} def perc_offbeat(bars, vel_mult=1.0): notes = [] for b in range(bars): o = b * 4.0 notes.append(_n(o + 0.75, 0.1, CH_PERC, int(85 * vel_mult))) notes.append(_n(o + 2.75, 0.1, CH_PERC, int(80 * vel_mult))) return {CH_PERC: notes} def rim_build(bars, vel_mult=1.0): """Rim roll building intensity.""" PATTERNS = [[0,2,8,14], [0,2,4,8,10,14], [0,2,4,6,8,10,12,14], list(range(16))] VELS = [50, 65, 80, 100] notes = [] for b in range(bars): o = b * 4.0 v = int(VELS[b % 4] * vel_mult) for idx in PATTERNS[b % 4]: notes.append(_n(o + idx * 0.25, 0.1, CH_RIM, v)) return {CH_RIM: notes} # ══════════════════════════════════════════════════════════════════════════════ # MELODIC GENERATORS # ══════════════════════════════════════════════════════════════════════════════ def _mn(pos, length, key, vel): """Melodic note — pitch matters.""" return {"pos": pos, "len": length, "key": key, "vel": max(1, min(127, vel))} def bass_808_full(bars, vel_mult=1.0): """808 bass with chord-root movement + fifth variation.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD for ci in range(chords): ch = PROGRESSION[ci % 4] base = ci * BEATS_PER_CHORD r = ch["bass"] f = r + 7 v = vel_mult notes.append(_mn(base + 0.0, 2.5, r, int(110*v))) notes.append(_mn(base + 2.5, 0.5, f, int(80*v))) notes.append(_mn(base + 3.0, 2.0, r, int(105*v))) notes.append(_mn(base + 5.0, 1.0, r, int(90*v))) notes.append(_mn(base + 6.0, 0.5, f, int(75*v))) notes.append(_mn(base + 6.5, 1.5, r, int(100*v))) return {CH_808: notes} def bass_808_sparse(bars, vel_mult=1.0): """Sparse 808 for intro — just root, long sustain.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD for ci in range(chords): ch = PROGRESSION[ci % 4] notes.append(_mn(ci * BEATS_PER_CHORD, 7.5, ch["bass"], int(60 * vel_mult))) return {CH_808: notes} def bell_chords(bars, vel_mult=1.0): """Bell playing offbeat chord stabs — 4-note voicings.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD stabs = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5] for ci in range(chords): ch = PROGRESSION[ci % 4] base = ci * BEATS_PER_CHORD for sp in stabs: v = int((85 + (hash((ci, sp)) % 10)) * vel_mult) for pitch in ch["triad"]: notes.append(_mn(base + sp, 0.12, pitch, v)) return {CH_BELL: notes} def bell_sparse(bars, vel_mult=1.0): """Sparse bell for intro — 4-note voicings, beats 2.5 and 6.5.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD for ci in range(chords): ch = PROGRESSION[ci % 4] base = ci * BEATS_PER_CHORD for sp in [2.5, 6.5]: v = int(60 * vel_mult) for pitch in ch["chord"]: notes.append(_mn(base + sp, 0.15, pitch, v)) return {CH_BELL: notes} def lead_hook(bars, vel_mult=1.0): """Lead melody — arch contour, chord tones on strong beats.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD for ci in range(chords): ch = PROGRESSION[ci % 4] base = ci * BEATS_PER_CHORD lr = ch["root"] c = ch["triad"] v = vel_mult notes.append(_mn(base + 0.0, 1.0, c[0], int(95*v))) notes.append(_mn(base + 1.0, 0.5, c[1], int(85*v))) notes.append(_mn(base + 1.5, 0.5, c[2], int(100*v))) notes.append(_mn(base + 2.0, 1.5, lr, int(105*v))) notes.append(_mn(base + 3.5, 0.5, c[2], int(90*v))) notes.append(_mn(base + 4.0, 0.5, c[1], int(80*v))) notes.append(_mn(base + 4.5, 1.5, c[0], int(95*v))) notes.append(_mn(base + 6.0, 0.5, lr-2, int(75*v))) notes.append(_mn(base + 6.5, 1.5, c[0], int(90*v))) return {CH_LEAD: notes} def pad_sustained(bars, vel_mult=1.0): """Sustained pad — 4-note voicings.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD for ci in range(chords): ch = PROGRESSION[ci % 4] base = ci * BEATS_PER_CHORD for pitch in ch["chord"]: notes.append(_mn(base, 7.5, pitch, int(60 * vel_mult))) return {CH_PAD: notes} def pad_swell(bars, vel_mult=1.0): """Pad swell for pre-chorus — crescendo within chord.""" notes = [] total = bars * 4 chords = total // BEATS_PER_CHORD for ci in range(chords): ch = PROGRESSION[ci % 4] base = ci * BEATS_PER_CHORD for pitch in ch["chord"]: notes.append(_mn(base, 4.0, pitch, int(45 * vel_mult))) notes.append(_mn(base + 4, 3.5, pitch, int(70 * vel_mult))) return {CH_PAD: notes} # ══════════════════════════════════════════════════════════════════════════════ # PATTERN DEFINITIONS — 20 patterns # ══════════════════════════════════════════════════════════════════════════════ # All generators return {ch_idx: [notes]} ALL_GENERATORS = { "dembow_kick": dembow_kick, "perreador_kick": perreador_kick, "sparse_kick": sparse_kick, "snare_std": snare_standard, "snare_intense": snare_intense, "hh_offbeat": hihat_offbeat, "hh_8th": hihat_8th, "hh_16th": hihat_16th, "clap_std": clap_standard, "clap_soft": clap_soft, "perc_offbeat": perc_offbeat, "rim_build": rim_build, "bass_full": bass_808_full, "bass_sparse": bass_808_sparse, "bell_chords": bell_chords, "bell_sparse": bell_sparse, "lead_hook": lead_hook, "pad_sustained": pad_sustained, "pad_swell": pad_swell, } PATTERNS = [ {"id": 1, "name": "Kick Dembow", "gen": "dembow_kick", "bars": 8}, {"id": 2, "name": "Kick Perreador", "gen": "perreador_kick","bars": 8}, {"id": 3, "name": "Kick Sparse", "gen": "sparse_kick", "bars": 8}, {"id": 4, "name": "Snare Standard", "gen": "snare_std", "bars": 8}, {"id": 5, "name": "Snare Intense", "gen": "snare_intense", "bars": 8}, {"id": 6, "name": "HH Offbeat", "gen": "hh_offbeat", "bars": 8}, {"id": 7, "name": "HH 8th", "gen": "hh_8th", "bars": 8}, {"id": 8, "name": "HH 16th Full", "gen": "hh_16th", "bars": 8}, {"id": 9, "name": "Clap Standard", "gen": "clap_std", "bars": 8}, {"id": 10, "name": "Perc Offbeat", "gen": "perc_offbeat", "bars": 8}, {"id": 11, "name": "Rim Build", "gen": "rim_build", "bars": 4}, {"id": 12, "name": "808 Bass Full", "gen": "bass_full", "bars": 8}, {"id": 13, "name": "808 Bass Sparse", "gen": "bass_sparse", "bars": 8}, {"id": 14, "name": "Bell Chords", "gen": "bell_chords", "bars": 8}, {"id": 15, "name": "Bell Sparse", "gen": "bell_sparse", "bars": 8}, {"id": 16, "name": "Lead Hook", "gen": "lead_hook", "bars": 8}, {"id": 17, "name": "Pad Sustained", "gen": "pad_sustained", "bars": 8}, {"id": 18, "name": "Pad Swell", "gen": "pad_swell", "bars": 8}, ] # ══════════════════════════════════════════════════════════════════════════════ # ARRANGEMENT — 48 bars, 7 sections # 10 tracks (one per sampler channel Ch10-19) # Track index in arrangement: 0=kick, 1=snare, 2=hh, 3=808, 4=bell, # 5=lead, 6=pad, 7=clap, 8=perc, 9=rim # ══════════════════════════════════════════════════════════════════════════════ ARRANGEMENT_ITEMS = [ # INTRO (0-4): ghostly, sparse {"pattern": 3, "bar": 0, "bars": 4, "track": 0}, # sparse kick {"pattern": 6, "bar": 0, "bars": 4, "track": 2}, # offbeat HH {"pattern": 13, "bar": 0, "bars": 4, "track": 3}, # sparse 808 {"pattern": 15, "bar": 0, "bars": 4, "track": 4}, # sparse bell {"pattern": 17, "bar": 0, "bars": 4, "track": 6}, # pad sustained # VERSE 1 (4-12): warming up {"pattern": 1, "bar": 4, "bars": 8, "track": 0}, # dembow kick {"pattern": 4, "bar": 4, "bars": 8, "track": 1}, # snare std {"pattern": 7, "bar": 4, "bars": 8, "track": 2}, # HH 8th {"pattern": 12, "bar": 4, "bars": 8, "track": 3}, # 808 full {"pattern": 15, "bar": 4, "bars": 8, "track": 4}, # sparse bell {"pattern": 17, "bar": 4, "bars": 8, "track": 6}, # pad # PRE-CHORUS (12-16): building tension {"pattern": 1, "bar": 12, "bars": 4, "track": 0}, # dembow kick {"pattern": 5, "bar": 12, "bars": 4, "track": 1}, # snare intense {"pattern": 11, "bar": 12, "bars": 4, "track": 9}, # rim build {"pattern": 7, "bar": 12, "bars": 4, "track": 2}, # HH 8th {"pattern": 12, "bar": 12, "bars": 4, "track": 3}, # 808 full {"pattern": 14, "bar": 12, "bars": 4, "track": 4}, # bell chords {"pattern": 18, "bar": 12, "bars": 4, "track": 6}, # pad swell # CHORUS (16-24): FULL ENERGY {"pattern": 2, "bar": 16, "bars": 8, "track": 0}, # perreador kick! {"pattern": 5, "bar": 16, "bars": 8, "track": 1}, # snare intense {"pattern": 8, "bar": 16, "bars": 8, "track": 2}, # HH 16th {"pattern": 9, "bar": 16, "bars": 8, "track": 7}, # clap {"pattern": 10, "bar": 16, "bars": 8, "track": 8}, # perc offbeat {"pattern": 12, "bar": 16, "bars": 8, "track": 3}, # 808 full {"pattern": 14, "bar": 16, "bars": 8, "track": 4}, # bell chords {"pattern": 16, "bar": 16, "bars": 8, "track": 5}, # lead hook {"pattern": 17, "bar": 16, "bars": 8, "track": 6}, # pad # VERSE 2 (24-32): energy maintained, no lead {"pattern": 1, "bar": 24, "bars": 8, "track": 0}, # dembow kick {"pattern": 4, "bar": 24, "bars": 8, "track": 1}, # snare std {"pattern": 7, "bar": 24, "bars": 8, "track": 2}, # HH 8th {"pattern": 9, "bar": 24, "bars": 8, "track": 7}, # clap {"pattern": 12, "bar": 24, "bars": 8, "track": 3}, # 808 full {"pattern": 14, "bar": 24, "bars": 8, "track": 4}, # bell chords {"pattern": 17, "bar": 24, "bars": 8, "track": 6}, # pad # BREAKDOWN (32-36): stripped {"pattern": 3, "bar": 32, "bars": 4, "track": 0}, # sparse kick {"pattern": 6, "bar": 32, "bars": 4, "track": 2}, # offbeat HH {"pattern": 13, "bar": 32, "bars": 4, "track": 3}, # sparse 808 {"pattern": 15, "bar": 32, "bars": 4, "track": 4}, # sparse bell {"pattern": 17, "bar": 32, "bars": 4, "track": 6}, # pad # OUTRO (36-48): fading {"pattern": 1, "bar": 36, "bars": 12, "track": 0}, # dembow kick {"pattern": 4, "bar": 36, "bars": 12, "track": 1}, # snare std {"pattern": 7, "bar": 36, "bars": 12, "track": 2}, # HH 8th {"pattern": 17, "bar": 36, "bars": 12, "track": 6}, # pad ] # ══════════════════════════════════════════════════════════════════════════════ # HEADER BUILDER # ══════════════════════════════════════════════════════════════════════════════ def _read_ev(data, pos): s = pos ib = data[pos]; pos += 1 if ib < 64: return pos + 1, s, ib, data[s + 1], "byte" elif ib < 128: return pos + 2, s, ib, struct.unpack(" Dm -> F -> E") print(f"Samples from: libreria/reggaeton/") print(f"Channels: Ch10-19 (all sampler)") print(f"Arrangement: 48 bars, 7 sections") print("=" * 60) assert os.path.isfile(REF_FLP), f"MISSING: {REF_FLP}" ref_bytes = open(REF_FLP, "rb").read() num_channels = struct.unpack("