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