"""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( "--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), ] 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()