# HG changeset patch # User Chris Cannam # Date 1308667250 -3600 # Node ID 5cb29843576531b25dc5484ff95cbf0e17884f9c # Parent c10fb3b3d02998e122e1ace383aea417ec662f08 Add support for changing window shape in PluginInputDomainAdapter diff -r c10fb3b3d029 -r 5cb298435765 CHANGELOG --- a/CHANGELOG Tue Apr 05 13:31:16 2011 +0100 +++ b/CHANGELOG Tue Jun 21 15:40:50 2011 +0100 @@ -1,3 +1,7 @@ + +Version 2.3 (minor feature release): + + * Add window type property to PluginInputDomainAdapter Version 2.2.1, 2011-04-05 (maintenance release): diff -r c10fb3b3d029 -r 5cb298435765 src/vamp-hostsdk/PluginInputDomainAdapter.cpp --- a/src/vamp-hostsdk/PluginInputDomainAdapter.cpp Tue Apr 05 13:31:16 2011 +0100 +++ b/src/vamp-hostsdk/PluginInputDomainAdapter.cpp Tue Jun 21 15:40:50 2011 +0100 @@ -41,6 +41,8 @@ #include +#include "Window.h" + /** * If you want to compile using FFTW instead of the built-in FFT @@ -95,6 +97,9 @@ RealTime getTimestampAdjustment() const; + WindowType getWindowType() const; + void setWindowType(WindowType type); + protected: Plugin *m_plugin; float m_inputSampleRate; @@ -104,7 +109,9 @@ float **m_freqbuf; double *m_ri; - double *m_window; + + WindowType m_windowType; + Window *m_window; ProcessTimestampMethod m_method; int m_processCount; @@ -124,6 +131,8 @@ FeatureSet processShiftingData(const float *const *inputBuffers, RealTime timestamp); size_t makeBlockSizeAcceptable(size_t) const; + + Window::WindowType convertType(WindowType t) const; }; PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) : @@ -191,6 +200,18 @@ return m_impl->getTimestampAdjustment(); } +PluginInputDomainAdapter::WindowType +PluginInputDomainAdapter::getWindowType() const +{ + return m_impl->getWindowType(); +} + +void +PluginInputDomainAdapter::setWindowType(WindowType w) +{ + m_impl->setWindowType(w); +} + PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) : m_plugin(plugin), @@ -200,6 +221,7 @@ m_blockSize(0), m_freqbuf(0), m_ri(0), + m_windowType(HanningWindow), m_window(0), m_method(ShiftTimestamp), m_processCount(0), @@ -242,7 +264,8 @@ delete[] m_ro; delete[] m_io; #endif - delete[] m_window; + + delete m_window; } } @@ -290,7 +313,7 @@ delete[] m_ro; delete[] m_io; #endif - delete[] m_window; + delete m_window; } m_stepSize = int(stepSize); @@ -301,12 +324,8 @@ for (int c = 0; c < m_channels; ++c) { m_freqbuf[c] = new float[m_blockSize + 2]; } - m_window = new double[m_blockSize]; - for (int i = 0; i < m_blockSize; ++i) { - // Hanning window - m_window[i] = (0.50 - 0.50 * cos((2.0 * M_PI * i) / m_blockSize)); - } + m_window = new Window(convertType(m_windowType), m_blockSize); #ifdef HAVE_FFTW3 m_ri = (double *)fftw_malloc(blockSize * sizeof(double)); @@ -426,6 +445,44 @@ return m_method; } +void +PluginInputDomainAdapter::Impl::setWindowType(WindowType t) +{ + if (m_windowType == t) return; + m_windowType = t; + if (m_window) { + delete m_window; + m_window = new Window(convertType(m_windowType), m_blockSize); + } +} + +PluginInputDomainAdapter::WindowType +PluginInputDomainAdapter::Impl::getWindowType() const +{ + return m_windowType; +} + +Window::WindowType +PluginInputDomainAdapter::Impl::convertType(WindowType t) const +{ + switch (t) { + case RectangularWindow: + return Window::RectangularWindow; + case BartlettWindow: + return Window::BartlettWindow; + case HammingWindow: + return Window::HammingWindow; + case HanningWindow: + return Window::HanningWindow; + case BlackmanWindow: + return Window::BlackmanWindow; + case NuttallWindow: + return Window::NuttallWindow; + case BlackmanHarrisWindow: + return Window::BlackmanHarrisWindow; + } +} + Plugin::FeatureSet PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp) @@ -451,9 +508,7 @@ for (int c = 0; c < m_channels; ++c) { - for (int i = 0; i < m_blockSize; ++i) { - m_ri[i] = double(inputBuffers[c][i]) * m_window[i]; - } + m_window->cut(inputBuffers[c], m_ri); for (int i = 0; i < m_blockSize/2; ++i) { // FFT shift @@ -511,9 +566,7 @@ for (int c = 0; c < m_channels; ++c) { - for (int i = 0; i < m_blockSize; ++i) { - m_ri[i] = double(m_shiftBuffers[c][i]) * m_window[i]; - } + m_window->cut(m_shiftBuffers[c], m_ri); for (int i = 0; i < m_blockSize/2; ++i) { // FFT shift diff -r c10fb3b3d029 -r 5cb298435765 src/vamp-hostsdk/Window.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vamp-hostsdk/Window.h Tue Jun 21 15:40:50 2011 +0100 @@ -0,0 +1,167 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006-2011 Chris Cannam and QMUL. + + 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 AUTHORS 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 _WINDOW_H_ +#define _WINDOW_H_ + +#include + +#include +#include + +_VAMP_SDK_HOSTSPACE_BEGIN(Window.h) + +template +class Window +{ +public: + enum WindowType { + RectangularWindow, + BartlettWindow, + HammingWindow, + HanningWindow, + BlackmanWindow, + NuttallWindow, + BlackmanHarrisWindow + }; + + /** + * Construct a windower of the given type. + */ + Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); } + Window(const Window &w) : m_type(w.m_type), m_size(w.m_size) { encache(); } + Window &operator=(const Window &w) { + if (&w == this) return *this; + m_type = w.m_type; + m_size = w.m_size; + encache(); + return *this; + } + virtual ~Window() { delete[] m_cache; } + + void cut(T *src) const { cut(src, src); } + void cut(T *src, T *dst) const { + for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i]; + } + template + void cut(T0 *src, T *dst) const { + for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i]; + } + + T getArea() { return m_area; } + T getValue(size_t i) { return m_cache[i]; } + + WindowType getType() const { return m_type; } + size_t getSize() const { return m_size; } + +protected: + WindowType m_type; + size_t m_size; + T *m_cache; + T m_area; + + void encache(); + void cosinewin(T *, T, T, T, T); +}; + +template +void Window::encache() +{ + int n = int(m_size); + T *mult = new T[n]; + int i; + for (i = 0; i < n; ++i) mult[i] = 1.0; + + switch (m_type) { + + case RectangularWindow: + for (i = 0; i < n; ++i) { + mult[i] *= 0.5; + } + break; + + case BartlettWindow: + for (i = 0; i < n/2; ++i) { + mult[i] *= (i / T(n/2)); + mult[i + n/2] *= (1.0 - (i / T(n/2))); + } + break; + + case HammingWindow: + cosinewin(mult, 0.54, 0.46, 0.0, 0.0); + break; + + case HanningWindow: + cosinewin(mult, 0.50, 0.50, 0.0, 0.0); + break; + + case BlackmanWindow: + cosinewin(mult, 0.42, 0.50, 0.08, 0.0); + break; + + case NuttallWindow: + cosinewin(mult, 0.3635819, 0.4891775, 0.1365995, 0.0106411); + break; + + case BlackmanHarrisWindow: + cosinewin(mult, 0.35875, 0.48829, 0.14128, 0.01168); + break; + } + + m_cache = mult; + + m_area = 0; + for (int i = 0; i < n; ++i) { + m_area += m_cache[i]; + } + m_area /= n; +} + +template +void Window::cosinewin(T *mult, T a0, T a1, T a2, T a3) +{ + int n = int(m_size); + for (int i = 0; i < n; ++i) { + mult[i] *= (a0 + - a1 * cos((2 * M_PI * i) / n) + + a2 * cos((4 * M_PI * i) / n) + - a3 * cos((6 * M_PI * i) / n)); + } +} + +_VAMP_SDK_HOSTSPACE_END(Window.h) + +#endif diff -r c10fb3b3d029 -r 5cb298435765 vamp-hostsdk/PluginInputDomainAdapter.h --- a/vamp-hostsdk/PluginInputDomainAdapter.h Tue Apr 05 13:31:16 2011 +0100 +++ b/vamp-hostsdk/PluginInputDomainAdapter.h Tue Jun 21 15:40:50 2011 +0100 @@ -54,13 +54,14 @@ * it. This permits a host to use time- and frequency-domain plugins * interchangeably without needing to handle the conversion itself. * - * This adapter uses a basic Hanning windowed FFT that supports - * power-of-two block sizes only. If a frequency domain plugin - * requests a non-power-of-two blocksize, the adapter will adjust it - * to a nearby power of two instead. Thus, getPreferredBlockSize() - * will always return a power of two if the wrapped plugin is a - * frequency domain one. If the plugin doesn't accept the adjusted - * power of two block size, initialise() will fail. + * This adapter uses a basic windowed FFT (using Hann window by + * default) that supports power-of-two block sizes only. If a + * frequency domain plugin requests a non-power-of-two blocksize, the + * adapter will adjust it to a nearby power of two instead. Thus, + * getPreferredBlockSize() will always return a power of two if the + * wrapped plugin is a frequency domain one. If the plugin doesn't + * accept the adjusted power of two block size, initialise() will + * fail. * * The adapter provides no way for the host to discover whether the * underlying plugin is actually a time or frequency domain plugin @@ -71,6 +72,10 @@ * to be the fastest available: a host can usually do better if it * cares enough. * + * The window shape for the FFT frame can be set using setWindowType + * and the current shape retrieved using getWindowType. (This was + * added in v2.3 of the SDK.) + * * In every respect other than its input domain handling, the * PluginInputDomainAdapter behaves identically to the plugin that it * wraps. The wrapped plugin will be deleted when the wrapper is @@ -184,6 +189,39 @@ */ RealTime getTimestampAdjustment() const; + /** + * The set of supported window shapes. + */ + enum WindowType { + + RectangularWindow = 0, + + BartlettWindow = 1, /// synonym for RectangularWindow + TriangularWindow = 1, /// synonym for BartlettWindow + + HammingWindow = 2, + + HanningWindow = 3, /// synonym for HannWindow + HannWindow = 3, /// synonym for HanningWindow + + BlackmanWindow = 4, + + NuttallWindow = 7, + + BlackmanHarrisWindow = 8 + }; + + /** + * Return the current window shape. The default is HanningWindow. + */ + WindowType getWindowType() const; + + /** + * Set the current window shape. + */ + void setWindowType(WindowType type); + + protected: class Impl; Impl *m_impl;