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;