Files
reaper-control/.sdd/changes/mix-calibration/proposal.md
renato97 014e636889 feat: professional reggaeton production engine — 7 SDD changes, 302 tests
- section-energy: track activity matrix + volume/velocity multipliers per section
- smart-chords: ChordEngine with voice leading, inversions, 4 emotion modes
- hook-melody: melody engine with hook/stabs/smooth styles, call-and-response
- mix-calibration: Calibrator module (LUFS volumes, HPF/LPF, stereo, sends, master)
- transitions-fx: FX track with risers/impacts/sweeps at section boundaries
- sidechain: MIDI CC11 bass ducking on kick hits via DrumLoopAnalyzer
- presets-pack: role-aware plugin presets (Serum/Decapitator/Omnisphere per role)

Full SDD pipeline (propose→spec→design→tasks→apply→verify) for all 7 changes.
302/302 tests passing.
2026-05-03 23:54:29 -03:00

4.5 KiB

Proposal: Automated Mix Calibration

Intent

All track volumes, pans, and sends are hardcoded constants. No frequency balancing. Master chain uses Pro-Q_3/Pro-C_2/Pro-L_2 with DEFAULT presets. Result: flat, amateur sound with bass-drum masking and no stereo width.

Add a post-processing calibrator that sets role-based LUFS volumes, HPF/LPF EQ, stereo panning, calibrated sends, and a proper mastering chain.

Scope

In Scope

  • src/calibrator/ module — calibrates a SongDefinition with role-aware mix settings
  • LUFS-targeted volumes per role (kick -8 → drumloop 0.85, bass -10 → 0.72, lead -12 → 0.78, etc.)
  • HPF/LPF via ReaEQ plugins prepended to each track (HPF on non-bass, LPF on bass)
  • Stereo width management: bass/kick mono, lead wide (±0.3), chords wider (±0.5), clap off-center
  • Calibrated send levels: lead 25% verb / 15% delay, chords 30% / 10%, pad 40% / 20%, drums 10% / 0%
  • Master chain swap: Pro-Q_3 → Ozone 12 Equalizer, Pro-C_2 → Ozone 12 Dynamics, Pro-L_2 → Ozone 12 Maximizer
  • --no-calibrate flag on compose.py to skip calibration

Out of Scope

  • True LUFS measurement (requires REAPER rendering — Phase 2 via ReaScript)
  • ReaEQ parameter automation (parametric curves, dynamic EQ)
  • Reference-track matching
  • Multi-genre calibration profiles (reggaeton only for now)

Capabilities

New Capabilities

  • mix-calibration: Role-based volume/pan/send/EQ calibration applied as post-processing step on SongDefinition

Modified Capabilities

None

Approach

Separate calibrator module (src/calibrator/), NOT inline in compose.py. Rationale:

  • compose.py is 612 lines — adding 200+ calibration lines would bloat it
  • Calibration is a separate concern (mixing vs. composition)
  • Independently testable, skippable via --no-calibrate
  • Follows existing module pattern (selector/, builder/, validator/)

Data flow: compose.main()SongDefinitionCalibrator.apply(song) → calibrated SongDefinitionRPPBuilder.build()

HPF/LPF strategy: Add ReaEQ plugin to each track's plugin list. Extend _build_plugin() to serialize PluginDef.params into VST parameter slots (currently ignored). ReaEQ uses 19 fixed param slots; we populate band 0 (type=1 HPF or type=0 LPF) with frequency values.

Master chain: Replace master_plugins=["Pro-Q_3","Pro-C_2","Pro-L_2"] with ["Ozone_12_Equalizer","Ozone_12_Dynamics","Ozone_12_Maximizer"] using default presets already in registry.

Affected Areas

Area Impact Description
src/calibrator/__init__.py New Calibrator class with apply(song) method
src/calibrator/presets.py New Calibration presets (LUFS targets, HPF/LPF freqs, pans, sends)
src/reaper_builder/__init__.py Modified _build_plugin() — serialize PluginDef.params to VST slots
scripts/compose.py Modified Import Calibrator, call after track build, add --no-calibrate flag
tests/test_calibrator.py New Unit tests for calibrator output
src/core/schema.py Modified Add calibrate: bool flag to SongMeta (optional)

Risks

Risk Likelihood Mitigation
ReaEQ param serialization breaks existing .rpp Low Feature-gated: only when PluginDef.params is non-empty; zero backcompat impact
Ozone 12 plugins missing on some machines Med Fallback to Pro-Q_3/Pro-C_2/Pro-L_2 if Ozone registry lookup fails
Too-aggressive HPF cuts thin out full sections Low Conservative cutoffs: HPF 60Hz for drums, 200Hz for lead/chords; tunable via presets

Rollback Plan

  1. Revert compose.py: remove --no-calibrate flag, remove calibrator import
  2. Revert builder: remove params serialization in _build_plugin()
  3. Delete src/calibrator/
  4. Restore original VOLUME_LEVELS, SEND_LEVELS, MASTER_VOLUME, master_plugins constants

Dependencies

  • PLUGIN_REGISTRY entries for ReaEQ, Ozone_12_Equalizer, Ozone_12_Dynamics, Ozone_12_Maximizer (all exist)
  • No new Python dependencies required

Success Criteria

  • Calibrator.apply(song) returns a SongDefinition with volume/pan/send values matching role-based presets
  • Each non-return track has at least one ReaEQ plugin with HPF or LPF params set
  • --no-calibrate flag preserves existing behavior (no calibration applied)
  • Generated .rpp with calibration produces audibly cleaner mix (verified by ear)
  • All 110 existing tests still pass (calibration is additive, not breaking)