hertz/
lib.rs

1/*!
2
3**useful functions for working with frame-rates, sample-rates,
4other rates,
5time durations,
6frequencies, etc and for keeping a constant framerate.**
7
8`rate`, `sample rate`, `frame rate`, `fps`, `frequency`, etc
9express the same concept and are therefore used interchangeably.
10
11you can use hertz to compute the time resolution
12and frequency range that can be meaningfully analyzed by a
13[short-time fourier transform](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation)
14of a signal:
15
16```
17extern crate hertz;
18
19fn main() {
20    let sample_rate = 44100;
21    let window_size = 4096;
22    let step_size = 512;
23
24    // 11 hertz is the maximum frequency that can be meaningfully analyzed
25    assert_eq!(
26        11.,
27        hertz::rayleigh(sample_rate as f64, window_size as f64).round());
28
29    // 22050 hertz is the maximum frequency that can be meaningfully analyzed
30    assert_eq!(
31        22050.,
32        hertz::nyquist(sample_rate as f64).round());
33
34    // 12 ms is the time resolution we get when analyzing a 44100 hertz
35    // signal with a step size of 512
36    assert_eq!(
37        12.,
38        hertz::s_to_ms(hertz::cycles_per_second_to_seconds_per_cycle(
39            sample_rate as f64,
40            step_size as f64)).round());
41}
42```
43
44you can use hertz to keep a constant framerate in a game or other
45computer graphics application:
46
47```no_run
48fn main() {
49    let frames_per_second: usize = 60;
50
51    loop {
52        let instant_at_frame_start = std::time::Instant::now();
53
54        // here's where logic and rendering would go.
55        // this is never called more than frames_per_second
56        // times per second.
57
58        hertz::sleep_for_constant_rate(
59            frames_per_second, instant_at_frame_start);
60    }
61}
62```
63*/
64
65use std::time;
66use std::ops::{Mul, Div, Range};
67
68/// nanoseconds to seconds
69#[inline]
70pub fn ns_to_s(ns: u64) -> f64 {
71    (ns as f64) / 1_000_000_000.
72}
73
74/// seconds to nanoseconds
75#[inline]
76pub fn s_to_ns(s: f64) -> f64 {
77    s * 1_000_000_000.
78}
79
80/// nanoseconds to milliseconds
81#[inline]
82pub fn ns_to_ms(ns: u64) -> f64 {
83    (ns as f64) / 1_000_000.
84}
85
86/// seconds to milliseconds
87#[inline]
88pub fn s_to_ms<T>(s: T) -> T
89    where T: Mul<T, Output=T> + From<u16>
90{
91    s * T::from(1000 as u16)
92}
93
94#[test]
95fn test_s_to_ms() {
96    assert_eq!(s_to_ms(1), 1000);
97    assert_eq!(s_to_ms(1.), 1000.);
98}
99
100/// milliseconds to seconds
101#[inline]
102pub fn ms_to_s<T>(ms: T) -> T
103    where T: Div<T, Output=T> + From<u16>
104{
105    ms / T::from(1000 as u16)
106}
107
108#[test]
109fn test_ms_to_s() {
110    assert_eq!(ms_to_s(1000), 1);
111    assert_eq!(ms_to_s(1000.), 1.);
112}
113
114/// milliseconds to nanoseconds
115#[inline]
116pub fn ms_to_ns(ns: f64) -> f64 {
117    ns * 1_000_000.
118}
119
120/// when given frames per second (or sample rate)
121/// returns the duration of a single frame
122#[inline]
123pub fn fps_to_ns_per_frame(fps: usize) -> u64 {
124    (s_to_ns(1.0) / (fps as f64)).round() as u64
125}
126
127// /// returns the nanoseconds of sleep which are needed to keep a constant
128// /// frame rate
129// pub fn ns_sleep_needed_for_constant_rate(fps: usize, ns_at_last_frame_start: u64) -> u64 {
130
131/// useful for keeping a constant framerate
132pub fn sleep_for_constant_rate(fps: usize, instant_at_last_frame_start: time::Instant) {
133    let ns_per_frame = fps_to_ns_per_frame(fps);
134    let frame_duration = time::Duration::new(ns_per_frame * 1000000000, (ns_per_frame % 1000000000) as u32);
135    let elapsed = instant_at_last_frame_start.elapsed();
136    if elapsed < frame_duration {
137        std::thread::sleep(frame_duration - elapsed);
138    }
139}
140
141/// frequency range that can be modeled when taking the short
142/// time fourier transform of a signal with `sample_rate` with
143/// a sliding window of `window_sizew`.
144/// equivalent to `rayleigh(sample_rate, window_size)..nyquist(sample_rate)`
145/// increase the window size to increase the lower frequency
146/// increase the sample rate to increase the upper frequency
147pub fn hertz_range<T>(sample_rate: T, window_size: T) -> Range<T>
148    where T: Div<T, Output=T> + From<u16> + Clone
149{
150    rayleigh(sample_rate.clone(), window_size)..nyquist(sample_rate)
151}
152
153#[test]
154fn test_hertz_range() {
155    assert_eq!(
156        hertz_range(44100., 1024. * 8.),
157        (5.38330078125)..22050.);
158    assert_eq!(
159        hertz_range(44100., 1024. * 4.),
160        (10.7666015625)..22050.);
161    assert_eq!(
162        hertz_range(44100., 1024.),
163        (43.06640625)..22050.);
164    assert_eq!(
165        hertz_range(44100., 512.),
166        (86.1328125)..22050.);
167}
168
169/// maximum frequency in hertz that can be meaningfully analyzed with a given sample rate
170/// [https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation)
171pub fn nyquist<T>(sample_rate: T) -> T
172    where T: Div<T, Output=T> + From<u16>
173{
174    sample_rate / T::from(2 as u16)
175}
176
177#[test]
178fn test_nyquist() {
179    assert_eq!(nyquist(44100.), 22050.);
180}
181
182/// minimum frequency in hertz that can be meaningfully analyzed with a given sample rate and
183/// window size
184/// [https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Rayleigh_frequency](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Rayleigh_frequency)
185pub fn rayleigh<T>(sample_rate: T, window_size: T) -> T
186    where T: Div<T, Output=T> + From<u16>
187{
188    T::from(1 as u16) / cycles_per_second_to_seconds_per_cycle(sample_rate, window_size)
189}
190
191#[test]
192fn test_rayleigh() {
193    assert_eq!(rayleigh(44100., 1024.), 43.06640625);
194}
195
196/// window duration in seconds
197/// sample_rate in hertz
198pub fn cycles_per_second_to_seconds_per_cycle<T>(cycles_per_second: T, cycles: T) -> T
199    where T: Div<T, Output=T>
200{
201    cycles / cycles_per_second
202}
203
204#[test]
205fn test_seconds_per_window() {
206    // 11ms time resolution
207    assert_eq!(cycles_per_second_to_seconds_per_cycle(44100., 512.), 0.011609977324263039);
208    assert_eq!(cycles_per_second_to_seconds_per_cycle(44100., 1024.), 0.023219954648526078);
209}