annotate base/RingBuffer.h @ 1829:51fd27fbce9a audio-source-refactor

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