pub mod desktop;
pub mod shell;
pub mod studio;
pub mod ch_strip;
pub mod reverb;
use {
super::{tcat::*, *},
ta1394_avc_general::{general::*, *},
};
const BASE_OFFSET: usize = 0x00a01000;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TcKonnektSegment<U> {
pub data: U,
raw: Vec<u8>,
}
pub trait TcKonnektSegmentSerdes<T> {
const NAME: &'static str;
const OFFSET: usize;
const SIZE: usize;
fn serialize(params: &T, raw: &mut [u8]) -> Result<(), String>;
fn deserialize(params: &mut T, raw: &[u8]) -> Result<(), String>;
}
fn generate_error(segment_name: &str, cause: &str, raw: &[u8]) -> Error {
let msg = format!(
"segment: {}, cause: '{}', raw: {:02x?}",
segment_name, cause, raw
);
Error::new(GeneralProtocolError::VendorDependent, &msg)
}
pub trait TcKonnektSegmentOperation<T>: TcatOperation + TcKonnektSegmentSerdes<T> {
fn cache_whole_segment(
req: &FwReq,
node: &FwNode,
segment: &mut TcKonnektSegment<T>,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(segment.raw.len(), Self::SIZE);
Self::read(
req,
node,
BASE_OFFSET + Self::OFFSET,
&mut segment.raw,
timeout_ms,
)?;
Self::deserialize(&mut segment.data, &segment.raw)
.map_err(|cause| generate_error(Self::NAME, &cause, &segment.raw))
}
}
impl<O: TcatOperation + TcKonnektSegmentSerdes<T>, T> TcKonnektSegmentOperation<T> for O {}
pub trait TcKonnektMutableSegmentOperation<T>: TcatOperation + TcKonnektSegmentSerdes<T> {
fn update_partial_segment(
req: &FwReq,
node: &FwNode,
params: &T,
segment: &mut TcKonnektSegment<T>,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(segment.raw.len(), Self::SIZE);
let mut raw = segment.raw.clone();
Self::serialize(params, &mut raw)
.map_err(|cause| generate_error(Self::NAME, &cause, &segment.raw))?;
(0..Self::SIZE).step_by(4).try_for_each(|pos| {
let new = &mut raw[pos..(pos + 4)];
if new != &segment.raw[pos..(pos + 4)] {
Self::write(req, node, BASE_OFFSET + Self::OFFSET + pos, new, timeout_ms)
.map(|_| segment.raw[pos..(pos + 4)].copy_from_slice(new))
} else {
Ok(())
}
})?;
Self::deserialize(&mut segment.data, &raw)
.map_err(|cause| generate_error(Self::NAME, &cause, &segment.raw))
}
fn update_whole_segment(
req: &FwReq,
node: &FwNode,
params: &T,
segment: &mut TcKonnektSegment<T>,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(segment.raw.len(), Self::SIZE);
let mut raw = segment.raw.clone();
Self::serialize(¶ms, &mut raw)
.map_err(|cause| generate_error(Self::NAME, &cause, &segment.raw))?;
Self::write(req, node, BASE_OFFSET + Self::OFFSET, &mut raw, timeout_ms)?;
segment.raw.copy_from_slice(&raw);
Self::deserialize(&mut segment.data, &segment.raw)
.map_err(|cause| generate_error(Self::NAME, &cause, &segment.raw))
}
}
pub trait TcKonnektNotifiedSegmentOperation<T> {
const NOTIFY_FLAG: u32;
fn is_notified_segment(_: &TcKonnektSegment<T>, msg: u32) -> bool {
msg & Self::NOTIFY_FLAG > 0
}
}
fn serialize_position<T: Eq + std::fmt::Debug>(
entries: &[T],
entry: &T,
raw: &mut [u8],
label: &str,
) -> Result<(), String> {
assert!(raw.len() >= 4);
entries
.iter()
.position(|t| entry.eq(t))
.ok_or_else(|| format!("{} {:?} is not supported", label, entry))
.map(|pos| serialize_usize(&pos, raw))
}
fn deserialize_position<T: Copy + Eq + std::fmt::Debug>(
entries: &[T],
entry: &mut T,
raw: &[u8],
label: &str,
) -> Result<(), String> {
assert!(raw.len() >= 4);
let mut val = 0usize;
deserialize_usize(&mut val, raw);
entries
.iter()
.nth(val as usize)
.ok_or_else(|| format!("{} not found for index {}", label, val))
.map(|&e| *entry = e)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FireWireLedState {
Off,
On,
BlinkFast,
BlinkSlow,
}
impl Default for FireWireLedState {
fn default() -> Self {
Self::Off
}
}
const FW_LED_STATES: &[FireWireLedState] = &[
FireWireLedState::Off,
FireWireLedState::On,
FireWireLedState::BlinkSlow,
FireWireLedState::BlinkFast,
];
const FW_LED_STATE_LABEL: &str = "FireWire LED state";
fn serialize_fw_led_state(state: &FireWireLedState, raw: &mut [u8]) -> Result<(), String> {
serialize_position(FW_LED_STATES, state, raw, FW_LED_STATE_LABEL)
}
fn deserialize_fw_led_state(state: &mut FireWireLedState, raw: &[u8]) -> Result<(), String> {
deserialize_position(FW_LED_STATES, state, raw, FW_LED_STATE_LABEL)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TcKonnektStandaloneClockRate {
R44100,
R48000,
R88200,
R96000,
}
impl Default for TcKonnektStandaloneClockRate {
fn default() -> Self {
Self::R44100
}
}
fn serialize_standalone_clock_rate(
rate: &TcKonnektStandaloneClockRate,
raw: &mut [u8],
) -> Result<(), String> {
assert!(raw.len() >= 4);
let val = match rate {
TcKonnektStandaloneClockRate::R96000 => 4,
TcKonnektStandaloneClockRate::R88200 => 3,
TcKonnektStandaloneClockRate::R48000 => 2,
TcKonnektStandaloneClockRate::R44100 => 1,
};
serialize_u32(&val, raw);
Ok(())
}
fn deserialize_standalone_clock_rate(
rate: &mut TcKonnektStandaloneClockRate,
raw: &[u8],
) -> Result<(), String> {
assert!(raw.len() >= 4);
let mut val = 0u32;
deserialize_u32(&mut val, raw);
*rate = match val {
4 => TcKonnektStandaloneClockRate::R96000,
3 => TcKonnektStandaloneClockRate::R88200,
2 => TcKonnektStandaloneClockRate::R48000,
1 => TcKonnektStandaloneClockRate::R44100,
_ => Err(format!(
"Unexpected value for standalone clock rate: {}",
val
))?,
};
Ok(())
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct TcKonnektMidiMsgParams {
pub ch: u8,
pub cc: u8,
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct TcKonnektMidiSender {
pub normal: TcKonnektMidiMsgParams,
pub pushed: TcKonnektMidiMsgParams,
pub send_to_port: bool,
pub send_to_stream: bool,
}
impl TcKonnektMidiSender {
pub(crate) const SIZE: usize = 36;
}
fn serialize_midi_sender(sender: &TcKonnektMidiSender, raw: &mut [u8]) -> Result<(), String> {
assert!(raw.len() >= TcKonnektMidiSender::SIZE);
serialize_u8(&sender.normal.ch, &mut raw[..4]);
serialize_u8(&sender.normal.cc, &mut raw[4..8]);
serialize_u8(&sender.pushed.ch, &mut raw[12..16]);
serialize_u8(&sender.pushed.cc, &mut raw[16..20]);
serialize_bool(&sender.send_to_port, &mut raw[24..28]);
serialize_bool(&sender.send_to_stream, &mut raw[28..32]);
Ok(())
}
fn deserialize_midi_sender(sender: &mut TcKonnektMidiSender, raw: &[u8]) -> Result<(), String> {
assert!(raw.len() >= TcKonnektMidiSender::SIZE);
deserialize_u8(&mut sender.normal.ch, &raw[..4]);
deserialize_u8(&mut sender.normal.cc, &raw[4..8]);
deserialize_u8(&mut sender.pushed.ch, &raw[12..16]);
deserialize_u8(&mut sender.pushed.cc, &raw[16..20]);
deserialize_bool(&mut sender.send_to_port, &raw[24..28]);
deserialize_bool(&mut sender.send_to_stream, &raw[28..32]);
Ok(())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TcKonnektLoadedProgram {
P0,
P1,
P2,
}
impl Default for TcKonnektLoadedProgram {
fn default() -> Self {
Self::P0
}
}
const LOADED_PROGRAMS: &[TcKonnektLoadedProgram] = &[
TcKonnektLoadedProgram::P0,
TcKonnektLoadedProgram::P1,
TcKonnektLoadedProgram::P2,
];
const LOADED_PROGRAM_LABEL: &str = "loaded program";
fn serialize_loaded_program(prog: &TcKonnektLoadedProgram, raw: &mut [u8]) -> Result<(), String> {
serialize_position(LOADED_PROGRAMS, prog, raw, LOADED_PROGRAM_LABEL)
}
fn deserialize_loaded_program(prog: &mut TcKonnektLoadedProgram, raw: &[u8]) -> Result<(), String> {
deserialize_position(LOADED_PROGRAMS, prog, raw, LOADED_PROGRAM_LABEL)
}
#[derive(Default, Debug)]
pub struct TcAvcCmd {
pub class_id: u8,
pub sequence_id: u8,
pub command_id: u16,
pub arguments: Vec<u8>,
op: VendorDependent,
}
fn tc_avc_cmd_prepare_vendor_dependent_data(cmd: &mut TcAvcCmd) {
cmd.op.data.resize(4 + cmd.arguments.len(), 0);
cmd.op.data[0] = cmd.class_id;
cmd.op.data[1] = 0xff;
cmd.op.data[2] = (0xff & (cmd.command_id >> 8)) as u8;
cmd.op.data[3] = (0xff & cmd.command_id) as u8;
cmd.op.data[4..].copy_from_slice(&cmd.arguments);
}
fn tc_avc_cmd_parse_vendor_dependent_data(cmd: &mut TcAvcCmd) {
cmd.class_id = cmd.op.data[0];
cmd.sequence_id = cmd.op.data[1];
cmd.command_id = ((cmd.op.data[2] as u16) << 8) | (cmd.op.data[3] as u16);
cmd.arguments = cmd.op.data[4..].to_owned();
}
impl TcAvcCmd {
pub fn new(company_id: &[u8; 3]) -> Self {
Self {
class_id: Default::default(),
sequence_id: Default::default(),
command_id: Default::default(),
arguments: Default::default(),
op: VendorDependent {
company_id: company_id.clone(),
data: vec![0; 4],
},
}
}
}
impl AvcOp for TcAvcCmd {
const OPCODE: u8 = VendorDependent::OPCODE;
}
impl AvcStatus for TcAvcCmd {
fn build_operands(&mut self, addr: &AvcAddr) -> Result<Vec<u8>, AvcCmdBuildError> {
tc_avc_cmd_prepare_vendor_dependent_data(self);
AvcStatus::build_operands(&mut self.op, addr)
}
fn parse_operands(&mut self, addr: &AvcAddr, operands: &[u8]) -> Result<(), AvcRespParseError> {
AvcStatus::parse_operands(&mut self.op, addr, operands)
.map(|_| tc_avc_cmd_parse_vendor_dependent_data(self))
}
}
impl AvcControl for TcAvcCmd {
fn build_operands(&mut self, addr: &AvcAddr) -> Result<Vec<u8>, AvcCmdBuildError> {
tc_avc_cmd_prepare_vendor_dependent_data(self);
AvcControl::build_operands(&mut self.op, addr)
}
fn parse_operands(&mut self, addr: &AvcAddr, operands: &[u8]) -> Result<(), AvcRespParseError> {
AvcControl::parse_operands(&mut self.op, addr, operands)
.map(|_| tc_avc_cmd_parse_vendor_dependent_data(self))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn tc_avc_operation_operands() {
let company_id = [0xfe, 0xdc, 0xba];
let operands = [0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10];
let mut op = TcAvcCmd::new(&company_id);
AvcStatus::parse_operands(&mut op, &AvcAddr::Unit, &operands).unwrap();
assert_eq!(op.op.company_id, company_id);
assert_eq!(op.class_id, operands[3]);
assert_eq!(op.sequence_id, operands[4]);
assert_eq!(
op.command_id,
((operands[5] as u16) << 8) | (operands[6] as u16)
);
assert_eq!(op.arguments, operands[7..]);
let target = AvcStatus::build_operands(&mut op, &AvcAddr::Unit).unwrap();
assert_eq!(&target[..4], &operands[..4]);
assert_eq!(&target[5..], &operands[5..]);
let target = AvcControl::build_operands(&mut op, &AvcAddr::Unit).unwrap();
assert_eq!(&target[..4], &operands[..4]);
assert_eq!(&target[5..], &operands[5..]);
let mut op = TcAvcCmd::new(&company_id);
AvcControl::parse_operands(&mut op, &AvcAddr::Unit, &operands).unwrap();
assert_eq!(op.op.company_id, company_id);
assert_eq!(op.class_id, operands[3]);
assert_eq!(op.sequence_id, operands[4]);
assert_eq!(
op.command_id,
((operands[5] as u16) << 8) | (operands[6] as u16)
);
assert_eq!(op.arguments, operands[7..]);
}
}