#[cfg(target_os = "macos")]
pub use macos::{host_ticks_per_period, host_time_now};
#[cfg(not(target_os = "macos"))]
pub use portable::{host_ticks_per_period, host_time_now};
#[cfg(target_os = "macos")]
mod macos {
#[repr(C)]
struct MachTimebaseInfo {
numer: u32,
denom: u32,
}
extern "C" {
fn mach_absolute_time() -> u64;
fn mach_timebase_info(info: *mut MachTimebaseInfo) -> i32;
}
#[must_use]
pub fn host_time_now() -> u64 {
unsafe { mach_absolute_time() }
}
#[must_use]
pub fn host_ticks_per_period(period_frames: f64, sample_rate: f64) -> f64 {
let mut timebase = MachTimebaseInfo { numer: 0, denom: 0 };
unsafe { mach_timebase_info(&mut timebase) };
if timebase.numer == 0 || sample_rate <= 0.0 {
return 0.0;
}
let period_nanoseconds = period_frames / sample_rate * 1.0e9;
period_nanoseconds * f64::from(timebase.denom) / f64::from(timebase.numer)
}
}
#[cfg(not(target_os = "macos"))]
mod portable {
use std::sync::OnceLock;
use std::time::Instant;
#[must_use]
pub fn host_time_now() -> u64 {
static ORIGIN: OnceLock<Instant> = OnceLock::new();
ORIGIN.get_or_init(Instant::now).elapsed().as_nanos() as u64
}
#[must_use]
pub fn host_ticks_per_period(period_frames: f64, sample_rate: f64) -> f64 {
if sample_rate <= 0.0 {
return 0.0;
}
period_frames / sample_rate * 1.0e9
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn host_time_advances_monotonically() {
let first = host_time_now();
let second = host_time_now();
assert!(second >= first, "the host clock must not run backwards");
}
#[test]
fn host_ticks_per_period_is_positive_for_a_real_format() {
let ticks = host_ticks_per_period(48_000.0, 48_000.0);
assert!(ticks > 0.0, "a one-second period must span > 0 host ticks");
}
#[test]
fn host_ticks_per_period_rejects_a_nonpositive_sample_rate() {
assert_eq!(host_ticks_per_period(48_000.0, 0.0), 0.0);
assert_eq!(host_ticks_per_period(48_000.0, -1.0), 0.0);
}
#[test]
fn host_ticks_per_period_scales_with_the_frame_count() {
let one = host_ticks_per_period(48_000.0, 48_000.0);
let two = host_ticks_per_period(96_000.0, 48_000.0);
assert!((two - one * 2.0).abs() < one * 1.0e-9);
}
}