use crate::SampleRate;
pub type Tick = u32;
pub type Tick16 = u16;
pub type Meas = u32;
pub type SampleT = u32;
pub type SamplesPerTick = f32;
const DEFAULT_BEATS_PER_MEAS: u8 = 4;
const DEFAULT_BPM: f32 = 120.;
const DEFAULT_TICKS_PER_BEAT: Tick16 = 480;
#[derive(Clone, Copy)]
pub struct Timing {
pub ticks_per_beat: Tick16,
pub bpm: f32,
pub beats_per_meas: BpMea,
}
pub type BpMea = u8;
impl Default for Timing {
fn default() -> Self {
Self {
ticks_per_beat: DEFAULT_TICKS_PER_BEAT,
bpm: DEFAULT_BPM,
beats_per_meas: DEFAULT_BEATS_PER_MEAS,
}
}
}
#[must_use]
pub fn tick_to_meas(tick: Tick, timing: Timing) -> Meas {
tick.div_ceil(u32::from(timing.ticks_per_beat))
.div_ceil(u32::from(timing.beats_per_meas))
}
#[must_use]
pub fn meas_to_tick(meas: Meas, timing: Timing) -> Tick {
meas * Tick::from(timing.ticks_per_beat) * Tick::from(timing.beats_per_meas)
}
#[must_use]
pub fn samples_per_tick(out_sample_rate: SampleRate, timing: Timing) -> SamplesPerTick {
60.0 * f32::from(out_sample_rate) / (timing.bpm * f32::from(timing.ticks_per_beat))
}
#[must_use]
pub fn tick_to_sample(tick: Tick, samples_per_tick: SamplesPerTick) -> SampleT {
#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss
)]
((tick as f32 * samples_per_tick) as SampleT)
}
#[must_use]
pub fn meas_to_sample(meas: Meas, clock_rate: f32, timing: Timing) -> SampleT {
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
((f64::from(meas)
* f64::from(timing.beats_per_meas)
* f64::from(timing.ticks_per_beat)
* f64::from(clock_rate)) as SampleT)
}