Files
reaper-control/scripts/generate.py
renato97 e99fa231dd fix: sidechain CC11 — pass kick_cache to build_bass_track + absolute position projection
Root cause: build_bass_track() never received the kick_cache in main().
Second issue: kick times were WAV-relative (0-12 beats) but bass expects
absolute positions (16+ beats). Added loop-duration projection to convert
relative → absolute positions across clip duration.

285 CC11 events now generated in output. 302/302 tests pass.
2026-05-04 00:26:03 -03:00

73 lines
2.3 KiB
Python

"""REAPER .rpp song generator — thin CLI wrapper around compose.main().
Usage:
python scripts/generate.py --bpm 95 --key Am --output output/song.rpp --seed 42
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(_ROOT))
import compose
def main() -> None:
parser = argparse.ArgumentParser(description="Generate a REAPER .rpp reggaeton song.")
parser.add_argument("--bpm", type=float, default=95, help="BPM (default: 95)")
parser.add_argument("--key", default="Am", help="Musical key (default: Am)")
parser.add_argument(
"--output", default="output/song.rpp", help="Output .rpp path (default: output/song.rpp)"
)
parser.add_argument("--seed", type=int, default=42, help="Random seed (default: 42)")
parser.add_argument(
"--emotion", default="romantic",
help="Chord emotion: romantic|dark|club|classic (default: romantic)"
)
parser.add_argument(
"--inversion", default="root",
help="Chord inversion: root|first|second (default: root)"
)
parser.add_argument(
"--validate", action="store_true", help="Run validator after generation"
)
args = parser.parse_args()
# BPM validation
if args.bpm <= 0:
raise ValueError("bpm must be > 0")
# Ensure output directory exists
output_path = Path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
# Delegate to compose.main() — set sys.argv so compose's argparse works
sys.argv = [
sys.argv[0],
"--bpm", str(args.bpm),
"--key", args.key,
"--output", str(output_path),
"--seed", str(args.seed),
"--emotion", args.emotion,
"--inversion", args.inversion,
]
compose.main()
# Post-generation validation
if args.validate:
from src.validator.rpp_validator import validate_rpp_output
errors = validate_rpp_output(str(output_path), expected_bpm=args.bpm, expected_bars=52)
if errors:
print("Validation errors:", file=sys.stderr)
for err in errors:
print(f" - {err}", file=sys.stderr)
sys.exit(1)
print("Validation passed.", file=sys.stderr)
if __name__ == "__main__":
main()