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@59
|
60 //!!! complain and adapt-or-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@57
|
90 Plugin::InputDomain
|
cannam@57
|
91 PluginInputDomainAdapter::getInputDomain() const
|
cannam@56
|
92 {
|
cannam@57
|
93 return TimeDomain;
|
cannam@56
|
94 }
|
cannam@56
|
95
|
cannam@56
|
96 size_t
|
cannam@56
|
97 PluginInputDomainAdapter::getPreferredStepSize() const
|
cannam@56
|
98 {
|
cannam@56
|
99 size_t step = m_plugin->getPreferredStepSize();
|
cannam@56
|
100
|
cannam@56
|
101 if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
|
cannam@56
|
102 step = getPreferredBlockSize() / 2;
|
cannam@56
|
103 }
|
cannam@56
|
104
|
cannam@56
|
105 return step;
|
cannam@56
|
106 }
|
cannam@56
|
107
|
cannam@56
|
108 size_t
|
cannam@56
|
109 PluginInputDomainAdapter::getPreferredBlockSize() const
|
cannam@56
|
110 {
|
cannam@59
|
111 //!!! complain and adapt-or-die if blocksize is not a power of 2
|
cannam@59
|
112
|
cannam@56
|
113 size_t block = m_plugin->getPreferredBlockSize();
|
cannam@56
|
114
|
cannam@56
|
115 if (block == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
|
cannam@56
|
116 block = 1024;
|
cannam@56
|
117 }
|
cannam@56
|
118
|
cannam@56
|
119 return block;
|
cannam@56
|
120 }
|
cannam@56
|
121
|
cannam@56
|
122 Plugin::FeatureSet
|
cannam@56
|
123 PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@56
|
124 {
|
cannam@56
|
125 if (m_plugin->getInputDomain() == TimeDomain) {
|
cannam@56
|
126 return m_plugin->process(inputBuffers, timestamp);
|
cannam@56
|
127 }
|
cannam@56
|
128
|
cannam@60
|
129 //!!! need to compensate for the fact that the first block is aligned
|
cannam@60
|
130 // with the zero frame but for frequency domain we want it to be
|
cannam@60
|
131 // centred on the zero frame
|
cannam@60
|
132
|
cannam@56
|
133 for (size_t c = 0; c < m_channels; ++c) {
|
cannam@56
|
134
|
cannam@56
|
135 for (size_t i = 0; i < m_blockSize; ++i) {
|
cannam@56
|
136 // Hanning window
|
cannam@56
|
137 m_ri[i] = double(inputBuffers[c][i])
|
cannam@56
|
138 * (0.50 - 0.50 * cos((2 * M_PI * i)
|
cannam@56
|
139 / m_blockSize));
|
cannam@56
|
140 }
|
cannam@56
|
141
|
cannam@56
|
142 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@56
|
143 // FFT shift
|
cannam@56
|
144 double value = m_ri[i];
|
cannam@56
|
145 m_ri[i] = m_ri[i + m_blockSize/2];
|
cannam@56
|
146 m_ri[i + m_blockSize/2] = value;
|
cannam@56
|
147 }
|
cannam@56
|
148
|
cannam@56
|
149 fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
|
cannam@56
|
150
|
cannam@56
|
151 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@56
|
152 m_freqbuf[c][i * 2] = m_ro[i];
|
cannam@56
|
153 m_freqbuf[c][i * 2 + 1] = m_io[i];
|
cannam@56
|
154 }
|
cannam@56
|
155 }
|
cannam@56
|
156
|
cannam@56
|
157 //!!! do we want to adjust the timestamp or anything so as to
|
cannam@56
|
158 // effectively centre the frame?
|
cannam@56
|
159
|
cannam@56
|
160 return m_plugin->process(m_freqbuf, timestamp);
|
cannam@56
|
161 }
|
cannam@56
|
162
|
cannam@56
|
163 void
|
cannam@56
|
164 PluginInputDomainAdapter::fft(unsigned int n, bool inverse,
|
cannam@56
|
165 double *ri, double *ii, double *ro, double *io)
|
cannam@56
|
166 {
|
cannam@56
|
167 if (!ri || !ro || !io) return;
|
cannam@56
|
168
|
cannam@56
|
169 unsigned int bits;
|
cannam@56
|
170 unsigned int i, j, k, m;
|
cannam@56
|
171 unsigned int blockSize, blockEnd;
|
cannam@56
|
172
|
cannam@56
|
173 double tr, ti;
|
cannam@56
|
174
|
cannam@56
|
175 if (n < 2) return;
|
cannam@56
|
176 if (n & (n-1)) return;
|
cannam@56
|
177
|
cannam@56
|
178 double angle = 2.0 * M_PI;
|
cannam@56
|
179 if (inverse) angle = -angle;
|
cannam@56
|
180
|
cannam@56
|
181 for (i = 0; ; ++i) {
|
cannam@56
|
182 if (n & (1 << i)) {
|
cannam@56
|
183 bits = i;
|
cannam@56
|
184 break;
|
cannam@56
|
185 }
|
cannam@56
|
186 }
|
cannam@56
|
187
|
cannam@56
|
188 static unsigned int tableSize = 0;
|
cannam@56
|
189 static int *table = 0;
|
cannam@56
|
190
|
cannam@56
|
191 if (tableSize != n) {
|
cannam@56
|
192
|
cannam@56
|
193 delete[] table;
|
cannam@56
|
194
|
cannam@56
|
195 table = new int[n];
|
cannam@56
|
196
|
cannam@56
|
197 for (i = 0; i < n; ++i) {
|
cannam@56
|
198
|
cannam@56
|
199 m = i;
|
cannam@56
|
200
|
cannam@56
|
201 for (j = k = 0; j < bits; ++j) {
|
cannam@56
|
202 k = (k << 1) | (m & 1);
|
cannam@56
|
203 m >>= 1;
|
cannam@56
|
204 }
|
cannam@56
|
205
|
cannam@56
|
206 table[i] = k;
|
cannam@56
|
207 }
|
cannam@56
|
208
|
cannam@56
|
209 tableSize = n;
|
cannam@56
|
210 }
|
cannam@56
|
211
|
cannam@56
|
212 if (ii) {
|
cannam@56
|
213 for (i = 0; i < n; ++i) {
|
cannam@56
|
214 ro[table[i]] = ri[i];
|
cannam@56
|
215 io[table[i]] = ii[i];
|
cannam@56
|
216 }
|
cannam@56
|
217 } else {
|
cannam@56
|
218 for (i = 0; i < n; ++i) {
|
cannam@56
|
219 ro[table[i]] = ri[i];
|
cannam@56
|
220 io[table[i]] = 0.0;
|
cannam@56
|
221 }
|
cannam@56
|
222 }
|
cannam@56
|
223
|
cannam@56
|
224 blockEnd = 1;
|
cannam@56
|
225
|
cannam@56
|
226 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
|
cannam@56
|
227
|
cannam@56
|
228 double delta = angle / (double)blockSize;
|
cannam@56
|
229 double sm2 = -sin(-2 * delta);
|
cannam@56
|
230 double sm1 = -sin(-delta);
|
cannam@56
|
231 double cm2 = cos(-2 * delta);
|
cannam@56
|
232 double cm1 = cos(-delta);
|
cannam@56
|
233 double w = 2 * cm1;
|
cannam@56
|
234 double ar[3], ai[3];
|
cannam@56
|
235
|
cannam@56
|
236 for (i = 0; i < n; i += blockSize) {
|
cannam@56
|
237
|
cannam@56
|
238 ar[2] = cm2;
|
cannam@56
|
239 ar[1] = cm1;
|
cannam@56
|
240
|
cannam@56
|
241 ai[2] = sm2;
|
cannam@56
|
242 ai[1] = sm1;
|
cannam@56
|
243
|
cannam@56
|
244 for (j = i, m = 0; m < blockEnd; j++, m++) {
|
cannam@56
|
245
|
cannam@56
|
246 ar[0] = w * ar[1] - ar[2];
|
cannam@56
|
247 ar[2] = ar[1];
|
cannam@56
|
248 ar[1] = ar[0];
|
cannam@56
|
249
|
cannam@56
|
250 ai[0] = w * ai[1] - ai[2];
|
cannam@56
|
251 ai[2] = ai[1];
|
cannam@56
|
252 ai[1] = ai[0];
|
cannam@56
|
253
|
cannam@56
|
254 k = j + blockEnd;
|
cannam@56
|
255 tr = ar[0] * ro[k] - ai[0] * io[k];
|
cannam@56
|
256 ti = ar[0] * io[k] + ai[0] * ro[k];
|
cannam@56
|
257
|
cannam@56
|
258 ro[k] = ro[j] - tr;
|
cannam@56
|
259 io[k] = io[j] - ti;
|
cannam@56
|
260
|
cannam@56
|
261 ro[j] += tr;
|
cannam@56
|
262 io[j] += ti;
|
cannam@56
|
263 }
|
cannam@56
|
264 }
|
cannam@56
|
265
|
cannam@56
|
266 blockEnd = blockSize;
|
cannam@56
|
267 }
|
cannam@56
|
268
|
cannam@56
|
269 if (inverse) {
|
cannam@56
|
270
|
cannam@56
|
271 double denom = (double)n;
|
cannam@56
|
272
|
cannam@56
|
273 for (i = 0; i < n; i++) {
|
cannam@56
|
274 ro[i] /= denom;
|
cannam@56
|
275 io[i] /= denom;
|
cannam@56
|
276 }
|
cannam@56
|
277 }
|
cannam@56
|
278 }
|
cannam@56
|
279
|
cannam@59
|
280 }
|
cannam@56
|
281
|
cannam@56
|
282 }
|
cannam@56
|
283
|