Mercurial > hg > svcore
comparison base/RingBuffer.h @ 0:da6937383da8
initial import
author | Chris Cannam |
---|---|
date | Tue, 10 Jan 2006 16:33:16 +0000 |
parents | |
children | d86891498eef |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:da6937383da8 |
---|---|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 A waveform viewer and audio annotation editor. | |
5 Chris Cannam, Queen Mary University of London, 2005 | |
6 | |
7 This is experimental software. Not for distribution. | |
8 */ | |
9 | |
10 /* | |
11 This is a modified version of a source file from the | |
12 Rosegarden MIDI and audio sequencer and notation editor. | |
13 This file copyright 2000-2005 Chris Cannam. | |
14 */ | |
15 | |
16 #ifndef _RINGBUFFER_H_ | |
17 #define _RINGBUFFER_H_ | |
18 | |
19 #include <sys/types.h> | |
20 | |
21 #include "System.h" | |
22 #include "Scavenger.h" | |
23 | |
24 //#define DEBUG_RINGBUFFER 1 | |
25 | |
26 #ifdef DEBUG_RINGBUFFER | |
27 #include <iostream> | |
28 #endif | |
29 | |
30 /** | |
31 * RingBuffer implements a lock-free ring buffer for one writer and N | |
32 * readers, that is to be used to store a sample type T. | |
33 * | |
34 * For efficiency, RingBuffer frequently initialises samples by | |
35 * writing zeroes into their memory space, so T should normally be a | |
36 * simple type that can safely be set to zero using memset. | |
37 */ | |
38 | |
39 template <typename T, int N = 1> | |
40 class RingBuffer | |
41 { | |
42 public: | |
43 /** | |
44 * Create a ring buffer with room to write n samples. | |
45 * | |
46 * Note that the internal storage size will actually be n+1 | |
47 * samples, as one element is unavailable for administrative | |
48 * reasons. Since the ring buffer performs best if its size is a | |
49 * power of two, this means n should ideally be some power of two | |
50 * minus one. | |
51 */ | |
52 RingBuffer(size_t n); | |
53 | |
54 virtual ~RingBuffer(); | |
55 | |
56 /** | |
57 * Return the total capacity of the ring buffer in samples. | |
58 * (This is the argument n passed to the constructor.) | |
59 */ | |
60 size_t getSize() const; | |
61 | |
62 /** | |
63 * Resize the ring buffer. This also empties it. Actually swaps | |
64 * in a new, larger buffer; the old buffer is scavenged after a | |
65 * seemly delay. Should be called from the write thread. | |
66 */ | |
67 void resize(size_t newSize); | |
68 | |
69 /** | |
70 * Lock the ring buffer into physical memory. Returns true | |
71 * for success. | |
72 */ | |
73 bool mlock(); | |
74 | |
75 /** | |
76 * Reset read and write pointers, thus emptying the buffer. | |
77 * Should be called from the write thread. | |
78 */ | |
79 void reset(); | |
80 | |
81 /** | |
82 * Return the amount of data available for reading by reader R, in | |
83 * samples. | |
84 */ | |
85 size_t getReadSpace(int R = 0) const; | |
86 | |
87 /** | |
88 * Return the amount of space available for writing, in samples. | |
89 */ | |
90 size_t getWriteSpace() const; | |
91 | |
92 /** | |
93 * Read n samples from the buffer, for reader R. If fewer than n | |
94 * are available, the remainder will be zeroed out. Returns the | |
95 * number of samples actually read. | |
96 */ | |
97 size_t read(T *destination, size_t n, int R = 0); | |
98 | |
99 /** | |
100 * Read n samples from the buffer, for reader R, adding them to | |
101 * the destination. If fewer than n are available, the remainder | |
102 * will be left alone. Returns the number of samples actually | |
103 * read. | |
104 */ | |
105 size_t readAdding(T *destination, size_t n, int R = 0); | |
106 | |
107 /** | |
108 * Read one sample from the buffer, for reader R. If no sample is | |
109 * available, this will silently return zero. Calling this | |
110 * repeatedly is obviously slower than calling read once, but it | |
111 * may be good enough if you don't want to allocate a buffer to | |
112 * read into. | |
113 */ | |
114 T readOne(int R = 0); | |
115 | |
116 /** | |
117 * Read n samples from the buffer, if available, for reader R, | |
118 * without advancing the read pointer -- i.e. a subsequent read() | |
119 * or skip() will be necessary to empty the buffer. If fewer than | |
120 * n are available, the remainder will be zeroed out. Returns the | |
121 * number of samples actually read. | |
122 */ | |
123 size_t peek(T *destination, size_t n, int R = 0) const; | |
124 | |
125 /** | |
126 * Read one sample from the buffer, if available, without | |
127 * advancing the read pointer -- i.e. a subsequent read() or | |
128 * skip() will be necessary to empty the buffer. Returns zero if | |
129 * no sample was available. | |
130 */ | |
131 T peekOne(int R = 0) const; | |
132 | |
133 /** | |
134 * Pretend to read n samples from the buffer, for reader R, | |
135 * without actually returning them (i.e. discard the next n | |
136 * samples). Returns the number of samples actually available for | |
137 * discarding. | |
138 */ | |
139 size_t skip(size_t n, int R = 0); | |
140 | |
141 /** | |
142 * Write n samples to the buffer. If insufficient space is | |
143 * available, not all samples may actually be written. Returns | |
144 * the number of samples actually written. | |
145 */ | |
146 size_t write(const T *source, size_t n); | |
147 | |
148 /** | |
149 * Write n zero-value samples to the buffer. If insufficient | |
150 * space is available, not all zeros may actually be written. | |
151 * Returns the number of zeroes actually written. | |
152 */ | |
153 size_t zero(size_t n); | |
154 | |
155 protected: | |
156 T *m_buffer; | |
157 volatile size_t m_writer; | |
158 volatile size_t m_readers[N]; | |
159 size_t m_size; | |
160 bool m_mlocked; | |
161 | |
162 static Scavenger<ScavengerArrayWrapper<T> > m_scavenger; | |
163 | |
164 private: | |
165 RingBuffer(const RingBuffer &); // not provided | |
166 RingBuffer &operator=(const RingBuffer &); // not provided | |
167 }; | |
168 | |
169 template <typename T, int N> | |
170 Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger; | |
171 | |
172 template <typename T, int N> | |
173 RingBuffer<T, N>::RingBuffer(size_t n) : | |
174 m_buffer(new T[n + 1]), | |
175 m_writer(0), | |
176 m_size(n + 1), | |
177 m_mlocked(false) | |
178 { | |
179 #ifdef DEBUG_RINGBUFFER | |
180 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl; | |
181 #endif | |
182 | |
183 for (int i = 0; i < N; ++i) m_readers[i] = 0; | |
184 | |
185 m_scavenger.scavenge(); | |
186 } | |
187 | |
188 template <typename T, int N> | |
189 RingBuffer<T, N>::~RingBuffer() | |
190 { | |
191 #ifdef DEBUG_RINGBUFFER | |
192 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl; | |
193 #endif | |
194 | |
195 if (m_mlocked) { | |
196 MUNLOCK((void *)m_buffer, m_size * sizeof(T)); | |
197 } | |
198 delete[] m_buffer; | |
199 | |
200 m_scavenger.scavenge(); | |
201 } | |
202 | |
203 template <typename T, int N> | |
204 size_t | |
205 RingBuffer<T, N>::getSize() const | |
206 { | |
207 #ifdef DEBUG_RINGBUFFER | |
208 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl; | |
209 #endif | |
210 | |
211 return m_size - 1; | |
212 } | |
213 | |
214 template <typename T, int N> | |
215 void | |
216 RingBuffer<T, N>::resize(size_t newSize) | |
217 { | |
218 #ifdef DEBUG_RINGBUFFER | |
219 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resize(" << newSize << ")" << std::endl; | |
220 #endif | |
221 | |
222 m_scavenger.scavenge(); | |
223 | |
224 if (m_mlocked) { | |
225 MUNLOCK((void *)m_buffer, m_size * sizeof(T)); | |
226 } | |
227 | |
228 m_scavenger.claim(new ScavengerArrayWrapper<T>(m_buffer)); | |
229 | |
230 reset(); | |
231 m_buffer = new T[newSize + 1]; | |
232 m_size = newSize + 1; | |
233 | |
234 if (m_mlocked) { | |
235 if (MLOCK((void *)m_buffer, m_size * sizeof(T))) { | |
236 m_mlocked = false; | |
237 } | |
238 } | |
239 } | |
240 | |
241 template <typename T, int N> | |
242 bool | |
243 RingBuffer<T, N>::mlock() | |
244 { | |
245 if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false; | |
246 m_mlocked = true; | |
247 return true; | |
248 } | |
249 | |
250 template <typename T, int N> | |
251 void | |
252 RingBuffer<T, N>::reset() | |
253 { | |
254 #ifdef DEBUG_RINGBUFFER | |
255 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl; | |
256 #endif | |
257 | |
258 m_writer = 0; | |
259 for (int i = 0; i < N; ++i) m_readers[i] = 0; | |
260 } | |
261 | |
262 template <typename T, int N> | |
263 size_t | |
264 RingBuffer<T, N>::getReadSpace(int R) const | |
265 { | |
266 size_t writer = m_writer; | |
267 size_t reader = m_readers[R]; | |
268 size_t space = 0; | |
269 | |
270 if (writer > reader) space = writer - reader; | |
271 else space = ((writer + m_size) - reader) % m_size; | |
272 | |
273 #ifdef DEBUG_RINGBUFFER | |
274 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl; | |
275 #endif | |
276 | |
277 return space; | |
278 } | |
279 | |
280 template <typename T, int N> | |
281 size_t | |
282 RingBuffer<T, N>::getWriteSpace() const | |
283 { | |
284 size_t space = 0; | |
285 for (int i = 0; i < N; ++i) { | |
286 size_t here = (m_readers[i] + m_size - m_writer - 1) % m_size; | |
287 if (i == 0 || here < space) space = here; | |
288 } | |
289 | |
290 #ifdef DEBUG_RINGBUFFER | |
291 size_t rs(getReadSpace()), rp(m_readers[0]); | |
292 | |
293 std::cerr << "RingBuffer: write space " << space << ", read space " | |
294 << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl; | |
295 std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl; | |
296 #endif | |
297 | |
298 #ifdef DEBUG_RINGBUFFER | |
299 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl; | |
300 #endif | |
301 | |
302 return space; | |
303 } | |
304 | |
305 template <typename T, int N> | |
306 size_t | |
307 RingBuffer<T, N>::read(T *destination, size_t n, int R) | |
308 { | |
309 #ifdef DEBUG_RINGBUFFER | |
310 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl; | |
311 #endif | |
312 | |
313 size_t available = getReadSpace(R); | |
314 if (n > available) { | |
315 #ifdef DEBUG_RINGBUFFER | |
316 std::cerr << "WARNING: Only " << available << " samples available" | |
317 << std::endl; | |
318 #endif | |
319 memset(destination + available, 0, (n - available) * sizeof(T)); | |
320 n = available; | |
321 } | |
322 if (n == 0) return n; | |
323 | |
324 size_t here = m_size - m_readers[R]; | |
325 if (here >= n) { | |
326 memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); | |
327 } else { | |
328 memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); | |
329 memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); | |
330 } | |
331 | |
332 m_readers[R] = (m_readers[R] + n) % m_size; | |
333 | |
334 #ifdef DEBUG_RINGBUFFER | |
335 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl; | |
336 #endif | |
337 | |
338 return n; | |
339 } | |
340 | |
341 template <typename T, int N> | |
342 size_t | |
343 RingBuffer<T, N>::readAdding(T *destination, size_t n, int R) | |
344 { | |
345 #ifdef DEBUG_RINGBUFFER | |
346 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl; | |
347 #endif | |
348 | |
349 size_t available = getReadSpace(R); | |
350 if (n > available) { | |
351 #ifdef DEBUG_RINGBUFFER | |
352 std::cerr << "WARNING: Only " << available << " samples available" | |
353 << std::endl; | |
354 #endif | |
355 n = available; | |
356 } | |
357 if (n == 0) return n; | |
358 | |
359 size_t here = m_size - m_readers[R]; | |
360 | |
361 if (here >= n) { | |
362 for (size_t i = 0; i < n; ++i) { | |
363 destination[i] += (m_buffer + m_readers[R])[i]; | |
364 } | |
365 } else { | |
366 for (size_t i = 0; i < here; ++i) { | |
367 destination[i] += (m_buffer + m_readers[R])[i]; | |
368 } | |
369 for (size_t i = 0; i < (n - here); ++i) { | |
370 destination[i + here] += m_buffer[i]; | |
371 } | |
372 } | |
373 | |
374 m_readers[R] = (m_readers[R] + n) % m_size; | |
375 return n; | |
376 } | |
377 | |
378 template <typename T, int N> | |
379 T | |
380 RingBuffer<T, N>::readOne(int R) | |
381 { | |
382 #ifdef DEBUG_RINGBUFFER | |
383 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl; | |
384 #endif | |
385 | |
386 if (m_writer == m_readers[R]) { | |
387 #ifdef DEBUG_RINGBUFFER | |
388 std::cerr << "WARNING: No sample available" | |
389 << std::endl; | |
390 #endif | |
391 T t; | |
392 memset(&t, 0, sizeof(T)); | |
393 return t; | |
394 } | |
395 T value = m_buffer[m_readers[R]]; | |
396 if (++m_readers[R] == m_size) m_readers[R] = 0; | |
397 return value; | |
398 } | |
399 | |
400 template <typename T, int N> | |
401 size_t | |
402 RingBuffer<T, N>::peek(T *destination, size_t n, int R) const | |
403 { | |
404 #ifdef DEBUG_RINGBUFFER | |
405 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl; | |
406 #endif | |
407 | |
408 size_t available = getReadSpace(R); | |
409 if (n > available) { | |
410 #ifdef DEBUG_RINGBUFFER | |
411 std::cerr << "WARNING: Only " << available << " samples available" | |
412 << std::endl; | |
413 #endif | |
414 memset(destination + available, 0, (n - available) * sizeof(T)); | |
415 n = available; | |
416 } | |
417 if (n == 0) return n; | |
418 | |
419 size_t here = m_size - m_readers[R]; | |
420 if (here >= n) { | |
421 memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); | |
422 } else { | |
423 memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); | |
424 memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); | |
425 } | |
426 | |
427 #ifdef DEBUG_RINGBUFFER | |
428 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl; | |
429 #endif | |
430 | |
431 return n; | |
432 } | |
433 | |
434 template <typename T, int N> | |
435 T | |
436 RingBuffer<T, N>::peekOne(int R) const | |
437 { | |
438 #ifdef DEBUG_RINGBUFFER | |
439 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl; | |
440 #endif | |
441 | |
442 if (m_writer == m_readers[R]) { | |
443 #ifdef DEBUG_RINGBUFFER | |
444 std::cerr << "WARNING: No sample available" | |
445 << std::endl; | |
446 #endif | |
447 T t; | |
448 memset(&t, 0, sizeof(T)); | |
449 return t; | |
450 } | |
451 T value = m_buffer[m_readers[R]]; | |
452 return value; | |
453 } | |
454 | |
455 template <typename T, int N> | |
456 size_t | |
457 RingBuffer<T, N>::skip(size_t n, int R) | |
458 { | |
459 #ifdef DEBUG_RINGBUFFER | |
460 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl; | |
461 #endif | |
462 | |
463 size_t available = getReadSpace(R); | |
464 if (n > available) { | |
465 #ifdef DEBUG_RINGBUFFER | |
466 std::cerr << "WARNING: Only " << available << " samples available" | |
467 << std::endl; | |
468 #endif | |
469 n = available; | |
470 } | |
471 if (n == 0) return n; | |
472 m_readers[R] = (m_readers[R] + n) % m_size; | |
473 return n; | |
474 } | |
475 | |
476 template <typename T, int N> | |
477 size_t | |
478 RingBuffer<T, N>::write(const T *source, size_t n) | |
479 { | |
480 #ifdef DEBUG_RINGBUFFER | |
481 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl; | |
482 #endif | |
483 | |
484 size_t available = getWriteSpace(); | |
485 if (n > available) { | |
486 #ifdef DEBUG_RINGBUFFER | |
487 std::cerr << "WARNING: Only room for " << available << " samples" | |
488 << std::endl; | |
489 #endif | |
490 n = available; | |
491 } | |
492 if (n == 0) return n; | |
493 | |
494 size_t here = m_size - m_writer; | |
495 if (here >= n) { | |
496 memcpy(m_buffer + m_writer, source, n * sizeof(T)); | |
497 } else { | |
498 memcpy(m_buffer + m_writer, source, here * sizeof(T)); | |
499 memcpy(m_buffer, source + here, (n - here) * sizeof(T)); | |
500 } | |
501 | |
502 m_writer = (m_writer + n) % m_size; | |
503 | |
504 #ifdef DEBUG_RINGBUFFER | |
505 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl; | |
506 #endif | |
507 | |
508 return n; | |
509 } | |
510 | |
511 template <typename T, int N> | |
512 size_t | |
513 RingBuffer<T, N>::zero(size_t n) | |
514 { | |
515 #ifdef DEBUG_RINGBUFFER | |
516 std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl; | |
517 #endif | |
518 | |
519 size_t available = getWriteSpace(); | |
520 if (n > available) { | |
521 #ifdef DEBUG_RINGBUFFER | |
522 std::cerr << "WARNING: Only room for " << available << " samples" | |
523 << std::endl; | |
524 #endif | |
525 n = available; | |
526 } | |
527 if (n == 0) return n; | |
528 | |
529 size_t here = m_size - m_writer; | |
530 if (here >= n) { | |
531 memset(m_buffer + m_writer, 0, n * sizeof(T)); | |
532 } else { | |
533 memset(m_buffer + m_writer, 0, here * sizeof(T)); | |
534 memset(m_buffer, 0, (n - here) * sizeof(T)); | |
535 } | |
536 | |
537 m_writer = (m_writer + n) % m_size; | |
538 return n; | |
539 } | |
540 | |
541 #endif // _RINGBUFFER_H_ |