Skip to main content

audiobox/mixer/
routing.rs

1//! Prepared, bounded audio routes between existing named buses.
2use super::effects::{self, Change};
3use super::queue::{self, Consumer, Producer, PushError};
4use super::{Action, BLOCK, BusId, Commands, Mixer, MixerEvent, MixerHandle};
5use crate::{Error, ErrorCode, Result};
6use std::collections::VecDeque;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10/// Total main outputs plus sends in one graph, including omitted default outputs.
11pub const MAX_BUS_ROUTES: usize = 1024;
12/// Both taps are after inserts and the bus pause fade. Pre-fader excludes bus
13/// gain/pan; post-fader includes them. Mute/solo gate both taps and sidechain keys.
14#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
15pub enum SendPosition {
16    PreFader,
17    #[default]
18    PostFader,
19}
20#[derive(Clone, Copy, Debug, PartialEq)]
21pub struct BusSend {
22    pub destination: BusId,
23    /// Finite nonnegative linear gain. Routes can sum above full scale.
24    pub gain: f32,
25    pub position: SendPosition,
26}
27/// One named bus's complete routing/settings. Omitted buses retain one unity
28/// post-fader output to master. The master is a sink, configured through its
29/// existing gain/pause/effect APIs, and cannot appear as a source here.
30#[derive(Clone, Debug, PartialEq)]
31pub struct BusRouting {
32    pub bus: BusId,
33    /// Main post-fader output, or None for a send-only/detector-only bus.
34    pub output: Option<BusId>,
35    pub sends: Vec<BusSend>,
36    /// Linear stereo balance, -1..=1. Nonzero requires a stereo mixer.
37    pub pan: f32,
38    /// Gates this bus's outgoing audio/keys; does not freeze voices or effect state.
39    pub muted: bool,
40    /// Selects audio paths through this bus, including upstream feeds/downstream returns.
41    pub solo: bool,
42}
43impl BusRouting {
44    pub fn new(bus: BusId) -> Self {
45        Self {
46            bus,
47            output: Some(BusId::MASTER),
48            sends: Vec::new(),
49            pan: 0.0,
50            muted: false,
51            solo: false,
52        }
53    }
54}
55/// A successfully queued edit ID. Poll RoutingApplied/Rejected for its outcome.
56#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
57pub struct RoutingId(pub u64);
58
59pub(super) struct Edge {
60    pub destination: usize,
61    pub gain: f64,
62    pub position: SendPosition,
63    pub enabled: bool,
64}
65pub(super) struct Node {
66    pub edges: Vec<Edge>,
67    pub pan: f64,
68    pub muted: bool,
69    pub solo: bool,
70    pub audible: bool,
71}
72pub(super) struct Graph {
73    pub nodes: Vec<Node>,
74    pub incoming: Vec<Vec<usize>>,
75    pub input: [bool; 256],
76    pub pre: Vec<f32>,
77}
78impl Graph {
79    fn new(settings: &[BusRouting], buses: usize, channels: usize) -> Result<Self> {
80        if settings.len() >= buses {
81            return Err(Error::invalid("Too many/duplicate routing bus definitions"));
82        }
83        let routes = settings
84            .iter()
85            .try_fold(buses - 1 - settings.len(), |n, s| {
86                n.checked_add(s.output.is_some() as usize)
87                    .and_then(|n| n.checked_add(s.sends.len()))
88            })
89            .ok_or_else(|| Error::limit("Routing edge count overflow"))?;
90        if routes > MAX_BUS_ROUTES {
91            return Err(Error::limit("Too many bus routes"));
92        }
93        let mut graph = Self {
94            nodes: (0..buses)
95                .map(|b| Node {
96                    edges: if b == 0 {
97                        Vec::new()
98                    } else {
99                        vec![Edge {
100                            destination: 0,
101                            gain: 1.0,
102                            position: SendPosition::PostFader,
103                            enabled: true,
104                        }]
105                    },
106                    pan: 0.0,
107                    muted: false,
108                    solo: false,
109                    audible: true,
110                })
111                .collect(),
112            incoming: (0..buses).map(|_| Vec::new()).collect(),
113            input: [true; 256],
114            pre: vec![0.0; BLOCK * channels],
115        };
116        let mut seen = [false; 256];
117        for setting in settings {
118            let b = setting.bus.0;
119            if b == 0 || b >= buses || seen[b] {
120                return Err(Error::invalid(
121                    "Routing needs distinct existing named source buses",
122                ));
123            }
124            seen[b] = true;
125            if !setting.pan.is_finite()
126                || !(-1.0..=1.0).contains(&setting.pan)
127                || (channels != 2 && setting.pan != 0.0)
128            {
129                return Err(Error::invalid(
130                    "Bus pan must be -1..=1 and requires stereo when nonzero",
131                ));
132            }
133            let node = &mut graph.nodes[b];
134            node.edges.clear();
135            node.pan = setting.pan as f64;
136            node.muted = setting.muted;
137            node.solo = setting.solo;
138            let mut destinations = [false; 256];
139            for (destination, gain, position) in setting
140                .output
141                .iter()
142                .map(|d| (*d, 1.0, SendPosition::PostFader))
143                .chain(
144                    setting
145                        .sends
146                        .iter()
147                        .map(|s| (s.destination, s.gain, s.position)),
148                )
149            {
150                let to = destination.0;
151                if to >= buses || to == b || destinations[to] {
152                    return Err(Error::invalid(
153                        "Missing, self or duplicate audio route destination",
154                    ));
155                }
156                if !gain.is_finite() || gain < 0.0 {
157                    return Err(Error::invalid("Send gain must be finite and nonnegative"));
158                }
159                destinations[to] = true;
160                node.edges.push(Edge {
161                    destination: to,
162                    gain: gain as f64,
163                    position,
164                    enabled: true,
165                });
166            }
167        }
168        for (from, node) in graph.nodes.iter().enumerate() {
169            for edge in &node.edges {
170                graph.incoming[edge.destination].push(from);
171            }
172        }
173        // Named buses are deterministically ordered; master is always last.
174        let mut order = [0usize; 256];
175        let mut done = [false; 256];
176        for entry in &mut order[..buses - 1] {
177            let ready = (1..buses)
178                .find(|&b| !done[b] && graph.incoming[b].iter().all(|&source| done[source]))
179                .ok_or_else(|| Error::invalid("Audio routes contain a cycle"))?;
180            *entry = ready;
181            done[ready] = true;
182        }
183        if graph.nodes.iter().any(|n| n.solo) {
184            let mut up = [false; 256];
185            let mut down = [false; 256];
186            for &b in order[..buses].iter().rev() {
187                up[b] =
188                    graph.nodes[b].solo || graph.nodes[b].edges.iter().any(|e| up[e.destination]);
189            }
190            for &b in &order[..buses] {
191                down[b] |= graph.nodes[b].solo;
192                for edge in &graph.nodes[b].edges {
193                    down[edge.destination] |= down[b];
194                }
195            }
196            for (b, node) in graph.nodes.iter_mut().enumerate() {
197                graph.input[b] = up[b];
198                node.audible = up[b] || down[b];
199                for edge in &mut node.edges {
200                    edge.enabled = down[b] || (up[b] && up[edge.destination]);
201                }
202            }
203        }
204        Ok(graph)
205    }
206}
207
208pub(super) struct Edit {
209    id: RoutingId,
210    graph: Option<Box<Graph>>,
211    event: Option<MixerEvent>,
212    credits: Arc<AtomicUsize>,
213}
214impl Drop for Edit {
215    fn drop(&mut self) {
216        self.credits.fetch_sub(1, Ordering::Release);
217    }
218}
219pub(super) struct AudioRouting {
220    pub graph: Option<Box<Graph>>,
221    returns: Producer<Box<Edit>>,
222    retired: VecDeque<Box<Edit>>,
223}
224pub(super) struct ControlRouting {
225    returns: Consumer<Box<Edit>>,
226    credits: Arc<AtomicUsize>,
227    capacity: usize,
228    next: u64,
229}
230pub(super) fn create(capacity: usize) -> (AudioRouting, ControlRouting) {
231    let (tx, rx) = queue::queue(capacity);
232    (
233        AudioRouting {
234            graph: None,
235            returns: tx,
236            retired: VecDeque::with_capacity(capacity),
237        },
238        ControlRouting {
239            returns: rx,
240            credits: Arc::new(AtomicUsize::new(0)),
241            capacity,
242            next: 0,
243        },
244    )
245}
246impl AudioRouting {
247    pub fn send_returns(&mut self) {
248        while let Some(edit) = self.retired.pop_front() {
249            match self.returns.push(edit) {
250                Ok(()) => (),
251                Err(PushError::Full(edit) | PushError::Disconnected(edit)) => {
252                    self.retired.push_front(edit);
253                    break;
254                }
255            }
256        }
257    }
258}
259impl ControlRouting {
260    pub fn poll(&mut self) -> Option<MixerEvent> {
261        self.returns.pop()?.event
262    }
263}
264impl MixerHandle {
265    /// Replace the named-bus routing graph/settings atomically. Empty settings
266    /// restore legacy flat routing. Preparation allocates; render does not.
267    /// Audio-only validation is synchronous; actual sidechain dependencies are
268    /// revalidated on application. Poll the returned ID's outcome. Each unpolled
269    /// edit consumes one credit, up to MixerConfig::queue, including retired data.
270    pub fn set_routing(&mut self, settings: &[BusRouting]) -> Result<RoutingId> {
271        self.now().set_routing(settings)
272    }
273}
274impl Commands<'_> {
275    pub fn set_routing(self, settings: &[BusRouting]) -> Result<RoutingId> {
276        let control = &self.handle.routing;
277        if control.credits.load(Ordering::Acquire) >= control.capacity {
278            return Err(Error::limit(
279                "Poll routing events before sending more edits",
280            ));
281        }
282        let next = control
283            .next
284            .checked_add(1)
285            .ok_or_else(|| Error::limit("Routing ID overflow"))?;
286        let graph = if settings.is_empty() {
287            None
288        } else {
289            Some(Box::new(Graph::new(
290                settings,
291                self.handle.buses.len() + 1,
292                self.handle.channels,
293            )?))
294        };
295        let id = RoutingId(control.next);
296        control.credits.fetch_add(1, Ordering::Relaxed);
297        let edit = Box::new(Edit {
298            id,
299            graph,
300            event: None,
301            credits: control.credits.clone(),
302        });
303        self.handle.send(self.frame, Action::Routing(edit))?;
304        self.handle.routing.next = next;
305        Ok(id)
306    }
307}
308impl Mixer {
309    pub(super) fn apply_routing(&mut self, frame: u64, mut edit: Box<Edit>) {
310        if let Some(order) = effects::topology(&self.buses, Change::Current, edit.graph.as_deref())
311        {
312            self.effects.order = order;
313            std::mem::swap(&mut self.routing.graph, &mut edit.graph);
314            edit.event = Some(MixerEvent::RoutingApplied(edit.id, frame));
315        } else {
316            edit.event = Some(MixerEvent::RoutingRejected(
317                edit.id,
318                ErrorCode::InvalidArgument,
319            ));
320        }
321        self.routing.retired.push_back(edit);
322    }
323}