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_