1use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
8
9use crate::engine::VuLevels;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct TrackId(pub usize);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum TrackKind {
18 Instrument,
20 Audio,
22 SendA,
24 SendB,
26 Master,
28}
29
30#[derive(Debug)]
35pub struct TrackConfig {
36 pub muted: AtomicBool,
37 pub soloed: AtomicBool,
38 pub armed: AtomicBool,
39 pub midi_active: AtomicBool,
42 pub volume: AtomicU32,
44}
45
46impl TrackConfig {
47 pub fn new() -> Self {
48 Self {
49 muted: AtomicBool::new(false),
50 soloed: AtomicBool::new(false),
51 armed: AtomicBool::new(false),
52 midi_active: AtomicBool::new(false),
53 volume: AtomicU32::new(0.75f32.to_bits()),
54 }
55 }
56
57 pub fn get_volume(&self) -> f32 {
58 f32::from_bits(self.volume.load(Ordering::Relaxed))
59 }
60
61 pub fn set_volume(&self, v: f32) {
62 self.volume.store(v.to_bits(), Ordering::Relaxed);
63 }
64
65 pub fn is_muted(&self) -> bool {
66 self.muted.load(Ordering::Relaxed)
67 }
68
69 pub fn is_soloed(&self) -> bool {
70 self.soloed.load(Ordering::Relaxed)
71 }
72
73 pub fn is_armed(&self) -> bool {
74 self.armed.load(Ordering::Relaxed)
75 }
76
77 pub fn is_midi_active(&self) -> bool {
78 self.midi_active.load(Ordering::Relaxed)
79 }
80}
81
82impl Default for TrackConfig {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88#[derive(Debug)]
91pub struct TrackHandle {
92 pub id: usize,
93 pub kind: TrackKind,
94 pub config: TrackConfig,
95 pub vu: VuLevels,
96}
97
98impl TrackHandle {
99 pub fn new(id: usize, kind: TrackKind) -> Self {
100 Self {
101 id,
102 kind,
103 config: TrackConfig::new(),
104 vu: VuLevels::new(),
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn track_config_defaults() {
115 let cfg = TrackConfig::new();
116 assert!(!cfg.is_muted());
117 assert!(!cfg.is_soloed());
118 assert!(!cfg.is_armed());
119 assert!((cfg.get_volume() - 0.75).abs() < 0.001);
120 }
121
122 #[test]
123 fn track_config_volume_round_trip() {
124 let cfg = TrackConfig::new();
125 cfg.set_volume(0.42);
126 assert!((cfg.get_volume() - 0.42).abs() < 0.001);
127 }
128
129 #[test]
130 fn track_config_atomics() {
131 let cfg = TrackConfig::new();
132 cfg.muted.store(true, Ordering::Relaxed);
133 assert!(cfg.is_muted());
134 cfg.soloed.store(true, Ordering::Relaxed);
135 assert!(cfg.is_soloed());
136 cfg.armed.store(true, Ordering::Relaxed);
137 assert!(cfg.is_armed());
138 }
139
140 #[test]
141 fn track_handle_new() {
142 let h = TrackHandle::new(0, TrackKind::Instrument);
143 assert_eq!(h.id, 0);
144 assert_eq!(h.kind, TrackKind::Instrument);
145 assert!(!h.config.is_muted());
146 }
147
148 #[test]
149 fn track_kind_variants() {
150 assert_ne!(TrackKind::Instrument, TrackKind::Audio);
151 assert_ne!(TrackKind::SendA, TrackKind::SendB);
152 assert_ne!(TrackKind::Master, TrackKind::Audio);
153 }
154}