Files
librubberband/src/base/RingBuffer.h

528 lines
13 KiB
C
Raw Normal View History

2007-11-06 21:41:16 +00:00
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
2012-09-09 16:57:42 +01:00
Rubber Band Library
2007-11-06 21:41:16 +00:00
An audio time-stretching and pitch-shifting library.
2021-01-08 17:13:52 +00:00
Copyright 2007-2021 Particular Programs Ltd.
2012-09-09 16:57:42 +01:00
2007-11-06 21:41:16 +00:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
2012-09-09 16:57:42 +01:00
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
2007-11-06 21:41:16 +00:00
*/
2021-02-10 11:07:33 +00:00
#ifndef RUBBERBAND_RINGBUFFER_H
#define RUBBERBAND_RINGBUFFER_H
2007-11-06 21:41:16 +00:00
#include <sys/types.h>
2007-11-28 11:50:47 +00:00
2007-11-06 21:41:16 +00:00
//#define DEBUG_RINGBUFFER 1
#include "system/sysutils.h"
#include "system/Allocators.h"
2007-11-06 21:41:16 +00:00
#include <iostream>
namespace RubberBand {
/**
* RingBuffer implements a lock-free ring buffer for one writer and
* one reader, that is to be used to store a sample type T.
*
* RingBuffer is thread-safe provided only one thread writes and only
* one thread reads.
2007-11-06 21:41:16 +00:00
*/
template <typename T>
2007-11-06 21:41:16 +00:00
class RingBuffer
{
public:
/**
* Create a ring buffer with room to write n samples.
*
* Note that the internal storage size will actually be n+1
* samples, as one element is unavailable for administrative
* reasons. Since the ring buffer performs best if its size is a
* power of two, this means n should ideally be some power of two
* minus one.
*/
2012-09-09 16:57:42 +01:00
RingBuffer(int n);
2007-11-06 21:41:16 +00:00
virtual ~RingBuffer();
/**
* Return the total capacity of the ring buffer in samples.
* (This is the argument n passed to the constructor.)
*/
int getSize() const;
2007-11-06 21:41:16 +00:00
/**
* Return a new ring buffer (allocated with "new" -- caller must
2007-11-06 21:41:16 +00:00
* delete when no longer needed) of the given size, containing the
* same data as this one. If another thread reads from or writes
* to this buffer during the call, the results may be incomplete
* or inconsistent. If this buffer's data will not fit in the new
* size, the contents are undefined.
*/
RingBuffer<T> *resized(int newSize) const;
2007-11-06 21:41:16 +00:00
/**
* Lock the ring buffer into physical memory. Returns true
* for success.
*/
bool mlock();
/**
* Reset read and write pointers, thus emptying the buffer.
* Should be called from the write thread.
*/
void reset();
/**
* Return the amount of data available for reading, in samples.
2007-11-06 21:41:16 +00:00
*/
int getReadSpace() const;
2007-11-06 21:41:16 +00:00
/**
* Return the amount of space available for writing, in samples.
*/
int getWriteSpace() const;
2007-11-06 21:41:16 +00:00
/**
* Read n samples from the buffer. If fewer than n are available,
* the remainder will be zeroed out. Returns the number of
* samples actually read.
*
* This is a template function, taking an argument S for the target
* sample type, which is permitted to differ from T if the two
* types are compatible for arithmetic operations.
2007-11-06 21:41:16 +00:00
*/
template <typename S>
int read(S *const R__ destination, int n);
2007-11-06 21:41:16 +00:00
/**
* Read n samples from the buffer, adding them to the destination.
* If fewer than n are available, the remainder will be left
* alone. Returns the number of samples actually read.
*
* This is a template function, taking an argument S for the target
* sample type, which is permitted to differ from T if the two
* types are compatible for arithmetic operations.
2007-11-06 21:41:16 +00:00
*/
template <typename S>
int readAdding(S *const R__ destination, int n);
2007-11-06 21:41:16 +00:00
/**
* Read one sample from the buffer. If no sample is available,
* this will silently return zero. Calling this repeatedly is
* obviously slower than calling read once, but it may be good
* enough if you don't want to allocate a buffer to read into.
2007-11-06 21:41:16 +00:00
*/
T readOne();
2007-11-06 21:41:16 +00:00
/**
* Read n samples from the buffer, if available, without advancing
* the read pointer -- i.e. a subsequent read() or skip() will be
* necessary to empty the buffer. If fewer than n are available,
* the remainder will be zeroed out. Returns the number of
* samples actually read.
2007-11-06 21:41:16 +00:00
*/
int peek(T *const R__ destination, int n) const;
2007-11-06 21:41:16 +00:00
/**
* Read one sample from the buffer, if available, without
* advancing the read pointer -- i.e. a subsequent read() or
* skip() will be necessary to empty the buffer. Returns zero if
* no sample was available.
*/
T peekOne() const;
2007-11-06 21:41:16 +00:00
/**
* Pretend to read n samples from the buffer, without actually
* returning them (i.e. discard the next n samples). Returns the
* number of samples actually available for discarding.
2007-11-06 21:41:16 +00:00
*/
int skip(int n);
2007-11-06 21:41:16 +00:00
/**
* Write n samples to the buffer. If insufficient space is
* available, not all samples may actually be written. Returns
* the number of samples actually written.
*
* This is a template function, taking an argument S for the source
* sample type, which is permitted to differ from T if the two
* types are compatible for assignment.
2007-11-06 21:41:16 +00:00
*/
template <typename S>
int write(const S *const R__ source, int n);
2007-11-06 21:41:16 +00:00
/**
* Write n zero-value samples to the buffer. If insufficient
* space is available, not all zeros may actually be written.
* Returns the number of zeroes actually written.
*/
int zero(int n);
2007-11-06 21:41:16 +00:00
protected:
T *const R__ m_buffer;
int m_writer;
int m_reader;
const int m_size;
bool m_mlocked;
int readSpaceFor(int w, int r) const {
int space;
if (w > r) space = w - r;
else if (w < r) space = (w + m_size) - r;
else space = 0;
return space;
}
2007-11-06 21:41:16 +00:00
int writeSpaceFor(int w, int r) const {
int space = (r + m_size - w - 1);
if (space >= m_size) space -= m_size;
return space;
}
2007-11-06 21:41:16 +00:00
private:
RingBuffer(const RingBuffer &); // not provided
RingBuffer &operator=(const RingBuffer &); // not provided
};
template <typename T>
RingBuffer<T>::RingBuffer(int n) :
m_buffer(allocate<T>(n + 1)),
2007-11-06 21:41:16 +00:00
m_writer(0),
m_size(n + 1),
m_mlocked(false)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T>[" << this << "]::RingBuffer(" << n << ")" << std::endl;
2007-11-06 21:41:16 +00:00
#endif
m_reader = 0;
2007-11-06 21:41:16 +00:00
}
template <typename T>
RingBuffer<T>::~RingBuffer()
2007-11-06 21:41:16 +00:00
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T>[" << this << "]::~RingBuffer" << std::endl;
2007-11-06 21:41:16 +00:00
#endif
if (m_mlocked) {
MUNLOCK((void *)m_buffer, m_size * sizeof(T));
}
deallocate(m_buffer);
2007-11-06 21:41:16 +00:00
}
template <typename T>
int
RingBuffer<T>::getSize() const
2007-11-06 21:41:16 +00:00
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T>[" << this << "]::getSize(): " << m_size-1 << std::endl;
2007-11-06 21:41:16 +00:00
#endif
return m_size - 1;
}
template <typename T>
RingBuffer<T> *
RingBuffer<T>::resized(int newSize) const
2007-11-06 21:41:16 +00:00
{
RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize);
2007-11-06 21:41:16 +00:00
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
while (r != w) {
T value = m_buffer[r];
newBuffer->write(&value, 1);
if (++r == m_size) r = 0;
}
return newBuffer;
}
template <typename T>
2007-11-06 21:41:16 +00:00
bool
RingBuffer<T>::mlock()
2007-11-06 21:41:16 +00:00
{
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
m_mlocked = true;
return true;
}
template <typename T>
2007-11-06 21:41:16 +00:00
void
RingBuffer<T>::reset()
2007-11-06 21:41:16 +00:00
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
2007-11-06 21:41:16 +00:00
#endif
2012-09-09 16:57:42 +01:00
m_reader = m_writer;
2007-11-06 21:41:16 +00:00
}
template <typename T>
int
RingBuffer<T>::getReadSpace() const
2007-11-06 21:41:16 +00:00
{
return readSpaceFor(m_writer, m_reader);
2007-11-06 21:41:16 +00:00
}
template <typename T>
int
RingBuffer<T>::getWriteSpace() const
2007-11-06 21:41:16 +00:00
{
return writeSpaceFor(m_writer, m_reader);
2007-11-06 21:41:16 +00:00
}
template <typename T>
template <typename S>
int
RingBuffer<T>::read(S *const R__ destination, int n)
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
int available = readSpaceFor(w, r);
2007-11-06 21:41:16 +00:00
if (n > available) {
std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
<< available << " available" << std::endl;
2012-09-09 16:57:42 +01:00
//!!! v_zero(destination + available, n - available);
2007-11-06 21:41:16 +00:00
n = available;
}
if (n == 0) return n;
int here = m_size - r;
T *const R__ bufbase = m_buffer + r;
2007-11-06 21:41:16 +00:00
if (here >= n) {
v_convert(destination, bufbase, n);
2007-11-06 21:41:16 +00:00
} else {
v_convert(destination, bufbase, here);
v_convert(destination + here, m_buffer, n - here);
2007-11-06 21:41:16 +00:00
}
r += n;
while (r >= m_size) r -= m_size;
2007-11-06 21:41:16 +00:00
MBARRIER();
m_reader = r;
2007-11-06 21:41:16 +00:00
return n;
}
template <typename T>
template <typename S>
int
RingBuffer<T>::readAdding(S *const R__ destination, int n)
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
int available = readSpaceFor(w, r);
2007-11-06 21:41:16 +00:00
if (n > available) {
std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
<< available << " available" << std::endl;
2007-11-06 21:41:16 +00:00
n = available;
}
if (n == 0) return n;
int here = m_size - r;
T *const R__ bufbase = m_buffer + r;
2007-11-06 21:41:16 +00:00
if (here >= n) {
v_add(destination, bufbase, n);
2007-11-06 21:41:16 +00:00
} else {
v_add(destination, bufbase, here);
v_add(destination + here, m_buffer, n - here);
2007-11-06 21:41:16 +00:00
}
r += n;
while (r >= m_size) r -= m_size;
MBARRIER();
m_reader = r;
2007-11-06 21:41:16 +00:00
return n;
}
template <typename T>
2007-11-06 21:41:16 +00:00
T
RingBuffer<T>::readOne()
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
if (w == r) {
std::cerr << "WARNING: RingBuffer::readOne: no sample available"
2007-11-06 21:41:16 +00:00
<< std::endl;
2012-09-09 16:57:42 +01:00
return T();
2007-11-06 21:41:16 +00:00
}
T value = m_buffer[r];
if (++r == m_size) r = 0;
MBARRIER();
m_reader = r;
2007-11-06 21:41:16 +00:00
return value;
}
template <typename T>
int
RingBuffer<T>::peek(T *const R__ destination, int n) const
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
int available = readSpaceFor(w, r);
2007-11-06 21:41:16 +00:00
if (n > available) {
std::cerr << "WARNING: RingBuffer::peek: " << n << " requested, only "
<< available << " available" << std::endl;
2007-11-06 21:41:16 +00:00
memset(destination + available, 0, (n - available) * sizeof(T));
n = available;
}
if (n == 0) return n;
int here = m_size - r;
const T *const R__ bufbase = m_buffer + r;
2007-11-06 21:41:16 +00:00
if (here >= n) {
v_copy(destination, bufbase, n);
2007-11-06 21:41:16 +00:00
} else {
v_copy(destination, bufbase, here);
v_copy(destination + here, m_buffer, n - here);
2007-11-06 21:41:16 +00:00
}
return n;
}
template <typename T>
2007-11-06 21:41:16 +00:00
T
RingBuffer<T>::peekOne() const
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
if (w == r) {
std::cerr << "WARNING: RingBuffer::peekOne: no sample available"
2007-11-06 21:41:16 +00:00
<< std::endl;
return 0;
}
T value = m_buffer[r];
2007-11-06 21:41:16 +00:00
return value;
}
template <typename T>
int
RingBuffer<T>::skip(int n)
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
int available = readSpaceFor(w, r);
2007-11-06 21:41:16 +00:00
if (n > available) {
std::cerr << "WARNING: RingBuffer::skip: " << n << " requested, only "
<< available << " available" << std::endl;
2007-11-06 21:41:16 +00:00
n = available;
}
if (n == 0) return n;
r += n;
while (r >= m_size) r -= m_size;
// No memory barrier required, because we didn't read any data
m_reader = r;
2007-11-06 21:41:16 +00:00
return n;
}
template <typename T>
template <typename S>
int
RingBuffer<T>::write(const S *const R__ source, int n)
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
int available = writeSpaceFor(w, r);
2007-11-06 21:41:16 +00:00
if (n > available) {
std::cerr << "WARNING: RingBuffer::write: " << n
<< " requested, only room for " << available << std::endl;
2007-11-06 21:41:16 +00:00
n = available;
}
if (n == 0) return n;
int here = m_size - w;
T *const R__ bufbase = m_buffer + w;
2007-11-06 21:41:16 +00:00
if (here >= n) {
v_convert<S, T>(bufbase, source, n);
2007-11-06 21:41:16 +00:00
} else {
v_convert<S, T>(bufbase, source, here);
v_convert<S, T>(m_buffer, source + here, n - here);
2007-11-06 21:41:16 +00:00
}
w += n;
while (w >= m_size) w -= m_size;
2007-11-06 21:41:16 +00:00
MBARRIER();
m_writer = w;
2007-11-06 21:41:16 +00:00
return n;
}
template <typename T>
int
RingBuffer<T>::zero(int n)
2007-11-06 21:41:16 +00:00
{
int w = m_writer;
int r = m_reader;
2007-11-06 21:41:16 +00:00
int available = writeSpaceFor(w, r);
2007-11-06 21:41:16 +00:00
if (n > available) {
std::cerr << "WARNING: RingBuffer::zero: " << n
<< " requested, only room for " << available << std::endl;
2007-11-06 21:41:16 +00:00
n = available;
}
if (n == 0) return n;
int here = m_size - w;
T *const R__ bufbase = m_buffer + w;
2007-11-06 21:41:16 +00:00
if (here >= n) {
v_zero(bufbase, n);
2007-11-06 21:41:16 +00:00
} else {
v_zero(bufbase, here);
v_zero(m_buffer, n - here);
2007-11-06 21:41:16 +00:00
}
w += n;
while (w >= m_size) w -= m_size;
2007-11-06 21:41:16 +00:00
MBARRIER();
m_writer = w;
2007-11-06 21:41:16 +00:00
return n;
}
}
2021-02-10 11:07:33 +00:00
#endif // _RINGBUFFER_H