Files
reaper-control/.sdd/changes/hook-melody/spec.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

122 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Delta for melody-engine
## ADDED Requirements
| # | Requirement | RFC |
|---|------------|-----|
| R1 | Motif generation with 3 reggaeton styles | MUST |
| R2 | Deterministic output from seed | MUST |
| R3 | Call-and-response phrase structure | MUST |
| R4 | Chord-aware note selection | MUST |
| R5 | Motif variation via transpose/rhythmic shift | SHOULD |
| R6 | build_lead_track() delegation | MUST |
### Requirement: Motif Generation (R1)
`build_motif(key_root, key_minor, style, bars, seed)` MUST generate a 24 bar repeating motif using scale-aware note selection. Three styles:
- **hook**: Arch contour (ascend then descend), chord tones on beats 0, 2, 4..., 48 notes
- **stabs**: Short 16th-duration hits on dembow grid positions [1.0, 2.5, 3.0, 3.5] per bar
- **smooth**: Stepwise scalar motion at eighth-note density, ≤2 semitones between consecutive notes
MUST accept `bars` parameter (28) defaulting to 4. MUST return `list[MidiNote]`.
#### Scenario: hook style generates arch contour with chord tones
- GIVEN key Am, style "hook", bars=4, seed=42
- WHEN `build_motif("A", True, "hook", 4, 42)` is called
- THEN returns 412 MidiNote objects
- AND notes on quarter-beat positions (0, 2, 4, …) are within the i-VI-III-VII chord tones ≥70% of the time
#### Scenario: stabs style generates dembow-positioned hits
- GIVEN key Am, style "stabs", bars=2, seed=1
- WHEN `build_motif("A", True, "stabs", 2, 1)` is called
- THEN all note start times are within {1.0, 2.5, 3.0, 3.5} per bar
- AND each note duration ≤ 0.25 beats (16th note)
#### Scenario: smooth style generates stepwise motion
- GIVEN key Am, style "smooth", bars=4, seed=7
- WHEN `build_motif("A", True, "smooth", 4, 7)` is called
- THEN pitch difference between consecutive notes ≤ 2 semitones
#### Scenario: invalid style raises ValueError
- GIVEN an unrecognized style string
- WHEN `build_motif("A", True, "invalid", 4, 42)` is called
- THEN raises ValueError with message containing valid styles
### Requirement: Deterministic Output (R2)
`build_motif()` and `apply_variation()` MUST produce identical output for identical input parameters (key, style, bars, seed). MUST NOT rely on global RNG state.
#### Scenario: same seed produces identical output
- GIVEN fixed parameters
- WHEN `build_motif("A", True, "hook", 4, 42)` is called twice
- THEN both calls return identical lists of MidiNote objects
#### Scenario: different seeds produce different output
- GIVEN same key and style but different seeds
- WHEN `build_motif("A", True, "hook", 4, 42)` and `build_motif("A", True, "hook", 4, 99)` are called
- THEN the returned note lists differ
### Requirement: Call-and-Response Structure (R3)
`build_call_response(motif, bars, key_root, key_minor, seed)` MUST generate two halves: **call** (motif + variation, ending on V or VII degree) and **response** (motif, resolving to tonic i). Total length MUST equal `bars` parameter. SHALL repeat motif to fill section length.
#### Scenario: call ends on tension, response resolves
- GIVEN an Am hook motif, bars=8, seed=42
- WHEN `build_call_response(motif, 8, "A", True, 42)` is called
- THEN the last note of the first 4 bars has pitch in {E, G} (V or VII of Am)
- AND the last note of the final bar (bar 8) has pitch in {A} (tonic)
#### Scenario: fills section with motif repetition
- GIVEN a 2-bar motif and bars=8
- WHEN `build_call_response(motif, 8, "A", True, 42)` is called
- THEN returns notes spanning 8 bars total
- AND motif content repeats at least 2 times within the 8 bars
### Requirement: Chord-Aware Notes (R4)
Note selection on strong beats (quarter note positions 0, 4, 8, 12 per bar in 16th-note grid) MUST favor chord tones from `CHORD_PROGRESSION`. Weak beats (all other positions) MAY use any scale degree.
#### Scenario: strong beats favor chord tones
- GIVEN key Am (CHORD_PROGRESSION = i-VI-III-VII), style "hook", bars=8
- WHEN a motif is generated
- THEN ≥70% of notes starting on quarter-beat boundaries belong to active chord tones
### Requirement: Motif Variation (R5)
`apply_variation(motif, shift_beats, transpose_semitones)` SHOULD produce a recognizable variant of the input motif. `shift_beats` offsets all start times within the loop. `transpose_semitones` shifts pitches within the scale. MUST return `list[MidiNote]`.
#### Scenario: rhythmic shift preserves note count and structure
- GIVEN a 4-bar hook motif
- WHEN `apply_variation(motif, shift_beats=0.25)` is called
- THEN note count equals original
- AND all note durations equal original
- AND inter-onset intervals are preserved
#### Scenario: transpose within scale preserves motif contour
- GIVEN a 4-bar hook motif in Am
- WHEN `apply_variation(motif, transpose_semitones=3)` is called
- THEN all pitches are offset by ±3 semitones (within pentatonic scale)
### Requirement: build_lead_track() Delegation (R6)
`build_lead_track()` in `compose.py` MUST delegate to `melody_engine.build_call_response()` instead of generating random pentatonic notes directly. MUST keep identical function signature. MUST pass existing tests after adjusting expected note counts.
#### Scenario: build_lead_track uses call-response structure
- GIVEN seed=42, key Am, sections containing "chorus" and "final"
- WHEN `build_lead_track(sections, offsets, "A", True, 42)` is called
- THEN returned TrackDef clips contain notes organized as call-response phrases
- AND at least one clip has notes ending on tonic pitch