# Delta: generate-song ## ADDED Requirements ### Requirement: CLI generates REAPER .rpp from arguments `scripts/generate.py` MUST accept `--bpm`, `--key`, `--output`, and `--seed` arguments. When invoked as `python scripts/generate.py --bpm 95 --key Am --output output/song.rpp --seed 42`, the script SHALL produce a valid REAPER .rpp file at the specified output path using seed 42 for all random choices. #### Scenario: Happy path — produces 52-bar arrangement - GIVEN no arguments beyond required ones; `seed=42` is the default - WHEN the CLI is invoked with `--bpm 95 --key Am --output /tmp/song.rpp --seed 42` - THEN a .rpp file SHALL be written containing 9 tracks (7 normal + 2 return) - AND the arrangement duration SHALL equal 52 bars at 95 BPM - AND all 19 plugins (across tracks, returns, master) SHALL be present in the FX chains #### Scenario: Default seed produces reproducible output - GIVEN two invocations with identical arguments (including `--seed 42`) - WHEN both are run in the same environment - THEN the resulting .rpp files SHALL be byte-for-byte identical #### Scenario: Invalid BPM raises ValueError - GIVEN `--bpm 0` or `--bpm -10` - WHEN the CLI is invoked - THEN a `ValueError` SHALL be raised with message matching `bpm must be > 0` --- ### Requirement: `validate_rpp_output(rpp_path) -> list[str]` checks all structural invariants The validation function MUST return an empty list for a valid .rpp, and a list of error strings for any violation. #### Scenario: Returns empty list for valid output - GIVEN a .rpp produced by the CLI with all required structure - WHEN `validate_rpp_output(path)` is called - THEN the result SHALL be `[]` #### Scenario: Detects wrong track count - GIVEN a .rpp with fewer than 9 tracks - WHEN `validate_rpp_output(path)` is called - THEN the returned list SHALL include `"Expected 9 tracks, got N"` #### Scenario: Detects missing plugin chains - GIVEN a .rpp where a track is missing its FXCHAIN block - WHEN `validate_rpp_output(path)` is called - THEN the returned list SHALL include `"Track 'X' missing FXCHAIN"` #### Scenario: Detects broken audio clip paths - GIVEN a .rpp containing an audio clip whose `SOURCE WAVE` path does not exist on disk - WHEN `validate_rpp_output(path)` is called - THEN the returned list SHALL include `"Audio clip path does not exist: /path/to/file.wav"` #### Scenario: Detects MIDI clips without notes - GIVEN a .rpp containing a MIDI clip with zero notes - WHEN `validate_rpp_output(path)` is called - THEN the returned list SHALL include `"MIDI clip has no notes"` #### Scenario: Detects incorrect arrangement duration - GIVEN a .rpp whose final item ends before the expected 52-bar duration at the given BPM - WHEN `validate_rpp_output(path)` is called - THEN the returned list SHALL include `"Arrangement ends at beat X, expected at least Y"` #### Scenario: Detects missing send routing - GIVEN a .rpp where a non-return track has no `AUXRECV` send to a return track - WHEN `validate_rpp_output(path)` is called - THEN the returned list SHALL include `"Track 'X' missing send to return track"` --- ### Requirement: `scripts/generate.py` uses REAPER drumloops with fallback The script SHALL pick drumloop files from the Ableton drumloop directory, cycling through available files per variant, and SHALL fall back gracefully when a file is absent. #### Scenario: Picks from seco and filtrado variants - GIVEN the CLI is run with `--seed 42` - WHEN the resulting .rpp is inspected - THEN audio clips SHALL reference files from the `seco` and `filtrado` pools as defined in `DRUMLOOP_FILES` - AND the variant per section SHALL follow `DRUMLOOP_ASSIGNMENTS` #### Scenario: Falls back when perc loop file is missing - GIVEN the file `91bpm bellako percloop.wav` does not exist - WHEN `build_perc_track` is called for a verse or chorus section - THEN no Perc clip SHALL be added for that section (skip silently rather than crash) --- ### Requirement: Test suite for generate-song A test file at `tests/test_generate_song.py` MUST cover the CLI and validation function. #### Scenario: CLI end-to-end smoke test - GIVEN `tmp_path` fixture - WHEN `python scripts/generate.py --bpm 95 --key Am --output {tmp_path}/song.rpp --seed 42` is executed as a subprocess - THEN the resulting file SHALL exist and be non-empty #### Scenario: Validation passes for valid output - GIVEN a generated .rpp at a known path - WHEN `validate_rpp_output(path)` is called - THEN it SHALL return `[]` #### Scenario: Validation detects track count violation - GIVEN a .rpp with 5 tracks (not 9) - WHEN `validate_rpp_output(path)` is called - THEN the error list SHALL contain a track-count violation message #### Scenario: Reproducibility — same seed gives same output - GIVEN two temp paths - WHEN the CLI is run with `--seed 42` to both paths - THEN both output files SHALL have identical content