changeset 4:9867e53a2592

Many fixes to buffering, etc
author Chris Cannam
date Fri, 07 Mar 2014 14:17:47 +0000
parents 2fc53e089662
children bf742ae09443
files LowFreq.cpp LowFreq.h
diffstat 2 files changed, 96 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/LowFreq.cpp	Fri Mar 07 08:29:01 2014 +0000
+++ b/LowFreq.cpp	Fri Mar 07 14:17:47 2014 +0000
@@ -5,6 +5,7 @@
 #include "dsp/transforms/FFT.h"
 
 #include <cmath>
+#include <cstdio>
 
 using std::cerr;
 using std::endl;
@@ -18,13 +19,20 @@
 static int maxN = 16384;
 static int defaultN = 64;
 
+static float minOverlap = 0.0;
+static float maxOverlap = 87.5;
+static float overlapStep = 12.5;
+static float defaultOverlap = 50.0;
+
 LowFreq::LowFreq(float inputSampleRate) :
     Plugin(inputSampleRate),
     m_p(defaultP),
     m_n(defaultN),
+    m_overlap(defaultOverlap),
     m_blockSize(0),
     m_resampler(0),
-    m_fft(0)
+    m_fft(0),
+    m_window(0)
 {
 }
 
@@ -32,6 +40,7 @@
 {
     delete m_resampler;
     delete m_fft;
+    delete m_window;
 }
 
 string
@@ -108,7 +117,7 @@
 
     ParameterDescriptor d;
     d.identifier = "p";
-    d.name = "Shortest Period";
+    d.name = "Shortest period";
     d.description = "Period in seconds of the highest-frequency component to include in the spectrogram. That is, 1/f where f is the highest frequency (in Hz) spanned by the spectrogram.";
     d.unit = "s";
     d.minValue = minP;
@@ -128,6 +137,17 @@
     d.quantizeStep = 1;
     list.push_back(d);
 
+    d.identifier = "overlap";
+    d.name = "Frame overlap";
+    d.description = "Amount of overlap between one Hann-windowed frame and the next. Should be at least 50% to avoid gaps due to windowing.";
+    d.unit = "%";
+    d.minValue = minOverlap;
+    d.maxValue = maxOverlap;
+    d.defaultValue = defaultOverlap;
+    d.isQuantized = true;
+    d.quantizeStep = overlapStep;
+    list.push_back(d);
+
     return list;
 }
 
@@ -138,6 +158,8 @@
 	return m_p;
     } else if (identifier == "n") {
 	return m_n;
+    } else if (identifier == "overlap") {
+	return m_overlap;
     }
     return 0;
 }
@@ -149,6 +171,8 @@
 	m_p = value;
     } else if (identifier == "n") {
 	m_n = int(value + 0.01);
+    } else if (identifier == "overlap") {
+	m_overlap = value;
     }
 }
 
@@ -170,6 +194,18 @@
 {
 }
 
+int
+LowFreq::getTargetSampleRate() const
+{
+    return int(ceil(2.0 / m_p));
+}
+
+int
+LowFreq::getTargetStepSize() const
+{
+    return int(round(m_n * (1.0 - (m_overlap / 100.0))));
+}    
+
 LowFreq::OutputList
 LowFreq::getOutputDescriptors() const
 {
@@ -182,9 +218,21 @@
     d.unit = "";
     d.hasFixedBinCount = true;
     d.binCount = m_n/2 + 1;
+
     d.hasKnownExtents = false;
     d.isQuantized = false;
-    d.sampleType = OutputDescriptor::OneSamplePerStep;
+    d.sampleType = OutputDescriptor::FixedSampleRate;
+
+    d.sampleRate = float(getTargetSampleRate()) / float(getTargetStepSize());
+    
+    char namebuf[50];
+    for (int i = 0; i <= m_n/2; ++i) {
+	sprintf(namebuf, "%.3gHz", double(getTargetSampleRate()) / m_n * i);
+	d.binNames.push_back(namebuf);
+    }
+
+    cerr << "output descriptor effective sample rate = " << d.sampleRate << endl;
+
     d.hasDuration = false;
     list.push_back(d);
 
@@ -224,6 +272,13 @@
 	return false;
     }
 
+    if (m_overlap < minOverlap || m_overlap > maxOverlap) {
+	cerr << "LowFreq::initialise: ERROR: overlap " << m_overlap
+	     << " out of acceptable range " << minOverlap
+	     << " -> " << maxOverlap << endl;
+	return false;
+    }
+
     if (fabs(m_inputSampleRate - round(m_inputSampleRate)) > 1e-6) {
 	cerr << "LowFreq::initialise: WARNING: input sample rate " 
 	     << m_inputSampleRate << " is non-integral, output frequencies "
@@ -242,20 +297,26 @@
 {
     delete m_resampler;
     delete m_fft;
+    delete m_window;
 
     // We have shortest period p and number of bins n.  The bin
     // frequency for the highest bin of a spectrogram is fs/2 where fs
     // is the sample rate of the signal. So we must downsample to a
     // sample rate of (1/p)*2 and then run an n-point FFT.
 
-    int targetRate = int(ceil(2.0 / m_p));
+    m_resampler = new Resampler(int(round(m_inputSampleRate)),
+				getTargetSampleRate());
+    m_fft = new FFT(m_n);
+    m_window = new Window<double>(HanningWindow, m_n);
+    m_buffer = std::vector<double>();
 
-    cerr << "LowFreq::reset: target rate for p = " << m_p
-	 << " is " << targetRate << endl;
-
-    m_resampler = new Resampler(int(round(m_inputSampleRate)), targetRate);
-    m_fft = new FFT(m_n);
-    m_buffer = std::vector<double>();
+    cerr << "LowFreq::reset: min period " << m_p << " so max freq " << 1.0/m_p
+	 << endl;
+    cerr << "LowFreq::reset: block size " << m_blockSize
+	 << ", input sample rate " << m_inputSampleRate << endl;
+    cerr << "LowFreq::reset: target rate " << getTargetSampleRate()
+	 << ", resampler latency " << m_resampler->getLatency()
+	 << ", fft size " << m_n << endl;
 }
 
 LowFreq::FeatureSet
@@ -271,14 +332,12 @@
 
     delete[] data;
 
-    //!!! ah, our processing block size and step size must be equal --
-    //!!! any overlap must be handled here by us
-
     FeatureSet fs;
 
     while (int(m_buffer.size()) >= m_n) {
 	Feature f = processColumn();
 	fs[0].push_back(f);
+	advance();
     }
 
     return fs;
@@ -289,9 +348,14 @@
 {
     FeatureSet fs;
 
+    while (!m_buffer.empty() && int(m_buffer.size()) < m_n) {
+	m_buffer.push_back(0.0);
+    }
+
     while (int(m_buffer.size()) >= m_n) {
 	Feature f = processColumn();
 	fs[0].push_back(f);
+	advance();
     }
 
     return fs;
@@ -305,24 +369,26 @@
     double *realOut = new double[m_n];
     double *imagOut = new double[m_n];
 
-    //!!! want window
+    double *windowed = new double[m_n];
+    m_window->cut(m_buffer.data(), windowed);	
 
-    m_fft->process(false, m_buffer.data(), 0, realOut, imagOut);
+    m_fft->process(false, windowed, 0, realOut, imagOut);
 
     for (int i = 0; i <= m_n/2; ++i) {
 	f.values.push_back(realOut[i] * realOut[i] + imagOut[i] * imagOut[i]);
     }
 
-    int step = m_n/2; //!!!
+    return f;
+}
 
-    //!!! this output is wrong, it's described as one sample per step
-    //!!! but actually we have an overlap
-
-    std::vector<double> advanced(m_buffer.data() + step,
+void
+LowFreq::advance()
+{
+    cerr << "step = " << getTargetStepSize() << endl;
+    
+    std::vector<double> advanced(m_buffer.data() + getTargetStepSize(),
 				 m_buffer.data() + m_buffer.size());
     
     m_buffer = advanced;
-
-    return f;
 }
 
--- a/LowFreq.h	Fri Mar 07 08:29:01 2014 +0000
+++ b/LowFreq.h	Fri Mar 07 14:17:47 2014 +0000
@@ -3,6 +3,8 @@
 
 #include <vamp-sdk/Plugin.h>
 
+#include "base/Window.h"
+
 using std::string;
 
 class Resampler;
@@ -47,14 +49,20 @@
 
 protected:
     Feature processColumn();
+    void advance();
+
+    int getTargetSampleRate() const;
+    int getTargetStepSize() const;
 
     float m_p;
     int m_n;
+    float m_overlap;
 
     int m_blockSize;
 
     Resampler *m_resampler;
     FFT *m_fft;
+    Window<double> *m_window;
     std::vector<double> m_buffer;
 };