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