Mercurial > hg > lowfreq
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; };