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@56
|
129 for (size_t c = 0; c < m_channels; ++c) {
|
cannam@56
|
130
|
cannam@56
|
131 for (size_t i = 0; i < m_blockSize; ++i) {
|
cannam@56
|
132 // Hanning window
|
cannam@56
|
133 m_ri[i] = double(inputBuffers[c][i])
|
cannam@56
|
134 * (0.50 - 0.50 * cos((2 * M_PI * i)
|
cannam@56
|
135 / m_blockSize));
|
cannam@56
|
136 }
|
cannam@56
|
137
|
cannam@56
|
138 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@56
|
139 // FFT shift
|
cannam@56
|
140 double value = m_ri[i];
|
cannam@56
|
141 m_ri[i] = m_ri[i + m_blockSize/2];
|
cannam@56
|
142 m_ri[i + m_blockSize/2] = value;
|
cannam@56
|
143 }
|
cannam@56
|
144
|
cannam@56
|
145 fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
|
cannam@56
|
146
|
cannam@56
|
147 for (size_t i = 0; i < m_blockSize/2; ++i) {
|
cannam@56
|
148 m_freqbuf[c][i * 2] = m_ro[i];
|
cannam@56
|
149 m_freqbuf[c][i * 2 + 1] = m_io[i];
|
cannam@56
|
150 }
|
cannam@56
|
151 }
|
cannam@56
|
152
|
cannam@56
|
153 //!!! do we want to adjust the timestamp or anything so as to
|
cannam@56
|
154 // effectively centre the frame?
|
cannam@56
|
155
|
cannam@56
|
156 return m_plugin->process(m_freqbuf, timestamp);
|
cannam@56
|
157 }
|
cannam@56
|
158
|
cannam@56
|
159 void
|
cannam@56
|
160 PluginInputDomainAdapter::fft(unsigned int n, bool inverse,
|
cannam@56
|
161 double *ri, double *ii, double *ro, double *io)
|
cannam@56
|
162 {
|
cannam@56
|
163 if (!ri || !ro || !io) return;
|
cannam@56
|
164
|
cannam@56
|
165 unsigned int bits;
|
cannam@56
|
166 unsigned int i, j, k, m;
|
cannam@56
|
167 unsigned int blockSize, blockEnd;
|
cannam@56
|
168
|
cannam@56
|
169 double tr, ti;
|
cannam@56
|
170
|
cannam@56
|
171 if (n < 2) return;
|
cannam@56
|
172 if (n & (n-1)) return;
|
cannam@56
|
173
|
cannam@56
|
174 double angle = 2.0 * M_PI;
|
cannam@56
|
175 if (inverse) angle = -angle;
|
cannam@56
|
176
|
cannam@56
|
177 for (i = 0; ; ++i) {
|
cannam@56
|
178 if (n & (1 << i)) {
|
cannam@56
|
179 bits = i;
|
cannam@56
|
180 break;
|
cannam@56
|
181 }
|
cannam@56
|
182 }
|
cannam@56
|
183
|
cannam@56
|
184 static unsigned int tableSize = 0;
|
cannam@56
|
185 static int *table = 0;
|
cannam@56
|
186
|
cannam@56
|
187 if (tableSize != n) {
|
cannam@56
|
188
|
cannam@56
|
189 delete[] table;
|
cannam@56
|
190
|
cannam@56
|
191 table = new int[n];
|
cannam@56
|
192
|
cannam@56
|
193 for (i = 0; i < n; ++i) {
|
cannam@56
|
194
|
cannam@56
|
195 m = i;
|
cannam@56
|
196
|
cannam@56
|
197 for (j = k = 0; j < bits; ++j) {
|
cannam@56
|
198 k = (k << 1) | (m & 1);
|
cannam@56
|
199 m >>= 1;
|
cannam@56
|
200 }
|
cannam@56
|
201
|
cannam@56
|
202 table[i] = k;
|
cannam@56
|
203 }
|
cannam@56
|
204
|
cannam@56
|
205 tableSize = n;
|
cannam@56
|
206 }
|
cannam@56
|
207
|
cannam@56
|
208 if (ii) {
|
cannam@56
|
209 for (i = 0; i < n; ++i) {
|
cannam@56
|
210 ro[table[i]] = ri[i];
|
cannam@56
|
211 io[table[i]] = ii[i];
|
cannam@56
|
212 }
|
cannam@56
|
213 } else {
|
cannam@56
|
214 for (i = 0; i < n; ++i) {
|
cannam@56
|
215 ro[table[i]] = ri[i];
|
cannam@56
|
216 io[table[i]] = 0.0;
|
cannam@56
|
217 }
|
cannam@56
|
218 }
|
cannam@56
|
219
|
cannam@56
|
220 blockEnd = 1;
|
cannam@56
|
221
|
cannam@56
|
222 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
|
cannam@56
|
223
|
cannam@56
|
224 double delta = angle / (double)blockSize;
|
cannam@56
|
225 double sm2 = -sin(-2 * delta);
|
cannam@56
|
226 double sm1 = -sin(-delta);
|
cannam@56
|
227 double cm2 = cos(-2 * delta);
|
cannam@56
|
228 double cm1 = cos(-delta);
|
cannam@56
|
229 double w = 2 * cm1;
|
cannam@56
|
230 double ar[3], ai[3];
|
cannam@56
|
231
|
cannam@56
|
232 for (i = 0; i < n; i += blockSize) {
|
cannam@56
|
233
|
cannam@56
|
234 ar[2] = cm2;
|
cannam@56
|
235 ar[1] = cm1;
|
cannam@56
|
236
|
cannam@56
|
237 ai[2] = sm2;
|
cannam@56
|
238 ai[1] = sm1;
|
cannam@56
|
239
|
cannam@56
|
240 for (j = i, m = 0; m < blockEnd; j++, m++) {
|
cannam@56
|
241
|
cannam@56
|
242 ar[0] = w * ar[1] - ar[2];
|
cannam@56
|
243 ar[2] = ar[1];
|
cannam@56
|
244 ar[1] = ar[0];
|
cannam@56
|
245
|
cannam@56
|
246 ai[0] = w * ai[1] - ai[2];
|
cannam@56
|
247 ai[2] = ai[1];
|
cannam@56
|
248 ai[1] = ai[0];
|
cannam@56
|
249
|
cannam@56
|
250 k = j + blockEnd;
|
cannam@56
|
251 tr = ar[0] * ro[k] - ai[0] * io[k];
|
cannam@56
|
252 ti = ar[0] * io[k] + ai[0] * ro[k];
|
cannam@56
|
253
|
cannam@56
|
254 ro[k] = ro[j] - tr;
|
cannam@56
|
255 io[k] = io[j] - ti;
|
cannam@56
|
256
|
cannam@56
|
257 ro[j] += tr;
|
cannam@56
|
258 io[j] += ti;
|
cannam@56
|
259 }
|
cannam@56
|
260 }
|
cannam@56
|
261
|
cannam@56
|
262 blockEnd = blockSize;
|
cannam@56
|
263 }
|
cannam@56
|
264
|
cannam@56
|
265 if (inverse) {
|
cannam@56
|
266
|
cannam@56
|
267 double denom = (double)n;
|
cannam@56
|
268
|
cannam@56
|
269 for (i = 0; i < n; i++) {
|
cannam@56
|
270 ro[i] /= denom;
|
cannam@56
|
271 io[i] /= denom;
|
cannam@56
|
272 }
|
cannam@56
|
273 }
|
cannam@56
|
274 }
|
cannam@56
|
275
|
cannam@59
|
276 }
|
cannam@56
|
277
|
cannam@56
|
278 }
|
cannam@56
|
279
|