cannam@92
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@92
|
2
|
cannam@92
|
3 /*
|
cannam@92
|
4 Vamp
|
cannam@92
|
5
|
cannam@92
|
6 An API for audio analysis and feature extraction plugins.
|
cannam@92
|
7
|
cannam@92
|
8 Centre for Digital Music, Queen Mary, University of London.
|
cannam@92
|
9 Copyright 2006-2007 Chris Cannam and QMUL.
|
cannam@92
|
10 This file by Mark Levy, Copyright 2007 QMUL.
|
cannam@92
|
11
|
cannam@92
|
12 Permission is hereby granted, free of charge, to any person
|
cannam@92
|
13 obtaining a copy of this software and associated documentation
|
cannam@92
|
14 files (the "Software"), to deal in the Software without
|
cannam@92
|
15 restriction, including without limitation the rights to use, copy,
|
cannam@92
|
16 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@92
|
17 of the Software, and to permit persons to whom the Software is
|
cannam@92
|
18 furnished to do so, subject to the following conditions:
|
cannam@92
|
19
|
cannam@92
|
20 The above copyright notice and this permission notice shall be
|
cannam@92
|
21 included in all copies or substantial portions of the Software.
|
cannam@92
|
22
|
cannam@92
|
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@92
|
24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@92
|
25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@92
|
26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@92
|
27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@92
|
28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@92
|
29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@92
|
30
|
cannam@92
|
31 Except as contained in this notice, the names of the Centre for
|
cannam@92
|
32 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@92
|
33 shall not be used in advertising or otherwise to promote the sale,
|
cannam@92
|
34 use or other dealings in this Software without prior written
|
cannam@92
|
35 authorization.
|
cannam@92
|
36 */
|
cannam@92
|
37
|
cannam@92
|
38 #include <vector>
|
cannam@92
|
39 #include <map>
|
cannam@92
|
40
|
cannam@92
|
41 #include "PluginBufferingAdapter.h"
|
cannam@92
|
42
|
cannam@92
|
43 using std::vector;
|
cannam@92
|
44 using std::map;
|
cannam@92
|
45
|
cannam@92
|
46 namespace Vamp {
|
cannam@92
|
47
|
cannam@92
|
48 namespace HostExt {
|
cannam@92
|
49
|
cannam@92
|
50 class PluginBufferingAdapter::Impl
|
cannam@92
|
51 {
|
cannam@92
|
52 public:
|
cannam@92
|
53 Impl(Plugin *plugin, float inputSampleRate);
|
cannam@92
|
54 ~Impl();
|
cannam@92
|
55
|
cannam@92
|
56 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
|
cannam@92
|
57
|
cannam@92
|
58 OutputList getOutputDescriptors() const;
|
cannam@92
|
59
|
cannam@92
|
60 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
|
cannam@92
|
61
|
cannam@92
|
62 FeatureSet getRemainingFeatures();
|
cannam@92
|
63
|
cannam@92
|
64 protected:
|
cannam@92
|
65 Plugin *m_plugin;
|
cannam@92
|
66 size_t m_inputStepSize;
|
cannam@92
|
67 size_t m_inputBlockSize;
|
cannam@92
|
68 size_t m_stepSize;
|
cannam@92
|
69 size_t m_blockSize;
|
cannam@92
|
70 size_t m_channels;
|
cannam@92
|
71 vector<vector<float> > m_queue;
|
cannam@92
|
72 float **m_buffers; // in fact an array of pointers into the queue
|
cannam@92
|
73 size_t m_inputPos; // start position in the queue of next input block
|
cannam@92
|
74 float m_inputSampleRate;
|
cannam@92
|
75 RealTime m_timestamp;
|
cannam@92
|
76 OutputList m_outputs;
|
cannam@92
|
77
|
cannam@92
|
78 void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
|
cannam@92
|
79 };
|
cannam@92
|
80
|
cannam@92
|
81 PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
|
cannam@92
|
82 PluginWrapper(plugin)
|
cannam@92
|
83 {
|
cannam@92
|
84 m_impl = new Impl(plugin, m_inputSampleRate);
|
cannam@92
|
85 }
|
cannam@92
|
86
|
cannam@92
|
87 PluginBufferingAdapter::~PluginBufferingAdapter()
|
cannam@92
|
88 {
|
cannam@92
|
89 delete m_impl;
|
cannam@92
|
90 }
|
cannam@92
|
91
|
cannam@92
|
92 bool
|
cannam@92
|
93 PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@92
|
94 {
|
cannam@92
|
95 return m_impl->initialise(channels, stepSize, blockSize);
|
cannam@92
|
96 }
|
cannam@92
|
97
|
cannam@92
|
98 PluginBufferingAdapter::OutputList
|
cannam@92
|
99 PluginBufferingAdapter::getOutputDescriptors() const
|
cannam@92
|
100 {
|
cannam@92
|
101 return m_impl->getOutputDescriptors();
|
cannam@92
|
102 }
|
cannam@92
|
103
|
cannam@92
|
104 PluginBufferingAdapter::FeatureSet
|
cannam@92
|
105 PluginBufferingAdapter::process(const float *const *inputBuffers,
|
cannam@92
|
106 RealTime timestamp)
|
cannam@92
|
107 {
|
cannam@92
|
108 return m_impl->process(inputBuffers, timestamp);
|
cannam@92
|
109 }
|
cannam@92
|
110
|
cannam@92
|
111 PluginBufferingAdapter::FeatureSet
|
cannam@92
|
112 PluginBufferingAdapter::getRemainingFeatures()
|
cannam@92
|
113 {
|
cannam@92
|
114 return m_impl->getRemainingFeatures();
|
cannam@92
|
115 }
|
cannam@92
|
116
|
cannam@92
|
117 PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
|
cannam@92
|
118 m_plugin(plugin),
|
cannam@92
|
119 m_inputStepSize(0),
|
cannam@92
|
120 m_inputBlockSize(0),
|
cannam@92
|
121 m_stepSize(0),
|
cannam@92
|
122 m_blockSize(0),
|
cannam@92
|
123 m_channels(0),
|
cannam@92
|
124 m_buffers(0),
|
cannam@92
|
125 m_inputPos(0),
|
cannam@92
|
126 m_inputSampleRate(inputSampleRate),
|
cannam@92
|
127 m_timestamp()
|
cannam@92
|
128 {
|
cannam@92
|
129 m_outputs = plugin->getOutputDescriptors();
|
cannam@92
|
130 }
|
cannam@92
|
131
|
cannam@92
|
132 PluginBufferingAdapter::Impl::~Impl()
|
cannam@92
|
133 {
|
cannam@92
|
134 // the adapter will delete the plugin
|
cannam@92
|
135
|
cannam@92
|
136 delete [] m_buffers;
|
cannam@92
|
137 }
|
cannam@92
|
138
|
cannam@92
|
139 size_t
|
cannam@92
|
140 PluginBufferingAdapter::getPreferredStepSize() const
|
cannam@92
|
141 {
|
cannam@92
|
142 return getPreferredBlockSize();
|
cannam@92
|
143 }
|
cannam@92
|
144
|
cannam@92
|
145 bool
|
cannam@92
|
146 PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
cannam@92
|
147 {
|
cannam@92
|
148 if (stepSize != blockSize) {
|
cannam@92
|
149 std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
|
cannam@92
|
150 return false;
|
cannam@92
|
151 }
|
cannam@92
|
152
|
cannam@92
|
153 m_channels = channels;
|
cannam@92
|
154 m_inputStepSize = stepSize;
|
cannam@92
|
155 m_inputBlockSize = blockSize;
|
cannam@92
|
156
|
cannam@92
|
157 // use the step and block sizes which the plugin prefers
|
cannam@92
|
158 m_stepSize = m_plugin->getPreferredStepSize();
|
cannam@92
|
159 m_blockSize = m_plugin->getPreferredBlockSize();
|
cannam@92
|
160
|
cannam@92
|
161 // or sensible defaults if it has no preference
|
cannam@92
|
162 if (m_blockSize == 0) {
|
cannam@92
|
163 m_blockSize = 1024;
|
cannam@92
|
164 }
|
cannam@92
|
165 if (m_stepSize == 0) {
|
cannam@92
|
166 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
|
cannam@92
|
167 m_stepSize = m_blockSize/2;
|
cannam@92
|
168 } else {
|
cannam@92
|
169 m_stepSize = m_blockSize;
|
cannam@92
|
170 }
|
cannam@92
|
171 } else if (m_stepSize > m_blockSize) {
|
cannam@92
|
172 if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
|
cannam@92
|
173 m_blockSize = m_stepSize * 2;
|
cannam@92
|
174 } else {
|
cannam@92
|
175 m_blockSize = m_stepSize;
|
cannam@92
|
176 }
|
cannam@92
|
177 }
|
cannam@92
|
178
|
cannam@92
|
179 std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize
|
cannam@92
|
180 << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
|
cannam@92
|
181
|
cannam@92
|
182 // current implementation breaks if step is greater than block
|
cannam@92
|
183 if (m_stepSize > m_blockSize) {
|
cannam@92
|
184 std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl;
|
cannam@92
|
185 return false;
|
cannam@92
|
186 }
|
cannam@92
|
187
|
cannam@92
|
188 m_queue.resize(m_channels);
|
cannam@92
|
189 m_buffers = new float*[m_channels];
|
cannam@92
|
190
|
cannam@92
|
191 return m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
|
cannam@92
|
192 }
|
cannam@92
|
193
|
cannam@92
|
194 PluginBufferingAdapter::OutputList
|
cannam@92
|
195 PluginBufferingAdapter::Impl::getOutputDescriptors() const
|
cannam@92
|
196 {
|
cannam@92
|
197 OutputList outs = m_plugin->getOutputDescriptors();
|
cannam@92
|
198 for (size_t i = 0; i < outs.size(); ++i) {
|
cannam@92
|
199 if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) {
|
cannam@92
|
200 outs[i].sampleRate = 1.f / m_stepSize;
|
cannam@92
|
201 }
|
cannam@92
|
202 outs[i].sampleType = OutputDescriptor::VariableSampleRate;
|
cannam@92
|
203 }
|
cannam@92
|
204 return outs;
|
cannam@92
|
205 }
|
cannam@92
|
206
|
cannam@92
|
207 PluginBufferingAdapter::FeatureSet
|
cannam@92
|
208 PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
|
cannam@92
|
209 RealTime timestamp)
|
cannam@92
|
210 {
|
cannam@92
|
211 FeatureSet allFeatureSets;
|
cannam@92
|
212
|
cannam@92
|
213 // queue the new input
|
cannam@92
|
214
|
cannam@92
|
215 //std::cerr << "unread " << m_queue[0].size() - m_inputPos << " samples" << std::endl;
|
cannam@92
|
216 //std::cerr << "queueing " << m_inputBlockSize - (m_queue[0].size() - m_inputPos) << " samples" << std::endl;
|
cannam@92
|
217
|
cannam@92
|
218 for (size_t i = 0; i < m_channels; ++i)
|
cannam@92
|
219 for (size_t j = m_queue[0].size() - m_inputPos; j < m_inputBlockSize; ++j)
|
cannam@92
|
220 m_queue[i].push_back(inputBuffers[i][j]);
|
cannam@92
|
221
|
cannam@92
|
222 m_inputPos += m_inputStepSize;
|
cannam@92
|
223
|
cannam@92
|
224 // process as much as we can
|
cannam@92
|
225 while (m_queue[0].size() >= m_blockSize)
|
cannam@92
|
226 {
|
cannam@92
|
227 processBlock(allFeatureSets, timestamp);
|
cannam@92
|
228 m_inputPos -= m_stepSize;
|
cannam@92
|
229
|
cannam@92
|
230 //std::cerr << m_queue[0].size() << " samples still left in queue" << std::endl;
|
cannam@92
|
231 //std::cerr << "inputPos = " << m_inputPos << std::endl;
|
cannam@92
|
232 }
|
cannam@92
|
233
|
cannam@92
|
234 return allFeatureSets;
|
cannam@92
|
235 }
|
cannam@92
|
236
|
cannam@92
|
237 PluginBufferingAdapter::FeatureSet
|
cannam@92
|
238 PluginBufferingAdapter::Impl::getRemainingFeatures()
|
cannam@92
|
239 {
|
cannam@92
|
240 FeatureSet allFeatureSets;
|
cannam@92
|
241
|
cannam@92
|
242 // process remaining samples in queue
|
cannam@92
|
243 while (m_queue[0].size() >= m_blockSize)
|
cannam@92
|
244 {
|
cannam@92
|
245 processBlock(allFeatureSets, m_timestamp);
|
cannam@92
|
246 }
|
cannam@92
|
247
|
cannam@92
|
248 // pad any last samples remaining and process
|
cannam@92
|
249 if (m_queue[0].size() > 0)
|
cannam@92
|
250 {
|
cannam@92
|
251 for (size_t i = 0; i < m_channels; ++i)
|
cannam@92
|
252 while (m_queue[i].size() < m_blockSize)
|
cannam@92
|
253 m_queue[i].push_back(0.0);
|
cannam@92
|
254 processBlock(allFeatureSets, m_timestamp);
|
cannam@92
|
255 }
|
cannam@92
|
256
|
cannam@92
|
257 // get remaining features
|
cannam@92
|
258 FeatureSet featureSet = m_plugin->getRemainingFeatures();
|
cannam@92
|
259 for (map<int, FeatureList>::iterator iter = featureSet.begin();
|
cannam@92
|
260 iter != featureSet.end(); ++iter)
|
cannam@92
|
261 {
|
cannam@92
|
262 FeatureList featureList = iter->second;
|
cannam@92
|
263 for (size_t i = 0; i < featureList.size(); ++i)
|
cannam@92
|
264 allFeatureSets[iter->first].push_back(featureList[i]);
|
cannam@92
|
265 }
|
cannam@92
|
266
|
cannam@92
|
267 return allFeatureSets;
|
cannam@92
|
268 }
|
cannam@92
|
269
|
cannam@92
|
270 void
|
cannam@92
|
271 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, RealTime timestamp)
|
cannam@92
|
272 {
|
cannam@92
|
273 //std::cerr << m_queue[0].size() << " samples left in queue" << std::endl;
|
cannam@92
|
274
|
cannam@92
|
275 // point the buffers to the head of the queue
|
cannam@92
|
276 for (size_t i = 0; i < m_channels; ++i)
|
cannam@92
|
277 m_buffers[i] = &m_queue[i][0];
|
cannam@92
|
278
|
cannam@92
|
279 FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp);
|
cannam@92
|
280
|
cannam@92
|
281 for (map<int, FeatureList>::iterator iter = featureSet.begin();
|
cannam@92
|
282 iter != featureSet.end(); ++iter)
|
cannam@92
|
283 {
|
cannam@92
|
284
|
cannam@92
|
285 FeatureList featureList = iter->second;
|
cannam@92
|
286 int outputNo = iter->first;
|
cannam@92
|
287
|
cannam@92
|
288 for (size_t i = 0; i < featureList.size(); ++i)
|
cannam@92
|
289 {
|
cannam@92
|
290
|
cannam@92
|
291 // make sure the timestamp is set
|
cannam@92
|
292 switch (m_outputs[outputNo].sampleType)
|
cannam@92
|
293 {
|
cannam@92
|
294 case OutputDescriptor::OneSamplePerStep:
|
cannam@92
|
295 // use our internal timestamp - OK????
|
cannam@92
|
296 featureList[i].timestamp = m_timestamp;
|
cannam@92
|
297 break;
|
cannam@92
|
298 case OutputDescriptor::FixedSampleRate:
|
cannam@92
|
299 // use our internal timestamp
|
cannam@92
|
300 featureList[i].timestamp = m_timestamp;
|
cannam@92
|
301 break;
|
cannam@92
|
302 case OutputDescriptor::VariableSampleRate:
|
cannam@92
|
303 break; // plugin must set timestamp
|
cannam@92
|
304 default:
|
cannam@92
|
305 break;
|
cannam@92
|
306 }
|
cannam@92
|
307
|
cannam@92
|
308 allFeatureSets[outputNo].push_back(featureList[i]);
|
cannam@92
|
309 }
|
cannam@92
|
310 }
|
cannam@92
|
311
|
cannam@92
|
312 // step forward
|
cannam@92
|
313 for (size_t i = 0; i < m_channels; ++i)
|
cannam@92
|
314 m_queue[i].erase(m_queue[i].begin(), m_queue[i].begin() + m_stepSize);
|
cannam@92
|
315
|
cannam@92
|
316 // fake up the timestamp each time we step forward
|
cannam@92
|
317 //std::cerr << m_timestamp;
|
cannam@92
|
318 long frame = RealTime::realTime2Frame(m_timestamp, int(m_inputSampleRate + 0.5));
|
cannam@92
|
319 m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, int(m_inputSampleRate + 0.5));
|
cannam@92
|
320 //std::cerr << "--->" << m_timestamp << std::endl;
|
cannam@92
|
321 }
|
cannam@92
|
322
|
cannam@92
|
323 }
|
cannam@92
|
324
|
cannam@92
|
325 }
|
cannam@92
|
326
|
cannam@92
|
327
|