Skip to main content

vst_audio_protocol/
layout.rs

1//! Speaker order is ascending VST 3 speaker bit, never channel-count guessing.
2use vst3::Steinberg::Vst::*;
3
4#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
5pub enum LayoutError {
6    #[error("unsupported speaker bit {0}")]
7    UnknownBit(u32),
8    #[error("unknown default layout for {0} channels")]
9    UnknownChannels(u16),
10    #[error("unsupported VST 2 speaker type {0}")]
11    UnknownSpeaker(i32),
12    #[error("duplicate or unordered speaker arrangement")]
13    Order,
14    #[error("empty speaker arrangement (0 channels)")]
15    Empty,
16    #[error("{0} main bus is missing or its channel count disagrees with arrangement {1:#x}")]
17    Bus(&'static str, u64),
18}
19
20const SPEAKERS: [(u64, u32); 18] = [
21    (kSpeakerL, 0x1),
22    (kSpeakerR, 0x2),
23    (kSpeakerC, 0x4),
24    (kSpeakerLfe, 0x8),
25    (kSpeakerLs, 0x10),
26    (kSpeakerRs, 0x20),
27    (kSpeakerLc, 0x40),
28    (kSpeakerRc, 0x80),
29    (kSpeakerCs, 0x100),
30    (kSpeakerSl, 0x200),
31    (kSpeakerSr, 0x400),
32    (kSpeakerTc, 0x800),
33    (kSpeakerTfl, 0x1000),
34    (kSpeakerTfc, 0x2000),
35    (kSpeakerTfr, 0x4000),
36    (kSpeakerTrl, 0x8000),
37    (kSpeakerTrc, 0x10000),
38    (kSpeakerTrr, 0x20000),
39];
40
41pub fn wave_bits(arrangement: u64) -> Result<Vec<u32>, LayoutError> {
42    if arrangement == 0 {
43        return Err(LayoutError::Empty);
44    }
45    let known = SPEAKERS.iter().fold(0, |bits, (speaker, _)| bits | speaker);
46    let unknown = arrangement & !known;
47    if unknown != 0 {
48        return Err(LayoutError::UnknownBit(unknown.trailing_zeros()));
49    }
50    Ok(SPEAKERS
51        .iter()
52        .filter_map(|(speaker, wave)| (arrangement & speaker != 0).then_some(*wave))
53        .collect())
54}
55
56/// Validate the main bus, retaining its declared channel order for the engine.
57pub fn main_bus(buses: &[crate::Bus], direction: &'static str) -> Result<Vec<u32>, LayoutError> {
58    let bus = buses.first().ok_or(LayoutError::Bus(direction, 0))?;
59    let bits = wave_bits(bus.arrangement)?;
60    if bus.channels == 0 || bus.channels > 32 || bits.len() != usize::from(bus.channels) {
61        return Err(LayoutError::Bus(direction, bus.arrangement));
62    }
63    Ok(bits)
64}
65
66pub fn default_arrangement(channels: u16) -> Result<u64, LayoutError> {
67    match channels {
68        1 => Ok(kSpeakerC),
69        2 => Ok(kSpeakerL | kSpeakerR),
70        6 => Ok(kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs),
71        8 => Ok(kSpeakerL
72            | kSpeakerR
73            | kSpeakerC
74            | kSpeakerLfe
75            | kSpeakerLs
76            | kSpeakerRs
77            | kSpeakerSl
78            | kSpeakerSr),
79        _ => Err(LayoutError::UnknownChannels(channels)),
80    }
81}
82
83/// Types confirmed in Xaymar's clean-room header. Reject reordered channels:
84/// a bitmask must not silently lose the plugin's physical channel order.
85pub fn vst2_arrangement(speakers: &[i32]) -> Result<u64, LayoutError> {
86    let mut result = 0;
87    let mut previous = 0;
88    for speaker in speakers {
89        let bit = match speaker {
90            0 | 3 => kSpeakerC,
91            1 => kSpeakerL,
92            2 => kSpeakerR,
93            4 => kSpeakerLfe,
94            5 => kSpeakerLs,
95            6 => kSpeakerRs,
96            10 => kSpeakerSl,
97            11 => kSpeakerSr,
98            other => return Err(LayoutError::UnknownSpeaker(*other)),
99        };
100        if bit <= previous {
101            return Err(LayoutError::Order);
102        }
103        previous = bit;
104        result |= bit;
105    }
106    Ok(result)
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    #[test]
113    fn common_layouts() {
114        for (channels, expected) in [
115            (1, vec![4]),
116            (2, vec![1, 2]),
117            (6, vec![1, 2, 4, 8, 16, 32]),
118            (8, vec![1, 2, 4, 8, 16, 32, 512, 1024]),
119        ] {
120            assert_eq!(
121                wave_bits(default_arrangement(channels).unwrap()).unwrap(),
122                expected
123            );
124        }
125        assert_eq!(
126            wave_bits(kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs).unwrap(),
127            [1, 2, 16, 32]
128        );
129        assert_eq!(
130            wave_bits(kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs).unwrap(),
131            [1, 2, 4, 16, 32]
132        );
133        for (bit, wave) in SPEAKERS {
134            assert_eq!(wave_bits(bit).unwrap(), [wave]);
135        }
136        assert_eq!(wave_bits((1 << 18) - 1).unwrap().len(), 18);
137    }
138    #[test]
139    fn unknown_layouts_are_errors() {
140        for bit in 18..64 {
141            assert_eq!(wave_bits(1 << bit), Err(LayoutError::UnknownBit(bit)));
142        }
143        for count in [0, 3, 4, 5, 7, 9, 32] {
144            assert!(default_arrangement(count).is_err());
145        }
146        assert!(vst2_arrangement(&[1, 1]).is_err());
147        assert!(vst2_arrangement(&[2, 1]).is_err());
148        assert!(vst2_arrangement(&[99]).is_err());
149        assert_eq!(
150            vst2_arrangement(&[1, 2, 3, 4, 5, 6]).unwrap(),
151            default_arrangement(6).unwrap()
152        );
153    }
154}