diff --git a/atem-connection-rs/src/commands/device_profile.rs b/atem-connection-rs/src/commands/device_profile.rs index b45eb6b..5226f58 100644 --- a/atem-connection-rs/src/commands/device_profile.rs +++ b/atem-connection-rs/src/commands/device_profile.rs @@ -1,5 +1,7 @@ +pub mod audio_mixer_config; pub mod media_pool_config; pub mod mix_effect_block_config; +pub mod multiviewer_config; pub mod product_identifier; pub mod topology; pub mod version; diff --git a/atem-connection-rs/src/commands/device_profile/audio_mixer_config.rs b/atem-connection-rs/src/commands/device_profile/audio_mixer_config.rs new file mode 100644 index 0000000..9cec08f --- /dev/null +++ b/atem-connection-rs/src/commands/device_profile/audio_mixer_config.rs @@ -0,0 +1,47 @@ +use std::sync::Arc; + +use crate::{ + commands::command_base::{CommandDeserializer, DeserializedCommand}, + state::{audio::AtemClassicAudioState, info::AudioMixerInfo}, +}; + +pub const DESERIALIZE_AUDIO_MIXER_CONFIG_NAME: &str = "_AMC"; + +#[derive(Debug)] +pub struct AudioMixerConfig { + inputs: u8, + monitors: u8, + headphones: u8, +} + +impl DeserializedCommand for AudioMixerConfig { + fn raw_name(&self) -> &'static str { + DESERIALIZE_AUDIO_MIXER_CONFIG_NAME + } + + fn apply_to_state(&self, state: &mut crate::state::AtemState) { + state.info.audio_mixer = Some(AudioMixerInfo::new( + self.inputs, + self.monitors, + self.headphones, + )); + state.audio = Some(AtemClassicAudioState::new(self.inputs, self.monitors != 0)) + } +} + +#[derive(Default)] +pub struct AudioMixerConfigDeserializer {} + +impl CommandDeserializer for AudioMixerConfigDeserializer { + fn deserialize( + &self, + buffer: &[u8], + version: &crate::enums::ProtocolVersion, + ) -> std::sync::Arc { + Arc::new(AudioMixerConfig { + inputs: buffer[0], + monitors: buffer[1], + headphones: buffer[2], + }) + } +} diff --git a/atem-connection-rs/src/commands/device_profile/multiviewer_config.rs b/atem-connection-rs/src/commands/device_profile/multiviewer_config.rs new file mode 100644 index 0000000..00099fb --- /dev/null +++ b/atem-connection-rs/src/commands/device_profile/multiviewer_config.rs @@ -0,0 +1,58 @@ +use std::sync::Arc; + +use crate::{ + commands::command_base::{CommandDeserializer, DeserializedCommand}, + enums::ProtocolVersion, + state::info::MultiviewerInfo, +}; + +pub const DESERIALIZE_MULTIVIEWER_NAME: &str = "_MvC"; + +#[derive(Debug)] +pub struct MultiviewerConfig { + count: Option, + window_count: u8, +} + +impl DeserializedCommand for MultiviewerConfig { + fn raw_name(&self) -> &'static str { + DESERIALIZE_MULTIVIEWER_NAME + } + + fn apply_to_state(&self, state: &mut crate::state::AtemState) { + // TODO: This can't be right... + + let existing_count = match &state.info.multiviewer { + Some(multiviewer) => multiviewer.count().as_ref().copied(), + None => None, + }; + let count = match self.count { + Some(count) => Some(count), + None => existing_count, + }; + state.info.multiviewer = Some(MultiviewerInfo::new(count, self.window_count)); + } +} + +#[derive(Default)] +pub struct MultiviewerConfigDeserializer {} + +impl CommandDeserializer for MultiviewerConfigDeserializer { + fn deserialize( + &self, + buffer: &[u8], + version: &crate::enums::ProtocolVersion, + ) -> std::sync::Arc { + if *version >= ProtocolVersion::V8_1_1 { + Arc::new(MultiviewerConfig { + count: None, + window_count: buffer[1], + }) + } else { + Arc::new(MultiviewerConfig { + count: Some(buffer[0]), + window_count: buffer[1], + }) + } + } +} diff --git a/atem-connection-rs/src/commands/device_profile/topology.rs b/atem-connection-rs/src/commands/device_profile/topology.rs index 39f3d51..0f69cd1 100644 --- a/atem-connection-rs/src/commands/device_profile/topology.rs +++ b/atem-connection-rs/src/commands/device_profile/topology.rs @@ -51,15 +51,14 @@ impl DeserializedCommand for Topology { self.advanced_chroma_keyers, self.only_configurable_outputs, )); - if let Some(multiviewers) = self.multiviewers { - let window_count = if let Some(mv) = &state.info.multiviewer { - *mv.window_count() - } else { - 10 - }; - state.info.multiviewer = Some(MultiviewerInfo::new(multiviewers, window_count)) - } + let window_count = if let Some(mv) = &state.info.multiviewer { + *mv.window_count() + } else { + 10 + }; + + state.info.multiviewer = Some(MultiviewerInfo::new(self.multiviewers, window_count)); } } diff --git a/atem-connection-rs/src/commands/parse_commands.rs b/atem-connection-rs/src/commands/parse_commands.rs index baa2305..1c51d1d 100644 --- a/atem-connection-rs/src/commands/parse_commands.rs +++ b/atem-connection-rs/src/commands/parse_commands.rs @@ -8,10 +8,12 @@ use crate::{ use super::{ command_base::{CommandDeserializer, DeserializedCommand}, device_profile::{ + audio_mixer_config::{AudioMixerConfigDeserializer, DESERIALIZE_AUDIO_MIXER_CONFIG_NAME}, media_pool_config::{MediaPoolConfigDeserializer, DESERIALIZE_MEDIA_POOL_CONFIG_NAME}, mix_effect_block_config::{ MixEffectBlockConfigDeserializer, DESERIALIZE_MIX_EFFECT_BLOCK_CONFIG_NAME, }, + multiviewer_config::{MultiviewerConfigDeserializer, DESERIALIZE_MULTIVIEWER_NAME}, product_identifier::{ ProductIdentifierDeserializer, DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME, }, @@ -79,6 +81,8 @@ fn command_deserializer_from_string(command_str: &str) -> Option::default()) } DESERIALIZE_MEDIA_POOL_CONFIG_NAME => Some(Box::::default()), + DESERIALIZE_MULTIVIEWER_NAME => Some(Box::::default()), + DESERIALIZE_AUDIO_MIXER_CONFIG_NAME => Some(Box::::default()), _ => None, } } diff --git a/atem-connection-rs/src/state/audio.rs b/atem-connection-rs/src/state/audio.rs index 72046d2..0255afd 100644 --- a/atem-connection-rs/src/state/audio.rs +++ b/atem-connection-rs/src/state/audio.rs @@ -44,14 +44,28 @@ pub struct ClassicAudioHeadphoneOutputChannel { pub talkback_gain: f64, } -#[derive(Clone, PartialEq, Getters, new)] +#[derive(Clone, PartialEq, Getters)] pub struct AtemClassicAudioState { - number_of_channels: Option, - has_monitor: Option, + number_of_channels: u8, + has_monitor: bool, pub channels: HashMap, pub monitor: Option, pub headphones: Option, pub master: Option, - pub audio_follow_video_crossfade_transition_enabled: Option, + pub audio_follow_video_crossfade_transition_enabled: bool, +} + +impl AtemClassicAudioState { + pub fn new(number_of_channels: u8, has_monitor: bool) -> Self { + Self { + number_of_channels, + has_monitor, + channels: Default::default(), + monitor: Default::default(), + headphones: Default::default(), + master: Default::default(), + audio_follow_video_crossfade_transition_enabled: false, + } + } } diff --git a/atem-connection-rs/src/state/info.rs b/atem-connection-rs/src/state/info.rs index e459622..c97f2be 100644 --- a/atem-connection-rs/src/state/info.rs +++ b/atem-connection-rs/src/state/info.rs @@ -31,9 +31,9 @@ pub struct SuperSourceInfo { #[derive(Clone, PartialEq, Getters, new)] pub struct AudioMixerInfo { - inputs: u64, - monitors: u64, - headphones: u64, + inputs: u8, + monitors: u8, + headphones: u8, } #[derive(Clone, PartialEq, Getters, new)] @@ -55,7 +55,7 @@ pub struct MediaPoolInfo { #[derive(Clone, PartialEq, Getters, new)] pub struct MultiviewerInfo { - count: u8, + count: Option, window_count: u8, }