- 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
126 lines
4.8 KiB
Markdown
126 lines
4.8 KiB
Markdown
# 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 |