RingBuffer.h
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 /*
16  This is a modified version of a source file from the
17  Rosegarden MIDI and audio sequencer and notation editor.
18  This file copyright 2000-2006 Chris Cannam.
19 */
20 
21 #ifndef SV_RINGBUFFER_H
22 #define SV_RINGBUFFER_H
23 
24 #include <sys/types.h>
25 
26 #include "system/System.h"
27 
28 #include <bqvec/Barrier.h>
29 #include <bqvec/VectorOps.h>
30 
31 #include <vector>
32 
33 //#define DEBUG_RINGBUFFER 1
34 
35 #ifdef DEBUG_RINGBUFFER
36 #include <iostream>
37 #endif
38 
44 template <typename T, int N = 1>
46 {
47 public:
57  RingBuffer(int n);
58 
59  virtual ~RingBuffer();
60 
65  int getSize() const;
66 
75  RingBuffer<T, N> resized(int newSize) const;
76 
81  void reset();
82 
87  int getReadSpace(int R = 0) const;
88 
92  int getWriteSpace() const;
93 
99  int read(T *destination, int n, int R = 0);
100 
107  int readAdding(T *destination, int n, int R = 0);
108 
116  T readOne(int R = 0);
117 
125  int peek(T *destination, int n, int R = 0) const;
126 
133  T peekOne(int R = 0) const;
134 
141  int skip(int n, int R = 0);
142 
148  int write(const T *source, int n);
149 
155  int zero(int n);
156 
157  RingBuffer(const RingBuffer &) =default;
158  RingBuffer &operator=(const RingBuffer &) =default;
159 
160 protected:
161  std::vector<T, breakfastquay::StlAllocator<T>> m_buffer;
162  int m_writer;
163  std::vector<int> m_readers;
164  int m_size;
165 };
166 
167 template <typename T, int N>
169  m_buffer(n + 1, T()),
170  m_writer(0),
171  m_readers(N, 0),
172  m_size(n + 1)
173 {
174 #ifdef DEBUG_RINGBUFFER
175  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
176 #endif
177 }
178 
179 template <typename T, int N>
181 {
182 #ifdef DEBUG_RINGBUFFER
183  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
184 #endif
185 }
186 
187 template <typename T, int N>
188 int
190 {
191 #ifdef DEBUG_RINGBUFFER
192  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl;
193 #endif
194 
195  return m_size - 1;
196 }
197 
198 template <typename T, int N>
200 RingBuffer<T, N>::resized(int newSize) const
201 {
202 #ifdef DEBUG_RINGBUFFER
203  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resized(" << newSize << ")" << std::endl;
204 #endif
205 
206  RingBuffer<T, N> newBuffer(newSize);
207 
208  int w = m_writer;
209  int r = m_readers[0];
210 
211  while (r != w) {
212  T value = m_buffer[r];
213  newBuffer->write(&value, 1);
214  if (++r == m_size) r = 0;
215  }
216 
217  return newBuffer;
218 }
219 
220 template <typename T, int N>
221 void
223 {
224 #ifdef DEBUG_RINGBUFFER
225  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
226 #endif
227 
228  m_writer = 0;
229  for (int i = 0; i < N; ++i) {
230  m_readers[i] = 0;
231  }
232 }
233 
234 template <typename T, int N>
235 int
237 {
238  int writer = m_writer;
239  int reader = m_readers[R];
240  int space = 0;
241 
242  if (writer > reader) space = writer - reader;
243  else space = ((writer + m_size) - reader) % m_size;
244 
245 #ifdef DEBUG_RINGBUFFER
246  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
247 #endif
248 
249  return space;
250 }
251 
252 template <typename T, int N>
253 int
255 {
256  int space = 0;
257  for (int i = 0; i < N; ++i) {
258  int here = (m_readers[i] + m_size - m_writer - 1) % m_size;
259  if (i == 0 || here < space) space = here;
260  }
261 
262 #ifdef DEBUG_RINGBUFFER
263  int rs(getReadSpace()), rp(m_readers[0]);
264 
265  std::cerr << "RingBuffer: write space " << space << ", read space "
266  << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl;
267  std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl;
268 #endif
269 
270 #ifdef DEBUG_RINGBUFFER
271  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
272 #endif
273 
274  return space;
275 }
276 
277 template <typename T, int N>
278 int
279 RingBuffer<T, N>::read(T *destination, int n, int R)
280 {
281 #ifdef DEBUG_RINGBUFFER
282  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
283 #endif
284 
285  int available = getReadSpace(R);
286  if (n > available) {
287 #ifdef DEBUG_RINGBUFFER
288  std::cerr << "WARNING: Only " << available << " samples available"
289  << std::endl;
290 #endif
291  breakfastquay::v_zero(destination + available, n - available);
292  n = available;
293  }
294  if (n == 0) return n;
295 
296  int here = m_size - m_readers[R];
297  if (here >= n) {
298  breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], n);
299  } else {
300  breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], here);
301  breakfastquay::v_copy(destination + here, m_buffer.data(), n - here);
302  }
303 
304  BQ_MBARRIER();
305  m_readers[R] = (m_readers[R] + n) % m_size;
306 
307 #ifdef DEBUG_RINGBUFFER
308  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
309 #endif
310 
311  return n;
312 }
313 
314 template <typename T, int N>
315 int
316 RingBuffer<T, N>::readAdding(T *destination, int n, int R)
317 {
318 #ifdef DEBUG_RINGBUFFER
319  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
320 #endif
321 
322  int available = getReadSpace(R);
323  if (n > available) {
324 #ifdef DEBUG_RINGBUFFER
325  std::cerr << "WARNING: Only " << available << " samples available"
326  << std::endl;
327 #endif
328  n = available;
329  }
330  if (n == 0) return n;
331 
332  int here = m_size - m_readers[R];
333 
334  if (here >= n) {
335  for (int i = 0; i < n; ++i) {
336  destination[i] += (m_buffer.data() + m_readers[R])[i];
337  }
338  } else {
339  for (int i = 0; i < here; ++i) {
340  destination[i] += (m_buffer.data() + m_readers[R])[i];
341  }
342  for (int i = 0; i < (n - here); ++i) {
343  destination[i + here] += m_buffer[i];
344  }
345  }
346 
347  BQ_MBARRIER();
348  m_readers[R] = (m_readers[R] + n) % m_size;
349  return n;
350 }
351 
352 template <typename T, int N>
353 T
355 {
356 #ifdef DEBUG_RINGBUFFER
357  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl;
358 #endif
359 
360  if (m_writer == m_readers[R]) {
361 #ifdef DEBUG_RINGBUFFER
362  std::cerr << "WARNING: No sample available"
363  << std::endl;
364 #endif
365  return T();
366  }
367  T value = m_buffer[m_readers[R]];
368  BQ_MBARRIER();
369  if (++m_readers[R] == m_size) m_readers[R] = 0;
370  return value;
371 }
372 
373 template <typename T, int N>
374 int
375 RingBuffer<T, N>::peek(T *destination, int n, int R) const
376 {
377 #ifdef DEBUG_RINGBUFFER
378  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
379 #endif
380 
381  int available = getReadSpace(R);
382  if (n > available) {
383 #ifdef DEBUG_RINGBUFFER
384  std::cerr << "WARNING: Only " << available << " samples available"
385  << std::endl;
386 #endif
387  breakfastquay::v_zero(destination + available, n - available);
388  n = available;
389  }
390  if (n == 0) return n;
391 
392  int here = m_size - m_readers[R];
393  if (here >= n) {
394  breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], n);
395  } else {
396  breakfastquay::v_copy(destination, m_buffer.data() + m_readers[R], here);
397  breakfastquay::v_copy(destination + here, m_buffer.data(), n - here);
398  }
399 
400 #ifdef DEBUG_RINGBUFFER
401  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
402 #endif
403 
404  return n;
405 }
406 
407 template <typename T, int N>
408 T
410 {
411 #ifdef DEBUG_RINGBUFFER
412  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl;
413 #endif
414 
415  if (m_writer == m_readers[R]) {
416 #ifdef DEBUG_RINGBUFFER
417  std::cerr << "WARNING: No sample available"
418  << std::endl;
419 #endif
420  return T();
421  }
422  T value = m_buffer[m_readers[R]];
423  return value;
424 }
425 
426 template <typename T, int N>
427 int
429 {
430 #ifdef DEBUG_RINGBUFFER
431  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
432 #endif
433 
434  int available = getReadSpace(R);
435  if (n > available) {
436 #ifdef DEBUG_RINGBUFFER
437  std::cerr << "WARNING: Only " << available << " samples available"
438  << std::endl;
439 #endif
440  n = available;
441  }
442  if (n == 0) return n;
443  m_readers[R] = (m_readers[R] + n) % m_size;
444  return n;
445 }
446 
447 template <typename T, int N>
448 int
449 RingBuffer<T, N>::write(const T *source, int n)
450 {
451 #ifdef DEBUG_RINGBUFFER
452  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
453 #endif
454 
455  int available = getWriteSpace();
456  if (n > available) {
457 #ifdef DEBUG_RINGBUFFER
458  std::cerr << "WARNING: Only room for " << available << " samples"
459  << std::endl;
460 #endif
461  n = available;
462  }
463  if (n == 0) return n;
464 
465  int here = m_size - m_writer;
466  if (here >= n) {
467  breakfastquay::v_copy(m_buffer.data() + m_writer, source, n);
468  } else {
469  breakfastquay::v_copy(m_buffer.data() + m_writer, source, here);
470  breakfastquay::v_copy(m_buffer.data(), source + here, n - here);
471  }
472 
473  BQ_MBARRIER();
474  m_writer = (m_writer + n) % m_size;
475 
476 #ifdef DEBUG_RINGBUFFER
477  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
478 #endif
479 
480  return n;
481 }
482 
483 template <typename T, int N>
484 int
486 {
487 #ifdef DEBUG_RINGBUFFER
488  std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
489 #endif
490 
491  int available = getWriteSpace();
492  if (n > available) {
493 #ifdef DEBUG_RINGBUFFER
494  std::cerr << "WARNING: Only room for " << available << " samples"
495  << std::endl;
496 #endif
497  n = available;
498  }
499  if (n == 0) return n;
500 
501  int here = m_size - m_writer;
502  if (here >= n) {
503  breakfastquay::v_zero(m_buffer.data() + m_writer, n);
504  } else {
505  breakfastquay::v_zero(m_buffer.data() + m_writer, here);
506  breakfastquay::v_zero(m_buffer.data(), n - here);
507  }
508 
509  BQ_MBARRIER();
510  m_writer = (m_writer + n) % m_size;
511  return n;
512 }
513 
514 #endif // _RINGBUFFER_H_
T peekOne(int R=0) const
Read one sample from the buffer, if available, without advancing the read pointer – i...
Definition: RingBuffer.h:409
std::vector< int > m_readers
Definition: RingBuffer.h:163
int getSize() const
Return the total capacity of the ring buffer in samples.
Definition: RingBuffer.h:189
std::vector< T, breakfastquay::StlAllocator< T > > m_buffer
Definition: RingBuffer.h:161
RingBuffer & operator=(const RingBuffer &)=default
T readOne(int R=0)
Read one sample from the buffer, for reader R.
Definition: RingBuffer.h:354
void reset()
Reset read and write pointers, thus emptying the buffer.
Definition: RingBuffer.h:222
int read(T *destination, int n, int R=0)
Read n samples from the buffer, for reader R.
Definition: RingBuffer.h:279
RingBuffer< T, N > resized(int newSize) const
Return a new ring buffer of the given size, containing the same data as this one as perceived by read...
Definition: RingBuffer.h:200
int peek(T *destination, int n, int R=0) const
Read n samples from the buffer, if available, for reader R, without advancing the read pointer – i...
Definition: RingBuffer.h:375
int write(const T *source, int n)
Write n samples to the buffer.
Definition: RingBuffer.h:449
int getReadSpace(int R=0) const
Return the amount of data available for reading by reader R, in samples.
Definition: RingBuffer.h:236
RingBuffer(int n)
Create a ring buffer with room to write n samples.
Definition: RingBuffer.h:168
int getWriteSpace() const
Return the amount of space available for writing, in samples.
Definition: RingBuffer.h:254
virtual ~RingBuffer()
Definition: RingBuffer.h:180
RingBuffer implements a lock-free ring buffer for one writer and N readers, that is to be used to sto...
Definition: RingBuffer.h:45
int skip(int n, int R=0)
Pretend to read n samples from the buffer, for reader R, without actually returning them (i...
Definition: RingBuffer.h:428
int m_writer
Definition: RingBuffer.h:162
int readAdding(T *destination, int n, int R=0)
Read n samples from the buffer, for reader R, adding them to the destination.
Definition: RingBuffer.h:316
int zero(int n)
Write n zero-value samples to the buffer.
Definition: RingBuffer.h:485