This commit is contained in:
2026-01-28 13:54:29 +01:00
parent 0beed16c31
commit 5952807240
5 changed files with 123 additions and 72 deletions

View File

@@ -1,80 +1,138 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{DEFAULT_LENGTH, MAX_BANKS, MAX_PATTERNS, MAX_STEPS};
#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub enum PatternSpeed {
Eighth, // 1/8x
Quarter, // 1/4x
Half, // 1/2x
#[default]
Normal, // 1x
Double, // 2x
Quad, // 4x
Octo, // 8x
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PatternSpeed {
pub num: u8,
pub denom: u8,
}
impl PatternSpeed {
pub const EIGHTH: Self = Self { num: 1, denom: 8 };
pub const FIFTH: Self = Self { num: 1, denom: 5 };
pub const QUARTER: Self = Self { num: 1, denom: 4 };
pub const THIRD: Self = Self { num: 1, denom: 3 };
pub const HALF: Self = Self { num: 1, denom: 2 };
pub const TWO_THIRDS: Self = Self { num: 2, denom: 3 };
pub const NORMAL: Self = Self { num: 1, denom: 1 };
pub const DOUBLE: Self = Self { num: 2, denom: 1 };
pub const QUAD: Self = Self { num: 4, denom: 1 };
pub const OCTO: Self = Self { num: 8, denom: 1 };
const PRESETS: &[Self] = &[
Self::EIGHTH,
Self::FIFTH,
Self::QUARTER,
Self::THIRD,
Self::HALF,
Self::TWO_THIRDS,
Self::NORMAL,
Self::DOUBLE,
Self::QUAD,
Self::OCTO,
];
pub fn multiplier(&self) -> f64 {
match self {
Self::Eighth => 0.125,
Self::Quarter => 0.25,
Self::Half => 0.5,
Self::Normal => 1.0,
Self::Double => 2.0,
Self::Quad => 4.0,
Self::Octo => 8.0,
}
self.num as f64 / self.denom as f64
}
pub fn label(&self) -> &'static str {
match self {
Self::Eighth => "1/8x",
Self::Quarter => "1/4x",
Self::Half => "1/2x",
Self::Normal => "1x",
Self::Double => "2x",
Self::Quad => "4x",
Self::Octo => "8x",
pub fn label(&self) -> String {
if self.denom == 1 {
format!("{}x", self.num)
} else {
format!("{}/{}x", self.num, self.denom)
}
}
pub fn next(&self) -> Self {
match self {
Self::Eighth => Self::Quarter,
Self::Quarter => Self::Half,
Self::Half => Self::Normal,
Self::Normal => Self::Double,
Self::Double => Self::Quad,
Self::Quad => Self::Octo,
Self::Octo => Self::Octo,
}
let current = self.multiplier();
Self::PRESETS
.iter()
.find(|p| p.multiplier() > current + 0.0001)
.copied()
.unwrap_or(*self)
}
pub fn prev(&self) -> Self {
match self {
Self::Eighth => Self::Eighth,
Self::Quarter => Self::Eighth,
Self::Half => Self::Quarter,
Self::Normal => Self::Half,
Self::Double => Self::Normal,
Self::Quad => Self::Double,
Self::Octo => Self::Quad,
}
let current = self.multiplier();
Self::PRESETS
.iter()
.rev()
.find(|p| p.multiplier() < current - 0.0001)
.copied()
.unwrap_or(*self)
}
pub fn from_label(s: &str) -> Option<Self> {
match s.trim() {
"1/8x" | "1/8" | "0.125x" => Some(Self::Eighth),
"1/4x" | "1/4" | "0.25x" => Some(Self::Quarter),
"1/2x" | "1/2" | "0.5x" => Some(Self::Half),
"1x" | "1" => Some(Self::Normal),
"2x" | "2" => Some(Self::Double),
"4x" | "4" => Some(Self::Quad),
"8x" | "8" => Some(Self::Octo),
_ => None,
let s = s.trim().trim_end_matches('x');
if let Some((num, denom)) = s.split_once('/') {
let num: u8 = num.parse().ok()?;
let denom: u8 = denom.parse().ok()?;
if denom == 0 {
return None;
}
return Some(Self { num, denom });
}
if let Ok(val) = s.parse::<f64>() {
if val <= 0.0 || val > 255.0 {
return None;
}
if (val - val.round()).abs() < 0.0001 {
return Some(Self {
num: val.round() as u8,
denom: 1,
});
}
for denom in 1..=16u8 {
let num = val * denom as f64;
if (num - num.round()).abs() < 0.0001 && (1.0..=255.0).contains(&num) {
return Some(Self {
num: num.round() as u8,
denom,
});
}
}
}
None
}
}
impl Default for PatternSpeed {
fn default() -> Self {
Self::NORMAL
}
}
impl Serialize for PatternSpeed {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
(self.num, self.denom).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for PatternSpeed {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum SpeedFormat {
Tuple((u8, u8)),
Legacy(String),
}
match SpeedFormat::deserialize(deserializer)? {
SpeedFormat::Tuple((num, denom)) => Ok(Self { num, denom }),
SpeedFormat::Legacy(s) => Ok(match s.as_str() {
"Eighth" => Self::EIGHTH,
"Quarter" => Self::QUARTER,
"Half" => Self::HALF,
"Normal" => Self::NORMAL,
"Double" => Self::DOUBLE,
"Quad" => Self::QUAD,
"Octo" => Self::OCTO,
_ => Self::NORMAL,
}),
}
}
}