Skip to main content

alpha2_model/
time_map.rs

1//! How a clip's timeline time maps onto its source range.
2
3use alpha2_time::MediaTime;
4use serde::{Deserialize, Serialize};
5
6/// How a clip plays its source range over its timeline range.
7///
8/// `forward` and `reverse` stretch the source range over the timeline range,
9/// so their speed is the ratio of the two. A loop plays the source range at
10/// its own speed, again and again, for as long as the timeline range lasts.
11#[derive(Copy, Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
12#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
13#[serde(tag = "type", rename_all = "snake_case", from = "TimeMapWire")]
14pub enum TimeMap {
15    /// From the start of the source range to its end.
16    #[default]
17    Forward,
18    /// From the end of the source range to its start: pictures in reverse
19    /// order and sound played backwards.
20    Reverse,
21    /// One source instant for the whole clip, and silence.
22    Freeze {
23        /// The instant shown; inside the source range.
24        source_time: MediaTime,
25    },
26    /// The source range again and again, at its own speed.
27    Loop {
28        /// How each pass follows the one before.
29        mode: LoopMode,
30    },
31}
32
33/// The wire form of [`TimeMap`]: empty struct variants reject extra fields
34/// where serde's unit variants would ignore them.
35#[derive(Deserialize)]
36#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
37#[cfg_attr(feature = "schema", schemars(rename = "TimeMap"))]
38#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
39pub(crate) enum TimeMapWire {
40    /// From the start of the source range to its end.
41    Forward {},
42    /// From the end of the source range to its start: pictures in reverse
43    /// order and sound played backwards.
44    Reverse {},
45    /// One source instant for the whole clip, and silence.
46    Freeze {
47        /// The instant shown; inside the source range.
48        source_time: MediaTime,
49    },
50    /// The source range again and again, at its own speed.
51    Loop {
52        /// How each pass follows the one before.
53        mode: LoopMode,
54    },
55}
56
57impl From<TimeMapWire> for TimeMap {
58    fn from(wire: TimeMapWire) -> Self {
59        match wire {
60            TimeMapWire::Forward {} => Self::Forward,
61            TimeMapWire::Reverse {} => Self::Reverse,
62            TimeMapWire::Freeze { source_time } => Self::Freeze { source_time },
63            TimeMapWire::Loop { mode } => Self::Loop { mode },
64        }
65    }
66}
67
68/// How each pass of a [`TimeMap::Loop`] follows the one before.
69#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
70#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
71#[serde(rename_all = "snake_case")]
72pub enum LoopMode {
73    /// Every pass plays forward, from the start again. Sound fades out and
74    /// in over [`LOOP_FADE_SECONDS`] at each wrap, so the jump does not click.
75    Repeat,
76    /// Passes alternate forward and backward.
77    PingPong,
78}
79
80/// How long sound fades out before, and in after, each wrap of a repeating
81/// loop.
82pub const LOOP_FADE_SECONDS: f64 = 0.005;