1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
//! Meta data for dealing with input / output channels. Not all hosts use this so it is not
//! necessary for plugin functionality.

use crate::api;
use crate::api::consts::{MAX_LABEL, MAX_SHORT_LABEL};

/// Information about an input / output channel. This isn't necessary for a channel to function but
/// informs the host how the channel is meant to be used.
pub struct ChannelInfo {
    name: String,
    short_name: String,
    active: bool,
    arrangement_type: SpeakerArrangementType,
}

impl ChannelInfo {
    /// Construct a new `ChannelInfo` object.
    ///
    /// `name` is a user friendly name for this channel limited to `MAX_LABEL` characters.
    /// `short_name` is an optional field which provides a short name limited to `MAX_SHORT_LABEL`.
    /// `active` determines whether this channel is active.
    /// `arrangement_type` describes the arrangement type for this channel.
    pub fn new(
        name: String,
        short_name: Option<String>,
        active: bool,
        arrangement_type: Option<SpeakerArrangementType>,
    ) -> ChannelInfo {
        ChannelInfo {
            name: name.clone(),

            short_name: if let Some(short_name) = short_name {
                short_name
            } else {
                name
            },

            active,

            arrangement_type: arrangement_type.unwrap_or(SpeakerArrangementType::Custom),
        }
    }
}

impl Into<api::ChannelProperties> for ChannelInfo {
    /// Convert to the VST api equivalent of this structure.
    fn into(self) -> api::ChannelProperties {
        api::ChannelProperties {
            name: {
                let mut label = [0; MAX_LABEL as usize];
                for (b, c) in self.name.bytes().zip(label.iter_mut()) {
                    *c = b;
                }
                label
            },
            flags: {
                let mut flag = api::ChannelFlags::empty();
                if self.active {
                    flag |= api::ChannelFlags::ACTIVE
                }
                if self.arrangement_type.is_left_stereo() {
                    flag |= api::ChannelFlags::STEREO
                }
                if self.arrangement_type.is_speaker_type() {
                    flag |= api::ChannelFlags::SPEAKER
                }
                flag.bits()
            },
            arrangement_type: self.arrangement_type.into(),
            short_name: {
                let mut label = [0; MAX_SHORT_LABEL as usize];
                for (b, c) in self.short_name.bytes().zip(label.iter_mut()) {
                    *c = b;
                }
                label
            },
            future: [0; 48],
        }
    }
}

impl From<api::ChannelProperties> for ChannelInfo {
    fn from(api: api::ChannelProperties) -> ChannelInfo {
        ChannelInfo {
            name: String::from_utf8_lossy(&api.name).to_string(),
            short_name: String::from_utf8_lossy(&api.short_name).to_string(),
            active: api::ChannelFlags::from_bits(api.flags)
                .expect("Invalid bits in channel info")
                .intersects(api::ChannelFlags::ACTIVE),
            arrangement_type: SpeakerArrangementType::from(api),
        }
    }
}

/// Target for Speaker arrangement type. Can be a cinema configuration or music configuration. Both
/// are technically identical but this provides extra information to the host.
pub enum ArrangementTarget {
    /// Music arrangement. Technically identical to Cinema.
    Music,
    /// Cinematic arrangement. Technically identical to Music.
    Cinema,
}

/// An enum for all channels in a stereo configuration.
pub enum StereoChannel {
    /// Left channel.
    Left,
    /// Right channel.
    Right,
}

/// Possible stereo speaker configurations.
#[allow(non_camel_case_types)]
pub enum StereoConfig {
    /// Regular.
    L_R,
    /// Left surround, right surround.
    Ls_Rs,
    /// Left center, right center.
    Lc_Rc,
    /// Side left, side right.
    Sl_Sr,
    /// Center, low frequency effects.
    C_Lfe,
}

/// Possible surround speaker configurations.
#[allow(non_camel_case_types)]
pub enum SurroundConfig {
    /// 3.0 surround sound.
    /// Cinema: L R C
    /// Music: L R S
    S3_0(ArrangementTarget),
    /// 3.1 surround sound.
    /// Cinema: L R C Lfe
    /// Music: L R Lfe S
    S3_1(ArrangementTarget),
    /// 4.0 surround sound.
    /// Cinema: L R C S (LCRS)
    /// Music: L R Ls Rs (Quadro)
    S4_0(ArrangementTarget),
    /// 4.1 surround sound.
    /// Cinema: L R C Lfe S (LCRS + Lfe)
    /// Music: L R Ls Rs (Quadro + Lfe)
    S4_1(ArrangementTarget),
    /// 5.0 surround sound.
    /// Cinema and music: L R C Ls Rs
    S5_0,
    /// 5.1 surround sound.
    /// Cinema and music: L R C Lfe Ls Rs
    S5_1,
    /// 6.0 surround sound.
    /// Cinema: L R C Ls Rs Cs
    /// Music: L R Ls Rs Sl Sr
    S6_0(ArrangementTarget),
    /// 6.1 surround sound.
    /// Cinema: L R C Lfe Ls Rs Cs
    /// Music: L R Ls Rs Sl Sr
    S6_1(ArrangementTarget),
    /// 7.0 surround sound.
    /// Cinema: L R C Ls Rs Lc Rc
    /// Music: L R C Ls Rs Sl Sr
    S7_0(ArrangementTarget),
    /// 7.1 surround sound.
    /// Cinema: L R C Lfe Ls Rs Lc Rc
    /// Music: L R C Lfe Ls Rs Sl Sr
    S7_1(ArrangementTarget),
    /// 8.0 surround sound.
    /// Cinema: L R C Ls Rs Lc Rc Cs
    /// Music: L R C Ls Rs Cs Sl Sr
    S8_0(ArrangementTarget),
    /// 8.1 surround sound.
    /// Cinema: L R C Lfe Ls Rs Lc Rc Cs
    /// Music: L R C Lfe Ls Rs Cs Sl Sr
    S8_1(ArrangementTarget),
    /// 10.2 surround sound.
    /// Cinema + Music: L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2
    S10_2,
}

/// Type representing how a channel is used. Only useful for some hosts.
pub enum SpeakerArrangementType {
    /// Custom arrangement not specified to host.
    Custom,
    /// Empty arrangement.
    Empty,
    /// Mono channel.
    Mono,
    /// Stereo channel. Contains type of stereo arrangement and speaker represented.
    Stereo(StereoConfig, StereoChannel),
    /// Surround channel. Contains surround arrangement and target (cinema or music).
    Surround(SurroundConfig),
}

impl Default for SpeakerArrangementType {
    fn default() -> SpeakerArrangementType {
        SpeakerArrangementType::Mono
    }
}

impl SpeakerArrangementType {
    /// Determine whether this channel is part of a surround speaker arrangement.
    pub fn is_speaker_type(&self) -> bool {
        if let SpeakerArrangementType::Surround(..) = *self {
            true
        } else {
            false
        }
    }

    /// Determine whether this channel is the left speaker in a stereo pair.
    pub fn is_left_stereo(&self) -> bool {
        if let SpeakerArrangementType::Stereo(_, StereoChannel::Left) = *self {
            true
        } else {
            false
        }
    }
}

impl Into<api::SpeakerArrangementType> for SpeakerArrangementType {
    /// Convert to VST API arrangement type.
    fn into(self) -> api::SpeakerArrangementType {
        use self::ArrangementTarget::{Cinema, Music};
        use self::SpeakerArrangementType::*;
        use api::SpeakerArrangementType as Raw;

        match self {
            Custom => Raw::Custom,
            Empty => Raw::Empty,
            Mono => Raw::Mono,
            Stereo(conf, _) => {
                match conf {
                    // Stereo channels.
                    StereoConfig::L_R => Raw::Stereo,
                    StereoConfig::Ls_Rs => Raw::StereoSurround,
                    StereoConfig::Lc_Rc => Raw::StereoCenter,
                    StereoConfig::Sl_Sr => Raw::StereoSide,
                    StereoConfig::C_Lfe => Raw::StereoCLfe,
                }
            }
            Surround(conf) => {
                match conf {
                    // Surround channels.
                    SurroundConfig::S3_0(Music) => Raw::Music30,
                    SurroundConfig::S3_0(Cinema) => Raw::Cinema30,

                    SurroundConfig::S3_1(Music) => Raw::Music31,
                    SurroundConfig::S3_1(Cinema) => Raw::Cinema31,

                    SurroundConfig::S4_0(Music) => Raw::Music40,
                    SurroundConfig::S4_0(Cinema) => Raw::Cinema40,

                    SurroundConfig::S4_1(Music) => Raw::Music41,
                    SurroundConfig::S4_1(Cinema) => Raw::Cinema41,

                    SurroundConfig::S5_0 => Raw::Surround50,
                    SurroundConfig::S5_1 => Raw::Surround51,

                    SurroundConfig::S6_0(Music) => Raw::Music60,
                    SurroundConfig::S6_0(Cinema) => Raw::Cinema60,

                    SurroundConfig::S6_1(Music) => Raw::Music61,
                    SurroundConfig::S6_1(Cinema) => Raw::Cinema61,

                    SurroundConfig::S7_0(Music) => Raw::Music70,
                    SurroundConfig::S7_0(Cinema) => Raw::Cinema70,

                    SurroundConfig::S7_1(Music) => Raw::Music71,
                    SurroundConfig::S7_1(Cinema) => Raw::Cinema71,

                    SurroundConfig::S8_0(Music) => Raw::Music80,
                    SurroundConfig::S8_0(Cinema) => Raw::Cinema80,

                    SurroundConfig::S8_1(Music) => Raw::Music81,
                    SurroundConfig::S8_1(Cinema) => Raw::Cinema81,

                    SurroundConfig::S10_2 => Raw::Surround102,
                }
            }
        }
    }
}

/// Convert the VST API equivalent struct into something more usable.
///
/// We implement `From<ChannelProperties>` as `SpeakerArrangementType` contains extra info about
/// stereo speakers found in the channel flags.
impl From<api::ChannelProperties> for SpeakerArrangementType {
    fn from(api: api::ChannelProperties) -> SpeakerArrangementType {
        use self::ArrangementTarget::{Cinema, Music};
        use self::SpeakerArrangementType::*;
        use self::SurroundConfig::*;
        use api::SpeakerArrangementType as Raw;

        let stereo = if api::ChannelFlags::from_bits(api.flags)
            .expect("Invalid Channel Flags")
            .intersects(api::ChannelFlags::STEREO)
        {
            StereoChannel::Left
        } else {
            StereoChannel::Right
        };

        match api.arrangement_type {
            Raw::Custom => Custom,
            Raw::Empty => Empty,
            Raw::Mono => Mono,

            Raw::Stereo => Stereo(StereoConfig::L_R, stereo),
            Raw::StereoSurround => Stereo(StereoConfig::Ls_Rs, stereo),
            Raw::StereoCenter => Stereo(StereoConfig::Lc_Rc, stereo),
            Raw::StereoSide => Stereo(StereoConfig::Sl_Sr, stereo),
            Raw::StereoCLfe => Stereo(StereoConfig::C_Lfe, stereo),

            Raw::Music30 => Surround(S3_0(Music)),
            Raw::Cinema30 => Surround(S3_0(Cinema)),

            Raw::Music31 => Surround(S3_1(Music)),
            Raw::Cinema31 => Surround(S3_1(Cinema)),

            Raw::Music40 => Surround(S4_0(Music)),
            Raw::Cinema40 => Surround(S4_0(Cinema)),

            Raw::Music41 => Surround(S4_1(Music)),
            Raw::Cinema41 => Surround(S4_1(Cinema)),

            Raw::Surround50 => Surround(S5_0),
            Raw::Surround51 => Surround(S5_1),

            Raw::Music60 => Surround(S6_0(Music)),
            Raw::Cinema60 => Surround(S6_0(Cinema)),

            Raw::Music61 => Surround(S6_1(Music)),
            Raw::Cinema61 => Surround(S6_1(Cinema)),

            Raw::Music70 => Surround(S7_0(Music)),
            Raw::Cinema70 => Surround(S7_0(Cinema)),

            Raw::Music71 => Surround(S7_1(Music)),
            Raw::Cinema71 => Surround(S7_1(Cinema)),

            Raw::Music80 => Surround(S8_0(Music)),
            Raw::Cinema80 => Surround(S8_0(Cinema)),

            Raw::Music81 => Surround(S8_1(Music)),
            Raw::Cinema81 => Surround(S8_1(Cinema)),

            Raw::Surround102 => Surround(S10_2),
        }
    }
}