#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss
)]
#[inline]
#[must_use]
pub fn midi_7bit(v: f32) -> u8 {
debug_assert!(
!v.is_nan(),
"midi_7bit: NaN input — caller's normalized value is uninitialized?",
);
(v.clamp(0.0, 1.0) * 127.0).round() as u8
}
#[inline]
#[must_use]
pub fn midi_14bit_pb(v: f32) -> u16 {
debug_assert!(
!v.is_nan(),
"midi_14bit_pb: NaN input — caller's normalized value is uninitialized?",
);
((v.clamp(-1.0, 1.0) + 1.0) * 8191.5).round() as u16
}
#[inline]
#[must_use]
pub fn len_u32(n: usize) -> u32 {
debug_assert!(
u32::try_from(n).is_ok(),
"len_u32: count {n} overflows u32; FFI field would silently truncate",
);
n as u32
}
#[inline]
#[must_use]
pub const fn size_of_u32<T>() -> u32 {
let n = core::mem::size_of::<T>();
assert!(
n <= u32::MAX as usize,
"size_of_u32: T's size overflows u32",
);
n as u32
}
#[inline]
#[must_use]
pub fn param_f32(v: f64) -> f32 {
debug_assert!(
!v.is_nan(),
"param_f32: NaN input — caller's f64 parameter value is uninitialized?",
);
v as f32
}
#[inline]
#[must_use]
pub fn sample_pos_i64(v: f64) -> i64 {
if v.is_nan() {
debug_assert!(false, "sample_pos_i64: NaN host sample position");
return 0;
}
if v >= i64::MAX as f64 {
return i64::MAX;
}
if v <= i64::MIN as f64 {
return i64::MIN;
}
v as i64
}
#[inline]
#[must_use]
pub fn sample_count_usize(v: f64) -> usize {
if v.is_nan() || v <= 0.0 {
return 0;
}
if v >= usize::MAX as f64 {
return usize::MAX;
}
v as usize
}
#[inline]
#[must_use]
pub fn frame_count_f64(n: usize) -> f64 {
n as f64
}
#[inline]
#[must_use]
pub fn sample_rate_u32(rate: f64) -> u32 {
debug_assert!(
!rate.is_nan() && rate >= 0.0,
"sample_rate_u32: invalid rate {rate} — host sample rate is uninitialized?",
);
if rate.is_nan() || rate < 0.0 {
return 0;
}
if rate >= f64::from(u32::MAX) {
return u32::MAX;
}
rate as u32
}
#[inline]
#[must_use]
pub fn discrete_norm(idx: usize, count: usize) -> f64 {
if count <= 1 {
return 0.0;
}
let max_idx = count - 1;
idx.min(max_idx) as f64 / max_idx as f64
}
#[inline]
#[must_use]
pub fn discrete_index(norm: f64, count: usize) -> usize {
if count <= 1 {
return 0;
}
let n = norm.clamp(0.0, 1.0);
let max_idx = count - 1;
(n * max_idx as f64).round() as usize
}
#[cfg(test)]
mod tests {
#![allow(clippy::float_cmp)]
use super::*;
#[test]
fn midi_7bit_endpoints() {
assert_eq!(midi_7bit(0.0), 0);
assert_eq!(midi_7bit(1.0), 127);
assert_eq!(midi_7bit(0.5), 64); }
#[test]
fn midi_7bit_out_of_range_clamps() {
assert_eq!(midi_7bit(-0.5), 0);
assert_eq!(midi_7bit(2.0), 127);
assert_eq!(midi_7bit(f32::INFINITY), 127);
assert_eq!(midi_7bit(f32::NEG_INFINITY), 0);
}
#[test]
fn midi_14bit_pb_endpoints() {
assert_eq!(midi_14bit_pb(-1.0), 0);
assert_eq!(midi_14bit_pb(0.0), 8192);
assert_eq!(midi_14bit_pb(1.0), 16383);
}
#[test]
fn midi_14bit_pb_clamps() {
assert_eq!(midi_14bit_pb(-2.0), 0);
assert_eq!(midi_14bit_pb(2.0), 16383);
}
#[test]
fn len_u32_basic() {
assert_eq!(len_u32(0), 0);
assert_eq!(len_u32(127), 127);
assert_eq!(len_u32(u32::MAX as usize), u32::MAX);
}
#[test]
#[should_panic(expected = "overflows u32")]
#[cfg(target_pointer_width = "64")]
fn len_u32_overflow_panics_in_debug() {
let _ = len_u32(u32::MAX as usize + 1);
}
#[test]
fn size_of_u32_basic() {
#[repr(C)]
struct AbiStruct {
_a: u32,
_b: u64,
}
assert_eq!(size_of_u32::<u8>(), 1);
assert_eq!(size_of_u32::<u32>(), 4);
assert_eq!(size_of_u32::<u64>(), 8);
assert_eq!(size_of_u32::<AbiStruct>(), 16);
}
#[test]
fn discrete_norm_endpoints() {
assert_eq!(discrete_norm(0, 4), 0.0);
assert!((discrete_norm(1, 4) - 1.0 / 3.0).abs() < 1e-12);
assert!((discrete_norm(2, 4) - 2.0 / 3.0).abs() < 1e-12);
assert_eq!(discrete_norm(3, 4), 1.0);
}
#[test]
fn discrete_norm_degenerate_collapses_to_zero() {
assert_eq!(discrete_norm(0, 0), 0.0);
assert_eq!(discrete_norm(0, 1), 0.0);
assert_eq!(discrete_norm(99, 1), 0.0);
}
#[test]
fn discrete_norm_clamps_oob_idx() {
assert_eq!(discrete_norm(99, 4), 1.0);
}
#[test]
fn discrete_index_endpoints() {
assert_eq!(discrete_index(0.0, 4), 0);
assert_eq!(discrete_index(1.0, 4), 3);
assert_eq!(discrete_index(1.0 / 3.0, 4), 1);
assert_eq!(discrete_index(2.0 / 3.0, 4), 2);
}
#[test]
fn discrete_index_degenerate_returns_zero() {
assert_eq!(discrete_index(0.5, 0), 0);
assert_eq!(discrete_index(0.5, 1), 0);
assert_eq!(discrete_index(1.0, 1), 0);
}
#[test]
fn discrete_index_clamps_oob_norm() {
assert_eq!(discrete_index(-0.5, 4), 0);
assert_eq!(discrete_index(2.0, 4), 3);
}
#[test]
fn param_f32_basic() {
assert_eq!(param_f32(0.0), 0.0_f32);
assert_eq!(param_f32(1.0), 1.0_f32);
assert_eq!(param_f32(-1.0), -1.0_f32);
assert_eq!(param_f32(0.5), 0.5_f32);
assert!(param_f32(f64::INFINITY).is_infinite());
assert!(param_f32(f64::NEG_INFINITY).is_infinite());
}
#[test]
fn sample_pos_i64_basic() {
assert_eq!(sample_pos_i64(0.0), 0);
assert_eq!(sample_pos_i64(48_000.0), 48_000);
assert_eq!(sample_pos_i64(-1.0), -1);
}
#[test]
fn sample_pos_i64_saturates_on_non_finite() {
assert_eq!(sample_pos_i64(f64::INFINITY), i64::MAX);
assert_eq!(sample_pos_i64(f64::NEG_INFINITY), i64::MIN);
}
#[test]
fn sample_count_usize_basic() {
assert_eq!(sample_count_usize(0.0), 0);
assert_eq!(sample_count_usize(48_000.0), 48_000);
}
#[test]
fn sample_count_usize_collapses_invalid() {
assert_eq!(sample_count_usize(-1.0), 0);
assert_eq!(sample_count_usize(f64::NAN), 0);
assert_eq!(sample_count_usize(f64::INFINITY), usize::MAX);
assert_eq!(sample_count_usize(f64::NEG_INFINITY), 0);
}
#[test]
fn frame_count_f64_basic() {
assert_eq!(frame_count_f64(0), 0.0);
assert_eq!(frame_count_f64(48_000), 48_000.0);
assert_eq!(sample_count_usize(frame_count_f64(192_000)), 192_000);
}
#[test]
fn sample_rate_u32_basic() {
assert_eq!(sample_rate_u32(44_100.0), 44_100);
assert_eq!(sample_rate_u32(48_000.0), 48_000);
assert_eq!(sample_rate_u32(192_000.0), 192_000);
}
#[test]
fn sample_rate_u32_saturates() {
assert_eq!(sample_rate_u32(f64::INFINITY), u32::MAX);
assert_eq!(sample_rate_u32(f64::from(u32::MAX) * 2.0), u32::MAX);
}
#[test]
fn sample_rate_u32_collapses_invalid_in_release() {
#[cfg(not(debug_assertions))]
{
assert_eq!(sample_rate_u32(-1.0), 0);
assert_eq!(sample_rate_u32(f64::NAN), 0);
assert_eq!(sample_rate_u32(f64::NEG_INFINITY), 0);
}
}
#[test]
fn discrete_norm_index_round_trip() {
for count in [2usize, 3, 4, 7, 16, 128] {
for idx in 0..count {
let norm = discrete_norm(idx, count);
let back = discrete_index(norm, count);
assert_eq!(back, idx, "count={count}, idx={idx}, norm={norm}");
}
}
}
}