Replace FL Studio binary .flp output with REAPER text-based .rpp output using the rpp Python library (Perlence/rpp). - Add core/schema.py: DAW-agnostic data types (SongDefinition, TrackDef, ClipDef, MidiNote, PluginDef) - Add reaper_builder/: RPP file generation via rpp.Element + headless render via reaper.exe CLI - Add composer/converters.py: bridge rhythm.py/melodic.py note dicts to core.schema MidiNote objects - Rewrite scripts/compose.py: real generator pipeline with --render flag - Delete src/flp_builder/, src/scanner/, mcp/, flstudio-mcp/, old scripts - Add 40 passing tests (schema, builder, converters, compose, render)
65 lines
2.0 KiB
Python
65 lines
2.0 KiB
Python
"""Converters — transform generator output to MIDI notes for SongDefinition.
|
|
|
|
rhythm generators → MidiNote list (channel → GM pitch mapping)
|
|
melodic generators → MidiNote list (note["key"] = pitch directly)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from src.core.schema import MidiNote
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GM drum pitch mapping — channels 10-16
|
|
# ---------------------------------------------------------------------------
|
|
CHANNEL_PITCH: dict[int, int] = {
|
|
10: 39, # perc (General MIDI channel 10 = percussion)
|
|
11: 36, # kick
|
|
12: 38, # snare
|
|
13: 37, # rim
|
|
14: 50, # perc2
|
|
15: 42, # hihat
|
|
16: 39, # clap
|
|
}
|
|
|
|
|
|
def rhythm_to_midi(note_dict: dict[int, list[dict]]) -> list[MidiNote]:
|
|
"""Convert rhythm generator output (channel → note list) to MidiNote list.
|
|
|
|
note_dict: {channel: [{"pos", "len", "key", "vel"}, ...]}
|
|
- channel must be in CHANNEL_PITCH (10-16)
|
|
- pitch = CHANNEL_PITCH[channel]
|
|
- start = note["pos"]
|
|
- duration = note["len"]
|
|
- velocity = note["vel"]
|
|
"""
|
|
midi_notes: list[MidiNote] = []
|
|
for channel, notes in note_dict.items():
|
|
pitch = CHANNEL_PITCH.get(channel, 60)
|
|
for note in notes:
|
|
midi_notes.append(MidiNote(
|
|
pitch=pitch,
|
|
start=note["pos"],
|
|
duration=note["len"],
|
|
velocity=note["vel"],
|
|
))
|
|
return midi_notes
|
|
|
|
|
|
def melodic_to_midi(note_list: list[dict]) -> list[MidiNote]:
|
|
"""Convert melodic generator output (list of note dicts) to MidiNote list.
|
|
|
|
note_list: [{"pos", "len", "key", "vel"}, ...]
|
|
- pitch = note["key"] (directly used, not mapped)
|
|
- start = note["pos"]
|
|
- duration = note["len"]
|
|
- velocity = note["vel"]
|
|
"""
|
|
return [
|
|
MidiNote(
|
|
pitch=note["key"],
|
|
start=note["pos"],
|
|
duration=note["len"],
|
|
velocity=note["vel"],
|
|
)
|
|
for note in note_list
|
|
] |