feat(bank_model): add Phase S seam fields (rootNote + loop points) to Sample
Additive optional MIDI root note and sustain-loop points with JSON round-trip and deserialize-boundary validation; capture leaves them empty (not derivable). Mirrors the provenance addition; BankIndex behavior unchanged.
This commit is contained in:
@@ -63,6 +63,22 @@ struct Levels {
|
||||
bool operator==(const Levels& o) const;
|
||||
};
|
||||
|
||||
// Sample-accurate sustain-loop bounds, as frame indices into the captured file
|
||||
// (Phase S seam field, D-B). A bank intrinsic — a fact about the file, like
|
||||
// sampleRate or length — consumed by the future MIDI-playback instrument to hold
|
||||
// notes past the recorded length. Modeled as one optional struct (not two loose
|
||||
// optionals) so "both points or neither" is a structural invariant, not a rule to
|
||||
// re-check at every boundary. Frame indices, not seconds, because the loop is a
|
||||
// per-sample-frame contract; the instrument reads the file's sample rate to relate
|
||||
// them to time. Invariant (enforced at the deserialize boundary): 0 <= start <= end.
|
||||
// start == end is a valid zero-length loop marker.
|
||||
struct LoopPoints {
|
||||
std::int64_t start = 0;
|
||||
std::int64_t end = 0;
|
||||
|
||||
bool operator==(const LoopPoints& o) const;
|
||||
};
|
||||
|
||||
// The metadata record for one captured sample. The audio itself lives in a
|
||||
// project-relative file; `relativePath` is ALWAYS relative (enforced at the
|
||||
// BankIndex::add boundary — see AddResult).
|
||||
@@ -95,6 +111,18 @@ struct Sample {
|
||||
|
||||
std::optional<std::string> key; // musical key, when known
|
||||
|
||||
// Phase S seam fields (D-B) — bank intrinsics for the MIDI-playback instrument,
|
||||
// additive like `provenance` (M1). Both default cleanly empty: pre-Phase-S
|
||||
// samples deserialize without them and re-serialize without inventing values.
|
||||
// - rootNote: MIDI note (0..127) the sample was recorded at, so the instrument
|
||||
// can repitch it across the keyboard. DISTINCT from the musical `key` above:
|
||||
// `key` is a human label ("F#m"); `rootNote` is the exact pitch for repitch.
|
||||
// Populated at/after capture only where derivable — left empty (never guessed)
|
||||
// when the source is not a single played note.
|
||||
// - loop: sustain-loop bounds, populated only where explicitly set.
|
||||
std::optional<int> rootNote;
|
||||
std::optional<LoopPoints> loop;
|
||||
|
||||
Levels levels;
|
||||
bool clipped = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user