changeset 9:ec39b9aa9f78

Handle resampler latency and FFT alignment padding
author Chris Cannam
date Wed, 12 Mar 2014 12:47:47 +0000
parents c037af3977e1
children 0a56b08b373f
files LowFreq.cpp LowFreq.h
diffstat 2 files changed, 49 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/LowFreq.cpp	Wed Mar 12 12:01:54 2014 +0000
+++ b/LowFreq.cpp	Wed Mar 12 12:47:47 2014 +0000
@@ -61,6 +61,8 @@
     m_n(defaultN),
     m_blockSize(0),
     m_roundedInputRate(round(inputSampleRate)),
+    m_drop(0),
+    m_pad(0),
     m_resampler(0),
     m_fft(0),
     m_window(0)
@@ -344,6 +346,7 @@
 
     m_resampler = new Resampler(m_roundedInputRate,
 				getTargetSampleRate());
+
     m_fft = new FFT(getFFTSize());
     m_window = new Window<double>(HanningWindow, getFFTSize());
     m_buffer = std::vector<double>();
@@ -356,7 +359,22 @@
 	 << ", resampler latency " << m_resampler->getLatency()
 	 << ", fft size " << getFFTSize() << endl;
 
-    //!!! not handling resampler latency
+    // Resampler's declared latency is its output latency. We need to
+    // drop that number of samples from the start of its output...
+    m_drop = m_resampler->getLatency();
+
+    // ...and add enough padding at the end of its input to ensure at
+    // least that number of samples come back.
+    m_pad = int(ceil((double(m_drop) * m_roundedInputRate) /
+                     getTargetSampleRate()));
+
+    // We also need to make sure that the FFT frames are properly
+    // aligned. That is, the first returned column is expected to show
+    // an FFT frame centred on time zero. So we need to pre-pad the
+    // buffer with half the FFT length.
+    for (int i = 0; i < getFFTSize()/2; ++i) {
+        m_buffer.push_back(0.0);
+    }
 }
 
 int
@@ -446,6 +464,15 @@
     vector<double> resampled = m_resampler->process(data, m_blockSize);
     m_buffer.insert(m_buffer.end(), resampled.begin(), resampled.end());
 
+    if (m_drop > 0) {
+        int dropHere = m_drop;
+        if (dropHere > int(m_buffer.size())) {
+            dropHere = int(m_buffer.size());
+        }
+        advanceBy(dropHere);
+        m_drop -= dropHere;
+    }
+
     delete[] data;
 
     FeatureSet fs;
@@ -464,8 +491,15 @@
 {
     FeatureSet fs;
 
-    while (!m_buffer.empty() && int(m_buffer.size()) < getFFTSize()) {
-	m_buffer.push_back(0.0);
+    double *padding = new double[m_pad];
+    for (int i = 0; i < m_pad; ++i) {
+        padding[i] = 0.0;
+    }
+    vector<double> lastBit = m_resampler->process(padding, m_pad);
+    m_buffer.insert(m_buffer.end(), lastBit.begin(), lastBit.end());
+
+    for (int i = 0; i < getFFTSize() * 2 - getTargetStepSize(); ++i) {
+        m_buffer.push_back(0.0);
     }
 
     while (int(m_buffer.size()) >= getFFTSize()) {
@@ -505,7 +539,13 @@
 void
 LowFreq::advance()
 {
-    std::vector<double> advanced(m_buffer.data() + getTargetStepSize(),
+    advanceBy(getTargetStepSize());
+}
+
+void
+LowFreq::advanceBy(int n)
+{
+    std::vector<double> advanced(m_buffer.data() + n,
 				 m_buffer.data() + m_buffer.size());
     
     m_buffer = advanced;
--- a/LowFreq.h	Wed Mar 12 12:01:54 2014 +0000
+++ b/LowFreq.h	Wed Mar 12 12:47:47 2014 +0000
@@ -81,6 +81,7 @@
 protected:
     Feature processColumn();
     void advance();
+    void advanceBy(int);
 
     int getTargetSampleRate() const;
     int getTargetStepSize() const;
@@ -97,6 +98,10 @@
     bool m_nonIntegralInputRate;
     int m_roundedInputRate;
 
+    // for handling resampler latency:
+    int m_drop;
+    int m_pad;
+
     Resampler *m_resampler;
     FFT *m_fft;
     Window<double> *m_window;