- 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
102 lines
4.2 KiB
Markdown
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` |