Files
reaper-control/.sdd/changes/archive/2026-05-03-reascript-hybrid/proposal.md
renato97 48bc271afc feat: SDD workflow — test sync, song generation + validation, ReaScript hybrid pipeline
- compose-test-sync: fix 3 failing tests (NOTE_TO_MIDI, DrumLoopAnalyzer mock, section name)
- generate-song: CLI wrapper + RPP validator (6 structural checks) + 4 e2e tests
- reascript-hybrid: ReaScriptGenerator + command protocol + CLI + 16 unit tests
- 110/110 tests passing
- Full SDD cycle (propose→spec→design→tasks→apply→verify) for all 3 changes
2026-05-03 22:00:26 -03:00

102 lines
4.2 KiB
Markdown

# 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`