docs: LLM-ready documentation suite — LLM_CONTEXT.md, module deep-dives, JSON schemas, CLI reference

Complete documentation system for LLM consumption: primary LLM_CONTEXT.md
(27KB system overview), 4 module deep-dives (composer, reaper-builder,
reaper-scripting, calibrator), 2 JSON Schema draft-07 contracts, CLI
reference, and README correction (FL Studio -> REAPER identity).
This commit is contained in:
renato97
2026-05-04 10:30:24 -03:00
parent b08dcccca2
commit 7bcd8052a9
13 changed files with 2402 additions and 29 deletions

544
docs/LLM_CONTEXT.md Normal file
View File

@@ -0,0 +1,544 @@
# fl_control — LLM System Overview
> **About the name**: The directory is named `fl_control` for historical reasons, but the system generates **REAPER `.rpp` files**, not FL Studio `.flp` files. There is NO FL Studio support. The name has been retained for backward compatibility with repository URLs and CI/CD pipelines.
## What Is This?
`fl_control` is a Python system that generates complete **REAPER `.rpp` projects** from the command line. Given a key, BPM, and emotion, it composes a full reggaetón instrumental with drums, 808 bass, chord progressions (with voice leading), lead melodies (hook-based call-and-response), pads, percussion, and transition FX. The output is a valid `.rpp` file that REAPER opens directly, plus an auto-generated ReaScript (`.py`) for post-processing (plugin loading, mix calibration, rendering).
**Target**: REAPER (Windows, v7.x). **Format**: `.rpp` text files. **No DAW dependencies** beyond reading sample files from disk.
## Quick Start
```bash
# Install dependencies
pip install -r requirements.txt
# Generate a song (defaults: 95 BPM, Am key, romantic emotion)
python scripts/generate.py --bpm 95 --key Am --output output/song.rpp --seed 42
# Generate with validation
python scripts/generate.py --bpm 95 --key Dm --output output/song.rpp --seed 42 --validate
# Compose directly (more detailed output)
python scripts/compose.py --bpm 99 --key Am --emotion romantic --output output/song.rpp
```
Output: a ready-to-open `.rpp` file at the specified path.
## Architecture
```
┌───────────────────────────────────────┐
│ CLI Layer │
│ scripts/generate.py (thin wrapper) │
│ scripts/compose.py (full pipeline) │
│ scripts/run_in_reaper.py (ReaScript) │
└──────────────┬────────────────────────┘
┌──────────────────────┼──────────────────────┐
│ ▼ │
┌────────────────┴──────┐ ┌────────────────────┴──┐ ┌───────┴──────────┐
│ src/composer/ │ │ src/calibrator/ │ │ src/selector/ │
│ ┌──────────────────┐ │ │ ┌────────────────┐ │ │ SampleSelector │
│ │ chords.py │ │ │ │ Calibrator │ │ └────────────────┘
│ │ ChordEngine │ │ │ │ .apply() │ │
│ │ melody_engine.py │ │ │ │ presets.py │ └───────────────────┘
│ │ build_motif() │ │ │ │ VOLUME_PRESETS │
│ │ patterns.py │ │ │ │ PAN_PRESETS │ ┌──────────────────┐
│ │ rhythm.py │ │ │ │ SEND_PRESETS │ │ src/core/ │
│ │ templates.py │ │ │ └────────────────┘ │ schema.py │
│ └──────────────────┘ │ └─────────────────────────────┘ │ SongDefinition │
└───────────┬────────────┘ │ TrackDef │
│ │ ClipDef │
▼ │ MidiNote │
┌──────────────────────────┐ │ PluginDef │
│ SongDefinition │◄──────────────────────────────│ SectionDef │
│ ┌────────────────────┐ │ │ PatternDef │
│ │ meta: SongMeta │ │ │ Arrangement... │
│ │ tracks: [TrackDef] │ │ │ CCEvent │
│ │ patterns: [...] │ │ └──────────────────┘
│ │ sections: [...] │ │
│ │ master_plugins │ │
│ └────────────────────┘ │
└───────────┬──────────────┘
┌─────────┼─────────┐
▼ ▼ ▼
┌──────────────┐ ┌───────────────┐ ┌─────────────────┐
│ RPPBuilder │ │ ReaScriptGen │ │ Validator │
│ .write() → │ │ generate() → │ │ validate_rpp() │
│ song.rpp │ │ fl_control_ │ │ │
│ │ │ phase2.py │ │ │
└──────────────┘ └───────┬───────┘ └─────────────────┘
┌──────────────┐
│ REAPER │
│ opens .rpp │
│ runs script │
│ renders WAV │
└──────────────┘
```
**Pipeline**: CLI → compose `SongDefinition` → Calibrator.apply() (post-processing) → RPPBuilder.write() → `.rpp` file → ReaScriptGenerator.generate() → ReaScript → REAPER execution.
## Data Model
All types live in `src/core/schema.py`. Every entity is a Python dataclass.
### SongMeta
Song-level metadata.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `bpm` | `float` | *(required)* | Tempo 20999 |
| `key` | `str` | *(required)* | Key string, e.g. `"Am"`, `"Dm"`, `"G"` |
| `title` | `str` | `""` | Song title |
| `ppq` | `int` | `960` | Ticks per quarter note (REAPER default) |
| `time_sig_num` | `int` | `4` | Time signature numerator |
| `time_sig_den` | `int` | `4` | Time signature denominator |
| `calibrate` | `bool` | `True` | Enable post-processing mix calibration |
### MidiNote
A single MIDI note event within a clip.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `pitch` | `int` | *(required)* | MIDI note number 0127 (60 = middle C) |
| `start` | `float` | *(required)* | Start time in beats from item start |
| `duration` | `float` | *(required)* | Duration in beats |
| `velocity` | `int` | `64` | Velocity 0127 |
### CCEvent
A MIDI CC event within a clip.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `controller` | `int` | *(required)* | CC number (e.g. 11 = Expression) |
| `time` | `float` | *(required)* | Position in beats from clip start |
| `value` | `int` | *(required)* | CC value 0127 |
### ClipDef
A clip placed on a track — either audio or MIDI.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `position` | `float` | *(required)* | Start position in beats |
| `length` | `float` | *(required)* | Duration in beats |
| `name` | `str` | `""` | Display name |
| `audio_path` | `str \| None` | `None` | Absolute path to audio file (audio clips) |
| `midi_notes` | `list[MidiNote]` | `[]` | MIDI notes (MIDI clips) |
| `midi_cc` | `list[CCEvent]` | `[]` | MIDI CC events |
| `loop` | `bool` | `False` | Whether the audio clip loops |
| `fade_in` | `float` | `0.0` | Fade-in duration in seconds |
| `fade_out` | `float` | `0.0` | Fade-out duration in seconds |
| `vol_mult` | `float` | `1.0` | Volume multiplier applied at clip level |
Properties: `is_midi` → True when `midi_notes` is non-empty; `is_audio` → True when `audio_path` is not None.
### PluginDef
A VST plugin instance on a track.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `str` | *(required)* | Display name (e.g. `"Serum 2"`) |
| `path` | `str` | *(required)* | Plugin path/identifier |
| `index` | `int` | `0` | Chain position (0 = first) |
| `params` | `dict[int, float]` | `{}` | Parameter index → value |
| `preset_data` | `list[str] \| None` | `None` | Base64 preset chunks |
| `role` | `str` | `""` | Track role for role-aware preset lookup |
| `builtin` | `bool` | `False` | True → deferred to ReaScript, not written to .rpp |
### TrackDef
A track in the REAPER project.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `str` | *(required)* | Track display name |
| `volume` | `float` | `0.85` | 0.01.0 (maps to REAPER volume fader) |
| `pan` | `float` | `0.0` | -1.0 (left) to 1.0 (right) |
| `color` | `int` | `0` | REAPER color index 067 |
| `clips` | `list[ClipDef]` | `[]` | Audio/MIDI clips |
| `plugins` | `list[PluginDef]` | `[]` | VST plugins on this track |
| `send_reverb` | `float` | `0.0` | Reverb send level 0.01.0 |
| `send_delay` | `float` | `0.0` | Delay send level 0.01.0 |
| `send_level` | `dict[int, float]` | `{}` | Return track index → send level |
### SectionDef
A section in the song arrangement.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | `str` | *(required)* | Display name (e.g. `"intro"`, `"chorus"`) |
| `bars` | `int` | *(required)* | Length in bars |
| `energy` | `float` | `0.5` | Energy level 0.01.0 |
| `velocity_mult` | `float` | `1.0` | Velocity multiplier for all notes in section |
| `vol_mult` | `float` | `1.0` | Volume multiplier for tracks in section |
### PatternDef
A pattern definition with generator and variation axes.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `id` | `int` | *(required)* | Unique pattern ID |
| `name` | `str` | *(required)* | Display name |
| `instrument` | `str` | *(required)* | Sample/instrument key |
| `channel` | `int` | *(required)* | MIDI channel |
| `bars` | `int` | *(required)* | Length in bars |
| `generator` | `str` | *(required)* | Generator function name |
| `velocity_mult` | `float` | `1.0` | Velocity multiplier 0.851.1 |
| `density` | `float` | `1.0` | Note density 0.01.0 |
### ArrangementItemDef
An item placed in the arrangement referencing a pattern on a track.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `pattern` | `int` | *(required)* | Pattern ID |
| `bar` | `float` | *(required)* | Start position in bars |
| `bars` | `float` | *(required)* | Length in bars |
| `track` | `int` | *(required)* | Track index |
### ArrangementTrack
A track in the REAPER arrangement with index and display name.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `index` | `int` | *(required)* | Track index |
| `name` | `str` | *(required)* | Display name |
### SongDefinition
Complete song definition — the source of truth for one `.rpp` file.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `meta` | `SongMeta` | *(required)* | Song metadata |
| `tracks` | `list[TrackDef]` | `[]` | REAPER tracks with clips and plugins |
| `patterns` | `list[PatternDef]` | `[]` | Pattern definitions for arrangement |
| `items` | `list[ArrangementItemDef]` | `[]` | Arrangement items referencing patterns |
| `progression_name` | `str` | `"i-VII-VI-VII"` | Chord progression label |
| `section_template` | `str` | `"standard"` | Section template name |
| `samples` | `dict[str, str]` | `{}` | Sample file map (name → filename) |
| `sections` | `list[SectionDef]` | `[]` | Section definitions in playback order |
| `master_plugins` | `list[str]` | `[]` | Plugin registry keys for master FX chain |
Key methods:
- `validate()``list[str]` — returns list of validation errors (empty = valid)
- `length_beats` (property) → `float` — total length computed from all clips
- `to_json(indent=2)``str` — serialize to JSON via `dataclasses.asdict`
## Module Index
| Module | Location | Role |
|--------|----------|------|
| **schema** | `src/core/schema.py` | All dataclass definitions. Single source of truth for data model. |
| **composer** | `src/composer/` | Composition engine: chord progression, melody generation, rhythm patterns, drum analysis |
| **reaper_builder** | `src/reaper_builder/` | RPPBuilder: `.rpp` file generation from `SongDefinition`. Plugin registry (~97 entries). VST2/VST3 element builders. Headless render. |
| **reaper_scripting** | `src/reaper_scripting/` | ReaScript generation: self-contained Python scripts for REAPER post-processing |
| **calibrator** | `src/calibrator/` | Post-processing mix calibration: volume, pan, sends, master chain by track role |
| **selector** | `src/selector/` | Sample selection: scores samples by key compatibility, BPM proximity, character |
| **validator** | `src/validator/` | `.rpp` output validation |
| **CLI scripts** | `scripts/` | `compose.py` (full pipeline), `generate.py` (thin wrapper), `run_in_reaper.py` (ReaScript execution) |
### composer/ sub-modules
| File | Role |
|------|------|
| `chords.py` | `ChordEngine` — emotion-aware chord progressions with voice leading |
| `melody_engine.py` | `build_motif()`, `build_call_response()` — hook-based melody generation |
| `patterns.py` | Pattern weight tables extracted from real reggaetón tracks |
| `rhythm.py` | Dembow rhythm generators (classic, perreo, trápico) |
| `templates.py` | RPP template extraction and generation |
| `converters.py` | Conversion utilities |
| `variation.py` | Pattern variation logic |
| `melodic.py` | Melodic pattern generators |
| `drum_analyzer.py` | `DrumLoopAnalyzer` — transient detection for sidechain CC generation |
| `__init__.py` | Scale intervals, chord type tables, legacy pattern generators |
## Pipeline
The complete composition and build pipeline, step by step:
### Phase 1: Composition (`scripts/compose.py`)
1. **Parse CLI args**: `--bpm`, `--key`, `--emotion`, `--inversion`, `--seed`, `--no-calibrate`
2. **Load `SampleSelector`**: Reads `data/sample_index.json` for clap/snare/FX sample selection
3. **Build sections**: 9-section structure (intro → verse → pre-chorus → chorus → verse2 → chorus2 → bridge → final → outro), each with energy level and velocity/volume multipliers
4. **Build tracks** (in order):
- `Drumloop` — audio clips cycling between seco/filtrado variants per section
- `Perc` — percussion audio loops per section
- `808 Bass` — MIDI notes using proven i-iv-i-V harmonic pattern with CC11 sidechain ducking
- `Chords``ChordEngine.progression()` with voice leading; i-VII-VI-VII progression
- `Lead``build_motif("hook")` + `build_call_response()` via melody engine
- `Clap` — Short audio samples on dembow grid (beats 2.0, 3.5)
- `Transition FX` — Audio clips at section boundaries (risers, impacts, sweeps)
- `Pad` — Arpeggiated chord tones on eighth notes
- `Reverb` / `Delay` — Return tracks with Pro-R 2 and ValhallaDelay
5. **Wire sends**: Return track sends by role from `SEND_LEVELS`
6. **Assemble `SongDefinition`**: Meta + tracks + sections + master_plugins
7. **Calibrate** (unless `--no-calibrate`): `Calibrator.apply(song)` — role-based volume, pan, sends, master chain
### Phase 2: Build (`src/reaper_builder/RPPBuilder`)
1. `RPPBuilder(song, seed).write(path)` serializes to `.rpp`:
- Project header (static metadata from ground-truth `test_vst3.rpp`)
- Dynamic TEMPO line
- Master track with FX chain (Ozone 12 triplet or FabFilter fallback)
- Per-track TRACK elements with VOLPAN, AUXRECV (sends)
- Per-track FXCHAIN with VST elements (resolved from `PLUGIN_REGISTRY`)
- Per-clip ITEM elements with SOURCE (WAVE or MIDI with E-lines)
- Built-in plugins (Cockos) deferred to ReaScript insertion
### Phase 3: ReaScript (Phase 2B)
`scripts/run_in_reaper.py` generates a self-contained Python ReaScript that REAPER executes:
1. Reads `fl_control_command.json` (command file)
2. Opens the `.rpp` project
3. Executes action pipeline: `add_plugins``configure_fx_params``verify_fx``calibrate``render`
4. Writes `fl_control_result.json` (LUFS metrics, FX errors, plugin results)
### Validation
`SongDefinition.validate()` checks: BPM range (20999), key format (regex), unique track names, clips with neither audio nor MIDI.
## Plugin System
### PLUGIN_REGISTRY
Located in `src/reaper_builder/__init__.py` (~97 entries). Format:
```python
PLUGIN_REGISTRY: dict[str, tuple[str, str, str]] = {
"key": ("display_name", "filename", "uid_guid"),
}
```
- **key**: Short registry key (e.g. `"Serum_2"`, `"Pro-Q_3"`, `"Decapitator"`)
- **display_name**: Full REAPER display name (e.g. `"VST3i: Serum 2 (Xfer Records)"`)
- **filename**: File on disk (e.g. `"Serum2.vst3"`, `"Decapitator.dll"`)
- **uid_guid**: Unique identifier — VST2 uses `<GUID>`, VST3 uses `{GUID}`
### ALIAS_MAP
Backward compatibility mapping: old key names → new `PLUGIN_REGISTRY` keys. Example:
```python
ALIAS_MAP = {
"Serum2": "Serum_2",
"FabFilter Pro-Q 3": "Pro-Q_3",
"Valhalla Delay": "ValhallaDelay",
"Pro-Q 3": "Pro-Q_3",
}
```
FabFilter plugins map space-separated names to short VST3 keys. SoundToys plugins keep their underscore names.
### PLUGIN_PRESETS
Role-aware preset data. Format:
```python
PLUGIN_PRESETS: dict[tuple[str, str], list[str]] = {
("Pro-Q_3", ""): [chunk1, chunk2, ...], # default
("Serum_2", "bass"): [chunk1, chunk2, ...], # role-specific
}
```
Lookup chain: `(key, role)``(key, "")` (default) → `fallback``None`.
### REAPER_BUILTINS
Set of Cockos native plugin keys (ReaEQ, ReaComp, ReaVerb, etc.). Plugins matching this are deferred to ReaScript insertion (`TrackFX_AddByName`) rather than written to `.rpp` as VST elements.
### Plugin Lookup in RPPBuilder
```
PluginDef.name → ALIAS_MAP.get(name, name) → PLUGIN_REGISTRY.get(resolved)
┌───────────────┤
found│ │not found
▼ ▼
Build VST element Fallback: .dll
with display_name, with 19 param
filename, uid_guid slots
```
## ReaScript Protocol
### Overview
ReaScript is a self-contained Python file generated by `ReaScriptGenerator` that REAPER executes internally. It communicates via JSON files on disk:
```
fl_control_command.json ──read──► ReaScript ──write──► fl_control_result.json
```
### ReaScriptCommand
Defined in `src/reaper_scripting/commands.py`:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `version` | `int` | `1` | Protocol version |
| `action` | `str \| list[str]` | `"calibrate"` | Single action or pipeline list |
| `rpp_path` | `str` | `""` | Path to `.rpp` file to open |
| `render_path` | `str` | `""` | Path for rendered WAV output |
| `timeout` | `int` | `120` | Polling timeout in seconds |
| `track_calibration` | `list[dict]` | `[]` | Volume/pan/send calibration per track |
| `plugins_to_add` | `list[dict]` | `[]` | `{"track_name": str, "fx_name": str, "params": {...}}` |
### ReaScriptResult
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `version` | `int` | `1` | Protocol version |
| `status` | `str` | `"ok"` | `"ok"`, `"error"`, or `"timeout"` |
| `message` | `str` | `""` | Error or status message |
| `lufs` | `float \| None` | `None` | Integrated LUFS |
| `integrated_lufs` | `float \| None` | `None` | Integrated LUFS reading |
| `short_term_lufs` | `float \| None` | `None` | Short-term LUFS |
| `fx_errors` | `list[dict]` | `[]` | FX verification errors |
| `tracks_verified` | `int` | `0` | Number of tracks verified |
| `added_plugins` | `list[dict]` | `[]` | `{"fx_name", "instance_id", "track_name", "status"}` |
### ProtocolVersionError
Raised by `read_result()` when result version doesn't match expected version.
### Key Functions
- `write_command(path, cmd)` — Serialize `ReaScriptCommand` to JSON file
- `read_result(path, expected_version=1)` — Deserialize `ReaScriptResult` from JSON file. Raises `ProtocolVersionError` on version mismatch.
### Known Actions
`add_plugins`, `configure_fx_params`, `verify_fx`, `calibrate`, `render`
### ReaScript JSON Parser
Since REAPER's internal Python has no `json` module, the ReaScript includes a hand-rolled JSON parser (~100 lines) that handles strings, integers, floats, booleans, nulls, nested objects, and arrays via string splitting.
## CLI Reference
### scripts/compose.py
Main composition pipeline. Builds a full reggaetón track from scratch.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--bpm` | `float` | `99` | Tempo |
| `--key` | `str` | `"Am"` | Musical key |
| `--output` | `str` | `"output/song.rpp"` | Output `.rpp` path |
| `--seed` | `int` | `None` | Random seed for determinism |
| `--emotion` | `str` | `"romantic"` | `romantic` / `dark` / `club` / `classic` |
| `--inversion` | `str` | `"root"` | `root` / `first` / `second` |
| `--no-calibrate` | `flag` | `False` | Skip post-processing mix calibration |
### scripts/generate.py
Thin wrapper around `compose.py`. Delegates via `sys.argv` manipulation.
| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--bpm` | `float` | `95` | Tempo |
| `--key` | `str` | `"Am"` | Musical key |
| `--output` | `str` | `"output/song.rpp"` | Output `.rpp` path |
| `--seed` | `int` | `42` | Random seed |
| `--emotion` | `str` | `"romantic"` | Chord emotion |
| `--inversion` | `str` | `"root"` | Chord inversion |
| `--validate` | `flag` | `False` | Run `validate_rpp_output()` after generation |
### scripts/run_in_reaper.py
ReaScript execution CLI. Generates and runs a Phase 2 ReaScript.
| Arg/Flag | Type | Default | Description |
|----------|------|---------|-------------|
| `rpp_path` | positional | *(required)* | Path to `.rpp` file |
| `--output`, `-o` | `str` | auto-derived | Rendered WAV output path |
| `--timeout` | `int` | `120` | Seconds to poll for result JSON |
| `--plugins-config` | `str` | `None` | JSON SongDefinition for deriving plugins_to_add |
| `--action` | `str` | `"calibrate"` | Space-separated action pipeline |
Example:
```bash
python scripts/run_in_reaper.py output/song.rpp --action "add_plugins calibrate render" --timeout 300
```
## Conventions
From `AGENTS.md`:
- **Python**: Type hints on all function signatures
- **Dataclasses**: Over dicts for structured data
- **Deterministic output**: Seed-based RNG (`random.Random(seed)`), no global random state
- **No bare except clauses**
- **Testing**: `pytest` only. Unit tests for all new functions. Integration tests for end-to-end flows.
- **Architecture**: Separate modules by concern (calibrator, composer, builder, selector, validator)
- **Post-processing**: Calibrator.apply() pattern — modify in-place after construction, not inline
- **Schema changes**: Must be backward-compatible (new fields get defaults)
- **SDD**: All changes follow spec-driven development pipeline (propose → spec → design → tasks → apply → verify)
## How to Extend
### Adding a New Track Role
1. Add role to `TRACK_ACTIVITY` in `scripts/compose.py` — define which sections it plays in
2. Add volume/presets to `src/calibrator/presets.py` (`VOLUME_PRESETS`, `PAN_PRESETS`, `SEND_PRESETS`)
3. Add FX chain to `FX_CHAINS` in `scripts/compose.py`
4. Write a `build_<role>_track()` function
5. Add the track to the `tracks` list in `main()`
6. Add calibration role mapping in `Calibrator._resolve_role()`
### Adding a New Plugin to the Registry
1. Scan the plugin in REAPER or extract from a ground-truth `.rpp`
2. Add entry to `PLUGIN_REGISTRY`: `"key": ("display_name", "filename", "uid_guid")`
3. If needed, add preset data to `_PRESETS_FLAT` with the key
4. Add aliases to `ALIAS_MAP` if there are alternate names
5. Use `make_plugin(registry_key, index, role)` to create `PluginDef` instances
### Adding a New CLI Flag
1. Add `parser.add_argument()` in `scripts/compose.py` (and `scripts/generate.py` if wrapping)
2. Thread the flag value through the composition pipeline
3. Pass to relevant builder/track functions
### Adding a New Emotion to ChordEngine
1. Add entry to `EMOTION_PROGRESSIONS` in `src/composer/chords.py`
2. Format: `"name": [(semitone_offset, quality), ...]` for a 4-chord loop
3. Add choice to `--emotion` argparse choices
## Known Limitations
- **Hardcoded paths**: Drumloop samples reference `C:\ProgramData\Ableton\...` paths. These must exist on the build machine or the pipeline will skip those clips.
- **Environment-specific registry**: `PLUGIN_REGISTRY` was generated from a specific REAPER/plugin installation. Different machines may have different plugin paths or GUIDs.
- **Windows only**: REAPER executable path in `render.py` defaults to `C:\Program Files\REAPER (x64)\reaper.exe`. The ReaScript REAPER API calls are Windows-specific.
- **No CI**: No automated test/validation pipeline. Tests run manually with `pytest`.
- **Single genre**: Only reggaetón is fully implemented. Other genres exist in `knowledge/` as JSON definitions but the main pipeline (`compose.py`) is hardcoded for reggaetón.
- **Sample library**: Requires pre-existing sample files in expected directories. No bundled samples.
- **ReaScript execution**: `run_in_reaper.py` generates scripts but does NOT automatically launch REAPER. REAPER must be running and monitoring the scripts directory, or the script must be executed manually from within REAPER's action list.
## Further Reading
- [CLI Reference](CLI.md) — Complete CLI documentation
- [Module: Composer](modules/composer.md) — Composition engine deep-dive
- [Module: Reaper Builder](modules/reaper-builder.md) — RPP format and plugin registry
- [Module: Reaper Scripting](modules/reaper-scripting.md) — ReaScript protocol
- [Module: Calibrator](modules/calibrator.md) — Mix calibration presets
- [JSON Schema: Song Definition](schemas/song-definition.json) — Data model schema
- [JSON Schema: ReaScript Protocol](schemas/reascript-protocol.json) — Command/result schema