1use std::collections::BTreeMap;
7
8use crate::wire::DeviceUid;
9
10use super::V2ipStreamSource;
11
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct AudioFeatures(u32);
15
16impl AudioFeatures {
17 pub const INPUT: Self = Self(1 << 0);
19 pub const OUTPUT: Self = Self(1 << 1);
21 pub const V2IP_TX: Self = Self(1 << 2);
23 pub const V2IP_RX: Self = Self(1 << 3);
25 pub const HDMI: Self = Self(1 << 4);
27 pub const RCA: Self = Self(1 << 5);
29 pub const SPDIF: Self = Self(1 << 6);
31 pub const TRIGGER: Self = Self(1 << 7);
33 pub const MUTE: Self = Self(1 << 8);
35 pub const ROUTE_INPUT: Self = Self(1 << 9);
37 pub const ROUTE_OUTPUT: Self = Self(1 << 10);
39 pub const ROUTE_IN_NONE: Self = Self(1 << 11);
41 pub const AMP_OUTPUT: Self = Self(1 << 12);
43 pub const VOLUME_CONTROL: Self = Self(1 << 13);
45 pub const GAIN_CONTROL: Self = Self(1 << 14);
47
48 pub const fn from_bits(bits: u32) -> Self {
50 Self(bits)
51 }
52
53 pub const fn bits(self) -> u32 {
55 self.0
56 }
57
58 pub const fn has(self, other: Self) -> bool {
60 self.0 & other.0 == other.0
61 }
62
63 pub const fn is_v2ip(self) -> bool {
65 self.has(Self::V2IP_TX) || self.has(Self::V2IP_RX)
66 }
67}
68
69#[derive(Clone, Debug, Default, PartialEq, Eq)]
74pub struct AudioEndpoint {
75 pub id: u8,
77 pub features: AudioFeatures,
79 pub address: Option<V2ipStreamSource>,
81 pub parent: Option<u8>,
83 pub children: Vec<u8>,
85 pub inputs_available: Option<u32>,
87 pub inputs_routed: Option<u32>,
89 pub linked_device: DeviceUid,
91 pub linked_endpoint: Option<u8>,
93}
94
95impl AudioEndpoint {
96 pub fn input(&self) -> Option<u8> {
98 let routed = self.inputs_routed?;
99 (0..32).find(|id| routed & (1 << id) != 0)
100 }
101
102 pub fn available_inputs(&self) -> Vec<u8> {
104 let Some(mask) = self.inputs_available else {
105 return Vec::new();
106 };
107 (0..32).filter(|id| mask & (1 << id) != 0).collect()
108 }
109}
110
111#[derive(Clone, Debug, Default, PartialEq, Eq)]
113pub struct AudioEndpoints {
114 order: Vec<u8>,
115 endpoints: BTreeMap<u8, AudioEndpoint>,
116}
117
118impl AudioEndpoints {
119 pub(crate) fn add(&mut self, endpoint: AudioEndpoint) {
122 if !self.endpoints.contains_key(&endpoint.id) {
123 self.order.push(endpoint.id);
124 }
125 self.endpoints.insert(endpoint.id, endpoint);
126 }
127
128 pub fn get(&self, id: u8) -> Option<&AudioEndpoint> {
130 self.endpoints.get(&id)
131 }
132
133 pub(crate) fn get_mut(&mut self, id: u8) -> Option<&mut AudioEndpoint> {
134 self.endpoints.get_mut(&id)
135 }
136
137 pub fn list(&self) -> impl Iterator<Item = &AudioEndpoint> {
139 self.order.iter().filter_map(|id| self.endpoints.get(id))
140 }
141
142 pub fn roots(&self) -> impl Iterator<Item = &AudioEndpoint> {
144 self.list().filter(|ep| ep.parent.is_none())
145 }
146
147 pub fn first_root_input(&self) -> Option<&AudioEndpoint> {
149 self.roots()
150 .find(|ep| ep.features.has(AudioFeatures::INPUT))
151 }
152
153 pub fn first_root_output(&self) -> Option<&AudioEndpoint> {
155 self.roots()
156 .find(|ep| ep.features.has(AudioFeatures::OUTPUT))
157 }
158
159 pub(crate) fn same_tree(&self, other: &Self) -> bool {
165 self.endpoints.len() == other.endpoints.len()
166 && self.endpoints.iter().all(|(id, ep)| {
167 other
168 .endpoints
169 .get(id)
170 .is_some_and(|o| o.features == ep.features && o.parent == ep.parent)
171 })
172 }
173
174 pub(crate) fn apply_link(&mut self, link: &AudioLink) {
176 if let Some(ep) = self.endpoints.get_mut(&link.endpoint) {
177 ep.linked_device = link.linked_device;
178 ep.linked_endpoint = Some(link.linked_endpoint);
179 }
180 }
181}
182
183#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
185pub struct AudioLink {
186 pub endpoint: u8,
188 pub linked_endpoint: u8,
190 pub linked_device: DeviceUid,
192}