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: cannam@233: cannam@233: /** cannam@233: * If you want to compile using FFTW instead of the built-in FFT cannam@233: * implementation for the PluginInputDomainAdapter, define HAVE_FFTW3 cannam@233: * in the Makefile. cannam@233: * cannam@233: * Be aware that FFTW is licensed under the GPL -- unlike this SDK, cannam@233: * which is provided under a more liberal BSD license in order to cannam@233: * permit use in closed source applications. The use of FFTW would cannam@233: * mean that your code would need to be licensed under the GPL as cannam@233: * well. Do not define this symbol unless you understand and accept cannam@233: * the implications of this. cannam@233: * cannam@233: * Parties such as Linux distribution packagers who redistribute this cannam@233: * SDK for use in other programs should _not_ define this symbol, as cannam@233: * it would change the effective licensing terms under which the SDK cannam@233: * was available to third party developers. cannam@233: * cannam@233: * The default is not to use FFTW, and to use the built-in FFT instead. cannam@233: * cannam@233: * Note: The FFTW code uses FFTW_MEASURE, and so will perform badly on cannam@233: * its first invocation unless the host has saved and restored FFTW cannam@233: * wisdom (see the FFTW documentation). cannam@233: */ cannam@233: #ifdef HAVE_FFTW3 cannam@233: #include Chris@356: #warning "Compiling with FFTW3 support will result in a GPL binary" Chris@337: #else Chris@337: #include "../vamp-sdk/FFTimpl.cpp" cannam@233: #endif cannam@233: cannam@233: cannam@263: _VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.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; cannam@233: cannam@233: double *m_ri; Chris@317: Chris@317: WindowType m_windowType; Chris@317: Window *m_window; cannam@233: cannam@288: ProcessTimestampMethod m_method; cannam@288: int m_processCount; cannam@289: float **m_shiftBuffers; cannam@288: cannam@233: #ifdef HAVE_FFTW3 cannam@233: fftw_plan m_plan; cannam@233: fftw_complex *m_cbuf; cannam@233: #else cannam@233: double *m_ro; cannam@233: double *m_io; cannam@233: #endif 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@317: Window::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), cannam@233: #ifdef HAVE_FFTW3 cannam@233: m_plan(0), cannam@233: m_cbuf(0) cannam@233: #else cannam@233: m_ro(0), cannam@233: m_io(0) cannam@233: #endif 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; cannam@233: #ifdef HAVE_FFTW3 cannam@233: if (m_plan) { cannam@233: fftw_destroy_plan(m_plan); cannam@233: fftw_free(m_ri); cannam@233: fftw_free(m_cbuf); cannam@233: m_plan = 0; cannam@233: } cannam@233: #else cannam@233: delete[] m_ri; cannam@233: delete[] m_ro; cannam@233: delete[] m_io; cannam@233: #endif Chris@317: 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: cannam@233: if (blockSize & (blockSize-1)) { cannam@283: std::cerr << "ERROR: PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << 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; cannam@233: #ifdef HAVE_FFTW3 cannam@233: if (m_plan) { cannam@233: fftw_destroy_plan(m_plan); cannam@233: fftw_free(m_ri); cannam@233: fftw_free(m_cbuf); cannam@233: m_plan = 0; cannam@233: } cannam@233: #else cannam@233: delete[] m_ri; cannam@233: delete[] m_ro; cannam@233: delete[] m_io; cannam@233: #endif 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: } cannam@233: Chris@317: m_window = new Window(convertType(m_windowType), m_blockSize); cannam@233: cannam@233: #ifdef HAVE_FFTW3 cannam@233: m_ri = (double *)fftw_malloc(blockSize * sizeof(double)); cannam@233: m_cbuf = (fftw_complex *)fftw_malloc((blockSize/2 + 1) * sizeof(fftw_complex)); cannam@233: m_plan = fftw_plan_dft_r2c_1d(blockSize, m_ri, m_cbuf, FFTW_MEASURE); cannam@233: #else cannam@233: m_ri = new double[m_blockSize]; cannam@233: m_ro = new double[m_blockSize]; cannam@233: m_io = new double[m_blockSize]; cannam@233: #endif cannam@233: cannam@288: m_processCount = 0; cannam@288: cannam@233: return m_plugin->initialise(channels, stepSize, 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; cannam@233: cannam@233: } else if (blockSize & (blockSize-1)) { cannam@233: cannam@233: #ifdef HAVE_FFTW3 cannam@233: // not an issue with FFTW cannam@233: #else cannam@233: cannam@233: // not a power of two, can't handle that with our built-in FFT cannam@233: // implementation cannam@233: cannam@233: size_t nearest = blockSize; cannam@233: size_t power = 0; cannam@233: while (nearest > 1) { cannam@233: nearest >>= 1; cannam@233: ++power; cannam@233: } cannam@233: nearest = 1; cannam@233: while (power) { cannam@233: nearest <<= 1; cannam@233: --power; cannam@233: } cannam@233: cannam@233: if (blockSize - nearest > (nearest*2) - blockSize) { cannam@233: nearest = nearest*2; cannam@233: } cannam@233: cannam@283: std::cerr << "WARNING: PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl; cannam@233: blockSize = nearest; cannam@233: cannam@233: #endif 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@317: m_window = new Window(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@317: Window::WindowType Chris@317: PluginInputDomainAdapter::Impl::convertType(WindowType t) const Chris@317: { Chris@317: switch (t) { Chris@317: case RectangularWindow: Chris@317: return Window::RectangularWindow; Chris@317: case BartlettWindow: Chris@317: return Window::BartlettWindow; Chris@317: case HammingWindow: Chris@317: return Window::HammingWindow; Chris@317: case HanningWindow: Chris@317: return Window::HanningWindow; Chris@317: case BlackmanWindow: Chris@317: return Window::BlackmanWindow; Chris@317: case NuttallWindow: Chris@317: return Window::NuttallWindow; Chris@317: case BlackmanHarrisWindow: Chris@317: return Window::BlackmanHarrisWindow; Chris@319: default: Chris@319: return Window::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: { cannam@298: if (m_method == ShiftTimestamp) { cannam@298: timestamp = timestamp + getTimestampAdjustment(); 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 cannam@233: double 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: cannam@233: #ifdef HAVE_FFTW3 cannam@233: fftw_execute(m_plan); cannam@233: cannam@233: for (int i = 0; i <= m_blockSize/2; ++i) { cannam@233: m_freqbuf[c][i * 2] = float(m_cbuf[i][0]); cannam@233: m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]); cannam@233: } cannam@233: #else cannam@233: fft(m_blockSize, false, m_ri, 0, m_ro, m_io); cannam@233: cannam@233: for (int i = 0; i <= m_blockSize/2; ++i) { cannam@233: m_freqbuf[c][i * 2] = float(m_ro[i]); cannam@233: m_freqbuf[c][i * 2 + 1] = float(m_io[i]); cannam@233: } cannam@233: #endif 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 cannam@289: double 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: cannam@289: #ifdef HAVE_FFTW3 cannam@289: fftw_execute(m_plan); cannam@289: cannam@289: for (int i = 0; i <= m_blockSize/2; ++i) { cannam@289: m_freqbuf[c][i * 2] = float(m_cbuf[i][0]); cannam@289: m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]); cannam@289: } cannam@289: #else cannam@289: fft(m_blockSize, false, m_ri, 0, m_ro, m_io); cannam@289: cannam@289: for (int i = 0; i <= m_blockSize/2; ++i) { cannam@289: m_freqbuf[c][i * 2] = float(m_ro[i]); cannam@289: m_freqbuf[c][i * 2 + 1] = float(m_io[i]); cannam@289: } cannam@289: #endif 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: #ifndef HAVE_FFTW3 cannam@233: cannam@233: #endif cannam@233: cannam@233: } cannam@233: cannam@233: } cannam@233: cannam@263: _VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.cpp) cannam@263: