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

4.2 KiB

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