Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@0: 
Chris@0: /*
Chris@52:     Sonic Visualiser
Chris@52:     An audio file viewer and annotation editor.
Chris@52:     Centre for Digital Music, Queen Mary, University of London.
Chris@0:     
Chris@52:     This program is free software; you can redistribute it and/or
Chris@52:     modify it under the terms of the GNU General Public License as
Chris@52:     published by the Free Software Foundation; either version 2 of the
Chris@52:     License, or (at your option) any later version.  See the file
Chris@52:     COPYING included with this distribution for more information.
Chris@0: */
Chris@0: 
Chris@0: /*
Chris@0:    This is a modified version of a source file from the 
Chris@0:    Rosegarden MIDI and audio sequencer and notation editor.
Chris@17:    This file copyright 2000-2006 Chris Cannam.
Chris@0: */
Chris@0: 
Chris@0: #ifndef _RINGBUFFER_H_
Chris@0: #define _RINGBUFFER_H_
Chris@0: 
Chris@0: #include <sys/types.h>
Chris@0: 
Chris@150: #include "system/System.h"
Chris@0: #include "Scavenger.h"
Chris@0: 
Chris@0: //#define DEBUG_RINGBUFFER 1
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: #include <iostream>
Chris@0: #endif
Chris@0: 
Chris@0: /**
Chris@0:  * RingBuffer implements a lock-free ring buffer for one writer and N
Chris@0:  * readers, that is to be used to store a sample type T.
Chris@0:  *
Chris@0:  * For efficiency, RingBuffer frequently initialises samples by
Chris@0:  * writing zeroes into their memory space, so T should normally be a
Chris@0:  * simple type that can safely be set to zero using memset.
Chris@0:  */
Chris@0: 
Chris@0: template <typename T, int N = 1>
Chris@0: class RingBuffer
Chris@0: {
Chris@0: public:
Chris@0:     /**
Chris@0:      * Create a ring buffer with room to write n samples.
Chris@0:      *
Chris@0:      * Note that the internal storage size will actually be n+1
Chris@0:      * samples, as one element is unavailable for administrative
Chris@0:      * reasons.  Since the ring buffer performs best if its size is a
Chris@0:      * power of two, this means n should ideally be some power of two
Chris@0:      * minus one.
Chris@0:      */
Chris@0:     RingBuffer(size_t n);
Chris@0: 
Chris@0:     virtual ~RingBuffer();
Chris@0: 
Chris@0:     /**
Chris@0:      * Return the total capacity of the ring buffer in samples.
Chris@0:      * (This is the argument n passed to the constructor.)
Chris@0:      */
Chris@0:     size_t getSize() const;
Chris@0: 
Chris@0:     /**
Chris@0:      * Resize the ring buffer.  This also empties it.  Actually swaps
Chris@0:      * in a new, larger buffer; the old buffer is scavenged after a
Chris@0:      * seemly delay.  Should be called from the write thread.
Chris@0:      */
Chris@0:     void resize(size_t newSize);
Chris@0: 
Chris@0:     /**
Chris@0:      * Lock the ring buffer into physical memory.  Returns true
Chris@0:      * for success.
Chris@0:      */
Chris@0:     bool mlock();
Chris@0: 
Chris@0:     /**
Chris@0:      * Reset read and write pointers, thus emptying the buffer.
Chris@0:      * Should be called from the write thread.
Chris@0:      */
Chris@0:     void reset();
Chris@0: 
Chris@0:     /**
Chris@0:      * Return the amount of data available for reading by reader R, in
Chris@0:      * samples.
Chris@0:      */
Chris@0:     size_t getReadSpace(int R = 0) const;
Chris@0: 
Chris@0:     /**
Chris@0:      * Return the amount of space available for writing, in samples.
Chris@0:      */
Chris@0:     size_t getWriteSpace() const;
Chris@0: 
Chris@0:     /**
Chris@0:      * Read n samples from the buffer, for reader R.  If fewer than n
Chris@0:      * are available, the remainder will be zeroed out.  Returns the
Chris@0:      * number of samples actually read.
Chris@0:      */
Chris@0:     size_t read(T *destination, size_t n, int R = 0);
Chris@0: 
Chris@0:     /**
Chris@0:      * Read n samples from the buffer, for reader R, adding them to
Chris@0:      * the destination.  If fewer than n are available, the remainder
Chris@0:      * will be left alone.  Returns the number of samples actually
Chris@0:      * read.
Chris@0:      */
Chris@0:     size_t readAdding(T *destination, size_t n, int R = 0);
Chris@0: 
Chris@0:     /**
Chris@0:      * Read one sample from the buffer, for reader R.  If no sample is
Chris@0:      * available, this will silently return zero.  Calling this
Chris@0:      * repeatedly is obviously slower than calling read once, but it
Chris@0:      * may be good enough if you don't want to allocate a buffer to
Chris@0:      * read into.
Chris@0:      */
Chris@0:     T readOne(int R = 0);
Chris@0: 
Chris@0:     /**
Chris@0:      * Read n samples from the buffer, if available, for reader R,
Chris@0:      * without advancing the read pointer -- i.e. a subsequent read()
Chris@0:      * or skip() will be necessary to empty the buffer.  If fewer than
Chris@0:      * n are available, the remainder will be zeroed out.  Returns the
Chris@0:      * number of samples actually read.
Chris@0:      */
Chris@0:     size_t peek(T *destination, size_t n, int R = 0) const;
Chris@0: 
Chris@0:     /**
Chris@0:      * Read one sample from the buffer, if available, without
Chris@0:      * advancing the read pointer -- i.e. a subsequent read() or
Chris@0:      * skip() will be necessary to empty the buffer.  Returns zero if
Chris@0:      * no sample was available.
Chris@0:      */
Chris@0:     T peekOne(int R = 0) const;
Chris@0: 
Chris@0:     /**
Chris@0:      * Pretend to read n samples from the buffer, for reader R,
Chris@0:      * without actually returning them (i.e. discard the next n
Chris@0:      * samples).  Returns the number of samples actually available for
Chris@0:      * discarding.
Chris@0:      */
Chris@0:     size_t skip(size_t n, int R = 0);
Chris@0: 
Chris@0:     /**
Chris@0:      * Write n samples to the buffer.  If insufficient space is
Chris@0:      * available, not all samples may actually be written.  Returns
Chris@0:      * the number of samples actually written.
Chris@0:      */
Chris@0:     size_t write(const T *source, size_t n);
Chris@0: 
Chris@0:     /**
Chris@0:      * Write n zero-value samples to the buffer.  If insufficient
Chris@0:      * space is available, not all zeros may actually be written.
Chris@0:      * Returns the number of zeroes actually written.
Chris@0:      */
Chris@0:     size_t zero(size_t n);
Chris@0: 
Chris@0: protected:
Chris@0:     T               *m_buffer;
Chris@0:     volatile size_t  m_writer;
Chris@0:     volatile size_t  m_readers[N];
Chris@0:     size_t           m_size;
Chris@0:     bool             m_mlocked;
Chris@0: 
Chris@0:     static Scavenger<ScavengerArrayWrapper<T> > m_scavenger;
Chris@0: 
Chris@0: private:
Chris@0:     RingBuffer(const RingBuffer &); // not provided
Chris@0:     RingBuffer &operator=(const RingBuffer &); // not provided
Chris@0: };
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger;
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: RingBuffer<T, N>::RingBuffer(size_t n) :
Chris@0:     m_buffer(new T[n + 1]),
Chris@0:     m_writer(0),
Chris@0:     m_size(n + 1),
Chris@0:     m_mlocked(false)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     for (int i = 0; i < N; ++i) m_readers[i] = 0;
Chris@0: 
Chris@0:     m_scavenger.scavenge();
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: RingBuffer<T, N>::~RingBuffer()
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     if (m_mlocked) {
Chris@0: 	MUNLOCK((void *)m_buffer, m_size * sizeof(T));
Chris@0:     }
Chris@0:     delete[] m_buffer;
Chris@0: 
Chris@0:     m_scavenger.scavenge();
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::getSize() const
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     return m_size - 1;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: void
Chris@0: RingBuffer<T, N>::resize(size_t newSize)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resize(" << newSize << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     m_scavenger.scavenge();
Chris@0: 
Chris@0:     if (m_mlocked) {
Chris@0: 	MUNLOCK((void *)m_buffer, m_size * sizeof(T));
Chris@0:     }
Chris@0: 
Chris@0:     m_scavenger.claim(new ScavengerArrayWrapper<T>(m_buffer));
Chris@0: 
Chris@0:     reset();
Chris@0:     m_buffer = new T[newSize + 1];
Chris@0:     m_size = newSize + 1;
Chris@0: 
Chris@0:     if (m_mlocked) {
Chris@0: 	if (MLOCK((void *)m_buffer, m_size * sizeof(T))) {
Chris@0: 	    m_mlocked = false;
Chris@0: 	}
Chris@0:     }
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: bool
Chris@0: RingBuffer<T, N>::mlock()
Chris@0: {
Chris@0:     if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
Chris@0:     m_mlocked = true;
Chris@0:     return true;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: void
Chris@0: RingBuffer<T, N>::reset()
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     m_writer = 0;
Chris@0:     for (int i = 0; i < N; ++i) m_readers[i] = 0;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::getReadSpace(int R) const
Chris@0: {
Chris@0:     size_t writer = m_writer;
Chris@0:     size_t reader = m_readers[R];
Chris@0:     size_t space = 0;
Chris@0: 
Chris@0:     if (writer > reader) space = writer - reader;
Chris@0:     else space = ((writer + m_size) - reader) % m_size;
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     return space;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::getWriteSpace() const
Chris@0: {
Chris@0:     size_t space = 0;
Chris@0:     for (int i = 0; i < N; ++i) {
Chris@0: 	size_t here = (m_readers[i] + m_size - m_writer - 1) % m_size;
Chris@0: 	if (i == 0 || here < space) space = here;
Chris@0:     }
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     size_t rs(getReadSpace()), rp(m_readers[0]);
Chris@0: 
Chris@0:     std::cerr << "RingBuffer: write space " << space << ", read space "
Chris@0: 	      << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl;
Chris@0:     std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     return space;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::read(T *destination, size_t n, int R)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     size_t available = getReadSpace(R);
Chris@0:     if (n > available) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: Only " << available << " samples available"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	memset(destination + available, 0, (n - available) * sizeof(T));
Chris@0: 	n = available;
Chris@0:     }
Chris@0:     if (n == 0) return n;
Chris@0: 
Chris@0:     size_t here = m_size - m_readers[R];
Chris@0:     if (here >= n) {
Chris@0: 	memcpy(destination, m_buffer + m_readers[R], n * sizeof(T));
Chris@0:     } else {
Chris@0: 	memcpy(destination, m_buffer + m_readers[R], here * sizeof(T));
Chris@0: 	memcpy(destination + here, m_buffer, (n - here) * sizeof(T));
Chris@0:     }
Chris@0: 
Chris@0:     m_readers[R] = (m_readers[R] + n) % m_size;
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     return n;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::readAdding(T *destination, size_t n, int R)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     size_t available = getReadSpace(R);
Chris@0:     if (n > available) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: Only " << available << " samples available"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	n = available;
Chris@0:     }
Chris@0:     if (n == 0) return n;
Chris@0: 
Chris@0:     size_t here = m_size - m_readers[R];
Chris@0: 
Chris@0:     if (here >= n) {
Chris@0: 	for (size_t i = 0; i < n; ++i) {
Chris@0: 	    destination[i] += (m_buffer + m_readers[R])[i];
Chris@0: 	}
Chris@0:     } else {
Chris@0: 	for (size_t i = 0; i < here; ++i) {
Chris@0: 	    destination[i] += (m_buffer + m_readers[R])[i];
Chris@0: 	}
Chris@0: 	for (size_t i = 0; i < (n - here); ++i) {
Chris@0: 	    destination[i + here] += m_buffer[i];
Chris@0: 	}
Chris@0:     }
Chris@0: 
Chris@0:     m_readers[R] = (m_readers[R] + n) % m_size;
Chris@0:     return n;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: T
Chris@0: RingBuffer<T, N>::readOne(int R)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     if (m_writer == m_readers[R]) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: No sample available"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	T t;
Chris@0: 	memset(&t, 0, sizeof(T));
Chris@0: 	return t;
Chris@0:     }
Chris@0:     T value = m_buffer[m_readers[R]];
Chris@0:     if (++m_readers[R] == m_size) m_readers[R] = 0;
Chris@0:     return value;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::peek(T *destination, size_t n, int R) const
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     size_t available = getReadSpace(R);
Chris@0:     if (n > available) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: Only " << available << " samples available"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	memset(destination + available, 0, (n - available) * sizeof(T));
Chris@0: 	n = available;
Chris@0:     }
Chris@0:     if (n == 0) return n;
Chris@0: 
Chris@0:     size_t here = m_size - m_readers[R];
Chris@0:     if (here >= n) {
Chris@0: 	memcpy(destination, m_buffer + m_readers[R], n * sizeof(T));
Chris@0:     } else {
Chris@0: 	memcpy(destination, m_buffer + m_readers[R], here * sizeof(T));
Chris@0: 	memcpy(destination + here, m_buffer, (n - here) * sizeof(T));
Chris@0:     }
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     return n;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: T
Chris@0: RingBuffer<T, N>::peekOne(int R) const
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     if (m_writer == m_readers[R]) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: No sample available"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	T t;
Chris@0: 	memset(&t, 0, sizeof(T));
Chris@0: 	return t;
Chris@0:     }
Chris@0:     T value = m_buffer[m_readers[R]];
Chris@0:     return value;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::skip(size_t n, int R)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     size_t available = getReadSpace(R);
Chris@0:     if (n > available) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: Only " << available << " samples available"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	n = available;
Chris@0:     }
Chris@0:     if (n == 0) return n;
Chris@0:     m_readers[R] = (m_readers[R] + n) % m_size;
Chris@0:     return n;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::write(const T *source, size_t n)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     size_t available = getWriteSpace();
Chris@0:     if (n > available) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: Only room for " << available << " samples"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	n = available;
Chris@0:     }
Chris@0:     if (n == 0) return n;
Chris@0: 
Chris@0:     size_t here = m_size - m_writer;
Chris@0:     if (here >= n) {
Chris@0: 	memcpy(m_buffer + m_writer, source, n * sizeof(T));
Chris@0:     } else {
Chris@0: 	memcpy(m_buffer + m_writer, source, here * sizeof(T));
Chris@0: 	memcpy(m_buffer, source + here, (n - here) * sizeof(T));
Chris@0:     }
Chris@0: 
Chris@0:     m_writer = (m_writer + n) % m_size;
Chris@0: 
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     return n;
Chris@0: }
Chris@0: 
Chris@0: template <typename T, int N>
Chris@0: size_t
Chris@0: RingBuffer<T, N>::zero(size_t n)
Chris@0: {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0:     std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
Chris@0: #endif
Chris@0: 
Chris@0:     size_t available = getWriteSpace();
Chris@0:     if (n > available) {
Chris@0: #ifdef DEBUG_RINGBUFFER
Chris@0: 	std::cerr << "WARNING: Only room for " << available << " samples"
Chris@0: 		  << std::endl;
Chris@0: #endif
Chris@0: 	n = available;
Chris@0:     }
Chris@0:     if (n == 0) return n;
Chris@0: 
Chris@0:     size_t here = m_size - m_writer;
Chris@0:     if (here >= n) {
Chris@0: 	memset(m_buffer + m_writer, 0, n * sizeof(T));
Chris@0:     } else {
Chris@0: 	memset(m_buffer + m_writer, 0, here * sizeof(T));
Chris@0: 	memset(m_buffer, 0, (n - here) * sizeof(T));
Chris@0:     }
Chris@0: 
Chris@0:     m_writer = (m_writer + n) % m_size;
Chris@0:     return n;
Chris@0: }
Chris@0: 
Chris@0: #endif // _RINGBUFFER_H_