Skip to main content

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}