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@56
|
43 PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
|
cannam@56
|
44 Plugin(0),
|
cannam@56
|
45 m_plugin(plugin),
|
cannam@56
|
46 m_channels(0),
|
cannam@56
|
47 m_blockSize(0),
|
cannam@56
|
48 m_freqbuf(0)
|
cannam@56
|
49 {
|
cannam@56
|
50 }
|
cannam@56
|
51
|
cannam@56
|
52 PluginInputDomainAdapter::~PluginInputDomainAdapter()
|
cannam@56
|
53 {
|
cannam@56
|
54 delete m_plugin;
|
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@56
|
60 //!!! complain and die if blocksize is not a power of 2
|
cannam@56
|
61
|
cannam@56
|
62 if (m_plugin->getInputDomain() == FrequencyDomain) {
|
cannam@56
|
63 if (m_channels > 0) {
|
cannam@56
|
64 for (size_t c = 0; c < m_channels; ++c) {
|
cannam@56
|
65 delete[] m_freqbuf[c];
|
cannam@56
|
66 }
|
cannam@56
|
67 delete[] m_freqbuf;
|
cannam@56
|
68 delete[] m_ri;
|
cannam@56
|
69 delete[] m_ro;
|
cannam@56
|
70 delete[] m_io;
|
cannam@56
|
71 }
|
cannam@56
|
72 }
|
cannam@56
|
73
|
cannam@56
|
74 m_channels = channels;
|
cannam@56
|
75 m_blockSize = blockSize;
|
cannam@56
|
76
|
cannam@56
|
77 if (m_plugin->getInputDomain() == FrequencyDomain) {
|
cannam@56
|
78 m_freqbuf = new float *[m_channels];
|
cannam@56
|
79 for (size_t c = 0; c < m_channels; ++c) {
|
cannam@56
|
80 m_freqbuf[c] = new float[m_blockSize + 2];
|
cannam@56
|
81 }
|
cannam@56
|
82 m_ri = new double[m_blockSize];
|
cannam@56
|
83 m_ro = new double[m_blockSize];
|
cannam@56
|
84 m_io = new double[m_blockSize];
|
cannam@56
|
85 }
|
cannam@56
|
86
|
cannam@56
|
87 return m_plugin->initialise(channels, stepSize, blockSize);
|
cannam@56
|
88 }
|
cannam@56
|
89
|
cannam@56
|
90 void
|
cannam@56
|
91 PluginInputDomainAdapter::reset()
|
cannam@56
|
92 {
|
cannam@56
|
93 m_plugin->reset();
|
cannam@56
|
94 }
|
cannam@56
|
95
|
cannam@56
|
96 unsigned int
|
cannam@56
|
97 PluginInputDomainAdapter::getVampApiVersion() const
|
cannam@56
|
98 {
|
cannam@56
|
99 return m_plugin->getVampApiVersion();
|
cannam@56
|
100 }
|
cannam@56
|
101
|
cannam@56
|
102 std::string
|
cannam@56
|
103 PluginInputDomainAdapter::getIdentifier() const
|
cannam@56
|
104 {
|
cannam@56
|
105 return m_plugin->getIdentifier();
|
cannam@56
|
106 }
|
cannam@56
|
107
|
cannam@56
|
108 std::string
|
cannam@56
|
109 PluginInputDomainAdapter::getName() const
|
cannam@56
|
110 {
|
cannam@56
|
111 return m_plugin->getName();
|
cannam@56
|
112 }
|
cannam@56
|
113
|
cannam@56
|
114 std::string
|
cannam@56
|
115 PluginInputDomainAdapter::getDescription() const
|
cannam@56
|
116 {
|
cannam@56
|
117 return m_plugin->getDescription();
|
cannam@56
|
118 }
|
cannam@56
|
119
|
cannam@56
|
120 std::string
|
cannam@56
|
121 PluginInputDomainAdapter::getMaker() const
|
cannam@56
|
122 {
|
cannam@56
|
123 return m_plugin->getMaker();
|
cannam@56
|
124 }
|
cannam@56
|
125
|
cannam@56
|
126 int
|
cannam@56
|
127 PluginInputDomainAdapter::getPluginVersion() const
|
cannam@56
|
128 {
|
cannam@56
|
129 return m_plugin->getPluginVersion();
|
cannam@56
|
130 }
|
cannam@56
|
131
|
cannam@56
|
132 std::string
|
cannam@56
|
133 PluginInputDomainAdapter::getCopyright() const
|
cannam@56
|
134 {
|
cannam@56
|
135 return m_plugin->getCopyright();
|
cannam@56
|
136 }
|
cannam@56
|
137
|
cannam@56
|
138 PluginBase::ParameterList
|
cannam@56
|
139 PluginInputDomainAdapter::getParameterDescriptors() const
|
cannam@56
|
140 {
|
cannam@56
|
141 return m_plugin->getParameterDescriptors();
|
cannam@56
|
142 }
|
cannam@56
|
143
|
cannam@56
|
144 float
|
cannam@56
|
145 PluginInputDomainAdapter::getParameter(std::string parameter) const
|
cannam@56
|
146 {
|
cannam@56
|
147 return m_plugin->getParameter(parameter);
|
cannam@56
|
148 }
|
cannam@56
|
149
|
cannam@56
|
150 void
|
cannam@56
|
151 PluginInputDomainAdapter::setParameter(std::string parameter, float value)
|
cannam@56
|
152 {
|
cannam@56
|
153 m_plugin->setParameter(parameter, value);
|
cannam@56
|
154 }
|
cannam@56
|
155
|
cannam@56
|
156 PluginBase::ProgramList
|
cannam@56
|
157 PluginInputDomainAdapter::getPrograms() const
|
cannam@56
|
158 {
|
cannam@56
|
159 return m_plugin->getPrograms();
|
cannam@56
|
160 }
|
cannam@56
|
161
|
cannam@56
|
162 std::string
|
cannam@56
|
163 PluginInputDomainAdapter::getCurrentProgram() const
|
cannam@56
|
164 {
|
cannam@56
|
165 return m_plugin->getCurrentProgram();
|
cannam@56
|
166 }
|
cannam@56
|
167
|
cannam@56
|
168 void
|
cannam@56
|
169 PluginInputDomainAdapter::selectProgram(std::string program)
|
cannam@56
|
170 {
|
cannam@56
|
171 m_plugin->selectProgram(program);
|
cannam@56
|
172 }
|
cannam@56
|
173
|
cannam@56
|
174 size_t
|
cannam@56
|
175 PluginInputDomainAdapter::getPreferredStepSize() const
|
cannam@56
|
176 {
|
cannam@56
|
177 size_t step = m_plugin->getPreferredStepSize();
|
cannam@56
|
178
|
cannam@56
|
179 if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
|
cannam@56
|
180 step = getPreferredBlockSize() / 2;
|
cannam@56
|
181 }
|
cannam@56
|
182
|
cannam@56
|
183 return step;
|
cannam@56
|
184 }
|
cannam@56
|
185
|
cannam@56
|
186 size_t
|
cannam@56
|
187 PluginInputDomainAdapter::getPreferredBlockSize() const
|
cannam@56
|
188 {
|
cannam@56
|
189 size_t block = m_plugin->getPreferredBlockSize();
|
cannam@56
|
190
|
cannam@56
|
191 if (block == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
|
cannam@56
|
192 block = 1024;
|
cannam@56
|
193 }
|
cannam@56
|
194
|
cannam@56
|
195 return block;
|
cannam@56
|
196 }
|
cannam@56
|
197
|
cannam@56
|
198 size_t
|
cannam@56
|
199 PluginInputDomainAdapter::getMinChannelCount() const
|
cannam@56
|
200 {
|
cannam@56
|
201 return m_plugin->getMinChannelCount();
|
cannam@56
|
202 }
|
cannam@56
|
203
|
cannam@56
|
204 size_t PluginInputDomainAdapter::getMaxChannelCount() const
|
cannam@56
|
205 {
|
cannam@56
|
206 return m_plugin->getMaxChannelCount();
|
cannam@56
|
207 }
|
cannam@56
|
208
|
cannam@56
|
209 Plugin::OutputList
|
cannam@56
|
210 PluginInputDomainAdapter::getOutputDescriptors() const
|
cannam@56
|
211 {
|
cannam@56
|
212 return m_plugin->getOutputDescriptors();
|
cannam@56
|
213 }
|
cannam@56
|
214
|
cannam@56
|
215 Plugin::FeatureSet
|
cannam@56
|
216 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@56
|
217 {
|
cannam@56
|
218 if (m_plugin->getInputDomain() == TimeDomain) {
|
cannam@56
|
219 return m_plugin->process(inputBuffers, timestamp);
|
cannam@56
|
220 }
|
cannam@56
|
221
|
cannam@56
|
222 for (size_t c = 0; c < m_channels; ++c) {
|
cannam@56
|
223
|
cannam@56
|
224 for (size_t i = 0; i < m_blockSize; ++i) {
|
cannam@56
|
225 // Hanning window
|
cannam@56
|
226 m_ri[i] = double(inputBuffers[c][i])
|
cannam@56
|
227 * (0.50 - 0.50 * cos((2 * M_PI * i)
|
cannam@56
|
228 / m_blockSize));
|
cannam@56
|
229 }
|
cannam@56
|
230
|
cannam@56
|
231 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@56
|
232 // FFT shift
|
cannam@56
|
233 double value = m_ri[i];
|
cannam@56
|
234 m_ri[i] = m_ri[i + m_blockSize/2];
|
cannam@56
|
235 m_ri[i + m_blockSize/2] = value;
|
cannam@56
|
236 }
|
cannam@56
|
237
|
cannam@56
|
238 fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
|
cannam@56
|
239
|
cannam@56
|
240 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@56
|
241 m_freqbuf[c][i * 2] = m_ro[i];
|
cannam@56
|
242 m_freqbuf[c][i * 2 + 1] = m_io[i];
|
cannam@56
|
243 }
|
cannam@56
|
244 }
|
cannam@56
|
245
|
cannam@56
|
246 //!!! do we want to adjust the timestamp or anything so as to
|
cannam@56
|
247 // effectively centre the frame?
|
cannam@56
|
248
|
cannam@56
|
249 return m_plugin->process(m_freqbuf, timestamp);
|
cannam@56
|
250 }
|
cannam@56
|
251
|
cannam@56
|
252 Plugin::FeatureSet
|
cannam@56
|
253 PluginInputDomainAdapter::getRemainingFeatures()
|
cannam@56
|
254 {
|
cannam@56
|
255 return m_plugin->getRemainingFeatures();
|
cannam@56
|
256 }
|
cannam@56
|
257
|
cannam@56
|
258 void
|
cannam@56
|
259 PluginInputDomainAdapter::fft(unsigned int n, bool inverse,
|
cannam@56
|
260 double *ri, double *ii, double *ro, double *io)
|
cannam@56
|
261 {
|
cannam@56
|
262 if (!ri || !ro || !io) return;
|
cannam@56
|
263
|
cannam@56
|
264 unsigned int bits;
|
cannam@56
|
265 unsigned int i, j, k, m;
|
cannam@56
|
266 unsigned int blockSize, blockEnd;
|
cannam@56
|
267
|
cannam@56
|
268 double tr, ti;
|
cannam@56
|
269
|
cannam@56
|
270 if (n < 2) return;
|
cannam@56
|
271 if (n & (n-1)) return;
|
cannam@56
|
272
|
cannam@56
|
273 double angle = 2.0 * M_PI;
|
cannam@56
|
274 if (inverse) angle = -angle;
|
cannam@56
|
275
|
cannam@56
|
276 for (i = 0; ; ++i) {
|
cannam@56
|
277 if (n & (1 << i)) {
|
cannam@56
|
278 bits = i;
|
cannam@56
|
279 break;
|
cannam@56
|
280 }
|
cannam@56
|
281 }
|
cannam@56
|
282
|
cannam@56
|
283 static unsigned int tableSize = 0;
|
cannam@56
|
284 static int *table = 0;
|
cannam@56
|
285
|
cannam@56
|
286 if (tableSize != n) {
|
cannam@56
|
287
|
cannam@56
|
288 delete[] table;
|
cannam@56
|
289
|
cannam@56
|
290 table = new int[n];
|
cannam@56
|
291
|
cannam@56
|
292 for (i = 0; i < n; ++i) {
|
cannam@56
|
293
|
cannam@56
|
294 m = i;
|
cannam@56
|
295
|
cannam@56
|
296 for (j = k = 0; j < bits; ++j) {
|
cannam@56
|
297 k = (k << 1) | (m & 1);
|
cannam@56
|
298 m >>= 1;
|
cannam@56
|
299 }
|
cannam@56
|
300
|
cannam@56
|
301 table[i] = k;
|
cannam@56
|
302 }
|
cannam@56
|
303
|
cannam@56
|
304 tableSize = n;
|
cannam@56
|
305 }
|
cannam@56
|
306
|
cannam@56
|
307 if (ii) {
|
cannam@56
|
308 for (i = 0; i < n; ++i) {
|
cannam@56
|
309 ro[table[i]] = ri[i];
|
cannam@56
|
310 io[table[i]] = ii[i];
|
cannam@56
|
311 }
|
cannam@56
|
312 } else {
|
cannam@56
|
313 for (i = 0; i < n; ++i) {
|
cannam@56
|
314 ro[table[i]] = ri[i];
|
cannam@56
|
315 io[table[i]] = 0.0;
|
cannam@56
|
316 }
|
cannam@56
|
317 }
|
cannam@56
|
318
|
cannam@56
|
319 blockEnd = 1;
|
cannam@56
|
320
|
cannam@56
|
321 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
|
cannam@56
|
322
|
cannam@56
|
323 double delta = angle / (double)blockSize;
|
cannam@56
|
324 double sm2 = -sin(-2 * delta);
|
cannam@56
|
325 double sm1 = -sin(-delta);
|
cannam@56
|
326 double cm2 = cos(-2 * delta);
|
cannam@56
|
327 double cm1 = cos(-delta);
|
cannam@56
|
328 double w = 2 * cm1;
|
cannam@56
|
329 double ar[3], ai[3];
|
cannam@56
|
330
|
cannam@56
|
331 for (i = 0; i < n; i += blockSize) {
|
cannam@56
|
332
|
cannam@56
|
333 ar[2] = cm2;
|
cannam@56
|
334 ar[1] = cm1;
|
cannam@56
|
335
|
cannam@56
|
336 ai[2] = sm2;
|
cannam@56
|
337 ai[1] = sm1;
|
cannam@56
|
338
|
cannam@56
|
339 for (j = i, m = 0; m < blockEnd; j++, m++) {
|
cannam@56
|
340
|
cannam@56
|
341 ar[0] = w * ar[1] - ar[2];
|
cannam@56
|
342 ar[2] = ar[1];
|
cannam@56
|
343 ar[1] = ar[0];
|
cannam@56
|
344
|
cannam@56
|
345 ai[0] = w * ai[1] - ai[2];
|
cannam@56
|
346 ai[2] = ai[1];
|
cannam@56
|
347 ai[1] = ai[0];
|
cannam@56
|
348
|
cannam@56
|
349 k = j + blockEnd;
|
cannam@56
|
350 tr = ar[0] * ro[k] - ai[0] * io[k];
|
cannam@56
|
351 ti = ar[0] * io[k] + ai[0] * ro[k];
|
cannam@56
|
352
|
cannam@56
|
353 ro[k] = ro[j] - tr;
|
cannam@56
|
354 io[k] = io[j] - ti;
|
cannam@56
|
355
|
cannam@56
|
356 ro[j] += tr;
|
cannam@56
|
357 io[j] += ti;
|
cannam@56
|
358 }
|
cannam@56
|
359 }
|
cannam@56
|
360
|
cannam@56
|
361 blockEnd = blockSize;
|
cannam@56
|
362 }
|
cannam@56
|
363
|
cannam@56
|
364 if (inverse) {
|
cannam@56
|
365
|
cannam@56
|
366 double denom = (double)n;
|
cannam@56
|
367
|
cannam@56
|
368 for (i = 0; i < n; i++) {
|
cannam@56
|
369 ro[i] /= denom;
|
cannam@56
|
370 io[i] /= denom;
|
cannam@56
|
371 }
|
cannam@56
|
372 }
|
cannam@56
|
373 }
|
cannam@56
|
374
|
cannam@56
|
375
|
cannam@56
|
376 }
|
cannam@56
|
377
|