cannam@56: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@56: cannam@56: /* cannam@56: Vamp cannam@56: cannam@56: An API for audio analysis and feature extraction plugins. cannam@56: cannam@56: Centre for Digital Music, Queen Mary, University of London. cannam@56: Copyright 2006 Chris Cannam. cannam@56: cannam@56: Permission is hereby granted, free of charge, to any person cannam@56: obtaining a copy of this software and associated documentation cannam@56: files (the "Software"), to deal in the Software without cannam@56: restriction, including without limitation the rights to use, copy, cannam@56: modify, merge, publish, distribute, sublicense, and/or sell copies cannam@56: of the Software, and to permit persons to whom the Software is cannam@56: furnished to do so, subject to the following conditions: cannam@56: cannam@56: The above copyright notice and this permission notice shall be cannam@56: included in all copies or substantial portions of the Software. cannam@56: cannam@56: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, cannam@56: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF cannam@56: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND cannam@56: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR cannam@56: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF cannam@56: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION cannam@56: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cannam@56: cannam@56: Except as contained in this notice, the names of the Centre for cannam@56: Digital Music; Queen Mary, University of London; and Chris Cannam cannam@56: shall not be used in advertising or otherwise to promote the sale, cannam@56: use or other dealings in this Software without prior written cannam@56: authorization. cannam@56: */ cannam@56: cannam@56: #include "PluginInputDomainAdapter.h" cannam@56: cannam@56: #include cannam@56: cannam@56: namespace Vamp { cannam@56: cannam@56: PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) : cannam@57: PluginWrapper(plugin), cannam@56: m_channels(0), cannam@56: m_blockSize(0), cannam@56: m_freqbuf(0) cannam@56: { cannam@56: } cannam@56: cannam@56: PluginInputDomainAdapter::~PluginInputDomainAdapter() cannam@56: { cannam@56: } cannam@56: cannam@56: bool cannam@56: PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize) cannam@56: { cannam@56: //!!! complain and die if blocksize is not a power of 2 cannam@56: cannam@56: if (m_plugin->getInputDomain() == FrequencyDomain) { cannam@56: if (m_channels > 0) { cannam@56: for (size_t c = 0; c < m_channels; ++c) { cannam@56: delete[] m_freqbuf[c]; cannam@56: } cannam@56: delete[] m_freqbuf; cannam@56: delete[] m_ri; cannam@56: delete[] m_ro; cannam@56: delete[] m_io; cannam@56: } cannam@56: } cannam@56: cannam@56: m_channels = channels; cannam@56: m_blockSize = blockSize; cannam@56: cannam@56: if (m_plugin->getInputDomain() == FrequencyDomain) { cannam@56: m_freqbuf = new float *[m_channels]; cannam@56: for (size_t c = 0; c < m_channels; ++c) { cannam@56: m_freqbuf[c] = new float[m_blockSize + 2]; cannam@56: } cannam@56: m_ri = new double[m_blockSize]; cannam@56: m_ro = new double[m_blockSize]; cannam@56: m_io = new double[m_blockSize]; cannam@56: } cannam@56: cannam@56: return m_plugin->initialise(channels, stepSize, blockSize); cannam@56: } cannam@56: cannam@57: Plugin::InputDomain cannam@57: PluginInputDomainAdapter::getInputDomain() const cannam@56: { cannam@57: return TimeDomain; cannam@56: } cannam@56: cannam@56: size_t cannam@56: PluginInputDomainAdapter::getPreferredStepSize() const cannam@56: { cannam@56: size_t step = m_plugin->getPreferredStepSize(); cannam@56: cannam@56: if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) { cannam@56: step = getPreferredBlockSize() / 2; cannam@56: } cannam@56: cannam@56: return step; cannam@56: } cannam@56: cannam@56: size_t cannam@56: PluginInputDomainAdapter::getPreferredBlockSize() const cannam@56: { cannam@56: size_t block = m_plugin->getPreferredBlockSize(); cannam@56: cannam@56: if (block == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) { cannam@56: block = 1024; cannam@56: } cannam@56: cannam@56: return block; cannam@56: } cannam@56: cannam@56: Plugin::FeatureSet cannam@56: PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp) cannam@56: { cannam@56: if (m_plugin->getInputDomain() == TimeDomain) { cannam@56: return m_plugin->process(inputBuffers, timestamp); cannam@56: } cannam@56: cannam@56: for (size_t c = 0; c < m_channels; ++c) { cannam@56: cannam@56: for (size_t i = 0; i < m_blockSize; ++i) { cannam@56: // Hanning window cannam@56: m_ri[i] = double(inputBuffers[c][i]) cannam@56: * (0.50 - 0.50 * cos((2 * M_PI * i) cannam@56: / m_blockSize)); cannam@56: } cannam@56: cannam@56: for (size_t i = 0; i < m_blockSize/2; ++i) { cannam@56: // FFT shift cannam@56: double value = m_ri[i]; cannam@56: m_ri[i] = m_ri[i + m_blockSize/2]; cannam@56: m_ri[i + m_blockSize/2] = value; cannam@56: } cannam@56: cannam@56: fft(m_blockSize, false, m_ri, 0, m_ro, m_io); cannam@56: cannam@56: for (size_t i = 0; i < m_blockSize/2; ++i) { cannam@56: m_freqbuf[c][i * 2] = m_ro[i]; cannam@56: m_freqbuf[c][i * 2 + 1] = m_io[i]; cannam@56: } cannam@56: } cannam@56: cannam@56: //!!! do we want to adjust the timestamp or anything so as to cannam@56: // effectively centre the frame? cannam@56: cannam@56: return m_plugin->process(m_freqbuf, timestamp); cannam@56: } cannam@56: cannam@56: void cannam@56: PluginInputDomainAdapter::fft(unsigned int n, bool inverse, cannam@56: double *ri, double *ii, double *ro, double *io) cannam@56: { cannam@56: if (!ri || !ro || !io) return; cannam@56: cannam@56: unsigned int bits; cannam@56: unsigned int i, j, k, m; cannam@56: unsigned int blockSize, blockEnd; cannam@56: cannam@56: double tr, ti; cannam@56: cannam@56: if (n < 2) return; cannam@56: if (n & (n-1)) return; cannam@56: cannam@56: double angle = 2.0 * M_PI; cannam@56: if (inverse) angle = -angle; cannam@56: cannam@56: for (i = 0; ; ++i) { cannam@56: if (n & (1 << i)) { cannam@56: bits = i; cannam@56: break; cannam@56: } cannam@56: } cannam@56: cannam@56: static unsigned int tableSize = 0; cannam@56: static int *table = 0; cannam@56: cannam@56: if (tableSize != n) { cannam@56: cannam@56: delete[] table; cannam@56: cannam@56: table = new int[n]; cannam@56: cannam@56: for (i = 0; i < n; ++i) { cannam@56: cannam@56: m = i; cannam@56: cannam@56: for (j = k = 0; j < bits; ++j) { cannam@56: k = (k << 1) | (m & 1); cannam@56: m >>= 1; cannam@56: } cannam@56: cannam@56: table[i] = k; cannam@56: } cannam@56: cannam@56: tableSize = n; cannam@56: } cannam@56: cannam@56: if (ii) { cannam@56: for (i = 0; i < n; ++i) { cannam@56: ro[table[i]] = ri[i]; cannam@56: io[table[i]] = ii[i]; cannam@56: } cannam@56: } else { cannam@56: for (i = 0; i < n; ++i) { cannam@56: ro[table[i]] = ri[i]; cannam@56: io[table[i]] = 0.0; cannam@56: } cannam@56: } cannam@56: cannam@56: blockEnd = 1; cannam@56: cannam@56: for (blockSize = 2; blockSize <= n; blockSize <<= 1) { cannam@56: cannam@56: double delta = angle / (double)blockSize; cannam@56: double sm2 = -sin(-2 * delta); cannam@56: double sm1 = -sin(-delta); cannam@56: double cm2 = cos(-2 * delta); cannam@56: double cm1 = cos(-delta); cannam@56: double w = 2 * cm1; cannam@56: double ar[3], ai[3]; cannam@56: cannam@56: for (i = 0; i < n; i += blockSize) { cannam@56: cannam@56: ar[2] = cm2; cannam@56: ar[1] = cm1; cannam@56: cannam@56: ai[2] = sm2; cannam@56: ai[1] = sm1; cannam@56: cannam@56: for (j = i, m = 0; m < blockEnd; j++, m++) { cannam@56: cannam@56: ar[0] = w * ar[1] - ar[2]; cannam@56: ar[2] = ar[1]; cannam@56: ar[1] = ar[0]; cannam@56: cannam@56: ai[0] = w * ai[1] - ai[2]; cannam@56: ai[2] = ai[1]; cannam@56: ai[1] = ai[0]; cannam@56: cannam@56: k = j + blockEnd; cannam@56: tr = ar[0] * ro[k] - ai[0] * io[k]; cannam@56: ti = ar[0] * io[k] + ai[0] * ro[k]; cannam@56: cannam@56: ro[k] = ro[j] - tr; cannam@56: io[k] = io[j] - ti; cannam@56: cannam@56: ro[j] += tr; cannam@56: io[j] += ti; cannam@56: } cannam@56: } cannam@56: cannam@56: blockEnd = blockSize; cannam@56: } cannam@56: cannam@56: if (inverse) { cannam@56: cannam@56: double denom = (double)n; cannam@56: cannam@56: for (i = 0; i < n; i++) { cannam@56: ro[i] /= denom; cannam@56: io[i] /= denom; cannam@56: } cannam@56: } cannam@56: } cannam@56: cannam@56: cannam@56: } cannam@56: