tono_core/dsl/tracks.rs
1//! Mixer-track types: one channel of a [`Node::Tracks`] root plus its
2//! automation lanes.
3
4use super::{Node, default_gain};
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8/// One mixer channel in a [`Node::Tracks`] root.
9#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
10pub struct Track {
11 /// Stable layer id — a short slug like `"kick"` or `"tail"`, unique within
12 /// the document. This is how edits address the track by id, so it never
13 /// shifts when sibling layers are added or
14 /// removed (unlike an array index). Omitted ids are backfilled
15 /// deterministically (`layer_<position>`) on the next build.
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub id: Option<String>,
18 /// The track's signal graph (usually a `seq` or a `chain`).
19 pub node: Node,
20 /// Stereo position, −1 (hard left) .. 1 (hard right). Equal-power law.
21 #[serde(default)]
22 pub pan: f32,
23 /// Channel fader, 0..2 (1 = unity).
24 #[serde(default = "default_gain")]
25 pub gain: f32,
26 /// Start offset in seconds: the rendered layer is shifted this far right
27 /// on the bus (the transient + body + tail recipe). The render keeps its
28 /// full length and the shifted tail is truncated at the document edge.
29 #[serde(default)]
30 pub at: f32,
31 /// Muted layers stay in the document but are left off the bus. This is
32 /// rendered state, not a monitoring convenience — exports ship without
33 /// muted layers.
34 #[serde(default)]
35 pub mute: bool,
36 /// Song-time automation lanes for this track's `gain` / `pan` (volume rides,
37 /// pan moves across sections). Empty ⇒ the static `gain`/`pan` apply and the
38 /// render is byte-identical to a document without this field. A lane's value
39 /// overrides the static one over time; per-node modulators still cover the
40 /// node level (this is the track/song level).
41 #[serde(default, skip_serializing_if = "Vec::is_empty")]
42 pub automation: Vec<AutoLane>,
43}
44
45/// What a track automation lane controls.
46#[non_exhaustive]
47#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
48#[serde(rename_all = "lowercase")]
49pub enum AutoTarget {
50 /// The track's channel fader (0..2).
51 Gain,
52 /// The track's stereo position (−1..1).
53 Pan,
54}
55
56/// One breakpoint in an automation lane: value `v` at song time `t` seconds.
57/// Between breakpoints the value is linearly interpolated; before the first /
58/// after the last it holds flat.
59#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
60pub struct AutoPoint {
61 /// Song time in seconds.
62 pub t: f32,
63 /// Target value at this time.
64 pub v: f32,
65}
66
67/// A track automation lane: a `target` driven by a list of breakpoints.
68#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
69pub struct AutoLane {
70 /// What this lane controls.
71 pub target: AutoTarget,
72 /// Breakpoints over song time.
73 pub points: Vec<AutoPoint>,
74}