From fc9419e580c78dec63c668613bb1c56359468567 Mon Sep 17 00:00:00 2001
From: Matt McPherrin <git@mcpherrin.ca>
Date: Wed, 15 Jul 2015 15:27:15 -0700
Subject: [PATCH] A basic implementation, mostly untested!

---
 src/lib.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/lib.rs b/src/lib.rs
index a93251b..bfe3258 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,67 @@
+// This (implicitly/naively) uses a rectangular window.  Some way to set up a window function
+// will be needed probably -- if mutating your samples before calling this isn't sufficient.
+
+/// Set up parameters (and some precomputed values for those).
+#[derive(Clone, Copy)]
+pub struct Parameters {
+    // Parameters we're working with
+    window_size: u32,
+    // Precomputed value:
+    sine: f32,
+    cosine: f32,
+    term_coefficient: f32,
+}
+
+pub struct Partial {
+    params: Parameters,
+    //count: u32,
+    prev: f32,
+    prevprev: f32,
+}
+
+impl Parameters {
+    pub fn new(target_freq: f32, sample_rate: f32, window_size: u32) -> Self {
+        use std::f32;
+        let k = target_freq * (window_size as f32) / sample_rate;
+        let omega = (f32::consts::PI * 2. * k) / (window_size as f32);
+        let cosine = omega.cos();
+        Parameters {
+            window_size: window_size,
+            sine: omega.sin(),
+            cosine: cosine,
+            term_coefficient: 2. * cosine,
+        }
+    }
+
+    pub fn start(self) -> Partial {
+        Partial{ params: self, prev: 0., prevprev: 0. }
+    }
+}
+
+impl Partial {
+    pub fn add(mut self, samples: &[u16]) -> Self {
+        for &sample in samples {
+            let this = self.params.term_coefficient * self.prev - self.prevprev + (sample as f32);
+            self.prevprev = self.prev;
+            self.prev = this;
+        }
+        self
+    }
+    pub fn finish(self) -> (f32, f32) {
+        let real = self.prev - self.prevprev * self.params.cosine;
+        let imag = self.prevprev * self.params.sine;
+        (real, imag)
+    }
+
+    pub fn finish_mag(self) -> f32 {
+        let (real, imag) = self.finish();
+        (real*real + imag*imag).sqrt()
+    }
+}
+
 #[test]
-fn it_works() {
+fn zero_data() {
+    let p = Parameters::new(1800., 44100., 256);
+    assert!(p.start().add(&[0; 256]).finish_mag() == 0.);
+    assert!(p.start().add(&[0; 128]).add(&[0;128]).finish_mag() == 0.);
 }
-- 
GitLab