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}