From a4f9a975250a75f7b87ce3f7be764578dc651aa1 Mon Sep 17 00:00:00 2001 From: vangel76 Date: Sun, 8 Nov 2026 03:32:08 +0100 Subject: [PATCH] aep export: pin the key-time loop's report, name the layout in the base::bytes log (#3278) --- crates/aep/src/export/base.rs | 6 +++++- crates/aep/src/export/keys.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/aep/src/export/base.rs b/crates/aep/src/export/base.rs index 3e2b043ff..9da1db2ba 100644 --- a/crates/aep/src/export/base.rs +++ b/crates/aep/src/export/base.rs @@ -12,7 +12,11 @@ use crate::export::tree::{R, fixed, put}; /// A zero buffer of `len` bytes with `fields` (offset, bytes) written into it. The layouts are /// fixed in code ([`fixed`]). pub(crate) fn bytes(len: usize, fields: &[(usize, &[u8])]) -> Vec { - fixed("a record", try_bytes(len, fields), || vec![0u8; len]) + fixed("base::bytes", try_bytes(len, fields), || { + // Which layout: its length and fields (the builders are named in the comments above). + log::error!("aep export: base::bytes layout of {len} bytes, fields at {:?}", fields.iter().map(|(at, v)| (*at, v.len())).collect::>()); + vec![0u8; len] + }) } fn try_bytes(len: usize, fields: &[(usize, &[u8])]) -> R> { diff --git a/crates/aep/src/export/keys.rs b/crates/aep/src/export/keys.rs index fdcad87c2..b3cd1158c 100644 --- a/crates/aep/src/export/keys.rs +++ b/crates/aep/src/export/keys.rs @@ -90,6 +90,38 @@ mod tests { use super::*; use crate::export::synth::key_list; + #[test] + fn keys_are_timed_sorted_merged_and_reported() { + use effectcraft_keyframe::Value; + use effectcraft_time::{TICKS_PER_SECOND, Tick}; + let tb = TimeBase { frame_duration: 1024, timescale: 25600 }; + // Out of order; 1/3 s is not on the time base; two keys meet at 1 s (the first one kept). + let keys = [ + Keyframe::new(Tick(TICKS_PER_SECOND), Value::Scalar(1.0)), + Keyframe::new(Tick(TICKS_PER_SECOND / 3), Value::Scalar(2.0)), + Keyframe::new(Tick(0), Value::Scalar(3.0)), + Keyframe::new(Tick(TICKS_PER_SECOND), Value::Scalar(4.0)), + ]; + let mut col = Collector::default(); + let timed = timed_keys(&keys, &tb, "L", "path key", &mut col); + let got: Vec<(i64, f64)> = timed.iter().map(|(u, k)| (*u, if let Value::Scalar(v) = k.value { v } else { f64::NAN })).collect(); + assert_eq!(got, [(0, 3.0), (8533, 2.0), (25600, 1.0)]); + // The report lists its categories in a fixed order; one merge report for the pair. + let report = col.finish(); + let lines: Vec<(LossCategory, &str)> = report.losses.iter().flat_map(|l| l.details.iter().map(move |d| (l.category, d.as_str()))).collect(); + assert_eq!( + lines, + [ + (LossCategory::ApproximatedKeyframes, "L: path keys at the same time were merged"), + (LossCategory::RoundedTimes, "L: path key at 0.3333 s moved to the time base"), + ] + ); + // No keys, no report. + let mut col = Collector::default(); + assert!(timed_keys(&[], &tb, "L", "key", &mut col).is_empty()); + assert!(col.finish().losses.is_empty()); + } + #[test] fn a_key_list_is_filled_and_replaces_the_value() { let mut list = Chunk::list(*b"list", vec![Chunk::data(*b"lhd3", vec![0; 52])]);