The MIDI-clip-end-grows-too-fast bug was a units confusion: AudioClip.duration is documented "seconds" but MIDI clips store their length in BEATS (they share the AudioClip struct with sampled clips). The timeline's effective_clip_duration read it as raw seconds, so at 120 BPM a MIDI clip rendered ~2x too long. The old backend-snapshot display had hidden this by forcing timeline_duration in beats. Root fix — make the domain explicit and unforgeable: - `duration` is now a private field. Reading it in the wrong unit is impossible because access goes through typed accessors: AudioClip::content_duration() -> ClipDuration (a Seconds|Beats enum tagged by clip_type) and set_content_duration(). serde still serializes the private field, so the .beam format is unchanged (bare number). - ClipDuration::to_seconds(tempo_map) for display/sizing; ::native() for code that already works in the clip's native domain (trim math shares it). - get_clip_duration + the timeline-endpoint calc go through to_seconds, so MIDI is converted correctly; effective_clip_duration delegates to get_clip_duration. - Recording mirrors (audio/MIDI progress + finalize) write via set_content_duration (debug-asserts the value's domain matches the clip type). Every former raw read/write of the field (core actions, timeline, piano roll, infopanel, asset library, recording handlers) now goes through the accessors. Whole workspace compiles; 299 core tests pass. |
||
|---|---|---|
| .. | ||
| assets | ||
| examples | ||
| src | ||
| Cargo.toml | ||
| PLAN.md | ||
| TOOL_IMPLEMENTATION_PLAN.md | ||
| build.rs | ||