summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/window_function.rs259
-rw-r--r--src/window_size.rs8
-rw-r--r--src/window_type.rs29
-rw-r--r--src/windowed_fft.rs11
4 files changed, 281 insertions, 26 deletions
diff --git a/src/window_function.rs b/src/window_function.rs
index 8e21694..b0ed54d 100644
--- a/src/window_function.rs
+++ b/src/window_function.rs
@@ -1,3 +1,6 @@
+#![allow(unused)]
+#![allow(dead_code)]
+
use crate::window_size::WindowSize;
use std::{collections::VecDeque, f32::consts::PI};
@@ -12,6 +15,7 @@ pub trait WindowFunction: Send {
fn window_size(&mut self, window_size: WindowSize);
}
+/// Effectively a chunking function rather than a sliding window
pub struct RectangularWindow {
window_size: usize,
}
@@ -33,9 +37,118 @@ impl WindowFunction for RectangularWindow {
self.window_size
}
- fn window_size(&mut self, _window_size: WindowSize) {}
+ 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 {
+ window_size: usize,
+ function_start: Vec<f32>,
+ function_end: 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;
+
+ pub fn new(window_size: &WindowSize) -> Self {
+ let window_size = window_size.inner();
+ let sample_count_f32 = Self::SAMPLE_COUNT as f32;
+
+ let overlap_add = vec![0.0; Self::SAMPLE_COUNT];
+
+ let function_start = (0..Self::SAMPLE_COUNT)
+ .map(|i| {
+ let x = i as f32 / (sample_count_f32 - 1.0);
+
+ f32::sin(PI * 0.5 * x)
+ })
+ .collect::<Vec<f32>>();
+
+ let function_end = (0..Self::SAMPLE_COUNT)
+ .map(|i| {
+ let x = i as f32 / (sample_count_f32 - 1.0);
+
+ f32::cos(PI * 0.5 * x)
+ })
+ .collect::<Vec<f32>>();
+
+ let previous = Vec::with_capacity(Self::SAMPLE_COUNT);
+
+ Self {
+ window_size,
+ function_start,
+ function_end,
+ previous,
+ overlap_add,
+ }
+ }
+}
+
+impl WindowFunction for HaemolacriaaWindow {
+ fn apply(&mut self, data: &mut VecDeque<f32>) {
+ let last_few = self.window_size - Self::SAMPLE_COUNT;
+
+ if self.previous.is_empty() {
+ for sample in data.iter().skip(last_few).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];
+ }
+
+ return;
+ }
+
+ for sample in self.previous.iter().rev().copied() {
+ data.push_front(sample);
+ }
+
+ self.previous.clear();
+
+ for sample in data.iter().skip(last_few).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];
+ }
+ }
+
+ fn reverse(&mut self, data: &mut [f32]) {
+ let last_few = self.window_size - Self::SAMPLE_COUNT;
+
+ for (i, sample) in data.iter_mut().take(Self::SAMPLE_COUNT).enumerate() {
+ *sample += self.overlap_add[i];
+ }
+
+ for (i, sample) in data.iter().skip(last_few).copied().enumerate() {
+ self.overlap_add[i] = sample;
+ }
+ }
+
+ fn needed(&self) -> usize {
+ if self.previous.is_empty() {
+ self.window_size
+ } else {
+ self.window_size - Self::SAMPLE_COUNT
+ }
+ }
+
+ fn window_size(&mut self, _window_size: WindowSize) {
+ todo!()
+ }
}
+/// Hann function
pub struct HannWindow {
window_size: usize,
function: Vec<f32>,
@@ -101,7 +214,6 @@ impl WindowFunction for HannWindow {
}
// In a real time "sliding window" it's necessary to add in the previous samples
- // to get the full resolution
for sample in self.previous.iter().rev().copied() {
data.push_front(sample);
}
@@ -122,15 +234,15 @@ impl WindowFunction for HannWindow {
fn reverse(&mut self, data: &mut [f32]) {
let half_window_size = self.window_size / 2;
- for i in 0..self.window_size {
- self.overlap_add[i] += data[i] * self.function[i];
+ for (i, sample) in data.iter().copied().enumerate() {
+ self.overlap_add[i] += sample * self.function[i];
}
- for i in 0..half_window_size {
+ for (i, sample) in data.iter_mut().enumerate().take(half_window_size) {
if self.normalize[i] > 1e-6 {
- data[i] = self.overlap_add[i] / self.normalize[i];
+ *sample = self.overlap_add[i] / self.normalize[i];
} else {
- data[i] = 0.0;
+ *sample = 0.0;
}
}
@@ -176,6 +288,7 @@ impl WindowFunction for HannWindow {
}
}
+/// 4-term Blackman-Harris window function
pub struct BlackmanHarrisWindow {
window_size: usize,
function: Vec<f32>,
@@ -267,15 +380,15 @@ impl WindowFunction for BlackmanHarrisWindow {
let quarter_window_size = self.window_size / 4;
let three_quarters_window_size = 3 * quarter_window_size;
- for i in 0..self.window_size {
- self.overlap_add[i] += data[i] * self.function[i];
+ for (i, sample) in data.iter().copied().enumerate() {
+ self.overlap_add[i] += sample * self.function[i];
}
- for i in 0..quarter_window_size {
+ for (i, sample) in data.iter_mut().enumerate().take(quarter_window_size) {
if self.normalize[i] > 1e-6 {
- data[i] = self.overlap_add[i] / self.normalize[i];
+ *sample = self.overlap_add[i] / self.normalize[i];
} else {
- data[i] = 0.0;
+ *sample = 0.0;
}
}
@@ -330,3 +443,125 @@ impl WindowFunction for BlackmanHarrisWindow {
.collect::<Vec<f32>>();
}
}
+
+/// 4th order power of sine window function
+pub struct Sine4Window {
+ window_size: usize,
+ function: Vec<f32>,
+ normalize: Vec<f32>,
+ previous: Vec<f32>,
+ overlap_add: Vec<f32>,
+}
+
+impl Sine4Window {
+ const A_0: f32 = 0.375;
+ const A_1: f32 = 0.5;
+ const A_2: f32 = 0.125;
+
+ pub fn new(window_size: &WindowSize) -> Self {
+ 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;
+
+ let overlap_add = vec![0.0; window_size];
+
+ let previous = Vec::with_capacity(three_quarters_window_size);
+
+ let 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));
+
+ Self::A_0 - (Self::A_1 * two) + (Self::A_2 * four)
+ })
+ .collect::<Vec<f32>>();
+
+ #[rustfmt::skip]
+ let normalize = (0..quarter_window_size)
+ .map(|i| {
+ (function[i] * function[i])
+ + (function[i + quarter_window_size] * function[i + quarter_window_size])
+ + (function[i + 2 * quarter_window_size] * function[i + 2 * quarter_window_size])
+ + (function[i + 3 * quarter_window_size] * function[i + 3 * quarter_window_size])
+ })
+ .collect::<Vec<f32>>();
+
+ Self {
+ window_size,
+ function,
+ normalize,
+ previous,
+ overlap_add,
+ }
+ }
+}
+
+impl WindowFunction for Sine4Window {
+ fn apply(&mut self, data: &mut VecDeque<f32>) {
+ let quarter_window_size = self.window_size / 4;
+
+ if self.previous.is_empty() {
+ for sample in data.iter().skip(quarter_window_size).copied() {
+ self.previous.push(sample);
+ }
+
+ for (i, sample) in data.iter_mut().enumerate() {
+ *sample *= self.function[i];
+ }
+
+ return;
+ }
+
+ for sample in self.previous.iter().rev().copied() {
+ data.push_front(sample);
+ }
+
+ self.previous.clear();
+
+ for sample in data.iter().skip(quarter_window_size).copied() {
+ self.previous.push(sample);
+ }
+
+ for (i, sample) in data.iter_mut().enumerate() {
+ *sample *= self.function[i];
+ }
+ }
+
+ fn reverse(&mut self, data: &mut [f32]) {
+ let quarter_window_size = self.window_size / 4;
+ let three_quarters_window_size = 3 * quarter_window_size;
+
+ for (i, sample) in data.iter().copied().enumerate() {
+ self.overlap_add[i] += sample * self.function[i];
+ }
+
+ for (i, sample) in data.iter_mut().enumerate().take(quarter_window_size) {
+ if self.normalize[i] > 1e-6 {
+ *sample = self.overlap_add[i] / self.normalize[i];
+ } else {
+ *sample = 0.0;
+ }
+ }
+
+ self.overlap_add.rotate_left(quarter_window_size);
+
+ for sample in &mut self.overlap_add[three_quarters_window_size..] {
+ *sample = 0.0;
+ }
+ }
+
+ fn needed(&self) -> usize {
+ if self.previous.is_empty() {
+ self.window_size
+ } else {
+ self.window_size / 4
+ }
+ }
+
+ fn window_size(&mut self, window_size: WindowSize) {
+ todo!()
+ }
+}
diff --git a/src/window_size.rs b/src/window_size.rs
index bfc3957..620a307 100644
--- a/src/window_size.rs
+++ b/src/window_size.rs
@@ -1,8 +1,8 @@
/// The max value of the custom window size is `u32::MAX`.
+/// The min value of the custom window size is `WindowSize::Size256`.
/// This is because it's the maximum latency one can report to a CLAP host.
#[derive(Debug, PartialEq)]
pub enum WindowSize {
- Size128,
Size256,
Size512,
Size1024,
@@ -16,10 +16,9 @@ pub enum WindowSize {
impl From<usize> for WindowSize {
fn from(item: usize) -> Self {
- let item = item.clamp(0, u32::MAX as usize);
+ let item = item.clamp(256, u32::MAX as usize);
match item {
- 128 => Self::Size128,
256 => Self::Size256,
512 => Self::Size512,
1024 => Self::Size1024,
@@ -36,7 +35,6 @@ impl From<usize> for WindowSize {
impl WindowSize {
pub fn as_str(&self) -> &'static str {
match self {
- Self::Size128 => "128",
Self::Size256 => "256",
Self::Size512 => "512",
Self::Size1024 => "1024",
@@ -51,7 +49,6 @@ impl WindowSize {
pub fn inner(&self) -> usize {
match self {
- Self::Size128 => 128,
Self::Size256 => 256,
Self::Size512 => 512,
Self::Size1024 => 1024,
@@ -66,7 +63,6 @@ impl WindowSize {
pub fn iter() -> impl Iterator<Item = Self> {
[
- Self::Size128,
Self::Size256,
Self::Size512,
Self::Size1024,
diff --git a/src/window_type.rs b/src/window_type.rs
index 371a410..06c87be 100644
--- a/src/window_type.rs
+++ b/src/window_type.rs
@@ -1,32 +1,51 @@
+#![allow(unused)]
+#![allow(dead_code)]
+
use crate::{
- window_function::{BlackmanHarrisWindow, HannWindow, RectangularWindow, WindowFunction},
+ window_function::{
+ BlackmanHarrisWindow, HaemolacriaaWindow, HannWindow, RectangularWindow, Sine4Window,
+ WindowFunction,
+ },
window_size::WindowSize,
};
pub enum WindowType {
- Hann,
Rectangular,
+ Haemolacriaa,
+ Hann,
BlackmanHarris,
+ Sine4,
}
impl WindowType {
fn into_function(self, window_size: &WindowSize) -> Box<dyn WindowFunction> {
match self {
- Self::Hann => Box::new(HannWindow::new(window_size)),
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::BlackmanHarris => Box::new(BlackmanHarrisWindow::new(window_size)),
+ Self::Sine4 => Box::new(Sine4Window::new(window_size)),
}
}
pub fn as_str(&self) -> &'static str {
match self {
- Self::Hann => "Hann",
Self::Rectangular => "Rectangular",
+ Self::Haemolacriaa => "haemolacriaa",
+ Self::Hann => "Hann",
Self::BlackmanHarris => "Blackman-Harris",
+ Self::Sine4 => "Sin^4",
}
}
pub fn iter() -> impl Iterator<Item = Self> {
- [Self::Hann, Self::Rectangular].into_iter()
+ [
+ Self::Rectangular,
+ Self::Haemolacriaa,
+ Self::Hann,
+ Self::BlackmanHarris,
+ Self::Sine4,
+ ]
+ .into_iter()
}
}
diff --git a/src/windowed_fft.rs b/src/windowed_fft.rs
index c08c2b4..19bf566 100644
--- a/src/windowed_fft.rs
+++ b/src/windowed_fft.rs
@@ -1,8 +1,13 @@
#![allow(clippy::must_use_candidate)]
#![allow(clippy::return_self_not_must_use)]
+#![allow(unused)]
+#![allow(dead_code)]
use crate::{
- window_function::{BlackmanHarrisWindow, HannWindow, RectangularWindow, WindowFunction},
+ window_function::{
+ BlackmanHarrisWindow, HaemolacriaaWindow, HannWindow, RectangularWindow, Sine4Window,
+ WindowFunction,
+ },
window_size::WindowSize,
};
use num_complex::Complex;
@@ -23,7 +28,7 @@ pub struct WindowedRealFft {
impl WindowedRealFft {
pub fn new(window_size: WindowSize) -> Self {
- let window_function = Box::new(BlackmanHarrisWindow::new(&window_size));
+ let window_function = Box::new(HaemolacriaaWindow::new(&window_size));
let window_size = window_size.inner();
let mut planner = RealFftPlanner::new();
@@ -58,7 +63,7 @@ impl WindowedRealFft {
return;
}
- self.window_function = Box::new(HannWindow::new(&window_size));
+ self.window_function = Box::new(HaemolacriaaWindow::new(&window_size));
self.window_size = window_size.inner();
self.forward = self.planner.plan_fft_forward(self.window_size);