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 }