1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Vamp
5 
6     An API for audio analysis and feature extraction plugins.
7 
8     Centre for Digital Music, Queen Mary, University of London.
9     Copyright 2006-2009 Chris Cannam and QMUL.
10 
11     This file is based in part on Don Cross's public domain FFT
12     implementation.
13 
14     Permission is hereby granted, free of charge, to any person
15     obtaining a copy of this software and associated documentation
16     files (the "Software"), to deal in the Software without
17     restriction, including without limitation the rights to use, copy,
18     modify, merge, publish, distribute, sublicense, and/or sell copies
19     of the Software, and to permit persons to whom the Software is
20     furnished to do so, subject to the following conditions:
21 
22     The above copyright notice and this permission notice shall be
23     included in all copies or substantial portions of the Software.
24 
25     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
29     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
30     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 
33     Except as contained in this notice, the names of the Centre for
34     Digital Music; Queen Mary, University of London; and Chris Cannam
35     shall not be used in advertising or otherwise to promote the sale,
36     use or other dealings in this Software without prior written
37     authorization.
38 */
39 
40 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
41 
42 #include <cmath>
43 
44 #include "Window.h"
45 
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <math.h>
49 #include <string.h>
50 #include <limits.h>
51 
52 _VAMP_SDK_HOSTSPACE_BEGIN(PluginInputDomainAdapter.cpp)
53 
54 #include "../vamp-sdk/FFTimpl.cpp"
55 
56 namespace Vamp {
57 
58 namespace HostExt {
59 
60 class PluginInputDomainAdapter::Impl
61 {
62 public:
63     Impl(Plugin *plugin, float inputSampleRate);
64     ~Impl();
65 
66     bool initialise(size_t channels, size_t stepSize, size_t blockSize);
67     void reset();
68 
69     size_t getPreferredStepSize() const;
70     size_t getPreferredBlockSize() const;
71 
72     FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
73 
74     void setProcessTimestampMethod(ProcessTimestampMethod m);
75     ProcessTimestampMethod getProcessTimestampMethod() const;
76 
77     RealTime getTimestampAdjustment() const;
78 
79     WindowType getWindowType() const;
80     void setWindowType(WindowType type);
81 
82 protected:
83     Plugin *m_plugin;
84     float m_inputSampleRate;
85     int m_channels;
86     int m_stepSize;
87     int m_blockSize;
88     float **m_freqbuf;
89     Kiss::vamp_kiss_fft_scalar *m_ri;
90 
91     WindowType m_windowType;
92     typedef Window<Kiss::vamp_kiss_fft_scalar> W;
93     W *m_window;
94 
95     ProcessTimestampMethod m_method;
96     int m_processCount;
97     float **m_shiftBuffers;
98 
99     Kiss::vamp_kiss_fftr_cfg m_cfg;
100     Kiss::vamp_kiss_fft_cpx *m_cbuf;
101 
102     FeatureSet processShiftingTimestamp(const float *const *inputBuffers, RealTime timestamp);
103     FeatureSet processShiftingData(const float *const *inputBuffers, RealTime timestamp);
104 
105     size_t makeBlockSizeAcceptable(size_t) const;
106 
107     W::WindowType convertType(WindowType t) const;
108 };
109 
PluginInputDomainAdapter(Plugin * plugin)110 PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
111     PluginWrapper(plugin)
112 {
113     m_impl = new Impl(plugin, m_inputSampleRate);
114 }
115 
~PluginInputDomainAdapter()116 PluginInputDomainAdapter::~PluginInputDomainAdapter()
117 {
118     delete m_impl;
119 }
120 
121 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)122 PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
123 {
124     return m_impl->initialise(channels, stepSize, blockSize);
125 }
126 
127 void
reset()128 PluginInputDomainAdapter::reset()
129 {
130     m_impl->reset();
131 }
132 
133 Plugin::InputDomain
getInputDomain() const134 PluginInputDomainAdapter::getInputDomain() const
135 {
136     return TimeDomain;
137 }
138 
139 size_t
getPreferredStepSize() const140 PluginInputDomainAdapter::getPreferredStepSize() const
141 {
142     return m_impl->getPreferredStepSize();
143 }
144 
145 size_t
getPreferredBlockSize() const146 PluginInputDomainAdapter::getPreferredBlockSize() const
147 {
148     return m_impl->getPreferredBlockSize();
149 }
150 
151 Plugin::FeatureSet
process(const float * const * inputBuffers,RealTime timestamp)152 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
153 {
154     return m_impl->process(inputBuffers, timestamp);
155 }
156 
157 void
setProcessTimestampMethod(ProcessTimestampMethod m)158 PluginInputDomainAdapter::setProcessTimestampMethod(ProcessTimestampMethod m)
159 {
160     m_impl->setProcessTimestampMethod(m);
161 }
162 
163 PluginInputDomainAdapter::ProcessTimestampMethod
getProcessTimestampMethod() const164 PluginInputDomainAdapter::getProcessTimestampMethod() const
165 {
166     return m_impl->getProcessTimestampMethod();
167 }
168 
169 RealTime
getTimestampAdjustment() const170 PluginInputDomainAdapter::getTimestampAdjustment() const
171 {
172     return m_impl->getTimestampAdjustment();
173 }
174 
175 PluginInputDomainAdapter::WindowType
getWindowType() const176 PluginInputDomainAdapter::getWindowType() const
177 {
178     return m_impl->getWindowType();
179 }
180 
181 void
setWindowType(WindowType w)182 PluginInputDomainAdapter::setWindowType(WindowType w)
183 {
184     m_impl->setWindowType(w);
185 }
186 
187 
Impl(Plugin * plugin,float inputSampleRate)188 PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
189     m_plugin(plugin),
190     m_inputSampleRate(inputSampleRate),
191     m_channels(0),
192     m_stepSize(0),
193     m_blockSize(0),
194     m_freqbuf(0),
195     m_ri(0),
196     m_windowType(HanningWindow),
197     m_window(0),
198     m_method(ShiftTimestamp),
199     m_processCount(0),
200     m_shiftBuffers(0),
201     m_cfg(0),
202     m_cbuf(0)
203 {
204 }
205 
~Impl()206 PluginInputDomainAdapter::Impl::~Impl()
207 {
208     // the adapter will delete the plugin
209 
210     if (m_shiftBuffers) {
211         for (int c = 0; c < m_channels; ++c) {
212             delete[] m_shiftBuffers[c];
213         }
214         delete[] m_shiftBuffers;
215     }
216 
217     if (m_channels > 0) {
218         for (int c = 0; c < m_channels; ++c) {
219             delete[] m_freqbuf[c];
220         }
221         delete[] m_freqbuf;
222         delete[] m_ri;
223         if (m_cfg) {
224             Kiss::vamp_kiss_fftr_free(m_cfg);
225             m_cfg = 0;
226             delete[] m_cbuf;
227             m_cbuf = 0;
228         }
229         delete m_window;
230     }
231 }
232 
233 // for some visual studii apparently
234 #ifndef M_PI
235 #define M_PI 3.14159265358979232846
236 #endif
237 
238 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)239 PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
240 {
241     if (m_plugin->getInputDomain() == TimeDomain) {
242 
243         m_stepSize = int(stepSize);
244         m_blockSize = int(blockSize);
245         m_channels = int(channels);
246 
247         return m_plugin->initialise(channels, stepSize, blockSize);
248     }
249 
250     if (blockSize < 2) {
251         std::cerr << "ERROR: PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
252         return false;
253     }
254 
255     if (blockSize % 2) {
256         std::cerr << "ERROR: PluginInputDomainAdapter::initialise: odd blocksize " << blockSize << " not supported" << std::endl;
257         return false;
258     }
259 
260     if (m_channels > 0) {
261         for (int c = 0; c < m_channels; ++c) {
262             delete[] m_freqbuf[c];
263         }
264         delete[] m_freqbuf;
265         delete[] m_ri;
266         if (m_cfg) {
267             Kiss::vamp_kiss_fftr_free(m_cfg);
268             m_cfg = 0;
269             delete[] m_cbuf;
270             m_cbuf = 0;
271         }
272         delete m_window;
273     }
274 
275     m_stepSize = int(stepSize);
276     m_blockSize = int(blockSize);
277     m_channels = int(channels);
278 
279     m_freqbuf = new float *[m_channels];
280     for (int c = 0; c < m_channels; ++c) {
281         m_freqbuf[c] = new float[m_blockSize + 2];
282     }
283     m_ri = new Kiss::vamp_kiss_fft_scalar[m_blockSize];
284 
285     m_window = new W(convertType(m_windowType), m_blockSize);
286 
287     m_cfg = Kiss::vamp_kiss_fftr_alloc(m_blockSize, false, 0, 0);
288     m_cbuf = new Kiss::vamp_kiss_fft_cpx[m_blockSize/2+1];
289 
290     m_processCount = 0;
291 
292     return m_plugin->initialise(channels, stepSize, m_blockSize);
293 }
294 
295 void
reset()296 PluginInputDomainAdapter::Impl::reset()
297 {
298     m_processCount = 0;
299     m_plugin->reset();
300 }
301 
302 size_t
getPreferredStepSize() const303 PluginInputDomainAdapter::Impl::getPreferredStepSize() const
304 {
305     size_t step = m_plugin->getPreferredStepSize();
306 
307     if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
308         step = getPreferredBlockSize() / 2;
309     }
310 
311     return step;
312 }
313 
314 size_t
getPreferredBlockSize() const315 PluginInputDomainAdapter::Impl::getPreferredBlockSize() const
316 {
317     size_t block = m_plugin->getPreferredBlockSize();
318 
319     if (m_plugin->getInputDomain() == FrequencyDomain) {
320         if (block == 0) {
321             block = 1024;
322         } else {
323             block = makeBlockSizeAcceptable(block);
324         }
325     }
326 
327     return block;
328 }
329 
330 size_t
makeBlockSizeAcceptable(size_t blockSize) const331 PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
332 {
333     if (blockSize < 2) {
334 
335         std::cerr << "WARNING: PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
336                   << "supported, increasing from " << blockSize << " to 2" << std::endl;
337         blockSize = 2;
338 
339     } else if (blockSize % 2) {
340 
341         std::cerr << "WARNING: PluginInputDomainAdapter::initialise: odd blocksize not" << std::endl
342                   << "supported, increasing from " << blockSize << " to " << (blockSize+1) << std::endl;
343         blockSize = blockSize+1;
344     }
345 
346     return blockSize;
347 }
348 
349 RealTime
getTimestampAdjustment() const350 PluginInputDomainAdapter::Impl::getTimestampAdjustment() const
351 {
352     if (m_plugin->getInputDomain() == TimeDomain) {
353         return RealTime::zeroTime;
354     } else if (m_method == ShiftData || m_method == NoShift) {
355         return RealTime::zeroTime;
356     } else {
357         return RealTime::frame2RealTime
358             (m_blockSize/2, int(m_inputSampleRate + 0.5));
359     }
360 }
361 
362 void
setProcessTimestampMethod(ProcessTimestampMethod m)363 PluginInputDomainAdapter::Impl::setProcessTimestampMethod(ProcessTimestampMethod m)
364 {
365     m_method = m;
366 }
367 
368 PluginInputDomainAdapter::ProcessTimestampMethod
getProcessTimestampMethod() const369 PluginInputDomainAdapter::Impl::getProcessTimestampMethod() const
370 {
371     return m_method;
372 }
373 
374 void
setWindowType(WindowType t)375 PluginInputDomainAdapter::Impl::setWindowType(WindowType t)
376 {
377     if (m_windowType == t) return;
378     m_windowType = t;
379     if (m_window) {
380         delete m_window;
381         m_window = new W(convertType(m_windowType), m_blockSize);
382     }
383 }
384 
385 PluginInputDomainAdapter::WindowType
getWindowType() const386 PluginInputDomainAdapter::Impl::getWindowType() const
387 {
388     return m_windowType;
389 }
390 
391 PluginInputDomainAdapter::Impl::W::WindowType
convertType(WindowType t) const392 PluginInputDomainAdapter::Impl::convertType(WindowType t) const
393 {
394     switch (t) {
395     case RectangularWindow:
396         return W::RectangularWindow;
397     case BartlettWindow:
398         return W::BartlettWindow;
399     case HammingWindow:
400         return W::HammingWindow;
401     case HanningWindow:
402         return W::HanningWindow;
403     case BlackmanWindow:
404         return W::BlackmanWindow;
405     case NuttallWindow:
406         return W::NuttallWindow;
407     case BlackmanHarrisWindow:
408         return W::BlackmanHarrisWindow;
409     default:
410 	return W::HanningWindow;
411     }
412 }
413 
414 Plugin::FeatureSet
process(const float * const * inputBuffers,RealTime timestamp)415 PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
416                                         RealTime timestamp)
417 {
418     if (m_plugin->getInputDomain() == TimeDomain) {
419         return m_plugin->process(inputBuffers, timestamp);
420     }
421 
422     if (m_method == ShiftTimestamp || m_method == NoShift) {
423         return processShiftingTimestamp(inputBuffers, timestamp);
424     } else {
425         return processShiftingData(inputBuffers, timestamp);
426     }
427 }
428 
429 Plugin::FeatureSet
processShiftingTimestamp(const float * const * inputBuffers,RealTime timestamp)430 PluginInputDomainAdapter::Impl::processShiftingTimestamp(const float *const *inputBuffers,
431                                                          RealTime timestamp)
432 {
433     unsigned int roundedRate = 1;
434     if (m_inputSampleRate > 0.f) {
435         roundedRate = (unsigned int)round(m_inputSampleRate);
436     }
437 
438     if (m_method == ShiftTimestamp) {
439         // we may need to add one nsec if timestamp +
440         // getTimestampAdjustment() rounds down
441         timestamp = timestamp + getTimestampAdjustment();
442         RealTime nsec(0, 1);
443         if (RealTime::realTime2Frame(timestamp, roundedRate) <
444             RealTime::realTime2Frame(timestamp + nsec, roundedRate)) {
445             timestamp = timestamp + nsec;
446         }
447     }
448 
449     for (int c = 0; c < m_channels; ++c) {
450 
451         m_window->cut(inputBuffers[c], m_ri);
452 
453         for (int i = 0; i < m_blockSize/2; ++i) {
454             // FFT shift
455             Kiss::vamp_kiss_fft_scalar value = m_ri[i];
456             m_ri[i] = m_ri[i + m_blockSize/2];
457             m_ri[i + m_blockSize/2] = value;
458         }
459 
460         Kiss::vamp_kiss_fftr(m_cfg, m_ri, m_cbuf);
461 
462         for (int i = 0; i <= m_blockSize/2; ++i) {
463             m_freqbuf[c][i * 2] = float(m_cbuf[i].r);
464             m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i);
465         }
466     }
467 
468     return m_plugin->process(m_freqbuf, timestamp);
469 }
470 
471 Plugin::FeatureSet
processShiftingData(const float * const * inputBuffers,RealTime timestamp)472 PluginInputDomainAdapter::Impl::processShiftingData(const float *const *inputBuffers,
473                                                     RealTime timestamp)
474 {
475     if (m_processCount == 0) {
476         if (!m_shiftBuffers) {
477             m_shiftBuffers = new float *[m_channels];
478             for (int c = 0; c < m_channels; ++c) {
479                 m_shiftBuffers[c] = new float[m_blockSize + m_blockSize/2];
480             }
481         }
482         for (int c = 0; c < m_channels; ++c) {
483             for (int i = 0; i < m_blockSize + m_blockSize/2; ++i) {
484                 m_shiftBuffers[c][i] = 0.f;
485             }
486         }
487     }
488 
489     for (int c = 0; c < m_channels; ++c) {
490         for (int i = m_stepSize; i < m_blockSize + m_blockSize/2; ++i) {
491             m_shiftBuffers[c][i - m_stepSize] = m_shiftBuffers[c][i];
492         }
493         for (int i = 0; i < m_blockSize; ++i) {
494             m_shiftBuffers[c][i + m_blockSize/2] = inputBuffers[c][i];
495         }
496     }
497 
498     for (int c = 0; c < m_channels; ++c) {
499 
500         m_window->cut(m_shiftBuffers[c], m_ri);
501 
502         for (int i = 0; i < m_blockSize/2; ++i) {
503             // FFT shift
504             Kiss::vamp_kiss_fft_scalar value = m_ri[i];
505             m_ri[i] = m_ri[i + m_blockSize/2];
506             m_ri[i + m_blockSize/2] = value;
507         }
508 
509         Kiss::vamp_kiss_fftr(m_cfg, m_ri, m_cbuf);
510 
511         for (int i = 0; i <= m_blockSize/2; ++i) {
512             m_freqbuf[c][i * 2] = float(m_cbuf[i].r);
513             m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i].i);
514         }
515     }
516 
517     ++m_processCount;
518 
519     return m_plugin->process(m_freqbuf, timestamp);
520 }
521 
522 }
523 
524 }
525 
526 _VAMP_SDK_HOSTSPACE_END(PluginInputDomainAdapter.cpp)
527 
528