truce_rack_core/bus.rs
1//! Bus topology declaration.
2//!
3//! A [`BusLayout`] is the host-side description of one of the
4//! audio bus configurations a plugin can operate in. Plugins
5//! advertise multiple layouts (mono, stereo, stereo + sidechain,
6//! 5.1, …); the host picks one before [`crate::PluginCore::activate`].
7//!
8//! Mirrors `truce_core::bus::BusLayout`. Repeated rather than
9//! shared so a rack consumer doesn't transitively pull in any
10//! truce-plugin-side code.
11
12use smallvec::{SmallVec, smallvec};
13
14/// Channel count and grouping for one bus.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ChannelConfig {
17 /// 1 channel.
18 Mono,
19 /// 2 channels (L, R).
20 Stereo,
21 /// 6 channels (L, R, C, LFE, Ls, Rs).
22 Surround5_1,
23 /// 8 channels (L, R, C, LFE, Ls, Rs, Lb, Rb).
24 Surround7_1,
25 /// Arbitrary channel count for hosts that don't fit the
26 /// canonical configs.
27 Discrete(u32),
28}
29
30impl ChannelConfig {
31 /// Number of channels this config carries.
32 #[must_use]
33 pub const fn count(self) -> u32 {
34 match self {
35 Self::Mono => 1,
36 Self::Stereo => 2,
37 Self::Surround5_1 => 6,
38 Self::Surround7_1 => 8,
39 Self::Discrete(n) => n,
40 }
41 }
42}
43
44/// What a bus carries.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum BusKind {
47 /// Main audio path. Every layout has exactly one main input
48 /// bus (or zero for instruments) and one main output bus.
49 Main,
50 /// Sidechain or additional auxiliary input — driven by the
51 /// host's routing UI, fed independently from the main bus.
52 Sidechain,
53 /// Auxiliary output beyond the main bus (multi-out
54 /// instruments, split-out compressor diagnostics, etc.).
55 Auxiliary,
56}
57
58/// One bus's declaration: name, kind, channel config.
59#[derive(Debug, Clone)]
60pub struct Bus {
61 /// Display name for the host UI.
62 pub name: String,
63 /// Bus role.
64 pub kind: BusKind,
65 /// Channel grouping / count.
66 pub channels: ChannelConfig,
67}
68
69/// One complete I/O topology the plugin supports.
70///
71/// Hosts iterate over a plugin's declared layouts and pick one
72/// before activation. After activation the layout is fixed until
73/// [`crate::PluginCore::deactivate`] is called.
74#[derive(Debug, Clone)]
75pub struct BusLayout {
76 /// Input buses in declaration order. Index 0 is the main
77 /// input (when present); later indices are sidechains /
78 /// auxiliaries.
79 pub inputs: SmallVec<[Bus; 2]>,
80 /// Output buses in declaration order. Index 0 is the main
81 /// output; later indices are auxiliaries.
82 pub outputs: SmallVec<[Bus; 2]>,
83}
84
85impl BusLayout {
86 /// An empty layout — no audio buses. Useful for MIDI-only
87 /// plugins.
88 #[must_use]
89 pub fn new() -> Self {
90 Self {
91 inputs: SmallVec::new(),
92 outputs: SmallVec::new(),
93 }
94 }
95
96 /// Mono-in, mono-out, no sidechains. The simplest effect
97 /// layout.
98 #[must_use]
99 pub fn mono() -> Self {
100 Self {
101 inputs: smallvec![Bus::main("Input", ChannelConfig::Mono)],
102 outputs: smallvec![Bus::main("Output", ChannelConfig::Mono)],
103 }
104 }
105
106 /// Stereo-in, stereo-out, no sidechains.
107 #[must_use]
108 pub fn stereo() -> Self {
109 Self {
110 inputs: smallvec![Bus::main("Input", ChannelConfig::Stereo)],
111 outputs: smallvec![Bus::main("Output", ChannelConfig::Stereo)],
112 }
113 }
114
115 /// Stereo + sidechain input, stereo output. Compressors,
116 /// gates, vocoders.
117 #[must_use]
118 pub fn stereo_with_sidechain(sidechain_name: &str) -> Self {
119 Self {
120 inputs: smallvec![
121 Bus::main("Input", ChannelConfig::Stereo),
122 Bus::sidechain(sidechain_name, ChannelConfig::Stereo),
123 ],
124 outputs: smallvec![Bus::main("Output", ChannelConfig::Stereo)],
125 }
126 }
127
128 /// Total input channels across every bus.
129 #[must_use]
130 pub fn total_input_channels(&self) -> u32 {
131 self.inputs.iter().map(|b| b.channels.count()).sum()
132 }
133
134 /// Total output channels across every bus.
135 #[must_use]
136 pub fn total_output_channels(&self) -> u32 {
137 self.outputs.iter().map(|b| b.channels.count()).sum()
138 }
139}
140
141impl Default for BusLayout {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147impl Bus {
148 /// A main-bus shorthand.
149 #[must_use]
150 pub fn main(name: &str, channels: ChannelConfig) -> Self {
151 Self {
152 name: name.to_string(),
153 kind: BusKind::Main,
154 channels,
155 }
156 }
157
158 /// A sidechain-bus shorthand.
159 #[must_use]
160 pub fn sidechain(name: &str, channels: ChannelConfig) -> Self {
161 Self {
162 name: name.to_string(),
163 kind: BusKind::Sidechain,
164 channels,
165 }
166 }
167
168 /// An auxiliary-bus shorthand.
169 #[must_use]
170 pub fn auxiliary(name: &str, channels: ChannelConfig) -> Self {
171 Self {
172 name: name.to_string(),
173 kind: BusKind::Auxiliary,
174 channels,
175 }
176 }
177}