# Proposal: reascript-hybrid ## Intent The .rpp generator (RPPBuilder) works offline but has a hard ceiling: no verification that FX loaded in REAPER, no rendering, no loudness validation. This change adds a **Phase 2** that runs inside REAPER via ReaScript, enabling FX verification, precise mix calibration, rendering, and output validation — while keeping Phase 1 offline and composable. ## Scope ### In Scope - ReaScript generator that opens a .rpp and refines it via REAPER's native API - FX chain verification (confirm plugins loaded, report failures) - Track calibration (volume, pan, sends per track) - Audio rendering to file (using RenderProject) - Loudness validation (CalcMediaSrcLoudness) - New module: `src/reaper_scripting/__init__.py` — ReaScript generator - New script: `scripts/run_in_reaper.py` — CLI to trigger Phase 2 ### Out of Scope - Phase 1 changes (compose.py, RPPBuilder already work) - REAPER automation via OSC/HTTP (not needed yet) - Multi-DAW support ## Capabilities > This change introduces a new spec capability. The contract with sdd-spec will define exact function signatures and error handling. - `reascript-generator`: Generates Python ReaScript that runs inside REAPER to verify/mix/render .rpp projects ## Approach ### Bridge: ReaScript File + Action Trigger (Option B from discovery) 1. Our Python generates a self-contained ReaScript `.py` file to a watched folder 2. REAPER runs it via a custom Action (assigned via `__startup__.py` or manually) 3. ReaScript reads/writes state via a JSON command file for two-way communication 4. Our external Python polls/waits for completion **Why not python-reapy?** It requires REAPER running with distant API enabled and adds a network dependency. Option B is more robust: REAPER controls timing, our script is a dumb generator. ### Two-way Communication ``` Our Python REAPER (ReaScript) │ │ │--- write command.json ------------->│ │ {action: "calibrate", rpp: "..."} │ │ │ │<-- write result.json ---------------│ │ {status: "ok", lufs: -14.2} │ ``` ### Phase 2 Steps (inside REAPER via ReaScript) 1. Open .rpp via `Main_openProject` 2. Verify FX loaded: iterate tracks, call `TrackFX_GetFXName` for each slot 3. Set track volumes/pans/sends: `SetMediaTrackInfo_Value` + `CreateTrackSend` 4. Render: `Main_RenderFile` with format settings 5. Measure loudness: `CalcMediaSrcLoudness` on rendered file 6. Write result.json with status + metrics ### File Layout ``` src/reaper_scripting/ __init__.py # ReaScriptGenerator class commands.py # command protocol (read/write JSON) scripts/ run_in_reaper.py # CLI: run phase2 on a .rpp file ``` ## Affected Areas | Area | Impact | Description | |------|--------|-------------| | `src/reaper_scripting/__init__.py` | New | ReaScript generator + command protocol | | `scripts/run_in_reaper.py` | New | CLI entry point for Phase 2 | | `.sdd/changes/reascript-hybrid/` | New | Change artifact folder | ## Risks | Risk | Likelihood | Mitigation | |------|------------|------------| | ReaScript API changed in REAPER version | Medium | Pin to known API subset; log API availability on startup | | FX fail silently on load | Medium | Verify each FX chain post-load, report missing plugins | | Rendering blocks REAPER UI | Low | Run render in background thread via ReaScript; poll for completion | | JSON protocol desync | Low | Version the command protocol; timeout with retry | ## Rollback Plan 1. Revert `src/reaper_scripting/` and `scripts/run_in_reaper.py` deletions 2. Phase 1 (.rpp generation) is unaffected — it already works standalone 3. No schema or compose.py changes ## Dependencies - REAPER v7+ installed with Python 3.x ReaScript support - Plugins must be in same paths as .rpp expects (no change to path handling) ## Success Criteria - [ ] `python scripts/run_in_reaper.py output/song.rpp` — REAPER opens, calibrates, renders, reports LUFS - [ ] Loudness result written to `output/song_lufs.json` - [ ] Missing FX logged to `output/song_fx_errors.json` - [ ] Phase 1 still works standalone: `python scripts/compose.py --output output/song.rpp`