* Some code rearrangement
* Threading fixes (corrections to condition usage) * Avoid a potential hang when faced with some peculiar stretch factors * More modular calls out to vectorizable functions * Solaris build fixes * Bump version number
This commit is contained in:
187
src/base/Profiler.cpp
Normal file
187
src/base/Profiler.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
/*
|
||||
Rubber Band
|
||||
An audio time-stretching and pitch-shifting library.
|
||||
Copyright 2007-2009 Chris Cannam.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "Profiler.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace RubberBand {
|
||||
|
||||
#ifndef NO_TIMING
|
||||
|
||||
Profiler::ProfileMap
|
||||
Profiler::m_profiles;
|
||||
|
||||
Profiler::WorstCallMap
|
||||
Profiler::m_worstCalls;
|
||||
|
||||
void
|
||||
Profiler::add(const char *id, float ms)
|
||||
{
|
||||
ProfileMap::iterator pmi = m_profiles.find(id);
|
||||
if (pmi != m_profiles.end()) {
|
||||
++pmi->second.first;
|
||||
pmi->second.second += ms;
|
||||
} else {
|
||||
m_profiles[id] = TimePair(1, ms);
|
||||
}
|
||||
|
||||
WorstCallMap::iterator wci = m_worstCalls.find(id);
|
||||
if (wci != m_worstCalls.end()) {
|
||||
if (ms > wci->second) wci->second = ms;
|
||||
} else {
|
||||
m_worstCalls[id] = ms;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Profiler::dump()
|
||||
{
|
||||
#ifdef PROFILE_CLOCKS
|
||||
fprintf(stderr, "Profiling points [CPU time]:\n");
|
||||
#else
|
||||
fprintf(stderr, "Profiling points [Wall time]:\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "\nBy name:\n");
|
||||
|
||||
typedef std::set<const char *, std::less<std::string> > StringSet;
|
||||
|
||||
StringSet profileNames;
|
||||
for (ProfileMap::const_iterator i = m_profiles.begin();
|
||||
i != m_profiles.end(); ++i) {
|
||||
profileNames.insert(i->first);
|
||||
}
|
||||
|
||||
for (StringSet::const_iterator i = profileNames.begin();
|
||||
i != profileNames.end(); ++i) {
|
||||
|
||||
ProfileMap::const_iterator j = m_profiles.find(*i);
|
||||
if (j == m_profiles.end()) continue;
|
||||
|
||||
const TimePair &pp(j->second);
|
||||
fprintf(stderr, "%s(%d):\n", *i, pp.first);
|
||||
fprintf(stderr, "\tReal: \t%f ms \t[%f ms total]\n",
|
||||
(pp.second / pp.first),
|
||||
(pp.second));
|
||||
|
||||
WorstCallMap::const_iterator k = m_worstCalls.find(*i);
|
||||
if (k == m_worstCalls.end()) continue;
|
||||
|
||||
fprintf(stderr, "\tWorst:\t%f ms/call\n", k->second);
|
||||
}
|
||||
|
||||
typedef std::multimap<float, const char *> TimeRMap;
|
||||
typedef std::multimap<int, const char *> IntRMap;
|
||||
TimeRMap totmap, avgmap, worstmap;
|
||||
IntRMap ncallmap;
|
||||
|
||||
for (ProfileMap::const_iterator i = m_profiles.begin();
|
||||
i != m_profiles.end(); ++i) {
|
||||
totmap.insert(TimeRMap::value_type(i->second.second, i->first));
|
||||
avgmap.insert(TimeRMap::value_type(i->second.second /
|
||||
i->second.first, i->first));
|
||||
ncallmap.insert(IntRMap::value_type(i->second.first, i->first));
|
||||
}
|
||||
|
||||
for (WorstCallMap::const_iterator i = m_worstCalls.begin();
|
||||
i != m_worstCalls.end(); ++i) {
|
||||
worstmap.insert(TimeRMap::value_type(i->second, i->first));
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nBy total:\n");
|
||||
for (TimeRMap::const_iterator i = totmap.end(); i != totmap.begin(); ) {
|
||||
--i;
|
||||
fprintf(stderr, "%-40s %f ms\n", i->second, i->first);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nBy average:\n");
|
||||
for (TimeRMap::const_iterator i = avgmap.end(); i != avgmap.begin(); ) {
|
||||
--i;
|
||||
fprintf(stderr, "%-40s %f ms\n", i->second, i->first);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nBy worst case:\n");
|
||||
for (TimeRMap::const_iterator i = worstmap.end(); i != worstmap.begin(); ) {
|
||||
--i;
|
||||
fprintf(stderr, "%-40s %f ms\n", i->second, i->first);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nBy number of calls:\n");
|
||||
for (IntRMap::const_iterator i = ncallmap.end(); i != ncallmap.begin(); ) {
|
||||
--i;
|
||||
fprintf(stderr, "%-40s %d\n", i->second, i->first);
|
||||
}
|
||||
}
|
||||
|
||||
Profiler::Profiler(const char* c) :
|
||||
m_c(c),
|
||||
m_ended(false)
|
||||
{
|
||||
#ifdef PROFILE_CLOCKS
|
||||
m_start = clock();
|
||||
#else
|
||||
(void)gettimeofday(&m_start, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
Profiler::~Profiler()
|
||||
{
|
||||
if (!m_ended) end();
|
||||
}
|
||||
|
||||
void
|
||||
Profiler::end()
|
||||
{
|
||||
#ifdef PROFILE_CLOCKS
|
||||
clock_t end = clock();
|
||||
clock_t elapsed = end - m_start;
|
||||
float ms = float((double(elapsed) / double(CLOCKS_PER_SEC)) * 1000.0);
|
||||
#else
|
||||
struct timeval tv;
|
||||
(void)gettimeofday(&tv, 0);
|
||||
|
||||
tv.tv_sec -= m_start.tv_sec;
|
||||
if (tv.tv_usec < m_start.tv_usec) {
|
||||
tv.tv_usec += 1000000;
|
||||
tv.tv_sec -= 1;
|
||||
}
|
||||
tv.tv_usec -= m_start.tv_usec;
|
||||
float ms = float((double(tv.tv_sec) + (double(tv.tv_usec) / 1000000.0)) * 1000.0);
|
||||
#endif
|
||||
|
||||
add(m_c, ms);
|
||||
|
||||
m_ended = true;
|
||||
}
|
||||
|
||||
#else /* NO_TIMING */
|
||||
|
||||
#ifndef NO_TIMING_COMPLETE_NOOP
|
||||
|
||||
Profiler::Profiler(const char *) { }
|
||||
Profiler::~Profiler() { }
|
||||
void Profiler::end() { }
|
||||
void Profiler::dump() { }
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
109
src/base/Profiler.h
Normal file
109
src/base/Profiler.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
/*
|
||||
Rubber Band
|
||||
An audio time-stretching and pitch-shifting library.
|
||||
Copyright 2007-2009 Chris Cannam.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef _PROFILER_H_
|
||||
#define _PROFILER_H_
|
||||
|
||||
//#define NO_TIMING 1
|
||||
|
||||
//#define WANT_TIMING 1
|
||||
//#define PROFILE_CLOCKS 1
|
||||
|
||||
#ifdef NDEBUG
|
||||
#ifndef WANT_TIMING
|
||||
#define NO_TIMING 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NO_TIMING
|
||||
#ifdef PROFILE_CLOCKS
|
||||
#include <time.h>
|
||||
#else
|
||||
#include "system/sysutils.h"
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace RubberBand {
|
||||
|
||||
#ifndef NO_TIMING
|
||||
|
||||
class Profiler
|
||||
{
|
||||
public:
|
||||
Profiler(const char *name);
|
||||
~Profiler();
|
||||
|
||||
void end(); // same action as dtor
|
||||
|
||||
static void dump();
|
||||
|
||||
protected:
|
||||
const char* m_c;
|
||||
#ifdef PROFILE_CLOCKS
|
||||
clock_t m_start;
|
||||
#else
|
||||
struct timeval m_start;
|
||||
#endif
|
||||
bool m_showOnDestruct;
|
||||
bool m_ended;
|
||||
|
||||
typedef std::pair<int, float> TimePair;
|
||||
typedef std::map<const char *, TimePair> ProfileMap;
|
||||
typedef std::map<const char *, float> WorstCallMap;
|
||||
static ProfileMap m_profiles;
|
||||
static WorstCallMap m_worstCalls;
|
||||
static void add(const char *, float);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#ifdef NO_TIMING_COMPLETE_NOOP
|
||||
|
||||
// Fastest for release builds, but annoying because it can't be linked
|
||||
// with code built in debug mode (expecting non-inline functions), so
|
||||
// not preferred during development
|
||||
|
||||
class Profiler
|
||||
{
|
||||
public:
|
||||
Profiler(const char *) { }
|
||||
~Profiler() { }
|
||||
|
||||
void end() { }
|
||||
static void dump() { }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class Profiler
|
||||
{
|
||||
public:
|
||||
Profiler(const char *);
|
||||
~Profiler();
|
||||
|
||||
void end();
|
||||
static void dump();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
606
src/base/RingBuffer.h
Normal file
606
src/base/RingBuffer.h
Normal file
@@ -0,0 +1,606 @@
|
||||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
/*
|
||||
Rubber Band
|
||||
An audio time-stretching and pitch-shifting library.
|
||||
Copyright 2007-2009 Chris Cannam.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef _RUBBERBAND_RINGBUFFER_H_
|
||||
#define _RUBBERBAND_RINGBUFFER_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Scavenger.h"
|
||||
|
||||
//#define DEBUG_RINGBUFFER 1
|
||||
|
||||
#include "system/sysutils.h"
|
||||
#include "system/Allocators.h"
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
namespace RubberBand {
|
||||
|
||||
/**
|
||||
* RingBuffer implements a lock-free ring buffer for one writer and N
|
||||
* readers, that is to be used to store a sample type T.
|
||||
*/
|
||||
|
||||
template <typename T, int N = 1>
|
||||
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.
|
||||
*/
|
||||
RingBuffer(int n = 0);
|
||||
|
||||
virtual ~RingBuffer();
|
||||
|
||||
/**
|
||||
* Return the total capacity of the ring buffer in samples.
|
||||
* (This is the argument n passed to the constructor.)
|
||||
*/
|
||||
int getSize() const;
|
||||
|
||||
/**
|
||||
* Resize the ring buffer. This also empties it; use resized()
|
||||
* below if you do not want this to happen. Actually swaps in a
|
||||
* new, larger buffer; the old buffer is scavenged after a seemly
|
||||
* delay. Should be called from the write thread.
|
||||
*/
|
||||
void resize(int newSize);
|
||||
|
||||
/**
|
||||
* Return a new ring buffer (allocated with "new" -- called must
|
||||
* 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, N> *resized(int newSize, int R = 0) const;
|
||||
|
||||
/**
|
||||
* 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 by reader R, in
|
||||
* samples.
|
||||
*/
|
||||
int getReadSpace(int R = 0) const;
|
||||
|
||||
/**
|
||||
* Return the amount of space available for writing, in samples.
|
||||
*/
|
||||
int getWriteSpace() const;
|
||||
|
||||
/**
|
||||
* Read n samples from the buffer, for reader R. If fewer than n
|
||||
* are available, the remainder will be zeroed out. Returns the
|
||||
* number of samples actually read.
|
||||
*/
|
||||
int read(T *const R__ destination, int n, int R = 0);
|
||||
|
||||
/**
|
||||
* Read n samples from the buffer, for reader R, adding them to
|
||||
* the destination. If fewer than n are available, the remainder
|
||||
* will be left alone. Returns the number of samples actually
|
||||
* read.
|
||||
*/
|
||||
int readAdding(T *const R__ destination, int n, int R = 0);
|
||||
|
||||
/**
|
||||
* Read one sample from the buffer, for reader R. 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.
|
||||
*/
|
||||
T readOne(int R = 0);
|
||||
|
||||
/**
|
||||
* Read n samples from the buffer, if available, for reader R,
|
||||
* 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.
|
||||
*/
|
||||
int peek(T *const R__ destination, int n, int R = 0) const;
|
||||
|
||||
/**
|
||||
* 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(int R = 0) const;
|
||||
|
||||
/**
|
||||
* Pretend to read n samples from the buffer, for reader R,
|
||||
* without actually returning them (i.e. discard the next n
|
||||
* samples). Returns the number of samples actually available for
|
||||
* discarding.
|
||||
*/
|
||||
int skip(int n, int R = 0);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int write(const T *const R__ source, int n);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
protected:
|
||||
T *R__ m_buffer;
|
||||
volatile int m_writer;
|
||||
volatile int m_readers[N];
|
||||
int m_size;
|
||||
bool m_mlocked;
|
||||
|
||||
static Scavenger<ScavengerArrayWrapper<T> > m_scavenger;
|
||||
|
||||
private:
|
||||
RingBuffer(const RingBuffer &); // not provided
|
||||
RingBuffer &operator=(const RingBuffer &); // not provided
|
||||
};
|
||||
|
||||
template <typename T, int N>
|
||||
Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger;
|
||||
|
||||
template <typename T, int N>
|
||||
RingBuffer<T, N>::RingBuffer(int n) :
|
||||
m_writer(0),
|
||||
m_size(n + 1),
|
||||
m_mlocked(false)
|
||||
{
|
||||
m_buffer = allocate<T>(n + 1);
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < N; ++i) m_readers[i] = 0;
|
||||
|
||||
m_scavenger.scavenge();
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
RingBuffer<T, N>::~RingBuffer()
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_mlocked) {
|
||||
MUNLOCK((void *)m_buffer, m_size * sizeof(T));
|
||||
}
|
||||
|
||||
deallocate<T>(m_buffer);
|
||||
|
||||
m_scavenger.scavenge();
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::getSize() const
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl;
|
||||
#endif
|
||||
|
||||
return m_size - 1;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
void
|
||||
RingBuffer<T, N>::resize(int newSize)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resize(" << newSize << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
m_scavenger.scavenge();
|
||||
|
||||
if (m_mlocked) {
|
||||
MUNLOCK((void *)m_buffer, m_size * sizeof(T));
|
||||
}
|
||||
|
||||
m_scavenger.claim(new ScavengerArrayWrapper<T>(m_buffer));
|
||||
|
||||
reset();
|
||||
m_buffer = allocate<T>(newSize + 1);
|
||||
m_size = newSize + 1;
|
||||
|
||||
if (m_mlocked) {
|
||||
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) {
|
||||
m_mlocked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
RingBuffer<T, N> *
|
||||
RingBuffer<T, N>::resized(int newSize, int R) const
|
||||
{
|
||||
RingBuffer<T, N> *newBuffer = new RingBuffer<T, N>(newSize);
|
||||
|
||||
int w = m_writer;
|
||||
int r = m_readers[R];
|
||||
|
||||
while (r != w) {
|
||||
T value = m_buffer[r];
|
||||
newBuffer->write(&value, 1);
|
||||
if (++r == m_size) r = 0;
|
||||
}
|
||||
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
bool
|
||||
RingBuffer<T, N>::mlock()
|
||||
{
|
||||
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
|
||||
m_mlocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
void
|
||||
RingBuffer<T, N>::reset()
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
|
||||
#endif
|
||||
|
||||
m_writer = 0;
|
||||
for (int i = 0; i < N; ++i) m_readers[i] = 0;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::getReadSpace(int R) const
|
||||
{
|
||||
int writer = m_writer;
|
||||
int reader = m_readers[R];
|
||||
int space;
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): reader " << reader << ", writer " << writer << std::endl;
|
||||
#endif
|
||||
|
||||
if (writer > reader) space = writer - reader;
|
||||
else if (writer < reader) space = (writer + m_size) - reader;
|
||||
else space = 0;
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
|
||||
#endif
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::getWriteSpace() const
|
||||
{
|
||||
int space = 0;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
int writer = m_writer;
|
||||
int reader = m_readers[i];
|
||||
int here = (reader + m_size - writer - 1);
|
||||
if (here >= m_size) here -= m_size;
|
||||
if (i == 0 || here < space) space = here;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
int rs(getReadSpace()), rp(m_readers[0]);
|
||||
|
||||
std::cerr << "RingBuffer: write space " << space << ", read space "
|
||||
<< rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl;
|
||||
std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
|
||||
#endif
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::read(T *const R__ destination, int n, int R)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
int available = getReadSpace(R);
|
||||
if (n > available) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: Only " << available << " samples available"
|
||||
<< std::endl;
|
||||
#endif
|
||||
for (int i = available; i < n; ++i) {
|
||||
destination[i] = 0;
|
||||
}
|
||||
n = available;
|
||||
}
|
||||
if (n == 0) return n;
|
||||
|
||||
int reader = m_readers[R];
|
||||
int here = m_size - reader;
|
||||
T *const R__ bufbase = m_buffer + reader;
|
||||
|
||||
if (here >= n) {
|
||||
v_copy(destination, bufbase, n);
|
||||
} else {
|
||||
v_copy(destination, bufbase, here);
|
||||
v_copy(destination + here, m_buffer, n - here);
|
||||
}
|
||||
|
||||
reader += n;
|
||||
while (reader >= m_size) reader -= m_size;
|
||||
m_readers[R] = reader;
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::readAdding(T *const R__ destination, int n, int R)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
int available = getReadSpace(R);
|
||||
if (n > available) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: Only " << available << " samples available"
|
||||
<< std::endl;
|
||||
#endif
|
||||
n = available;
|
||||
}
|
||||
if (n == 0) return n;
|
||||
|
||||
int reader = m_readers[R];
|
||||
int here = m_size - reader;
|
||||
const T *const R__ bufbase = m_buffer + reader;
|
||||
|
||||
if (here >= n) {
|
||||
v_add(destination, bufbase, n);
|
||||
} else {
|
||||
v_add(destination, bufbase, here);
|
||||
v_add(destination + here, m_buffer, n - here);
|
||||
}
|
||||
|
||||
reader += n;
|
||||
while (reader >= m_size) reader -= m_size;
|
||||
m_readers[R] = reader;
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
T
|
||||
RingBuffer<T, N>::readOne(int R)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_writer == m_readers[R]) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: No sample available"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
int reader = m_readers[R];
|
||||
T value = m_buffer[reader];
|
||||
if (++reader == m_size) reader = 0;
|
||||
m_readers[R] = reader;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::peek(T *const R__ destination, int n, int R) const
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
int available = getReadSpace(R);
|
||||
if (n > available) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: Only " << available << " samples available"
|
||||
<< std::endl;
|
||||
#endif
|
||||
memset(destination + available, 0, (n - available) * sizeof(T));
|
||||
n = available;
|
||||
}
|
||||
if (n == 0) return n;
|
||||
|
||||
int reader = m_readers[R];
|
||||
int here = m_size - reader;
|
||||
const T *const R__ bufbase = m_buffer + reader;
|
||||
|
||||
if (here >= n) {
|
||||
v_copy(destination, bufbase, n);
|
||||
} else {
|
||||
v_copy(destination, bufbase, here);
|
||||
v_copy(destination + here, m_buffer, n - here);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
T
|
||||
RingBuffer<T, N>::peekOne(int R) const
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_writer == m_readers[R]) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: No sample available"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
T value = m_buffer[m_readers[R]];
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::skip(int n, int R)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
int available = getReadSpace(R);
|
||||
if (n > available) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: Only " << available << " samples available"
|
||||
<< std::endl;
|
||||
#endif
|
||||
n = available;
|
||||
}
|
||||
if (n == 0) return n;
|
||||
|
||||
int reader = m_readers[R];
|
||||
reader += n;
|
||||
while (reader >= m_size) reader -= m_size;
|
||||
m_readers[R] = reader;
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::write(const T *const R__ source, int n)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
int available = getWriteSpace();
|
||||
if (n > available) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: Only room for " << available << " samples"
|
||||
<< std::endl;
|
||||
#endif
|
||||
n = available;
|
||||
}
|
||||
if (n == 0) return n;
|
||||
|
||||
int writer = m_writer;
|
||||
int here = m_size - writer;
|
||||
T *const R__ bufbase = m_buffer + writer;
|
||||
|
||||
if (here >= n) {
|
||||
v_copy(bufbase, source, n);
|
||||
} else {
|
||||
v_copy(bufbase, source, here);
|
||||
v_copy(m_buffer, source + here, n - here);
|
||||
}
|
||||
|
||||
writer += n;
|
||||
while (writer >= m_size) writer -= m_size;
|
||||
m_writer = writer;
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
int
|
||||
RingBuffer<T, N>::zero(int n)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
int available = getWriteSpace();
|
||||
if (n > available) {
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "WARNING: Only room for " << available << " samples"
|
||||
<< std::endl;
|
||||
#endif
|
||||
n = available;
|
||||
}
|
||||
if (n == 0) return n;
|
||||
|
||||
int writer = m_writer;
|
||||
int here = m_size - writer;
|
||||
T *const R__ bufbase = m_buffer + writer;
|
||||
|
||||
if (here >= n) {
|
||||
v_zero(bufbase, n);
|
||||
} else {
|
||||
v_zero(bufbase, here);
|
||||
v_zero(m_buffer, n - here);
|
||||
}
|
||||
|
||||
writer += n;
|
||||
while (writer >= m_size) writer -= m_size;
|
||||
m_writer = writer;
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "writer -> " << m_writer << std::endl;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // _RINGBUFFER_H_
|
||||
236
src/base/Scavenger.h
Normal file
236
src/base/Scavenger.h
Normal file
@@ -0,0 +1,236 @@
|
||||
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
||||
|
||||
/*
|
||||
Rubber Band
|
||||
An audio time-stretching and pitch-shifting library.
|
||||
Copyright 2007-2009 Chris Cannam.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef _RUBBERBAND_SCAVENGER_H_
|
||||
#define _RUBBERBAND_SCAVENGER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "system/Thread.h"
|
||||
#include "system/sysutils.h"
|
||||
#include "system/Allocators.h"
|
||||
|
||||
//#define DEBUG_SCAVENGER 1
|
||||
|
||||
namespace RubberBand {
|
||||
|
||||
/**
|
||||
* A very simple class that facilitates running things like plugins
|
||||
* without locking, by collecting unwanted objects and deleting them
|
||||
* after a delay so as to be sure nobody's in the middle of using
|
||||
* them. Requires scavenge() to be called regularly from a non-RT
|
||||
* thread.
|
||||
*
|
||||
* This is currently not at all suitable for large numbers of objects
|
||||
* -- it's just a quick hack for use with things like plugins.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class Scavenger
|
||||
{
|
||||
public:
|
||||
Scavenger(int sec = 2, int defaultObjectListSize = 200);
|
||||
~Scavenger();
|
||||
|
||||
/**
|
||||
* Call from an RT thread etc., to pass ownership of t to us.
|
||||
* Only one thread should be calling this on any given scavenger.
|
||||
*/
|
||||
void claim(T *t);
|
||||
|
||||
/**
|
||||
* Call from a non-RT thread.
|
||||
* Only one thread should be calling this on any given scavenger.
|
||||
*/
|
||||
void scavenge(bool clearNow = false);
|
||||
|
||||
protected:
|
||||
typedef std::pair<T *, int> ObjectTimePair;
|
||||
typedef std::vector<ObjectTimePair> ObjectTimeList;
|
||||
ObjectTimeList m_objects;
|
||||
int m_sec;
|
||||
|
||||
typedef std::list<T *> ObjectList;
|
||||
ObjectList m_excess;
|
||||
int m_lastExcess;
|
||||
Mutex m_excessMutex;
|
||||
void pushExcess(T *);
|
||||
void clearExcess(int);
|
||||
|
||||
unsigned int m_claimed;
|
||||
unsigned int m_scavenged;
|
||||
unsigned int m_asExcess;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper to permit arrays allocated with new[] to be scavenged.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class ScavengerArrayWrapper
|
||||
{
|
||||
public:
|
||||
ScavengerArrayWrapper(T *array) : m_array(array) { }
|
||||
~ScavengerArrayWrapper() { delete[] m_array; }
|
||||
|
||||
private:
|
||||
T *m_array;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper to permit arrays allocated with the Allocators functions
|
||||
* to be scavenged.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class ScavengerAllocArrayWrapper
|
||||
{
|
||||
public:
|
||||
ScavengerAllocArrayWrapper(T *array) : m_array(array) { }
|
||||
~ScavengerAllocArrayWrapper() { deallocate<T>(m_array); }
|
||||
|
||||
private:
|
||||
T *m_array;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
Scavenger<T>::Scavenger(int sec, int defaultObjectListSize) :
|
||||
m_objects(ObjectTimeList(defaultObjectListSize)),
|
||||
m_sec(sec),
|
||||
m_claimed(0),
|
||||
m_scavenged(0),
|
||||
m_asExcess(0)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Scavenger<T>::~Scavenger()
|
||||
{
|
||||
if (m_scavenged < m_claimed) {
|
||||
for (size_t i = 0; i < m_objects.size(); ++i) {
|
||||
ObjectTimePair &pair = m_objects[i];
|
||||
if (pair.first != 0) {
|
||||
T *ot = pair.first;
|
||||
pair.first = 0;
|
||||
delete ot;
|
||||
++m_scavenged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearExcess(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
Scavenger<T>::claim(T *t)
|
||||
{
|
||||
// std::cerr << "Scavenger::claim(" << t << ")" << std::endl;
|
||||
|
||||
struct timeval tv;
|
||||
(void)gettimeofday(&tv, 0);
|
||||
int sec = tv.tv_sec;
|
||||
|
||||
for (size_t i = 0; i < m_objects.size(); ++i) {
|
||||
ObjectTimePair &pair = m_objects[i];
|
||||
if (pair.first == 0) {
|
||||
pair.second = sec;
|
||||
pair.first = t;
|
||||
++m_claimed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SCAVENGER
|
||||
std::cerr << "WARNING: Scavenger::claim(" << t << "): run out of slots (at "
|
||||
<< m_objects.size() << "), using non-RT-safe method" << std::endl;
|
||||
#endif
|
||||
pushExcess(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
Scavenger<T>::scavenge(bool clearNow)
|
||||
{
|
||||
#ifdef DEBUG_SCAVENGER
|
||||
std::cerr << "Scavenger::scavenge: claimed " << m_claimed << ", scavenged " << m_scavenged << ", cleared as excess " << m_asExcess << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_scavenged >= m_claimed) return;
|
||||
|
||||
struct timeval tv;
|
||||
(void)gettimeofday(&tv, 0);
|
||||
int sec = tv.tv_sec;
|
||||
bool anything = false;
|
||||
|
||||
for (size_t i = 0; i < m_objects.size(); ++i) {
|
||||
ObjectTimePair &pair = m_objects[i];
|
||||
if (!pair.first) continue;
|
||||
if (clearNow || pair.second + m_sec < sec) {
|
||||
T *ot = pair.first;
|
||||
pair.first = 0;
|
||||
delete ot;
|
||||
++m_scavenged;
|
||||
anything = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (clearNow || anything || (sec > m_lastExcess + m_sec)) {
|
||||
clearExcess(sec);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
Scavenger<T>::pushExcess(T *t)
|
||||
{
|
||||
m_excessMutex.lock();
|
||||
m_excess.push_back(t);
|
||||
struct timeval tv;
|
||||
(void)gettimeofday(&tv, 0);
|
||||
m_lastExcess = tv.tv_sec;
|
||||
m_excessMutex.unlock();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
Scavenger<T>::clearExcess(int sec)
|
||||
{
|
||||
#ifdef DEBUG_SCAVENGER
|
||||
std::cerr << "Scavenger::clearExcess: Excess now " << m_excess.size() << std::endl;
|
||||
#endif
|
||||
|
||||
m_excessMutex.lock();
|
||||
for (typename ObjectList::iterator i = m_excess.begin();
|
||||
i != m_excess.end(); ++i) {
|
||||
delete *i;
|
||||
++m_asExcess;
|
||||
}
|
||||
m_excess.clear();
|
||||
m_lastExcess = sec;
|
||||
m_excessMutex.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user