cannam@233: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@233: cannam@233: /* cannam@233: Vamp cannam@233: cannam@233: An API for audio analysis and feature extraction plugins. cannam@233: cannam@233: Centre for Digital Music, Queen Mary, University of London. cannam@290: Copyright 2006-2009 Chris Cannam and QMUL. cannam@233: cannam@233: This file is based in part on Don Cross's public domain FFT cannam@233: implementation. cannam@233: cannam@233: Permission is hereby granted, free of charge, to any person cannam@233: obtaining a copy of this software and associated documentation cannam@233: files (the "Software"), to deal in the Software without cannam@233: restriction, including without limitation the rights to use, copy, cannam@233: modify, merge, publish, distribute, sublicense, and/or sell copies cannam@233: of the Software, and to permit persons to whom the Software is cannam@233: furnished to do so, subject to the following conditions: cannam@233: cannam@233: The above copyright notice and this permission notice shall be cannam@233: included in all copies or substantial portions of the Software. cannam@233: cannam@233: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, cannam@233: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF cannam@233: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND cannam@233: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR cannam@233: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF cannam@233: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION cannam@233: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cannam@233: cannam@233: Except as contained in this notice, the names of the Centre for cannam@233: Digital Music; Queen Mary, University of London; and Chris Cannam cannam@233: shall not be used in advertising or otherwise to promote the sale, cannam@233: use or other dealings in this Software without prior written cannam@233: authorization. cannam@233: */ cannam@233: cannam@233: #include cannam@233: cannam@233: #include cannam@233: Chris@317: #include "Window.h" Chris@317: Chris@434: #include Chris@434: #include Chris@434: #include Chris@434: #include Chris@434: #include cannam@233: Chris@445: _VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.cpp) Chris@444: Chris@445: #include "../vamp-sdk/FFTimpl.cpp" cannam@263: cannam@233: namespace Vamp { cannam@233: cannam@233: namespace HostExt { cannam@233: cannam@233: class PluginInputDomainAdapter::Impl cannam@233: { cannam@233: public: cannam@233: Impl(Plugin *plugin, float inputSampleRate); cannam@233: ~Impl(); cannam@233: cannam@233: bool initialise(size_t channels, size_t stepSize, size_t blockSize); cannam@288: void reset(); cannam@233: cannam@233: size_t getPreferredStepSize() const; cannam@233: size_t getPreferredBlockSize() const; cannam@233: cannam@233: FeatureSet process(const float *const *inputBuffers, RealTime timestamp); cannam@288: cannam@288: void setProcessTimestampMethod(ProcessTimestampMethod m); cannam@288: ProcessTimestampMethod getProcessTimestampMethod() const; cannam@233: cannam@233: RealTime getTimestampAdjustment() const; cannam@233: Chris@317: WindowType getWindowType() const; Chris@317: void setWindowType(WindowType type); Chris@317: cannam@233: protected: cannam@233: Plugin *m_plugin; cannam@233: float m_inputSampleRate; cannam@233: int m_channels; cannam@288: int m_stepSize; cannam@233: int m_blockSize; cannam@233: float **m_freqbuf; Chris@501: Kiss::vamp_kiss_fft_scalar *m_ri; Chris@317: Chris@317: WindowType m_windowType; Chris@501: typedef Window W; Chris@444: W *m_window; cannam@233: cannam@288: ProcessTimestampMethod m_method; cannam@288: int m_processCount; cannam@289: float **m_shiftBuffers; cannam@288: Chris@501: Kiss::vamp_kiss_fftr_cfg m_cfg; Chris@501: Kiss::vamp_kiss_fft_cpx *m_cbuf; cannam@233: cannam@289: FeatureSet processShiftingTimestamp(const float *const *inputBuffers, RealTime timestamp); cannam@289: FeatureSet processShiftingData(const float *const *inputBuffers, RealTime timestamp); cannam@289: cannam@233: size_t makeBlockSizeAcceptable(size_t) const; Chris@317: Chris@444: W::WindowType convertType(WindowType t) const; cannam@233: }; cannam@233: cannam@233: PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) : cannam@233: PluginWrapper(plugin) cannam@233: { cannam@233: m_impl = new Impl(plugin, m_inputSampleRate); cannam@233: } cannam@233: cannam@233: PluginInputDomainAdapter::~PluginInputDomainAdapter() cannam@233: { cannam@233: delete m_impl; cannam@233: } cannam@233: cannam@233: bool cannam@233: PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@233: { cannam@233: return m_impl->initialise(channels, stepSize, blockSize); cannam@233: } cannam@233: cannam@288: void cannam@288: PluginInputDomainAdapter::reset() cannam@288: { cannam@288: m_impl->reset(); cannam@288: } cannam@288: cannam@233: Plugin::InputDomain cannam@233: PluginInputDomainAdapter::getInputDomain() const cannam@233: { cannam@233: return TimeDomain; cannam@233: } cannam@233: cannam@233: size_t cannam@233: PluginInputDomainAdapter::getPreferredStepSize() const cannam@233: { cannam@233: return m_impl->getPreferredStepSize(); cannam@233: } cannam@233: cannam@233: size_t cannam@233: PluginInputDomainAdapter::getPreferredBlockSize() const cannam@233: { cannam@233: return m_impl->getPreferredBlockSize(); cannam@233: } cannam@233: cannam@233: Plugin::FeatureSet cannam@233: PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp) cannam@233: { cannam@233: return m_impl->process(inputBuffers, timestamp); cannam@233: } cannam@233: cannam@288: void cannam@288: PluginInputDomainAdapter::setProcessTimestampMethod(ProcessTimestampMethod m) cannam@288: { cannam@288: m_impl->setProcessTimestampMethod(m); cannam@288: } cannam@288: cannam@288: PluginInputDomainAdapter::ProcessTimestampMethod cannam@288: PluginInputDomainAdapter::getProcessTimestampMethod() const cannam@288: { cannam@288: return m_impl->getProcessTimestampMethod(); cannam@288: } cannam@288: cannam@233: RealTime cannam@233: PluginInputDomainAdapter::getTimestampAdjustment() const cannam@233: { cannam@233: return m_impl->getTimestampAdjustment(); cannam@233: } cannam@233: Chris@317: PluginInputDomainAdapter::WindowType Chris@317: PluginInputDomainAdapter::getWindowType() const Chris@317: { Chris@317: return m_impl->getWindowType(); Chris@317: } Chris@317: Chris@317: void Chris@317: PluginInputDomainAdapter::setWindowType(WindowType w) Chris@317: { Chris@317: m_impl->setWindowType(w); Chris@317: } Chris@317: cannam@233: cannam@233: PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : cannam@233: m_plugin(plugin), cannam@233: m_inputSampleRate(inputSampleRate), cannam@233: m_channels(0), cannam@288: m_stepSize(0), cannam@233: m_blockSize(0), cannam@233: m_freqbuf(0), cannam@233: m_ri(0), Chris@317: m_windowType(HanningWindow), cannam@233: m_window(0), cannam@288: m_method(ShiftTimestamp), cannam@288: m_processCount(0), cannam@289: m_shiftBuffers(0), Chris@434: m_cfg(0), cannam@233: m_cbuf(0) cannam@233: { cannam@233: } cannam@233: cannam@233: PluginInputDomainAdapter::Impl::~Impl() cannam@233: { cannam@233: // the adapter will delete the plugin cannam@233: cannam@289: if (m_shiftBuffers) { cannam@289: for (int c = 0; c < m_channels; ++c) { cannam@289: delete[] m_shiftBuffers[c]; cannam@289: } cannam@289: delete[] m_shiftBuffers; cannam@289: } cannam@289: cannam@233: if (m_channels > 0) { cannam@233: for (int c = 0; c < m_channels; ++c) { cannam@233: delete[] m_freqbuf[c]; cannam@233: } cannam@233: delete[] m_freqbuf; Chris@435: delete[] m_ri; Chris@434: if (m_cfg) { Chris@501: Kiss::vamp_kiss_fftr_free(m_cfg); Chris@435: m_cfg = 0; Chris@434: delete[] m_cbuf; Chris@435: m_cbuf = 0; cannam@233: } Chris@317: delete m_window; cannam@233: } cannam@233: } cannam@233: cannam@233: // for some visual studii apparently cannam@233: #ifndef M_PI cannam@233: #define M_PI 3.14159265358979232846 cannam@233: #endif cannam@233: cannam@233: bool cannam@233: PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@233: { cannam@233: if (m_plugin->getInputDomain() == TimeDomain) { cannam@233: cannam@288: m_stepSize = int(stepSize); cannam@233: m_blockSize = int(blockSize); cannam@233: m_channels = int(channels); cannam@233: cannam@233: return m_plugin->initialise(channels, stepSize, blockSize); cannam@233: } cannam@233: cannam@233: if (blockSize < 2) { cannam@283: std::cerr << "ERROR: PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl; cannam@233: return false; cannam@233: } cannam@233: Chris@434: if (blockSize % 2) { Chris@434: std::cerr << "ERROR: PluginInputDomainAdapter::initialise: odd blocksize " << blockSize << " not supported" << std::endl; cannam@233: return false; cannam@233: } cannam@233: cannam@233: if (m_channels > 0) { cannam@233: for (int c = 0; c < m_channels; ++c) { cannam@233: delete[] m_freqbuf[c]; cannam@233: } cannam@233: delete[] m_freqbuf; Chris@435: delete[] m_ri; Chris@434: if (m_cfg) { Chris@501: Kiss::vamp_kiss_fftr_free(m_cfg); Chris@435: m_cfg = 0; Chris@434: delete[] m_cbuf; Chris@435: m_cbuf = 0; cannam@233: } Chris@317: delete m_window; cannam@233: } cannam@233: cannam@288: m_stepSize = int(stepSize); cannam@233: m_blockSize = int(blockSize); cannam@233: m_channels = int(channels); cannam@233: cannam@233: m_freqbuf = new float *[m_channels]; cannam@233: for (int c = 0; c < m_channels; ++c) { cannam@233: m_freqbuf[c] = new float[m_blockSize + 2]; cannam@233: } Chris@501: m_ri = new Kiss::vamp_kiss_fft_scalar[m_blockSize]; cannam@233: Chris@444: m_window = new W(convertType(m_windowType), m_blockSize); cannam@233: Chris@501: m_cfg = Kiss::vamp_kiss_fftr_alloc(m_blockSize, false, 0, 0); Chris@501: m_cbuf = new Kiss::vamp_kiss_fft_cpx[m_blockSize/2+1]; cannam@233: cannam@288: m_processCount = 0; cannam@288: Chris@435: return m_plugin->initialise(channels, stepSize, m_blockSize); cannam@233: } cannam@233: cannam@288: void cannam@288: PluginInputDomainAdapter::Impl::reset() cannam@288: { cannam@288: m_processCount = 0; cannam@288: m_plugin->reset(); cannam@288: } cannam@288: cannam@233: size_t cannam@233: PluginInputDomainAdapter::Impl::getPreferredStepSize() const cannam@233: { cannam@233: size_t step = m_plugin->getPreferredStepSize(); cannam@233: cannam@233: if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) { cannam@233: step = getPreferredBlockSize() / 2; cannam@233: } cannam@233: cannam@233: return step; cannam@233: } cannam@233: cannam@233: size_t cannam@233: PluginInputDomainAdapter::Impl::getPreferredBlockSize() const cannam@233: { cannam@233: size_t block = m_plugin->getPreferredBlockSize(); cannam@233: cannam@233: if (m_plugin->getInputDomain() == FrequencyDomain) { cannam@233: if (block == 0) { cannam@233: block = 1024; cannam@233: } else { cannam@233: block = makeBlockSizeAcceptable(block); cannam@233: } cannam@233: } cannam@233: cannam@233: return block; cannam@233: } cannam@233: cannam@233: size_t cannam@233: PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const cannam@233: { cannam@233: if (blockSize < 2) { cannam@233: cannam@283: std::cerr << "WARNING: PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl cannam@233: << "supported, increasing from " << blockSize << " to 2" << std::endl; cannam@233: blockSize = 2; Chris@434: Chris@434: } else if (blockSize % 2) { cannam@233: Chris@434: std::cerr << "WARNING: PluginInputDomainAdapter::initialise: odd blocksize not" << std::endl Chris@434: << "supported, increasing from " << blockSize << " to " << (blockSize+1) << std::endl; Chris@434: blockSize = blockSize+1; cannam@233: } cannam@233: cannam@233: return blockSize; cannam@233: } cannam@233: cannam@233: RealTime cannam@233: PluginInputDomainAdapter::Impl::getTimestampAdjustment() const cannam@233: { cannam@233: if (m_plugin->getInputDomain() == TimeDomain) { cannam@233: return RealTime::zeroTime; cannam@298: } else if (m_method == ShiftData || m_method == NoShift) { cannam@289: return RealTime::zeroTime; cannam@233: } else { cannam@233: return RealTime::frame2RealTime cannam@233: (m_blockSize/2, int(m_inputSampleRate + 0.5)); cannam@233: } cannam@233: } cannam@233: cannam@288: void cannam@288: PluginInputDomainAdapter::Impl::setProcessTimestampMethod(ProcessTimestampMethod m) cannam@288: { cannam@288: m_method = m; cannam@288: } cannam@288: cannam@288: PluginInputDomainAdapter::ProcessTimestampMethod cannam@288: PluginInputDomainAdapter::Impl::getProcessTimestampMethod() const cannam@288: { cannam@288: return m_method; cannam@288: } cannam@288: Chris@317: void Chris@317: PluginInputDomainAdapter::Impl::setWindowType(WindowType t) Chris@317: { Chris@317: if (m_windowType == t) return; Chris@317: m_windowType = t; Chris@317: if (m_window) { Chris@317: delete m_window; Chris@444: m_window = new W(convertType(m_windowType), m_blockSize); Chris@317: } Chris@317: } Chris@317: Chris@317: PluginInputDomainAdapter::WindowType Chris@317: PluginInputDomainAdapter::Impl::getWindowType() const Chris@317: { Chris@317: return m_windowType; Chris@317: } Chris@317: Chris@444: PluginInputDomainAdapter::Impl::W::WindowType Chris@317: PluginInputDomainAdapter::Impl::convertType(WindowType t) const Chris@317: { Chris@317: switch (t) { Chris@317: case RectangularWindow: Chris@444: return W::RectangularWindow; Chris@317: case BartlettWindow: Chris@444: return W::BartlettWindow; Chris@317: case HammingWindow: Chris@444: return W::HammingWindow; Chris@317: case HanningWindow: Chris@444: return W::HanningWindow; Chris@317: case BlackmanWindow: Chris@444: return W::BlackmanWindow; Chris@317: case NuttallWindow: Chris@444: return W::NuttallWindow; Chris@317: case BlackmanHarrisWindow: Chris@444: return W::BlackmanHarrisWindow; Chris@319: default: Chris@444: return W::HanningWindow; Chris@317: } Chris@317: } Chris@317: cannam@233: Plugin::FeatureSet cannam@233: PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers, cannam@233: RealTime timestamp) cannam@233: { cannam@233: if (m_plugin->getInputDomain() == TimeDomain) { cannam@233: return m_plugin->process(inputBuffers, timestamp); cannam@233: } cannam@233: cannam@298: if (m_method == ShiftTimestamp || m_method == NoShift) { cannam@289: return processShiftingTimestamp(inputBuffers, timestamp); cannam@289: } else { cannam@289: return processShiftingData(inputBuffers, timestamp); cannam@289: } cannam@289: } cannam@233: cannam@289: Plugin::FeatureSet cannam@289: PluginInputDomainAdapter::Impl::processShiftingTimestamp(const float *const *inputBuffers, cannam@289: RealTime timestamp) cannam@289: { Chris@421: unsigned int roundedRate = 1; Chris@421: if (m_inputSampleRate > 0.f) { Chris@421: roundedRate = (unsigned int)round(m_inputSampleRate); Chris@421: } Chris@421: cannam@298: if (m_method == ShiftTimestamp) { Chris@386: // we may need to add one nsec if timestamp + Chris@386: // getTimestampAdjustment() rounds down cannam@298: timestamp = timestamp + getTimestampAdjustment(); Chris@386: RealTime nsec(0, 1); Chris@421: if (RealTime::realTime2Frame(timestamp, roundedRate) < Chris@421: RealTime::realTime2Frame(timestamp + nsec, roundedRate)) { Chris@386: timestamp = timestamp + nsec; Chris@386: } cannam@298: } cannam@233: cannam@233: for (int c = 0; c < m_channels; ++c) { cannam@233: Chris@317: m_window->cut(inputBuffers[c], m_ri); cannam@233: cannam@233: for (int i = 0; i < m_blockSize/2; ++i) { cannam@233: // FFT shift Chris@501: Kiss::vamp_kiss_fft_scalar value = m_ri[i]; cannam@233: m_ri[i] = m_ri[i + m_blockSize/2]; cannam@233: m_ri[i + m_blockSize/2] = value; cannam@233: } cannam@233: Chris@501: Kiss::vamp_kiss_fftr(m_cfg, m_ri, m_cbuf); Chris@434: cannam@233: for (int i = 0; i <= m_blockSize/2; ++i) { Chris@470: m_freqbuf[c][i * 2] = float(m_cbuf[i].r); Chris@470: m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i); cannam@233: } cannam@233: } cannam@233: cannam@289: return m_plugin->process(m_freqbuf, timestamp); cannam@288: } cannam@288: cannam@288: Plugin::FeatureSet cannam@289: PluginInputDomainAdapter::Impl::processShiftingData(const float *const *inputBuffers, cannam@289: RealTime timestamp) cannam@288: { cannam@289: if (m_processCount == 0) { cannam@289: if (!m_shiftBuffers) { cannam@289: m_shiftBuffers = new float *[m_channels]; cannam@289: for (int c = 0; c < m_channels; ++c) { cannam@289: m_shiftBuffers[c] = new float[m_blockSize + m_blockSize/2]; cannam@289: } cannam@289: } cannam@289: for (int c = 0; c < m_channels; ++c) { cannam@289: for (int i = 0; i < m_blockSize + m_blockSize/2; ++i) { cannam@289: m_shiftBuffers[c][i] = 0.f; cannam@289: } cannam@289: } cannam@289: } cannam@289: cannam@289: for (int c = 0; c < m_channels; ++c) { cannam@289: for (int i = m_stepSize; i < m_blockSize + m_blockSize/2; ++i) { cannam@289: m_shiftBuffers[c][i - m_stepSize] = m_shiftBuffers[c][i]; cannam@289: } cannam@289: for (int i = 0; i < m_blockSize; ++i) { cannam@289: m_shiftBuffers[c][i + m_blockSize/2] = inputBuffers[c][i]; cannam@289: } cannam@289: } cannam@289: cannam@289: for (int c = 0; c < m_channels; ++c) { cannam@289: Chris@317: m_window->cut(m_shiftBuffers[c], m_ri); cannam@289: cannam@289: for (int i = 0; i < m_blockSize/2; ++i) { cannam@289: // FFT shift Chris@501: Kiss::vamp_kiss_fft_scalar value = m_ri[i]; cannam@289: m_ri[i] = m_ri[i + m_blockSize/2]; cannam@289: m_ri[i + m_blockSize/2] = value; cannam@289: } cannam@289: Chris@501: Kiss::vamp_kiss_fftr(m_cfg, m_ri, m_cbuf); Chris@434: cannam@289: for (int i = 0; i <= m_blockSize/2; ++i) { Chris@470: m_freqbuf[c][i * 2] = float(m_cbuf[i].r); Chris@470: m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i); cannam@289: } cannam@289: } cannam@289: cannam@289: ++m_processCount; cannam@289: cannam@289: return m_plugin->process(m_freqbuf, timestamp); cannam@233: } cannam@233: cannam@233: } cannam@233: cannam@233: } cannam@233: cannam@263: _VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.cpp) cannam@263: