annotate vamp-sdk/hostext/PluginInputDomainAdapter.cpp @ 63:c747d9b97af3 host-factory-stuff

* documentation
author cannam
date Fri, 01 Jun 2007 15:04:19 +0000
parents 97c5ac99d725
children
rev   line source
cannam@56 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@56 2
cannam@56 3 /*
cannam@56 4 Vamp
cannam@56 5
cannam@56 6 An API for audio analysis and feature extraction plugins.
cannam@56 7
cannam@56 8 Centre for Digital Music, Queen Mary, University of London.
cannam@56 9 Copyright 2006 Chris Cannam.
cannam@56 10
cannam@56 11 Permission is hereby granted, free of charge, to any person
cannam@56 12 obtaining a copy of this software and associated documentation
cannam@56 13 files (the "Software"), to deal in the Software without
cannam@56 14 restriction, including without limitation the rights to use, copy,
cannam@56 15 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@56 16 of the Software, and to permit persons to whom the Software is
cannam@56 17 furnished to do so, subject to the following conditions:
cannam@56 18
cannam@56 19 The above copyright notice and this permission notice shall be
cannam@56 20 included in all copies or substantial portions of the Software.
cannam@56 21
cannam@56 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@56 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@56 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@56 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@56 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@56 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@56 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@56 29
cannam@56 30 Except as contained in this notice, the names of the Centre for
cannam@56 31 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@56 32 shall not be used in advertising or otherwise to promote the sale,
cannam@56 33 use or other dealings in this Software without prior written
cannam@56 34 authorization.
cannam@56 35 */
cannam@56 36
cannam@56 37 #include "PluginInputDomainAdapter.h"
cannam@56 38
cannam@56 39 #include <cmath>
cannam@56 40
cannam@56 41 namespace Vamp {
cannam@56 42
cannam@59 43 namespace HostExt {
cannam@59 44
cannam@56 45 PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
cannam@57 46 PluginWrapper(plugin),
cannam@56 47 m_channels(0),
cannam@56 48 m_blockSize(0),
cannam@56 49 m_freqbuf(0)
cannam@56 50 {
cannam@56 51 }
cannam@56 52
cannam@56 53 PluginInputDomainAdapter::~PluginInputDomainAdapter()
cannam@56 54 {
cannam@56 55 }
cannam@56 56
cannam@56 57 bool
cannam@56 58 PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
cannam@56 59 {
cannam@61 60 if (m_plugin->getInputDomain() == TimeDomain) {
cannam@56 61
cannam@61 62 m_blockSize = blockSize;
cannam@61 63 m_channels = channels;
cannam@61 64
cannam@61 65 return m_plugin->initialise(channels, stepSize, blockSize);
cannam@56 66 }
cannam@56 67
cannam@61 68 if (blockSize < 2) {
cannam@61 69 std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
cannam@61 70 return false;
cannam@61 71 }
cannam@61 72
cannam@61 73 if (blockSize & (blockSize-1)) {
cannam@61 74 std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported" << std::endl;
cannam@61 75 return false;
cannam@61 76 }
cannam@61 77
cannam@61 78 if (m_channels > 0) {
cannam@61 79 for (size_t c = 0; c < m_channels; ++c) {
cannam@61 80 delete[] m_freqbuf[c];
cannam@61 81 }
cannam@61 82 delete[] m_freqbuf;
cannam@61 83 delete[] m_ri;
cannam@61 84 delete[] m_ro;
cannam@61 85 delete[] m_io;
cannam@61 86 }
cannam@61 87
cannam@61 88 m_blockSize = blockSize;
cannam@56 89 m_channels = channels;
cannam@56 90
cannam@61 91 m_freqbuf = new float *[m_channels];
cannam@61 92 for (size_t c = 0; c < m_channels; ++c) {
cannam@61 93 m_freqbuf[c] = new float[m_blockSize + 2];
cannam@56 94 }
cannam@61 95 m_ri = new double[m_blockSize];
cannam@61 96 m_ro = new double[m_blockSize];
cannam@61 97 m_io = new double[m_blockSize];
cannam@56 98
cannam@56 99 return m_plugin->initialise(channels, stepSize, blockSize);
cannam@56 100 }
cannam@56 101
cannam@57 102 Plugin::InputDomain
cannam@57 103 PluginInputDomainAdapter::getInputDomain() const
cannam@56 104 {
cannam@57 105 return TimeDomain;
cannam@56 106 }
cannam@56 107
cannam@56 108 size_t
cannam@56 109 PluginInputDomainAdapter::getPreferredStepSize() const
cannam@56 110 {
cannam@56 111 size_t step = m_plugin->getPreferredStepSize();
cannam@56 112
cannam@56 113 if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
cannam@56 114 step = getPreferredBlockSize() / 2;
cannam@56 115 }
cannam@56 116
cannam@56 117 return step;
cannam@56 118 }
cannam@56 119
cannam@56 120 size_t
cannam@56 121 PluginInputDomainAdapter::getPreferredBlockSize() const
cannam@56 122 {
cannam@56 123 size_t block = m_plugin->getPreferredBlockSize();
cannam@56 124
cannam@61 125 if (m_plugin->getInputDomain() == FrequencyDomain) {
cannam@61 126 if (block == 0) {
cannam@61 127 block = 1024;
cannam@61 128 } else {
cannam@61 129 block = makeBlockSizeAcceptable(block);
cannam@61 130 }
cannam@56 131 }
cannam@56 132
cannam@56 133 return block;
cannam@56 134 }
cannam@56 135
cannam@61 136 size_t
cannam@61 137 PluginInputDomainAdapter::makeBlockSizeAcceptable(size_t blockSize) const
cannam@61 138 {
cannam@61 139 if (blockSize < 2) {
cannam@61 140
cannam@61 141 std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
cannam@61 142 << "supported, increasing from " << blockSize << " to 2" << std::endl;
cannam@61 143 blockSize = 2;
cannam@61 144
cannam@61 145 } else if (blockSize & (blockSize-1)) {
cannam@61 146
cannam@61 147 // not a power of two, can't handle that with our current fft
cannam@61 148 // implementation
cannam@61 149
cannam@61 150 size_t nearest = blockSize;
cannam@61 151 size_t power = 0;
cannam@61 152 while (nearest > 1) {
cannam@61 153 nearest >>= 1;
cannam@61 154 ++power;
cannam@61 155 }
cannam@61 156 nearest = 1;
cannam@61 157 while (power) {
cannam@61 158 nearest <<= 1;
cannam@61 159 --power;
cannam@61 160 }
cannam@61 161
cannam@61 162 if (blockSize - nearest > (nearest*2) - blockSize) {
cannam@61 163 nearest = nearest*2;
cannam@61 164 }
cannam@61 165
cannam@61 166 std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
cannam@61 167 blockSize = nearest;
cannam@61 168 }
cannam@61 169
cannam@61 170 return blockSize;
cannam@61 171 }
cannam@61 172
cannam@56 173 Plugin::FeatureSet
cannam@56 174 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
cannam@56 175 {
cannam@56 176 if (m_plugin->getInputDomain() == TimeDomain) {
cannam@56 177 return m_plugin->process(inputBuffers, timestamp);
cannam@56 178 }
cannam@56 179
cannam@61 180 // The timestamp supplied should be (according to the Vamp::Plugin
cannam@61 181 // spec) the time of the start of the time-domain input block.
cannam@61 182 // However, we want to pass to the plugin an FFT output calculated
cannam@61 183 // from the block of samples _centred_ on that timestamp.
cannam@61 184 //
cannam@61 185 // We have two options:
cannam@61 186 //
cannam@61 187 // 1. Buffer the input, calculating the fft of the values at the
cannam@61 188 // passed-in block minus blockSize/2 rather than starting at the
cannam@61 189 // passed-in block. So each time we call process on the plugin,
cannam@61 190 // we are passing in the same timestamp as was passed to our own
cannam@61 191 // process plugin, but not (the frequency domain representation
cannam@61 192 // of) the same set of samples. Advantages: avoids confusion in
cannam@61 193 // the host by ensuring the returned values have timestamps
cannam@61 194 // comparable with that passed in to this function (in fact this
cannam@61 195 // is pretty much essential for one-value-per-block outputs);
cannam@61 196 // consistent with hosts such as SV that deal with the
cannam@61 197 // frequency-domain transform themselves. Disadvantages: means
cannam@61 198 // making the not necessarily correct assumption that the samples
cannam@61 199 // preceding the first official block are all zero (or some other
cannam@61 200 // known value).
cannam@61 201 //
cannam@61 202 // 2. Increase the passed-in timestamps by half the blocksize. So
cannam@61 203 // when we call process, we are passing in the frequency domain
cannam@61 204 // representation of the same set of samples as passed to us, but
cannam@61 205 // with a different timestamp. Advantages: simplicity; avoids
cannam@61 206 // iffy assumption mentioned above. Disadvantages: inconsistency
cannam@61 207 // with SV in cases where stepSize != blockSize/2; potential
cannam@61 208 // confusion arising from returned timestamps being calculated
cannam@61 209 // from the adjusted input timestamps rather than the original
cannam@61 210 // ones (and inaccuracy where the returned timestamp is implied,
cannam@61 211 // as in one-value-per-block).
cannam@61 212 //
cannam@61 213 // Neither way is ideal, but I don't think either is strictly
cannam@61 214 // incorrect either. I think this is just a case where the same
cannam@61 215 // plugin can legitimately produce differing results from the same
cannam@61 216 // input data, depending on how that data is packaged.
cannam@61 217 //
cannam@61 218 // We'll go for option 2, adjusting the timestamps. Note in
cannam@61 219 // particular that this means some results can differ from those
cannam@61 220 // produced by SV.
cannam@61 221
cannam@61 222 std::cerr << "PluginInputDomainAdapter: sampleRate " << m_inputSampleRate << ", blocksize " << m_blockSize << ", adjusting time from " << timestamp;
cannam@61 223
cannam@61 224 timestamp = timestamp + RealTime::frame2RealTime(m_blockSize/2,
cannam@61 225 m_inputSampleRate);
cannam@61 226
cannam@61 227 std::cerr << " to " << timestamp << std::endl;
cannam@60 228
cannam@56 229 for (size_t c = 0; c < m_channels; ++c) {
cannam@56 230
cannam@56 231 for (size_t i = 0; i < m_blockSize; ++i) {
cannam@56 232 // Hanning window
cannam@56 233 m_ri[i] = double(inputBuffers[c][i])
cannam@56 234 * (0.50 - 0.50 * cos((2 * M_PI * i)
cannam@56 235 / m_blockSize));
cannam@56 236 }
cannam@56 237
cannam@56 238 for (size_t i = 0; i < m_blockSize/2; ++i) {
cannam@56 239 // FFT shift
cannam@56 240 double value = m_ri[i];
cannam@56 241 m_ri[i] = m_ri[i + m_blockSize/2];
cannam@56 242 m_ri[i + m_blockSize/2] = value;
cannam@56 243 }
cannam@56 244
cannam@56 245 fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
cannam@56 246
cannam@61 247 for (size_t i = 0; i <= m_blockSize/2; ++i) {
cannam@56 248 m_freqbuf[c][i * 2] = m_ro[i];
cannam@56 249 m_freqbuf[c][i * 2 + 1] = m_io[i];
cannam@56 250 }
cannam@56 251 }
cannam@56 252
cannam@56 253 return m_plugin->process(m_freqbuf, timestamp);
cannam@56 254 }
cannam@56 255
cannam@56 256 void
cannam@56 257 PluginInputDomainAdapter::fft(unsigned int n, bool inverse,
cannam@56 258 double *ri, double *ii, double *ro, double *io)
cannam@56 259 {
cannam@56 260 if (!ri || !ro || !io) return;
cannam@56 261
cannam@56 262 unsigned int bits;
cannam@56 263 unsigned int i, j, k, m;
cannam@56 264 unsigned int blockSize, blockEnd;
cannam@56 265
cannam@56 266 double tr, ti;
cannam@56 267
cannam@56 268 if (n < 2) return;
cannam@56 269 if (n & (n-1)) return;
cannam@56 270
cannam@56 271 double angle = 2.0 * M_PI;
cannam@56 272 if (inverse) angle = -angle;
cannam@56 273
cannam@56 274 for (i = 0; ; ++i) {
cannam@56 275 if (n & (1 << i)) {
cannam@56 276 bits = i;
cannam@56 277 break;
cannam@56 278 }
cannam@56 279 }
cannam@56 280
cannam@56 281 static unsigned int tableSize = 0;
cannam@56 282 static int *table = 0;
cannam@56 283
cannam@56 284 if (tableSize != n) {
cannam@56 285
cannam@56 286 delete[] table;
cannam@56 287
cannam@56 288 table = new int[n];
cannam@56 289
cannam@56 290 for (i = 0; i < n; ++i) {
cannam@56 291
cannam@56 292 m = i;
cannam@56 293
cannam@56 294 for (j = k = 0; j < bits; ++j) {
cannam@56 295 k = (k << 1) | (m & 1);
cannam@56 296 m >>= 1;
cannam@56 297 }
cannam@56 298
cannam@56 299 table[i] = k;
cannam@56 300 }
cannam@56 301
cannam@56 302 tableSize = n;
cannam@56 303 }
cannam@56 304
cannam@56 305 if (ii) {
cannam@56 306 for (i = 0; i < n; ++i) {
cannam@56 307 ro[table[i]] = ri[i];
cannam@56 308 io[table[i]] = ii[i];
cannam@56 309 }
cannam@56 310 } else {
cannam@56 311 for (i = 0; i < n; ++i) {
cannam@56 312 ro[table[i]] = ri[i];
cannam@56 313 io[table[i]] = 0.0;
cannam@56 314 }
cannam@56 315 }
cannam@56 316
cannam@56 317 blockEnd = 1;
cannam@56 318
cannam@56 319 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
cannam@56 320
cannam@56 321 double delta = angle / (double)blockSize;
cannam@56 322 double sm2 = -sin(-2 * delta);
cannam@56 323 double sm1 = -sin(-delta);
cannam@56 324 double cm2 = cos(-2 * delta);
cannam@56 325 double cm1 = cos(-delta);
cannam@56 326 double w = 2 * cm1;
cannam@56 327 double ar[3], ai[3];
cannam@56 328
cannam@56 329 for (i = 0; i < n; i += blockSize) {
cannam@56 330
cannam@56 331 ar[2] = cm2;
cannam@56 332 ar[1] = cm1;
cannam@56 333
cannam@56 334 ai[2] = sm2;
cannam@56 335 ai[1] = sm1;
cannam@56 336
cannam@56 337 for (j = i, m = 0; m < blockEnd; j++, m++) {
cannam@56 338
cannam@56 339 ar[0] = w * ar[1] - ar[2];
cannam@56 340 ar[2] = ar[1];
cannam@56 341 ar[1] = ar[0];
cannam@56 342
cannam@56 343 ai[0] = w * ai[1] - ai[2];
cannam@56 344 ai[2] = ai[1];
cannam@56 345 ai[1] = ai[0];
cannam@56 346
cannam@56 347 k = j + blockEnd;
cannam@56 348 tr = ar[0] * ro[k] - ai[0] * io[k];
cannam@56 349 ti = ar[0] * io[k] + ai[0] * ro[k];
cannam@56 350
cannam@56 351 ro[k] = ro[j] - tr;
cannam@56 352 io[k] = io[j] - ti;
cannam@56 353
cannam@56 354 ro[j] += tr;
cannam@56 355 io[j] += ti;
cannam@56 356 }
cannam@56 357 }
cannam@56 358
cannam@56 359 blockEnd = blockSize;
cannam@56 360 }
cannam@56 361
cannam@56 362 if (inverse) {
cannam@56 363
cannam@56 364 double denom = (double)n;
cannam@56 365
cannam@56 366 for (i = 0; i < n; i++) {
cannam@56 367 ro[i] /= denom;
cannam@56 368 io[i] /= denom;
cannam@56 369 }
cannam@56 370 }
cannam@56 371 }
cannam@56 372
cannam@59 373 }
cannam@56 374
cannam@56 375 }
cannam@56 376