""" Build a COMPLETE reggaeton FLP with drums + melodic MIDI patterns. Strategy: 1. Load 20 sampler channels from reference FLP (ChannelSkeletonLoader) 2. Melodic MIDI notes go on existing EMPTY channels (3, 4, 8, 17) which are empty samplers — user assigns VST plugins in FL Studio. 3. Build 14 patterns with drum generators + inline melodic generators 4. Build 36-bar arrangement (~1:31 at 95 BPM) 5. Assemble identically to proven v15 builder — 20 channels, no VST hacks. Output: output/reggaeton_completo.flp """ import struct import sys import os # ── Paths ────────────────────────────────────────────────────────────────────── BASE = r"C:\Users\Administrator\Documents\fl_control" SAMPLES_DIR = os.path.join(BASE, "output", "samples") 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_completo.flp") sys.path.insert(0, BASE) from src.flp_builder.events import ( EventID, encode_text_event, encode_word_event, encode_data_event, encode_byte_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, ) from src.composer.rhythm import get_notes # ── Constants ────────────────────────────────────────────────────────────────── BPM = 95 PPQ = 96 # Channel indices — drums (from rhythm.py) CH_P1 = 10; CH_K = 11; CH_S = 12; CH_R = 13 CH_P2 = 14; CH_H = 15; CH_CL = 16 # Channel indices — melodic (reuse empty sampler channels from reference) # Ch 3, 4, 8, 17 are empty samplers (no sample loaded, cloned from ch11 tmpl) # MIDI notes go here — user assigns VSTs manually in FL Studio CH_808 = 3 CH_PIANO = 4 CH_LEAD = 8 CH_PAD = 17 # ── Chord Progression: Am → G → F → G (each chord = 2 bars = 8 beats) ────── PROGRESSION = [ { "name": "Am", "bass_root": 45, # A2 "chord": [57, 60, 64], # A3, C4, E4 "pad": [45, 48, 52], # A2, C3, E3 "lead_root": 69, # A4 }, { "name": "G", "bass_root": 43, # G2 "chord": [55, 59, 62], # G3, B3, D4 "pad": [43, 47, 50], # G2, B2, D3 "lead_root": 67, # G4 }, { "name": "F", "bass_root": 41, # F2 "chord": [53, 57, 60], # F3, A3, C4 "pad": [41, 45, 48], # F2, A2, C3 "lead_root": 65, # F4 }, { "name": "G", "bass_root": 43, "chord": [55, 59, 62], "pad": [43, 47, 50], "lead_root": 67, }, ] BEATS_PER_CHORD = 8 # 2 bars per chord # ══════════════════════════════════════════════════════════════════════════════ # MELODIC GENERATORS (inline) # ══════════════════════════════════════════════════════════════════════════════ def _note(pos, length, key, vel): return {"pos": pos, "len": length, "key": key, "vel": vel} def bass_808_notes(bars): """808 bass following root notes. Pattern per chord (2 bars): Beat 0: root vel110 dur3 | Beat 3.5: root vel90 dur1.5 Beat 5: root vel100 dur2 | Beat 7.5: root vel85 dur0.5 """ notes = [] total_beats = bars * 4 chords_needed = total_beats // BEATS_PER_CHORD for ci in range(chords_needed): ch = PROGRESSION[ci % len(PROGRESSION)] base = ci * BEATS_PER_CHORD root = ch["bass_root"] notes.append(_note(base + 0.0, 3.0, root, 110)) notes.append(_note(base + 3.5, 1.5, root, 90)) notes.append(_note(base + 5.0, 2.0, root, 100)) notes.append(_note(base + 7.5, 0.5, root, 85)) return {CH_808: notes} def piano_stabs_notes(bars): """Offbeat piano stabs: beats 1.5, 2.5, 3.5, 5.5, 6.5, 7.5 per chord. 3-note triads, vel 80-90.""" notes = [] total_beats = bars * 4 chords_needed = total_beats // BEATS_PER_CHORD stab_positions = [1.5, 2.5, 3.5, 5.5, 6.5, 7.5] for ci in range(chords_needed): ch = PROGRESSION[ci % len(PROGRESSION)] base = ci * BEATS_PER_CHORD for sp in stab_positions: vel = 80 + (hash((ci, sp)) % 11) for pitch in ch["chord"]: notes.append(_note(base + sp, 0.15, pitch, vel)) return {CH_PIANO: notes} def piano_sparse_notes(bars): """Sparse piano for intro/breakdown: beats 2.5 and 6.5 only, vel 65-70.""" notes = [] total_beats = bars * 4 chords_needed = total_beats // BEATS_PER_CHORD for ci in range(chords_needed): ch = PROGRESSION[ci % len(PROGRESSION)] base = ci * BEATS_PER_CHORD for sp in [2.5, 6.5]: vel = 65 + (hash((ci, sp)) % 6) for pitch in ch["chord"]: notes.append(_note(base + sp, 0.15, pitch, vel)) return {CH_PIANO: notes} def lead_hook_notes(bars): """Melodic hook emphasizing chord tones per 2-bar cycle.""" notes = [] total_beats = bars * 4 chords_needed = total_beats // BEATS_PER_CHORD for ci in range(chords_needed): ch = PROGRESSION[ci % len(PROGRESSION)] base = ci * BEATS_PER_CHORD lr = ch["lead_root"] notes.append(_note(base + 0.0, 1.0, ch["chord"][0], 100)) notes.append(_note(base + 1.0, 0.5, ch["chord"][2], 95)) notes.append(_note(base + 2.0, 0.75, ch["chord"][1], 90)) notes.append(_note(base + 3.5, 0.25, lr, 85)) notes.append(_note(base + 5.0, 0.5, ch["chord"][2], 95)) notes.append(_note(base + 5.5, 1.0, ch["chord"][0], 100)) notes.append(_note(base + 6.5, 0.5, lr + 2, 80)) return {CH_LEAD: notes} def pad_sustained_notes(bars): """Long sustained pad chords. 3 notes per chord, vel 65, dur 7.5 beats.""" notes = [] total_beats = bars * 4 chords_needed = total_beats // BEATS_PER_CHORD for ci in range(chords_needed): ch = PROGRESSION[ci % len(PROGRESSION)] base = ci * BEATS_PER_CHORD for pitch in ch["pad"]: notes.append(_note(base + 0.0, 7.5, pitch, 65)) return {CH_PAD: notes} MELODIC_GENERATORS = { "bass_808_notes": bass_808_notes, "piano_stabs_notes": piano_stabs_notes, "piano_sparse_notes": piano_sparse_notes, "lead_hook_notes": lead_hook_notes, "pad_sustained_notes": pad_sustained_notes, } # ══════════════════════════════════════════════════════════════════════════════ # PATTERN DEFINITIONS # ══════════════════════════════════════════════════════════════════════════════ PATTERNS = [ {"id": 1, "name": "Kick Main", "generator": "kick_main_notes", "bars": 8}, {"id": 2, "name": "Kick Sparse", "generator": "kick_sparse_notes", "bars": 8}, {"id": 3, "name": "Snare Verse", "generator": "snare_verse_notes", "bars": 8}, {"id": 4, "name": "Hihat 16th", "generator": "hihat_16th_notes", "bars": 8}, {"id": 5, "name": "Hihat 8th", "generator": "hihat_8th_notes", "bars": 8}, {"id": 6, "name": "Clap 24", "generator": "clap_24_notes", "bars": 8}, {"id": 7, "name": "Rim Build", "generator": "rim_build_notes", "bars": 4}, {"id": 8, "name": "Perc Combo", "generator": "perc_combo_notes", "bars": 8}, {"id": 9, "name": "Kick Outro", "generator": "kick_outro_notes", "bars": 8}, {"id": 10, "name": "808 Bass", "generator": "bass_808_notes", "bars": 8, "melodic": True}, {"id": 11, "name": "Piano Stabs", "generator": "piano_stabs_notes", "bars": 8, "melodic": True}, {"id": 12, "name": "Piano Sparse", "generator": "piano_sparse_notes","bars": 8, "melodic": True}, {"id": 13, "name": "Lead Hook", "generator": "lead_hook_notes", "bars": 8, "melodic": True}, {"id": 14, "name": "Pad Sustained","generator": "pad_sustained_notes","bars": 8, "melodic": True}, ] # ══════════════════════════════════════════════════════════════════════════════ # ARRANGEMENT (36 bars = ~1:31 at 95 BPM) # 9 arrangement tracks # ══════════════════════════════════════════════════════════════════════════════ ARRANGEMENT_ITEMS = [ # INTRO (0-4) {"pattern": 2, "bar": 0, "bars": 4, "track": 0}, {"pattern": 5, "bar": 0, "bars": 4, "track": 2}, {"pattern": 14, "bar": 0, "bars": 4, "track": 8}, {"pattern": 12, "bar": 0, "bars": 4, "track": 6}, # VERSE (4-12) {"pattern": 1, "bar": 4, "bars": 8, "track": 0}, {"pattern": 3, "bar": 4, "bars": 8, "track": 1}, {"pattern": 4, "bar": 4, "bars": 8, "track": 2}, {"pattern": 8, "bar": 4, "bars": 8, "track": 4}, {"pattern": 10, "bar": 4, "bars": 8, "track": 5}, {"pattern": 11, "bar": 4, "bars": 8, "track": 6}, {"pattern": 14, "bar": 4, "bars": 8, "track": 8}, # PRE-CHORUS (12-16) {"pattern": 1, "bar": 12, "bars": 4, "track": 0}, {"pattern": 3, "bar": 12, "bars": 4, "track": 1}, {"pattern": 4, "bar": 12, "bars": 4, "track": 2}, {"pattern": 7, "bar": 12, "bars": 4, "track": 3}, {"pattern": 8, "bar": 12, "bars": 4, "track": 4}, {"pattern": 10, "bar": 12, "bars": 4, "track": 5}, {"pattern": 11, "bar": 12, "bars": 4, "track": 6}, {"pattern": 14, "bar": 12, "bars": 4, "track": 8}, # CHORUS (16-24) {"pattern": 1, "bar": 16, "bars": 8, "track": 0}, {"pattern": 3, "bar": 16, "bars": 8, "track": 1}, {"pattern": 4, "bar": 16, "bars": 8, "track": 2}, {"pattern": 6, "bar": 16, "bars": 8, "track": 3}, {"pattern": 8, "bar": 16, "bars": 8, "track": 4}, {"pattern": 10, "bar": 16, "bars": 8, "track": 5}, {"pattern": 11, "bar": 16, "bars": 8, "track": 6}, {"pattern": 13, "bar": 16, "bars": 8, "track": 7}, {"pattern": 14, "bar": 16, "bars": 8, "track": 8}, # BREAKDOWN (24-28) {"pattern": 5, "bar": 24, "bars": 4, "track": 2}, {"pattern": 14, "bar": 24, "bars": 4, "track": 8}, {"pattern": 12, "bar": 24, "bars": 4, "track": 6}, # OUTRO (28-36) {"pattern": 9, "bar": 28, "bars": 8, "track": 0}, {"pattern": 3, "bar": 28, "bars": 8, "track": 1}, {"pattern": 4, "bar": 28, "bars": 8, "track": 2}, {"pattern": 14, "bar": 28, "bars": 8, "track": 8}, ] # ══════════════════════════════════════════════════════════════════════════════ # 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(" Serum2") print(f" Ch {CH_PIANO}: Piano -> Pigments") print(f" Ch {CH_LEAD}: Lead -> Serum2") print(f" Ch {CH_PAD}: Pad -> Omnisphere") return flp if __name__ == "__main__": build_complete_reggaeton()