From f5a82152394fde4d8f740f664ab7737b82861618 Mon Sep 17 00:00:00 2001 From: cashmere Date: Tue, 28 Jul 2026 19:41:16 +0200 Subject: feat: GPUI desktop port foundation - scene crate: full project/scene/element model, serde round-trip proven against the web SerializedProject shape - transfer crate: .ocp zip container, cross-compat with web fflate archives proven in both directions - storage crate: filesystem project store replacing IndexedDB, .ocp import/export - media crate: ffmpeg-next 8 decoder (frames, audio, thumbnails, waveform summaries) - playback crate: wall-clock anchored, frame-rounded playback clock - gpu crate: native texture readback; compositor renders headless offscreen on Vulkan (spike test) - flake devshell: rust toolchain, GPUI native deps, ffmpeg dev, bindgen clang args; GPUI hello-world builds - port plan in notes/gpui-port.md --- rust/crates/timeline/src/retime.rs | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 rust/crates/timeline/src/retime.rs (limited to 'rust/crates/timeline/src/retime.rs') diff --git a/rust/crates/timeline/src/retime.rs b/rust/crates/timeline/src/retime.rs new file mode 100644 index 00000000..7215f2ec --- /dev/null +++ b/rust/crates/timeline/src/retime.rs @@ -0,0 +1,71 @@ +use scene::RetimeConfig; +use time::MediaTime; + +pub const DEFAULT_RETIME_RATE: f64 = 1.0; +pub const MIN_RETIME_RATE: f64 = 0.01; +pub const MAX_RETIME_RATE: f64 = 5.0; + +pub fn clamp_retime_rate(rate: f64) -> f64 { + if !rate.is_finite() || rate <= 0.0 { + return DEFAULT_RETIME_RATE; + } + + rate.clamp(MIN_RETIME_RATE, MAX_RETIME_RATE) +} + +pub fn can_maintain_pitch(rate: f64) -> bool { + rate.is_finite() && rate > 0.0 +} + +pub fn should_maintain_pitch(rate: f64, maintain_pitch: Option) -> bool { + maintain_pitch == Some(true) && can_maintain_pitch(rate) +} + +fn safe_rate(retime: Option<&RetimeConfig>) -> f64 { + clamp_retime_rate(retime.map(|retime| retime.rate).unwrap_or(1.0)) +} + +pub fn get_source_time_at_clip_time(clip_time: f64, retime: Option<&RetimeConfig>) -> f64 { + clip_time * safe_rate(retime) +} + +pub fn get_clip_time_at_source_time(source_time: f64, retime: Option<&RetimeConfig>) -> f64 { + source_time / safe_rate(retime) +} + +pub fn get_effective_rate_at(retime: Option<&RetimeConfig>) -> f64 { + safe_rate(retime) +} + +pub fn get_timeline_duration_for_source_span( + source_span: f64, + retime: Option<&RetimeConfig>, +) -> f64 { + if source_span <= 0.0 { + return 0.0; + } + source_span / safe_rate(retime) +} + +pub fn get_source_span_at_clip_time(clip_time: f64, retime: Option<&RetimeConfig>) -> f64 { + get_source_time_at_clip_time(clip_time, retime).max(0.0) +} + +pub fn split_retime_at_clip_time( + retime: Option<&RetimeConfig>, +) -> (Option, Option) { + (retime.copied(), retime.copied()) +} + +pub fn adjust_retime_for_trim_change(retime: Option<&RetimeConfig>) -> Option { + retime.copied() +} + +pub fn round_media_time(time: f64) -> MediaTime { + let rounded_magnitude = time.abs().round(); + if rounded_magnitude == 0.0 { + return MediaTime::ZERO; + } + let magnitude = rounded_magnitude as i64; + MediaTime::from_ticks(if time < 0.0 { -magnitude } else { magnitude }) +} -- cgit v1.3.1