use serde::{Serialize, Deserialize}; use derive_more::Display; use rand::distributions::{Distribution, Standard}; use rand::Rng; #[derive(Debug, PartialEq, Copy, Clone, Display, Serialize, Deserialize)] #[display(fmt="{} bpm, with {} swing", bpm, swing)] pub struct Tempo { bpm: u16, swing: Swing, } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Tempo { // These parameters are static and known-good let raw_bpm_distr: f32 = rand_distr::SkewNormal::new(25.0, 75.0, 5.0).unwrap().sample(rng); let bpm: u16 = if raw_bpm_distr < 33. { 33 } else if raw_bpm_distr > 300.0 { 300 } else { raw_bpm_distr as u16 }; let swing: Swing = Standard::sample(&self, rng); Tempo { bpm, swing } } } #[derive(Debug, PartialEq, Copy, Clone, Display, Serialize, Deserialize)] pub enum Swing { #[display(fmt = "no")] None, #[display(fmt = "light")] Light, #[display(fmt = "heavy")] Heavy } impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Swing { let index: u8 = rng.gen_range(0..6); match index { 0 | 1 | 2=> Swing::None, 3 | 4 => Swing::Light, 5 => Swing::Heavy, _ => unreachable!(), } } } #[test] fn fmt_tempo_struct() { assert_eq!(&format!("{}", Tempo { bpm: 80, swing: Swing::Light}), "80 bpm, with light swing"); }