ableton-link-rs 0.1.1

Native Rust implementation of the Ableton Link protocol
Documentation
use std::mem;

use chrono::Duration;

use crate::discovery::ENCODING_CONFIG;

use super::{
    beats::Beats, ghostxform::GhostXForm, payload::PayloadEntryHeader, timeline::Timeline, Result,
};

pub const START_STOP_STATE_HEADER_KEY: u32 = u32::from_be_bytes(*b"stst");
pub const START_STOP_STATE_SIZE: u32 =
    (mem::size_of::<bool>() + mem::size_of::<Beats>() + mem::size_of::<u64>()) as u32;
pub const START_STOP_STATE_HEADER: PayloadEntryHeader = PayloadEntryHeader {
    key: START_STOP_STATE_HEADER_KEY,
    size: START_STOP_STATE_SIZE,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StartStopState {
    pub is_playing: bool,
    pub beats: Beats,
    pub timestamp: Duration,
}

impl Default for StartStopState {
    fn default() -> Self {
        Self {
            is_playing: false,
            beats: Beats { value: 0i64 },
            timestamp: Duration::zero(),
        }
    }
}

impl bincode::Encode for StartStopState {
    fn encode<E: bincode::enc::Encoder>(
        &self,
        encoder: &mut E,
    ) -> std::result::Result<(), bincode::error::EncodeError> {
        bincode::Encode::encode(
            &(
                self.is_playing,
                self.beats,
                self.timestamp.num_microseconds().unwrap(),
            ),
            encoder,
        )
    }
}

impl bincode::Decode<()> for StartStopState {
    fn decode<D: bincode::de::Decoder>(
        decoder: &mut D,
    ) -> std::result::Result<Self, bincode::error::DecodeError> {
        let (is_playing, beats, timestamp) = bincode::Decode::decode(decoder)?;
        Ok(Self {
            is_playing,
            beats,
            timestamp: Duration::microseconds(timestamp),
        })
    }
}

impl StartStopState {
    pub fn encode(&self) -> Result<Vec<u8>> {
        let mut encoded = START_STOP_STATE_HEADER.encode()?;
        encoded.append(&mut bincode::encode_to_vec(self, ENCODING_CONFIG)?);
        Ok(encoded)
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ClientStartStopState {
    pub is_playing: bool,
    pub time: Duration,
    pub timestamp: Duration,
}

impl Default for ClientStartStopState {
    fn default() -> Self {
        Self {
            is_playing: false,
            time: Duration::zero(),
            timestamp: Duration::zero(),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct ControllerClientState {
    pub state: ClientState,
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct SessionState {
    pub timeline: Timeline,
    pub start_stop_state: StartStopState,
    pub ghost_x_form: GhostXForm,
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct ClientState {
    pub timeline: Timeline,
    pub start_stop_state: ClientStartStopState,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_key() {
        assert_eq!(START_STOP_STATE_HEADER_KEY, 0x73747374);
        println!("size: {}", START_STOP_STATE_SIZE);
    }

    #[test]
    fn roundtrip() {
        let start_stop_state = StartStopState {
            beats: Beats { value: 123 },
            is_playing: true,
            timestamp: Duration::zero(),
        };

        let encoded = bincode::encode_to_vec(start_stop_state, ENCODING_CONFIG).unwrap();

        let (decoded, _) =
            bincode::decode_from_slice::<StartStopState, _>(&encoded[..], ENCODING_CONFIG).unwrap();

        assert_eq!(decoded, start_stop_state);
    }
}