Mercurial > hg > vamp-plugin-sdk
comparison vamp-sdk/hostext/PluginBufferingAdapter.cpp @ 102:ca40f3bc99f0
* Make PluginBufferingAdapter more efficient with use of ring buffer in place
of copy-shuffling queue
author | cannam |
---|---|
date | Tue, 29 Jan 2008 14:23:06 +0000 |
parents | c94c066a4897 |
children | 08d8c8ee6097 |
comparison
equal
deleted
inserted
replaced
101:5c9f267c48c0 | 102:ca40f3bc99f0 |
---|---|
5 | 5 |
6 An API for audio analysis and feature extraction plugins. | 6 An API for audio analysis and feature extraction plugins. |
7 | 7 |
8 Centre for Digital Music, Queen Mary, University of London. | 8 Centre for Digital Music, Queen Mary, University of London. |
9 Copyright 2006-2007 Chris Cannam and QMUL. | 9 Copyright 2006-2007 Chris Cannam and QMUL. |
10 This file by Mark Levy, Copyright 2007 QMUL. | 10 This file by Mark Levy and Chris Cannam. |
11 | 11 |
12 Permission is hereby granted, free of charge, to any person | 12 Permission is hereby granted, free of charge, to any person |
13 obtaining a copy of this software and associated documentation | 13 obtaining a copy of this software and associated documentation |
14 files (the "Software"), to deal in the Software without | 14 files (the "Software"), to deal in the Software without |
15 restriction, including without limitation the rights to use, copy, | 15 restriction, including without limitation the rights to use, copy, |
60 FeatureSet process(const float *const *inputBuffers, RealTime timestamp); | 60 FeatureSet process(const float *const *inputBuffers, RealTime timestamp); |
61 | 61 |
62 FeatureSet getRemainingFeatures(); | 62 FeatureSet getRemainingFeatures(); |
63 | 63 |
64 protected: | 64 protected: |
65 class RingBuffer | |
66 { | |
67 public: | |
68 RingBuffer(int n) : | |
69 m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { } | |
70 virtual ~RingBuffer() { delete[] m_buffer; } | |
71 | |
72 int getSize() const { return m_size-1; } | |
73 void reset() { m_writer = 0; m_reader = 0; } | |
74 | |
75 int getReadSpace() const { | |
76 int writer = m_writer, reader = m_reader, space; | |
77 if (writer > reader) space = writer - reader; | |
78 else if (writer < reader) space = (writer + m_size) - reader; | |
79 else space = 0; | |
80 return space; | |
81 } | |
82 | |
83 int getWriteSpace() const { | |
84 int writer = m_writer; | |
85 int reader = m_reader; | |
86 int space = (reader + m_size - writer - 1); | |
87 if (space >= m_size) space -= m_size; | |
88 return space; | |
89 } | |
90 | |
91 int peek(float *destination, int n) const { | |
92 | |
93 int available = getReadSpace(); | |
94 | |
95 if (n > available) { | |
96 for (int i = available; i < n; ++i) { | |
97 destination[i] = 0.f; | |
98 } | |
99 n = available; | |
100 } | |
101 if (n == 0) return n; | |
102 | |
103 int reader = m_reader; | |
104 int here = m_size - reader; | |
105 const float *const bufbase = m_buffer + reader; | |
106 | |
107 if (here >= n) { | |
108 for (int i = 0; i < n; ++i) { | |
109 destination[i] = bufbase[i]; | |
110 } | |
111 } else { | |
112 for (int i = 0; i < here; ++i) { | |
113 destination[i] = bufbase[i]; | |
114 } | |
115 float *const destbase = destination + here; | |
116 const int nh = n - here; | |
117 for (int i = 0; i < nh; ++i) { | |
118 destbase[i] = m_buffer[i]; | |
119 } | |
120 } | |
121 | |
122 return n; | |
123 } | |
124 | |
125 int skip(int n) { | |
126 | |
127 int available = getReadSpace(); | |
128 if (n > available) { | |
129 n = available; | |
130 } | |
131 if (n == 0) return n; | |
132 | |
133 int reader = m_reader; | |
134 reader += n; | |
135 while (reader >= m_size) reader -= m_size; | |
136 m_reader = reader; | |
137 return n; | |
138 } | |
139 | |
140 int write(const float *source, int n) { | |
141 | |
142 int available = getWriteSpace(); | |
143 if (n > available) { | |
144 n = available; | |
145 } | |
146 if (n == 0) return n; | |
147 | |
148 int writer = m_writer; | |
149 int here = m_size - writer; | |
150 float *const bufbase = m_buffer + writer; | |
151 | |
152 if (here >= n) { | |
153 for (int i = 0; i < n; ++i) { | |
154 bufbase[i] = source[i]; | |
155 } | |
156 } else { | |
157 for (int i = 0; i < here; ++i) { | |
158 bufbase[i] = source[i]; | |
159 } | |
160 const int nh = n - here; | |
161 const float *const srcbase = source + here; | |
162 float *const buf = m_buffer; | |
163 for (int i = 0; i < nh; ++i) { | |
164 buf[i] = srcbase[i]; | |
165 } | |
166 } | |
167 | |
168 writer += n; | |
169 while (writer >= m_size) writer -= m_size; | |
170 m_writer = writer; | |
171 | |
172 return n; | |
173 } | |
174 | |
175 int zero(int n) { | |
176 | |
177 int available = getWriteSpace(); | |
178 if (n > available) { | |
179 n = available; | |
180 } | |
181 if (n == 0) return n; | |
182 | |
183 int writer = m_writer; | |
184 int here = m_size - writer; | |
185 float *const bufbase = m_buffer + writer; | |
186 | |
187 if (here >= n) { | |
188 for (int i = 0; i < n; ++i) { | |
189 bufbase[i] = 0.f; | |
190 } | |
191 } else { | |
192 for (int i = 0; i < here; ++i) { | |
193 bufbase[i] = 0.f; | |
194 } | |
195 const int nh = n - here; | |
196 for (int i = 0; i < nh; ++i) { | |
197 m_buffer[i] = 0.f; | |
198 } | |
199 } | |
200 | |
201 writer += n; | |
202 while (writer >= m_size) writer -= m_size; | |
203 m_writer = writer; | |
204 | |
205 return n; | |
206 } | |
207 | |
208 protected: | |
209 float *m_buffer; | |
210 int m_writer; | |
211 int m_reader; | |
212 int m_size; | |
213 | |
214 private: | |
215 RingBuffer(const RingBuffer &); // not provided | |
216 RingBuffer &operator=(const RingBuffer &); // not provided | |
217 }; | |
218 | |
65 Plugin *m_plugin; | 219 Plugin *m_plugin; |
66 size_t m_inputStepSize; | 220 size_t m_inputStepSize; |
67 size_t m_inputBlockSize; | 221 size_t m_inputBlockSize; |
68 size_t m_stepSize; | 222 size_t m_stepSize; |
69 size_t m_blockSize; | 223 size_t m_blockSize; |
70 size_t m_channels; | 224 size_t m_channels; |
71 vector<vector<float> > m_queue; | 225 vector<RingBuffer *> m_queue; |
72 float **m_buffers; // in fact an array of pointers into the queue | 226 float **m_buffers; |
73 size_t m_inputPos; // start position in the queue of next input block | |
74 float m_inputSampleRate; | 227 float m_inputSampleRate; |
75 RealTime m_timestamp; | 228 RealTime m_timestamp; |
76 OutputList m_outputs; | 229 OutputList m_outputs; |
77 | 230 |
78 void processBlock(FeatureSet& allFeatureSets, RealTime timestamp); | 231 void processBlock(FeatureSet& allFeatureSets, RealTime timestamp); |
119 m_inputStepSize(0), | 272 m_inputStepSize(0), |
120 m_inputBlockSize(0), | 273 m_inputBlockSize(0), |
121 m_stepSize(0), | 274 m_stepSize(0), |
122 m_blockSize(0), | 275 m_blockSize(0), |
123 m_channels(0), | 276 m_channels(0), |
277 m_queue(0), | |
124 m_buffers(0), | 278 m_buffers(0), |
125 m_inputPos(0), | |
126 m_inputSampleRate(inputSampleRate), | 279 m_inputSampleRate(inputSampleRate), |
127 m_timestamp() | 280 m_timestamp() |
128 { | 281 { |
129 m_outputs = plugin->getOutputDescriptors(); | 282 m_outputs = plugin->getOutputDescriptors(); |
130 } | 283 } |
131 | 284 |
132 PluginBufferingAdapter::Impl::~Impl() | 285 PluginBufferingAdapter::Impl::~Impl() |
133 { | 286 { |
134 // the adapter will delete the plugin | 287 // the adapter will delete the plugin |
135 | 288 |
136 delete [] m_buffers; | 289 for (size_t i = 0; i < m_channels; ++i) { |
290 delete m_queue[i]; | |
291 delete[] m_buffers[i]; | |
292 } | |
293 delete[] m_buffers; | |
137 } | 294 } |
138 | 295 |
139 size_t | 296 size_t |
140 PluginBufferingAdapter::getPreferredStepSize() const | 297 PluginBufferingAdapter::getPreferredStepSize() const |
141 { | 298 { |
182 // current implementation breaks if step is greater than block | 339 // current implementation breaks if step is greater than block |
183 if (m_stepSize > m_blockSize) { | 340 if (m_stepSize > m_blockSize) { |
184 std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl; | 341 std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl; |
185 return false; | 342 return false; |
186 } | 343 } |
187 | 344 |
188 m_queue.resize(m_channels); | 345 m_buffers = new float *[m_channels]; |
189 m_buffers = new float*[m_channels]; | 346 |
347 for (size_t i = 0; i < m_channels; ++i) { | |
348 m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize)); | |
349 m_buffers[i] = new float[m_blockSize]; | |
350 } | |
190 | 351 |
191 return m_plugin->initialise(m_channels, m_stepSize, m_blockSize); | 352 return m_plugin->initialise(m_channels, m_stepSize, m_blockSize); |
192 } | 353 } |
193 | 354 |
194 PluginBufferingAdapter::OutputList | 355 PluginBufferingAdapter::OutputList |
210 { | 371 { |
211 FeatureSet allFeatureSets; | 372 FeatureSet allFeatureSets; |
212 | 373 |
213 // queue the new input | 374 // queue the new input |
214 | 375 |
215 //std::cerr << "unread " << m_queue[0].size() - m_inputPos << " samples" << std::endl; | 376 for (size_t i = 0; i < m_channels; ++i) { |
216 //std::cerr << "queueing " << m_inputBlockSize - (m_queue[0].size() - m_inputPos) << " samples" << std::endl; | 377 int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize); |
217 | 378 if (written < int(m_inputBlockSize) && i == 0) { |
218 for (size_t i = 0; i < m_channels; ++i) | 379 std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: " |
219 for (size_t j = m_queue[0].size() - m_inputPos; j < m_inputBlockSize; ++j) | 380 << "Buffer overflow: wrote " << written |
220 m_queue[i].push_back(inputBuffers[i][j]); | 381 << " of " << m_inputBlockSize |
221 | 382 << " input samples (for plugin step size " |
222 m_inputPos += m_inputStepSize; | 383 << m_stepSize << ", block size " << m_blockSize << ")" |
384 << std::endl; | |
385 } | |
386 } | |
223 | 387 |
224 // process as much as we can | 388 // process as much as we can |
225 while (m_queue[0].size() >= m_blockSize) | 389 |
226 { | 390 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { |
227 processBlock(allFeatureSets, timestamp); | 391 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 } | 392 } |
233 | 393 |
234 return allFeatureSets; | 394 return allFeatureSets; |
235 } | 395 } |
236 | 396 |
238 PluginBufferingAdapter::Impl::getRemainingFeatures() | 398 PluginBufferingAdapter::Impl::getRemainingFeatures() |
239 { | 399 { |
240 FeatureSet allFeatureSets; | 400 FeatureSet allFeatureSets; |
241 | 401 |
242 // process remaining samples in queue | 402 // process remaining samples in queue |
243 while (m_queue[0].size() >= m_blockSize) | 403 while (m_queue[0]->getReadSpace() >= int(m_blockSize)) { |
244 { | |
245 processBlock(allFeatureSets, m_timestamp); | 404 processBlock(allFeatureSets, m_timestamp); |
246 } | 405 } |
247 | 406 |
248 // pad any last samples remaining and process | 407 // pad any last samples remaining and process |
249 if (m_queue[0].size() > 0) | 408 if (m_queue[0]->getReadSpace() > 0) { |
250 { | 409 for (size_t i = 0; i < m_channels; ++i) { |
251 for (size_t i = 0; i < m_channels; ++i) | 410 m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace()); |
252 while (m_queue[i].size() < m_blockSize) | 411 } |
253 m_queue[i].push_back(0.0); | |
254 processBlock(allFeatureSets, m_timestamp); | 412 processBlock(allFeatureSets, m_timestamp); |
255 } | 413 } |
256 | 414 |
257 // get remaining features | 415 // get remaining features |
416 | |
258 FeatureSet featureSet = m_plugin->getRemainingFeatures(); | 417 FeatureSet featureSet = m_plugin->getRemainingFeatures(); |
418 | |
259 for (map<int, FeatureList>::iterator iter = featureSet.begin(); | 419 for (map<int, FeatureList>::iterator iter = featureSet.begin(); |
260 iter != featureSet.end(); ++iter) | 420 iter != featureSet.end(); ++iter) { |
261 { | |
262 FeatureList featureList = iter->second; | 421 FeatureList featureList = iter->second; |
263 for (size_t i = 0; i < featureList.size(); ++i) | 422 for (size_t i = 0; i < featureList.size(); ++i) { |
264 allFeatureSets[iter->first].push_back(featureList[i]); | 423 allFeatureSets[iter->first].push_back(featureList[i]); |
424 } | |
265 } | 425 } |
266 | 426 |
267 return allFeatureSets; | 427 return allFeatureSets; |
268 } | 428 } |
269 | 429 |
270 void | 430 void |
271 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, RealTime timestamp) | 431 PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets, |
272 { | 432 RealTime timestamp) |
273 //std::cerr << m_queue[0].size() << " samples left in queue" << std::endl; | 433 { |
274 | 434 for (size_t i = 0; i < m_channels; ++i) { |
275 // point the buffers to the head of the queue | 435 m_queue[i]->peek(m_buffers[i], m_blockSize); |
276 for (size_t i = 0; i < m_channels; ++i) | 436 } |
277 m_buffers[i] = &m_queue[i][0]; | 437 |
278 | |
279 FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp); | 438 FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp); |
280 | 439 |
281 for (map<int, FeatureList>::iterator iter = featureSet.begin(); | 440 for (map<int, FeatureList>::iterator iter = featureSet.begin(); |
282 iter != featureSet.end(); ++iter) | 441 iter != featureSet.end(); ++iter) { |
283 { | |
284 | 442 |
285 FeatureList featureList = iter->second; | 443 FeatureList featureList = iter->second; |
286 int outputNo = iter->first; | 444 int outputNo = iter->first; |
287 | 445 |
288 for (size_t i = 0; i < featureList.size(); ++i) | 446 for (size_t i = 0; i < featureList.size(); ++i) { |
289 { | |
290 | 447 |
291 // make sure the timestamp is set | 448 // make sure the timestamp is set |
292 switch (m_outputs[outputNo].sampleType) | 449 switch (m_outputs[outputNo].sampleType) { |
293 { | 450 |
294 case OutputDescriptor::OneSamplePerStep: | 451 case OutputDescriptor::OneSamplePerStep: |
295 // use our internal timestamp - OK???? | 452 // use our internal timestamp - OK???? |
296 featureList[i].timestamp = m_timestamp; | 453 featureList[i].timestamp = m_timestamp; |
297 break; | 454 break; |
455 | |
298 case OutputDescriptor::FixedSampleRate: | 456 case OutputDescriptor::FixedSampleRate: |
299 // use our internal timestamp | 457 // use our internal timestamp |
300 featureList[i].timestamp = m_timestamp; | 458 featureList[i].timestamp = m_timestamp; |
301 break; | 459 break; |
460 | |
302 case OutputDescriptor::VariableSampleRate: | 461 case OutputDescriptor::VariableSampleRate: |
303 break; // plugin must set timestamp | 462 break; // plugin must set timestamp |
463 | |
304 default: | 464 default: |
305 break; | 465 break; |
306 } | 466 } |
307 | 467 |
308 allFeatureSets[outputNo].push_back(featureList[i]); | 468 allFeatureSets[outputNo].push_back(featureList[i]); |
309 } | 469 } |
310 } | 470 } |
311 | 471 |
312 // step forward | 472 // step forward |
313 for (size_t i = 0; i < m_channels; ++i) | 473 |
314 m_queue[i].erase(m_queue[i].begin(), m_queue[i].begin() + m_stepSize); | 474 for (size_t i = 0; i < m_channels; ++i) { |
475 m_queue[i]->skip(m_stepSize); | |
476 } | |
315 | 477 |
316 // fake up the timestamp each time we step forward | 478 // fake up the timestamp each time we step forward |
317 //std::cerr << m_timestamp; | 479 |
318 long frame = RealTime::realTime2Frame(m_timestamp, int(m_inputSampleRate + 0.5)); | 480 long frame = RealTime::realTime2Frame(m_timestamp, |
319 m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, int(m_inputSampleRate + 0.5)); | 481 int(m_inputSampleRate + 0.5)); |
320 //std::cerr << "--->" << m_timestamp << std::endl; | 482 m_timestamp = RealTime::frame2RealTime(frame + m_stepSize, |
483 int(m_inputSampleRate + 0.5)); | |
321 } | 484 } |
322 | 485 |
323 } | 486 } |
324 | 487 |
325 } | 488 } |