summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorozpv <39195175+ozpv@users.noreply.github.com>2026-05-18 10:55:39 -0500
committerGitHub <noreply@github.com>2026-05-18 10:55:39 -0500
commit13e872165ed41e032246eddbe1b0750154ce7259 (patch)
treebdd8ba908b7170ec9b4844490247855566edeb2e /src
parent20414d4b6ab6d1177d8008ee987931c9e4ee65d3 (diff)
add hamming window
Diffstat (limited to 'src')
-rw-r--r--src/window_function.rs206
-rw-r--r--src/window_type.rs12
-rw-r--r--src/windowed_fft.rs6
3 files changed, 78 insertions, 146 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!()
- }
}
diff --git a/src/window_type.rs b/src/window_type.rs
index 06c87be..acbf727 100644
--- a/src/window_type.rs
+++ b/src/window_type.rs
@@ -3,16 +3,16 @@
use crate::{
window_function::{
- BlackmanHarrisWindow, HaemolacriaaWindow, HannWindow, RectangularWindow, Sine4Window,
- WindowFunction,
+ BlackmanHarrisWindow, HammingWindow, HannWindow, RectangularWindow,
+ Sine4Window, WindowFunction,
},
window_size::WindowSize,
};
pub enum WindowType {
Rectangular,
- Haemolacriaa,
Hann,
+ Hamming,
BlackmanHarris,
Sine4,
}
@@ -21,8 +21,8 @@ impl WindowType {
fn into_function(self, window_size: &WindowSize) -> Box<dyn WindowFunction> {
match self {
Self::Rectangular => Box::new(RectangularWindow::new(window_size)),
- Self::Haemolacriaa => Box::new(HaemolacriaaWindow::new(window_size)),
Self::Hann => Box::new(HannWindow::new(window_size)),
+ Self::Hamming => Box::new(HammingWindow::new(window_size)),
Self::BlackmanHarris => Box::new(BlackmanHarrisWindow::new(window_size)),
Self::Sine4 => Box::new(Sine4Window::new(window_size)),
}
@@ -31,8 +31,8 @@ impl WindowType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Rectangular => "Rectangular",
- Self::Haemolacriaa => "haemolacriaa",
Self::Hann => "Hann",
+ Self::Hamming => "Hamming",
Self::BlackmanHarris => "Blackman-Harris",
Self::Sine4 => "Sin^4",
}
@@ -41,8 +41,8 @@ impl WindowType {
pub fn iter() -> impl Iterator<Item = Self> {
[
Self::Rectangular,
- Self::Haemolacriaa,
Self::Hann,
+ Self::Hamming,
Self::BlackmanHarris,
Self::Sine4,
]
diff --git a/src/windowed_fft.rs b/src/windowed_fft.rs
index 19bf566..3e4e648 100644
--- a/src/windowed_fft.rs
+++ b/src/windowed_fft.rs
@@ -5,7 +5,7 @@
use crate::{
window_function::{
- BlackmanHarrisWindow, HaemolacriaaWindow, HannWindow, RectangularWindow, Sine4Window,
+ BlackmanHarrisWindow, HannWindow, RectangularWindow, Sine4Window, HammingWindow,
WindowFunction,
},
window_size::WindowSize,
@@ -28,7 +28,7 @@ pub struct WindowedRealFft {
impl WindowedRealFft {
pub fn new(window_size: WindowSize) -> Self {
- let window_function = Box::new(HaemolacriaaWindow::new(&window_size));
+ let window_function = Box::new(HannWindow::new(&window_size));
let window_size = window_size.inner();
let mut planner = RealFftPlanner::new();
@@ -63,7 +63,7 @@ impl WindowedRealFft {
return;
}
- self.window_function = Box::new(HaemolacriaaWindow::new(&window_size));
+ self.window_function = Box::new(HannWindow::new(&window_size));
self.window_size = window_size.inner();
self.forward = self.planner.plan_fft_forward(self.window_size);