Mercurial > hg > lowfreq
changeset 7:4a777e3b515e
Details of output sample rate / step size calculation, plus copyright etc
author | Chris Cannam |
---|---|
date | Wed, 12 Mar 2014 11:50:40 +0000 |
parents | e502cf649389 |
children | c037af3977e1 |
files | COPYING LowFreq.cpp LowFreq.h README plugins.cpp |
diffstat | 5 files changed, 213 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Wed Mar 12 11:50:40 2014 +0000 @@ -0,0 +1,31 @@ + + Low-frequency spectrogram + Copyright (c) 2013-2014 Queen Mary, University of London + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. + + +
--- a/LowFreq.cpp Tue Mar 11 18:36:24 2014 +0000 +++ b/LowFreq.cpp Wed Mar 12 11:50:40 2014 +0000 @@ -1,3 +1,33 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + Low-frequency spectrogram + Copyright (c) 2014 Queen Mary, University of London + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ #include "LowFreq.h" @@ -21,12 +51,12 @@ static float defaultFmax = 10; static int minN = 1; -static int maxN = 16384; +static int maxN = 2048; static int defaultN = 64; static float minOverlap = 0.0; -static float maxOverlap = 87.5; -static float overlapStep = 12.5; +static float maxOverlap = 90.0; +static float overlapStep = 5; static float defaultOverlap = 50.0; LowFreq::LowFreq(float inputSampleRate) : @@ -40,6 +70,14 @@ m_fft(0), m_window(0) { + m_nonIntegralInputRate = false; + if (fabs(double(m_inputSampleRate) - round(m_inputSampleRate)) > 1e-6) { + m_nonIntegralInputRate = true; + } + + if (inputSampleRate / 4 < maxFmax) { + maxFmax = inputSampleRate / 4; + } } LowFreq::~LowFreq() @@ -155,7 +193,7 @@ 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.description = "Amount of overlap between one Hann-windowed frame and the next. Should be at least 50% to avoid gaps due to windowing. This may be rounded, sometimes drastically, to ensure that output columns fall on integral sample boundaries in the input signal."; d.unit = "%"; d.minValue = minOverlap; d.maxValue = maxOverlap; @@ -230,11 +268,11 @@ d.hasKnownExtents = false; d.isQuantized = false; d.sampleType = OutputDescriptor::FixedSampleRate; + d.sampleRate = getOutputSampleRate(); - d.sampleRate = float(getTargetSampleRate()) / float(getTargetStepSize()); - -//!!! We need to make sure the input sample rate / d.sampleRate is an -//!!! integer (otherwise e.g. SV won't display correctly) + cerr << "output descriptor effective sample rate = " << d.sampleRate << endl; + cerr << "input sample rate = " << m_inputSampleRate << endl; + cerr << "input rate / output rate = " << m_inputSampleRate / d.sampleRate << endl; char namebuf[50]; for (int i = 0; i < m_n; ++i) { @@ -242,8 +280,6 @@ d.binNames.push_back(namebuf); } - cerr << "output descriptor effective sample rate = " << d.sampleRate << endl; - d.hasDuration = false; list.push_back(d); @@ -297,7 +333,7 @@ return false; } - if (fabs(m_inputSampleRate - round(m_inputSampleRate)) > 1e-6) { + if (m_nonIntegralInputRate) { cerr << "LowFreq::initialise: WARNING: input sample rate " << m_inputSampleRate << " is non-integral, output frequencies " << "will be skewed by rounding it to nearest integer" << endl; @@ -347,8 +383,6 @@ << ", resampler latency " << m_resampler->getLatency() << ", fft size " << getFFTSize() << endl; - //!!! want to make sure our output sample rate is - //!!! not handling resampler latency } @@ -361,11 +395,66 @@ return tfs; } +float +LowFreq::getOutputSampleRate() const +{ + return float(getTargetSampleRate()) / float(getTargetStepSize()); +} + +static int gcd(int a, int b) +{ + int c = a % b; + if (c == 0) { + return b; + } else { + return gcd(b, c); + } +} + int LowFreq::getTargetStepSize() const { + // We need to make sure that m_inputSampleRate / output sample + // rate is an integer, otherwise hosts like SV will end up with + // rounding error when counting columns with non-integral width in + // terms of the input file sample rate. + // + // Since the output sample rate (see getOutputSampleRate above) is + // target rate / target step size, and we can't mess with the + // target rate, we should adjust the target step size instead. + // + // So we want m_inputSampleRate / (targetRate / step) to be + // integral, that is, step * m_inputSampleRate == n * targetRate + // (where n is a positive integer) and step = n * targetRate / + // m_inputSampleRate. Of course, step must also be integral. We + // can achieve this by rounding step to the nearest multiple of + // targetRate / g, where g is the gcd of m_inputSampleRate and + // targetRate. + + //!!! though actually it might be better not to offer overlap as + //!!! an option at all. Instead the target step size could always + //!!! just be the minimum that makes for an integral column width + //!!! in terms of the input sample rate. + int step = int(round(getFFTSize() * (1.0 - (m_overlap / 100.0)))); - cerr << "LowFreq::getTargetStepSize: step is " << step << endl; + cerr << "LowFreq::getTargetStepSize: preferred step is " << step << endl; + + // Note: if the input sample rate turns out not to be integral + // anyway, then we don't need to care about the details -- the + // host can evidently cope + if (m_nonIntegralInputRate) return step; + + int g = gcd(int(round(m_inputSampleRate)), getTargetSampleRate()); + int m = getTargetSampleRate() / g; + cerr << "m = " << m << endl; + + int rounded = (step / m) * m; + if (rounded == 0 || ((step - rounded) > (rounded + m - step))) { + rounded = rounded + m; + } + + cerr << "rounded step to " << rounded << " giving ri / ro = " << (m_inputSampleRate * rounded) / getTargetSampleRate() << endl; + return step; } @@ -402,8 +491,6 @@ data[i] = inputBuffers[0][i]; } - cout << "timestamp: " << timestamp << endl; - vector<double> resampled = m_resampler->process(data, m_blockSize); m_buffer.insert(m_buffer.end(), resampled.begin(), resampled.end()); @@ -451,23 +538,14 @@ double *windowed = new double[sz]; m_window->cut(m_buffer.data(), windowed); - cout << "in: "; - for (int i = 0; i < sz; ++i) { - cout << windowed[i] << " "; - } - cout << endl; - m_fft->process(false, windowed, 0, realOut, imagOut); - cout << "out: "; int base = getFirstOutputBin(); for (int i = 0; i < m_n; ++i) { int ix = base + i; float mag = (realOut[ix] * realOut[ix] + imagOut[ix] * imagOut[ix]); f.values.push_back(mag); - cout << mag << " "; } - cout << endl; return f; } @@ -475,8 +553,6 @@ void LowFreq::advance() { - cerr << "step = " << getTargetStepSize() << endl; - std::vector<double> advanced(m_buffer.data() + getTargetStepSize(), m_buffer.data() + m_buffer.size());
--- a/LowFreq.h Tue Mar 11 18:36:24 2014 +0000 +++ b/LowFreq.h Wed Mar 12 11:50:40 2014 +0000 @@ -1,3 +1,34 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + Low-frequency spectrogram + Copyright (c) 2014 Queen Mary, University of London + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + #ifndef _LOWFREQ_H_ #define _LOWFREQ_H_ @@ -56,6 +87,7 @@ int getFFTSize() const; int getFirstOutputBin() const; float getOutputBinFrequency(int i) const; + float getOutputSampleRate() const; float m_fmin; float m_fmax; @@ -64,6 +96,8 @@ int m_blockSize; + bool m_nonIntegralInputRate; + Resampler *m_resampler; FFT *m_fft; Window<double> *m_window;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Wed Mar 12 11:50:40 2014 +0000 @@ -0,0 +1,15 @@ + +Low-frequency spectrogram +========================= + +This is a Vamp plugin that produces a spectrogram from only the +low-frequency content of a signal. + +You supply a frequency range and a number of bins, and it returns a +magnitude spectrum containing the requested number of bins, spanning +the frequency range you asked for. It does this by first downsampling +the signal to an integer multiple of the maximum requested frequency, +then calculating a Hann-windowed FFT of an appropriate size and +returning a subset of its magnitudes. + +Chris Cannam, March 2014
--- a/plugins.cpp Tue Mar 11 18:36:24 2014 +0000 +++ b/plugins.cpp Wed Mar 12 11:50:40 2014 +0000 @@ -1,3 +1,33 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + Low-frequency spectrogram + Copyright (c) 2014 Queen Mary, University of London + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ #include <vamp/vamp.h> #include <vamp-sdk/PluginAdapter.h>