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