ableton-link-rs 0.1.2

Native Rust implementation of the Ableton Link protocol
Documentation
use std::{
    fmt::{self, Display},
    mem,
};

use chrono::Duration;

use super::beats::Beats;

pub const TEMPO_SIZE: u32 = mem::size_of::<f64>() as u32;

#[derive(Default, Debug, PartialEq, Clone, Copy)]
pub struct Tempo {
    pub value: f64,
}

impl Display for Tempo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:.1}", self.value)
    }
}

impl From<Duration> for Tempo {
    fn from(value: Duration) -> Self {
        Tempo {
            value: 60.0 * 1e6 / value.num_microseconds().unwrap() as f64,
        }
    }
}

impl Tempo {
    pub fn new(bpm: f64) -> Self {
        Tempo { value: bpm }
    }

    pub fn bpm(&self) -> f64 {
        self.value
    }

    pub fn micros_per_beat(&self) -> Duration {
        Duration::microseconds((60.0 * 1e6 / self.bpm()).round() as i64)
    }

    pub fn micros_to_beats(&self, micros: Duration) -> Beats {
        Beats::new(
            micros.num_microseconds().unwrap() as f64
                / self.micros_per_beat().num_microseconds().unwrap() as f64,
        )
    }

    pub fn beats_to_micros(&self, beats: Beats) -> Duration {
        Duration::microseconds(
            (beats.floating() * self.micros_per_beat().num_microseconds().unwrap() as f64).round()
                as i64,
        )
    }
}

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

impl bincode::Decode<()> for Tempo {
    fn decode<D: bincode::de::Decoder>(
        decoder: &mut D,
    ) -> std::result::Result<Self, bincode::error::DecodeError> {
        Ok(Self::from(Duration::microseconds(bincode::Decode::decode(
            decoder,
        )?)))
    }
}

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

    #[test]
    fn construct_from_bpm() {
        let tempo = Tempo::new(120.0);
        assert_eq!(tempo.micros_per_beat(), Duration::microseconds(500000));
    }

    #[test]
    fn micros_to_beats() {
        let tempo = Tempo::new(120.0);
        assert_eq!(
            tempo.micros_to_beats(Duration::microseconds(1000000)),
            Beats::new(2.0)
        );
    }

    #[test]
    fn beats_to_micros() {
        let tempo = Tempo::new(120.0);
        assert_eq!(
            tempo.beats_to_micros(Beats::new(2.0)),
            Duration::microseconds(1000000)
        );
    }
}