diff options
| author | ozpv <39195175+ozpv@users.noreply.github.com> | 2026-05-18 10:55:39 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-18 10:55:39 -0500 |
| commit | 13e872165ed41e032246eddbe1b0750154ce7259 (patch) | |
| tree | bdd8ba908b7170ec9b4844490247855566edeb2e /src/window_function.rs | |
| parent | 20414d4b6ab6d1177d8008ee987931c9e4ee65d3 (diff) | |
add hamming window
Diffstat (limited to 'src/window_function.rs')
| -rw-r--r-- | src/window_function.rs | 206 |
1 files changed, 69 insertions, 137 deletions
diff --git a/src/window_function.rs b/src/window_function.rs index b0ed54d..09c5d31 100644 --- a/src/window_function.rs +++ b/src/window_function.rs @@ -11,11 +11,9 @@ pub trait WindowFunction: Send { fn reverse(&mut self, data: &mut [f32]); /// Yields the amount of samples still required to apply the window fn needed(&self) -> usize; - /// Updates the window size - fn window_size(&mut self, window_size: WindowSize); } -/// Effectively a chunking function rather than a sliding window +/// Effectively a chunking function pub struct RectangularWindow { window_size: usize, } @@ -36,102 +34,114 @@ impl WindowFunction for RectangularWindow { fn needed(&self) -> usize { self.window_size } - - fn window_size(&mut self, _window_size: WindowSize) { - todo!() - } } -/// A window designed specifically to reduce clicking and leave all other artifacts -/// Results vary -pub struct HaemolacriaaWindow { +/// Hann function +pub struct HannWindow { window_size: usize, - function_start: Vec<f32>, - function_end: Vec<f32>, + function: Vec<f32>, + normalize: Vec<f32>, previous: Vec<f32>, overlap_add: Vec<f32>, } -impl HaemolacriaaWindow { - /// Ensure this value isn't greater than or equal to any default window size - const SAMPLE_COUNT: usize = 16; - +impl HannWindow { pub fn new(window_size: &WindowSize) -> Self { let window_size = window_size.inner(); - let sample_count_f32 = Self::SAMPLE_COUNT as f32; + let half_window_size = window_size / 2; + let window_size_f32 = window_size as f32; - let overlap_add = vec![0.0; Self::SAMPLE_COUNT]; + // 50% overlap + let overlap_add = vec![0.0; window_size]; + + // with capacity is important for the first needed samples + let previous = Vec::with_capacity(half_window_size); - let function_start = (0..Self::SAMPLE_COUNT) + let function = (0..window_size) .map(|i| { - let x = i as f32 / (sample_count_f32 - 1.0); + let i = i as f32; - f32::sin(PI * 0.5 * x) + 0.5 * (1.0 - f32::cos((2.0 * PI * i) / (window_size_f32))) }) .collect::<Vec<f32>>(); - let function_end = (0..Self::SAMPLE_COUNT) + let normalize = (0..half_window_size) .map(|i| { - let x = i as f32 / (sample_count_f32 - 1.0); - - f32::cos(PI * 0.5 * x) + (function[i] * function[i]) + + (function[i + half_window_size] * function[i + half_window_size]) }) .collect::<Vec<f32>>(); - let previous = Vec::with_capacity(Self::SAMPLE_COUNT); - Self { window_size, - function_start, - function_end, + function, + normalize, previous, overlap_add, } } } -impl WindowFunction for HaemolacriaaWindow { +impl WindowFunction for HannWindow { fn apply(&mut self, data: &mut VecDeque<f32>) { - let last_few = self.window_size - Self::SAMPLE_COUNT; + let half_window_size = self.window_size / 2; + // the full window size is needed to window over the samples the first time if self.previous.is_empty() { - for sample in data.iter().skip(last_few).copied() { + // store the latter half for when only half the size is needed + for sample in data.iter().skip(half_window_size).copied() { self.previous.push(sample); } - for i in 0..Self::SAMPLE_COUNT { - data[i] *= self.function_start[i]; - data[last_few + i] *= self.function_end[i]; + // apply the function + for (i, sample) in data.iter_mut().enumerate() { + *sample *= self.function[i]; } return; } + // In a real time "sliding window" it's necessary to add in the previous samples for sample in self.previous.iter().rev().copied() { data.push_front(sample); } self.previous.clear(); - for sample in data.iter().skip(last_few).copied() { + // keep this iteration's samples for the next window + for sample in data.iter().skip(half_window_size).copied() { self.previous.push(sample); } - for i in 0..Self::SAMPLE_COUNT { - data[i] *= self.function_start[i]; - data[last_few + i] *= self.function_end[i]; + // apply the function + for (i, sample) in data.iter_mut().enumerate() { + *sample *= self.function[i]; } } fn reverse(&mut self, data: &mut [f32]) { - let last_few = self.window_size - Self::SAMPLE_COUNT; + let half_window_size = self.window_size / 2; - for (i, sample) in data.iter_mut().take(Self::SAMPLE_COUNT).enumerate() { - *sample += self.overlap_add[i]; + // overlap add data and save the next overlap (latter half of this buffer) + for (i, sample) in data.iter().copied().enumerate() { + self.overlap_add[i] += sample * self.function[i]; } - for (i, sample) in data.iter().skip(last_few).copied().enumerate() { - self.overlap_add[i] = sample; + // normalize the output + for (i, sample) in data.iter_mut().enumerate().take(half_window_size) { + if self.normalize[i] > 1e-6 { + *sample = self.overlap_add[i] / self.normalize[i]; + } else { + *sample = 0.0; + } + } + + // shift the saved overlap to become the next overlap + self.overlap_add.rotate_left(half_window_size); + + // clear a spot for the overlap next time + for sample in &mut self.overlap_add[half_window_size..] { + *sample = 0.0; } } @@ -139,17 +149,13 @@ impl WindowFunction for HaemolacriaaWindow { if self.previous.is_empty() { self.window_size } else { - self.window_size - Self::SAMPLE_COUNT + self.window_size / 2 } } - - fn window_size(&mut self, _window_size: WindowSize) { - todo!() - } } -/// Hann function -pub struct HannWindow { +/// Hamming function +pub struct HammingWindow { window_size: usize, function: Vec<f32>, normalize: Vec<f32>, @@ -157,27 +163,25 @@ pub struct HannWindow { overlap_add: Vec<f32>, } -impl HannWindow { +impl HammingWindow { pub fn new(window_size: &WindowSize) -> Self { let window_size = window_size.inner(); let half_window_size = window_size / 2; let window_size_f32 = window_size as f32; - // 50% overlap let overlap_add = vec![0.0; window_size]; - // with capacity is important for the first needed samples - let previous = Vec::with_capacity(window_size); + let previous = Vec::with_capacity(half_window_size); let function = (0..window_size) .map(|i| { let i = i as f32; - 0.5 * (1.0 - f32::cos((2.0 * PI * i) / (window_size_f32 - 1.0))) + 0.53836 - (0.46164 * f32::cos((2.0 * PI * i) / (window_size_f32))) }) .collect::<Vec<f32>>(); - - let normalize = (0..half_window_size) + + let normalize = (0..half_window_size) .map(|i| { (function[i] * function[i]) + (function[i + half_window_size] * function[i + half_window_size]) @@ -194,18 +198,15 @@ impl HannWindow { } } -impl WindowFunction for HannWindow { +impl WindowFunction for HammingWindow { fn apply(&mut self, data: &mut VecDeque<f32>) { let half_window_size = self.window_size / 2; - // the full window size is needed to window over the samples the first time if self.previous.is_empty() { - // store the latter half for when only half the size is needed for sample in data.iter().skip(half_window_size).copied() { self.previous.push(sample); } - // apply the function for (i, sample) in data.iter_mut().enumerate() { *sample *= self.function[i]; } @@ -213,19 +214,16 @@ impl WindowFunction for HannWindow { return; } - // In a real time "sliding window" it's necessary to add in the previous samples for sample in self.previous.iter().rev().copied() { data.push_front(sample); } self.previous.clear(); - // keep this iteration's samples for the next window for sample in data.iter().skip(half_window_size).copied() { self.previous.push(sample); } - // apply the function for (i, sample) in data.iter_mut().enumerate() { *sample *= self.function[i]; } @@ -260,32 +258,6 @@ impl WindowFunction for HannWindow { self.window_size / 2 } } - - fn window_size(&mut self, window_size: WindowSize) { - let window_size = window_size.inner(); - let half_window_size = window_size / 2; - let window_size_f32 = window_size as f32; - - self.overlap_add.resize(window_size, 0.0); - - self.previous.clear(); - self.previous.reserve_exact(window_size); - - self.function = (0..window_size) - .map(|i| { - let i = i as f32; - - 0.5 * (1.0 - f32::cos((2.0 * PI * i) / (window_size_f32 - 1.0))) - }) - .collect::<Vec<f32>>(); - - self.normalize = (0..half_window_size) - .map(|i| { - (self.function[i] * self.function[i]) - + (self.function[i + half_window_size] * self.function[i + half_window_size]) - }) - .collect::<Vec<f32>>(); - } } /// 4-term Blackman-Harris window function @@ -317,9 +289,9 @@ impl BlackmanHarrisWindow { .map(|i| { let i = i as f32; - let two = f32::cos((2.0 * PI * i) / (window_size_f32 - 1.0)); - let four = f32::cos((4.0 * PI * i) / (window_size_f32 - 1.0)); - let six = f32::cos((6.0 * PI * i) / (window_size_f32 - 1.0)); + let two = f32::cos((2.0 * PI * i) / (window_size_f32)); + let four = f32::cos((4.0 * PI * i) / (window_size_f32)); + let six = f32::cos((6.0 * PI * i) / (window_size_f32)); Self::A_0 - (Self::A_1 * two) + (Self::A_2 * four) - (Self::A_3 * six) }) @@ -406,45 +378,9 @@ impl WindowFunction for BlackmanHarrisWindow { self.window_size / 4 } } - - fn window_size(&mut self, window_size: WindowSize) { - let window_size = window_size.inner(); - let quarter_window_size = window_size / 4; - let three_quarters_window_size = 3 * quarter_window_size; - let window_size_f32 = window_size as f32; - - self.overlap_add.resize(window_size, 0.0); - - self.previous.clear(); - self.previous.reserve_exact(window_size); - - self.function = (0..window_size) - .map(|i| { - let i = i as f32; - - let two = f32::cos((2.0 * PI * i) / (window_size_f32 - 1.0)); - let four = f32::cos((4.0 * PI * i) / (window_size_f32 - 1.0)); - let six = f32::cos((6.0 * PI * i) / (window_size_f32 - 1.0)); - - Self::A_0 - (Self::A_1 * two) + (Self::A_2 * four) - (Self::A_3 * six) - }) - .collect::<Vec<f32>>(); - - self.normalize = (0..quarter_window_size) - .map(|i| { - (self.function[i] * self.function[i]) - + (self.function[i + quarter_window_size] - * self.function[i + quarter_window_size]) - + (self.function[i + 2 * quarter_window_size] - * self.function[i + 2 * quarter_window_size]) - + (self.function[i + 3 * quarter_window_size] - * self.function[i + 3 * quarter_window_size]) - }) - .collect::<Vec<f32>>(); - } } -/// 4th order power of sine window function +/// 4th power of sine window function pub struct Sine4Window { window_size: usize, function: Vec<f32>, @@ -472,8 +408,8 @@ impl Sine4Window { .map(|i| { let i = i as f32; - let two = f32::cos((2.0 * PI * i) / (window_size_f32 - 1.0)); - let four = f32::cos((4.0 * PI * i) / (window_size_f32 - 1.0)); + let two = f32::cos((2.0 * PI * i) / (window_size_f32)); + let four = f32::cos((4.0 * PI * i) / (window_size_f32)); Self::A_0 - (Self::A_1 * two) + (Self::A_2 * four) }) @@ -560,8 +496,4 @@ impl WindowFunction for Sine4Window { self.window_size / 4 } } - - fn window_size(&mut self, window_size: WindowSize) { - todo!() - } } |
