ot-tools-io 0.11.2

A library crate for reading/writing binary data files used by the Elektron Octatrack DPS-1.
Documentation
/*
SPDX-License-Identifier: GPL-3.0-or-later
Copyright © 2024 Mike Robeson [dijksterhuis]
*/

use ot_tools_io_derive::{AsMutDerive, AsRefDerive};
use serde::{Deserialize, Serialize};

/// Pattern level scaling settings.
/// Some of these settings still apply when the pattern is in Per-Track scaling mode.
#[derive(
    Copy,
    Clone,
    Debug,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize,
    AsMutDerive,
    AsRefDerive,
)]
pub struct PatternScaleSettings {
    /// Multiply this value by `master_len_per_track` to get
    /// the real Master Length in Per Track Pattern mode.
    ///
    /// This field must be set to `255` when Master Length in
    /// Per Track Pattern mode is set to `INF`.
    ///
    /// ```text
    /// 0: From 2 steps to 255 steps.
    /// 1: From 256 steps to 511 steps.
    /// 2: From 512 steps to 767 steps.
    /// 3: From 768 steps to 1023 steps.
    /// 4: 1024 steps only.
    /// 255: `INF`.
    /// ```
    pub master_len_per_track_multiplier: u8,

    /// Master Length in Per Track Pattern mode.
    /// Must multiply this by multiplier like this `(x + 1) * (mult + 1)` to get the real number.
    ///
    /// This field must be set to `255` when Master Length in
    /// Per Track Pattern mode is set to `INF`.
    ///
    /// Minimum value is 2 when the multiplier equals 0.
    pub master_len_per_track: u8,

    /// The Audio Track's Scale when Pattern is in Per Track mode.
    ///
    /// Options
    /// ```text
    /// 0 -> 2x
    /// 1 -> 3/2x
    /// 2 -> 1x (Default)
    /// 3 -> 3/4x
    /// 4 -> 1/2x
    /// 5 -> 1/4x
    /// 6 -> 1/8x
    /// ```
    pub master_scale_per_track: u8,

    /// Master Pattern Length.
    /// Determines Pattern Length for all Tracks when NOT in Per Track mode.
    pub master_len: u8,

    /// Master Pattern playback multiplier.
    ///
    /// Options
    /// ```text
    /// 0 -> 2x
    /// 1 -> 3/2x
    /// 2 -> 1x (Default)
    /// 3 -> 3/4x
    /// 4 -> 1/2x
    /// 5 -> 1/4x
    /// 6 -> 1/8x
    /// ```
    pub master_scale: u8,

    /// Scale mode for the Pattern.
    ///
    /// Options
    /// ```text
    /// NORMAL: 0 (Default)
    /// PER TRACK: 1
    /// ```
    pub scale_mode: u8,
}

// todo
#[allow(dead_code)]
enum MasterLenCalc {
    PerTrack(u16),
    Global(u16),
    InvalidScaleMode(u16),
}

impl PatternScaleSettings {
    #[allow(dead_code)]
    fn calculate_master_length(&self) -> MasterLenCalc {
        match self.scale_mode {
            // normal
            0 => MasterLenCalc::Global(self.master_len as u16),
            // per-track
            1 => MasterLenCalc::PerTrack(
                (self.master_len_per_track as u16 + 1)
                    * (self.master_len_per_track_multiplier as u16 + 1),
            ),
            // something is wrong
            _ => MasterLenCalc::InvalidScaleMode(self.scale_mode as u16),
        }
    }
}

impl Default for PatternScaleSettings {
    fn default() -> Self {
        Self {
            master_len_per_track_multiplier: 0,
            master_len_per_track: 16,
            master_scale_per_track: 2,
            master_len: 16,
            master_scale: 2,
            scale_mode: 0,
        }
    }
}

/// Chaining behaviour for the pattern.
#[derive(
    Copy,
    Clone,
    Debug,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize,
    AsMutDerive,
    AsRefDerive,
)]
pub struct PatternChainBehavior {
    /// When `use_project_setting` field is set to `1`/`true`
    /// this field should be set to `N/A` with a value of `255`.
    pub use_pattern_setting: u8,

    /// Pattern Chain Behaviour -- Use the Project level setting for chain
    /// behaviour and disable any Pattern level chaining behaviour.
    /// Numeric Boolean.
    /// When this is `1` the `use_pattern_setting` should be set to `255`.
    pub use_project_setting: u8,
}

// allow the verbose implementation to keep things
// - (a) standardized across all types
// - (b) easier for non-rustaceans to follow when reading through data structures
#[allow(clippy::derivable_impls)]
impl Default for PatternChainBehavior {
    fn default() -> Self {
        Self {
            use_pattern_setting: 0,
            use_project_setting: 0,
        }
    }
}

/// Audio & MIDI Track Pattern playback settings.
#[derive(
    Copy,
    Clone,
    Debug,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize,
    AsMutDerive,
    AsRefDerive,
)]
pub struct TrackPatternSettings {
    /// Silence any existing audio playback on the Audio Track when switching Patterns.
    pub start_silent: u8,

    /// Trigger Audio Track playback without any quantization or syncing to other Audio Tracks.
    pub plays_free: u8,

    /// Quantization when this Audio Track is Triggered for Playback.
    ///
    /// Options
    /// ```text
    /// N/A and ONE: 0 (Default)
    /// ONE2: 1
    /// HOLD: 2
    /// ```
    pub trig_mode: u8,

    /// Track Trigger Quantization.
    ///
    /// Options
    /// ```text
    /// N/A and TR.LEN: 0 (Default)
    /// 1/16: 1
    /// 2/16: 2
    /// 3/16: 3
    /// 4/16: 4
    /// 6/16: 5
    /// 8/16: 6
    /// 12/16: 7
    /// 16/16: 8
    /// 24/16: 9
    /// 32/16: 10
    /// 48/16: 11
    /// 64/16: 12
    /// 96/16: 13
    /// 128/16: 14
    /// 192/16: 15
    /// 256/16: 16
    /// DIRECT: 255
    /// ```
    pub trig_quant: u8,

    /// Whether to play the track as a `ONESHOT` track.
    pub oneshot_trk: u8,
}

impl Default for TrackPatternSettings {
    fn default() -> Self {
        Self {
            start_silent: 255,
            plays_free: 0,
            trig_mode: 0,
            trig_quant: 0,
            oneshot_trk: 0,
        }
    }
}

/// Audio Track custom scaling when the Pattern is in PER TRACK scale mode.
#[derive(
    Copy,
    Clone,
    Debug,
    Eq,
    Hash,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
    Deserialize,
    AsMutDerive,
    AsRefDerive,
)]
pub struct TrackPerTrackModeScale {
    /// The Audio Track's Length when Pattern is in Per Track mode.
    /// Default: 16
    pub per_track_len: u8,

    /// The Audio Track's Scale when Pattern is in Per Track mode.
    ///
    /// Options
    /// ```text
    /// 0 -> 2x
    /// 1 -> 3/2x
    /// 2 -> 1x (Default)
    /// 3 -> 3/4x
    /// 4 -> 1/2x
    /// 5 -> 1/4x
    /// 6 -> 1/8x
    /// ```
    pub per_track_scale: u8,
}

impl Default for TrackPerTrackModeScale {
    fn default() -> Self {
        Self {
            per_track_len: 16,
            per_track_scale: 2,
        }
    }
}