Chris@35: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@35: Chris@42: /* Chris@42: Tipic Chris@42: Chris@42: Centre for Digital Music, Queen Mary, University of London. Chris@42: Chris@42: This program is free software; you can redistribute it and/or Chris@42: modify it under the terms of the GNU General Public License as Chris@42: published by the Free Software Foundation; either version 2 of the Chris@42: License, or (at your option) any later version. See the file Chris@42: COPYING included with this distribution for more information. Chris@42: */ Chris@42: Chris@35: #include "FeatureDownsample.h" Chris@35: Chris@42: #include "dsp/signalconditioning/Filter.h" Chris@42: #include "base/Window.h" Chris@42: #include "maths/MathUtilities.h" Chris@35: Chris@35: #include Chris@35: Chris@35: using namespace std; Chris@35: Chris@35: FeatureDownsample::FeatureDownsample(Parameters params) : Chris@35: m_params(params) Chris@35: { Chris@35: if (params.downsampleFactor < 2 || params.windowLength < 2) { Chris@35: throw invalid_argument Chris@35: ("Expected downsampleFactor and windowLength each to be at least 2"); Chris@35: } Chris@35: Chris@35: // Our windows are periodic rather than symmetric, but we want a Chris@35: // symmetric window here Chris@37: Window w(HanningWindow, params.windowLength + 1); Chris@37: vector wdat(w.getWindowData());; Chris@37: vector wd(wdat.begin()+1, wdat.end()); Chris@35: Chris@36: double divisor = 0.0; Chris@36: for (auto x: wd) divisor += x; Chris@36: for (auto &x: wd) x /= divisor; Chris@36: Chris@35: // FIR filter Chris@35: for (int i = 0; i < m_params.featureSize; ++i) { Chris@35: m_filters.push_back(new Filter({ {}, wd })); Chris@35: } Chris@35: Chris@36: m_toNext = 1; Chris@36: m_toDrop = m_params.windowLength / 2; Chris@36: m_inCount = 0; Chris@36: m_outCount = 0; Chris@35: } Chris@35: Chris@35: FeatureDownsample::~FeatureDownsample() Chris@35: { Chris@35: for (auto &f: m_filters) delete f; Chris@35: } Chris@35: Chris@35: void Chris@35: FeatureDownsample::reset() Chris@35: { Chris@35: for (auto &f: m_filters) f->reset(); Chris@36: m_toNext = 1; Chris@36: m_toDrop = m_params.windowLength / 2; Chris@36: m_inCount = 0; Chris@36: m_outCount = 0; Chris@35: } Chris@35: Chris@35: RealBlock Chris@35: FeatureDownsample::process(const RealBlock &in) Chris@35: { Chris@36: RealBlock out; Chris@35: Chris@35: for (const auto &col: in) { Chris@35: RealColumn outcol; Chris@36: if (m_toDrop > 0) { Chris@36: --m_toDrop; Chris@36: } else { Chris@36: --m_toNext; Chris@36: } Chris@35: for (int i = 0; i < m_params.featureSize; ++i) { Chris@35: double val = 0.0; Chris@35: m_filters[i]->process(&col[i], &val, 1); Chris@35: if (m_toNext == 0) { Chris@35: outcol.push_back(val); Chris@35: } Chris@35: } Chris@35: if (m_toNext == 0) { Chris@42: out.push_back(MathUtilities::normaliseLp Chris@37: (outcol, m_params.normP, m_params.normThresh)); Chris@35: m_toNext = m_params.downsampleFactor; Chris@36: ++m_outCount; Chris@35: } Chris@36: ++m_inCount; Chris@36: } Chris@36: Chris@36: return out; Chris@36: } Chris@36: Chris@36: RealBlock Chris@36: FeatureDownsample::getRemainingOutput() Chris@36: { Chris@36: RealBlock pad(m_params.windowLength, RealColumn(m_params.featureSize, 0.0)); Chris@36: RealBlock tail = process(pad); Chris@36: int expected = m_inCount / m_params.downsampleFactor; Chris@36: RealBlock out; Chris@36: for (int i = 0; Chris@36: m_outCount < expected && i < int(tail.size()); Chris@36: ++i, ++m_outCount) { Chris@42: out.push_back(MathUtilities::normaliseLp Chris@37: (tail[i], m_params.normP, m_params.normThresh)); Chris@35: } Chris@35: return out; Chris@35: } Chris@35: Chris@36: