Created
January 1, 2023 03:14
-
-
Save ericdrobinson/77c975b32af9912525623ae99ac9d100 to your computer and use it in GitHub Desktop.
Sample Time Conversion Tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Tests time conversion roundtripping between sample time and seconds using the specified sample | |
/// rate (`sr`). All possible sample times up-to-and-including the maximum unsigned integer value | |
/// are tested. This test uses single precision floating point math. | |
fn test_time_conversion_for_all_sample_lengths_f32(sr: f32) { | |
let mut sample: u32 = 0; | |
loop { | |
let seconds: f32 = sample as f32 / sr; | |
if sample != (seconds * sr).round() as u32 { | |
let hours: f32 = (seconds / 3600.).floor(); | |
let minutes: f32 = (seconds / 60.).floor().rem_euclid(60.); | |
let seconds: f32 = seconds.rem_euclid(60.).floor(); | |
println!("[f32 - {}] Broke on sample number {}, a time of {}:{:02}:{:02}", sr, sample, hours, minutes, seconds); | |
break; | |
} | |
else if sample == std::u32::MAX { | |
println!("[f32 - {}] Broke without reaching a non-roundtripable sample!", sr); | |
break; | |
} | |
sample += 1; | |
} | |
} | |
/// Tests time conversion roundtripping between sample time and seconds using the specified sample | |
/// rate (`sr`). All possible sample times up-to-and-including the maximum unsigned integer value | |
/// are tested. This test uses double precision floating point math. | |
fn test_time_conversion_for_all_sample_lengths_f64(sr: f64) { | |
let mut sample: u32 = 0; | |
loop { | |
let seconds: f64 = sample as f64 / sr; | |
if sample != (seconds * sr).round() as u32 { | |
let hours: f64 = (seconds / 3600.).floor(); | |
let minutes: f64 = (seconds / 60.).floor().rem_euclid(60.); | |
let seconds: f64 = seconds.rem_euclid(60.).floor(); | |
println!("[f64 - {}] Broke on sample number {}, a time of {}:{:02}:{:02}", sr, sample, hours, minutes, seconds); | |
break; | |
} | |
else if sample == std::u32::MAX { | |
println!("[f64 - {}] Broke without reaching a non-roundtripable sample!", sr); | |
break; | |
} | |
sample += 1; | |
} | |
} | |
/// A single-run version of this is available on the official Rust Playground. See: | |
/// - https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7b61c9d842e8efa9b535f380fd5302ba | |
fn test_single_percentage(total_samples: i32) -> (i32, i32) { | |
let mut sample_count: i32 = 0; | |
while sample_count < total_samples { | |
let percent: f32 = sample_count as f32 / total_samples as f32; | |
let new_samp: i32 = (percent * total_samples as f32).round() as i32; | |
if sample_count != new_samp { | |
return (sample_count, new_samp); | |
} | |
else { | |
sample_count += 1; | |
} | |
} | |
(-1, -1) | |
} | |
/// Tests round-tripping sample times to-from percentage (normalized values in range [0,1]). The | |
/// function runs in two loops: | |
/// | |
/// - **Outer Loop:** Test all possible sample lengths up to the specified `max_samples` value. | |
/// - **Inner Loop:** Test all possible sample positions given the current sample length. | |
/// | |
/// This function is useful for discovering the first sample length at which a percentage roundtrip | |
/// calculation using single precision floating point math for normalization encounters a problem | |
/// (read: roundtripping fails). | |
/// | |
/// **Note:** This function is _very_ long-running. A double precision test has not been | |
/// successfully run. A back-of-the-envelope estimation for maximum runtime put an upper bound on | |
/// a double-precision version of the function at something on the order of ~90 years. | |
fn test_percentages(max_samples: i32) { | |
let mut current_test_length: i32 = 1; | |
while current_test_length < max_samples { | |
let break_sample: (i32, i32) = test_single_percentage(current_test_length); | |
if break_sample.0 >= 0 { | |
println!("First sample length with percentage break is {} at sample position {} (round-tripped as {}).", current_test_length, break_sample.0, break_sample.1); | |
break; | |
} | |
current_test_length += 1; | |
} | |
if current_test_length == max_samples { | |
println!("All lengths up to {} are okay!", max_samples); | |
} | |
} | |
/// Runs a number of tests. | |
/// | |
/// Output: | |
/// | |
/// ```txt | |
/// [f32 - 44100] Broke on sample number 5644828, a time of 0:02:08 | |
/// [f32 - 48000] Broke on sample number 6144007, a time of 0:02:08 | |
/// [f32 - 96000] Broke on sample number 6144007, a time of 0:01:04 | |
/// [f64 - 44100] Broke without reaching a non-roundtripable sample! | |
/// [f64 - 48000] Broke without reaching a non-roundtripable sample! | |
/// [f64 - 96000] Broke without reaching a non-roundtripable sample! | |
/// First sample length with percentage break is 8388611 at sample position 7689560 (round-tripped as 7689561). | |
/// ``` | |
fn main() { | |
test_time_conversion_for_all_sample_lengths_f32(44100.); | |
test_time_conversion_for_all_sample_lengths_f32(48000.); | |
test_time_conversion_for_all_sample_lengths_f32(96000.); | |
test_time_conversion_for_all_sample_lengths_f64(44100.); | |
test_time_conversion_for_all_sample_lengths_f64(48000.); | |
test_time_conversion_for_all_sample_lengths_f64(96000.); | |
test_percentages(5 * 60 * 48000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment