Restore MovingMedian to the simpler single filter and provide ..Stack separately. Seems little point in coalescing memory there, now we have separate ring buffers anyway

This commit is contained in:
Chris Cannam
2022-06-10 13:09:48 +01:00
parent 6723ca3636
commit 0bfa94a76a

View File

@@ -38,74 +38,96 @@ namespace RubberBand
{
template <typename T>
class MovingMedianStack
class MovingMedian : public SampleFilter<T>
{
public:
MovingMedianStack(int nfilters, int filterLength, float percentile = 50.f) :
m_buffers(nfilters, filterLength),
m_sortspace(nfilters * filterLength, {}),
m_length(filterLength)
MovingMedian(int filterLength, float percentile = 50.f) :
m_buffer(filterLength),
m_sortspace(filterLength, {})
{
setPercentile(percentile);
}
~MovingMedianStack() {
~MovingMedian() {
}
int getNFilters() const {
return m_buffers.size();
}
MovingMedian(const MovingMedian &) =default;
MovingMedian &operator=(const MovingMedian &) =default;
int getSize() const {
return m_length;
return m_buffer.getSize();
}
void setPercentile(float p) {
m_index = int((m_length * p) / 100.f);
if (m_index >= m_length) m_index = m_length-1;
int length = getSize();
m_index = int((length * p) / 100.f);
if (m_index >= length) m_index = length-1;
if (m_index < 0) m_index = 0;
}
void push(int filter, T value) {
void push(T value) {
if (value != value) {
std::cerr << "WARNING: MovingMedian: NaN encountered" << std::endl;
value = T();
}
auto &buf = m_buffers[filter];
if (buf.getWriteSpace() == 0) {
T toDrop = buf.readOne();
dropAndPut(filter, toDrop, value);
buf.writeOne(value);
if (m_buffer.getWriteSpace() == 0) {
T toDrop = m_buffer.readOne();
dropAndPut(toDrop, value);
m_buffer.writeOne(value);
} else {
put(filter, value);
buf.writeOne(value);
put(value);
m_buffer.writeOne(value);
}
}
T get(int filter) const {
const T *sorted = sortedFor(filter);
return sorted[m_index];
T get() const {
return m_sortspace[m_index];
}
void reset() {
for (auto &buf : m_buffers) buf.reset();
m_buffer.reset();
v_zero(m_sortspace.data(), m_sortspace.size());
}
// Convenience function that applies a given filter to an array
// in-place. Array has length n. Modifies both the filter and the
// array.
//
static void filter(MovingMedian<T> &mm, T *v, int n) {
int fn = mm.getSize();
int lag = fn / 2;
mm.reset();
int i = 0;
for (; i < lag; ++i) {
if (i < n) mm.push(v[i]);
}
for (; i < n; ++i) {
mm.push(v[i]);
v[i-lag] = mm.get();
}
for (; i < lag; ++i) {
// just for the unusual case where lag > n
mm.push(T());
(void)mm.get();
}
for (; i < n + lag; ++i) {
mm.push(T());
v[i-lag] = mm.get();
}
}
// As above but with a vector argument
//
static void filter(MovingMedian<T> &mm, std::vector<T> &v) {
filter(mm, v.data(), v.size());
}
private:
FixedVector<SingleThreadRingBuffer<T>> m_buffers;
FixedVector<T> m_sortspace;
int m_length;
SingleThreadRingBuffer<T> m_buffer;
std::vector<T> m_sortspace;
int m_index;
const T *sortedFor(int filter) const {
return m_sortspace.data() + filter * m_length;
}
T *sortedFor(int filter) {
return m_sortspace.data() + filter * m_length;
}
void dropAndPut(int filter, const T &toDrop, const T &toPut) {
void dropAndPut(const T &toDrop, const T &toPut) {
// precondition: sorted contains m_length values, one of which is toDrop
// postcondition: sorted contains m_length values, one of which is toPut
// (and one instance of toDrop has been removed)
@@ -114,8 +136,8 @@ private:
// longer than maybe 16 items). Two binary searches plus a
// memmove should be faster for longer ones.
const int n = m_length;
T *sorted = sortedFor(filter);
const int n = getSize();
T *sorted = m_sortspace.data();
int dropIx;
if (toDrop <= *sorted) {
// this is quite a common short-circuit in situations
@@ -175,12 +197,12 @@ private:
#endif
}
void put(int filter, const T &toPut) {
void put(const T &toPut) {
// precondition: sorted contains fewer than m_length values,
// packed at the start
// postcondition: sorted contains up to m_length values,
// packed at the start, one of which is toPut
const int n = m_buffers[filter].getReadSpace(); // items in sorted
const int n = m_buffer.getReadSpace(); // items in sorted
#ifdef DEBUG_MM
if (n >= m_length) {
@@ -188,7 +210,7 @@ private:
}
#endif
T *sorted = sortedFor(filter);
T *sorted = m_sortspace.data();
int putIx = std::lower_bound(sorted, sorted + n, toPut) - sorted;
#ifdef DEBUG_MM
@@ -220,81 +242,46 @@ private:
}
#endif
}
MovingMedianStack(const MovingMedianStack &) =delete;
MovingMedianStack &operator=(const MovingMedianStack &) =delete;
};
template <typename T>
class MovingMedian : public SampleFilter<T>
class MovingMedianStack
{
public:
MovingMedian(int size, float percentile = 50.f) :
m_mm(1, size, percentile)
MovingMedianStack(int nfilters, int size, float percentile = 50.f) :
m_stack(nfilters, { size, percentile })
{
}
~MovingMedian() {
~MovingMedianStack() {
}
int getSize() const {
return m_mm.getSize();
return m_stack[0].getSize();
}
void setPercentile(float p) {
m_mm.setPercentile(p);
for (auto &f: m_stack) {
f.setPercentile(p);
}
}
void push(T value) {
m_mm.push(0, value);
void push(int filter, T value) {
m_stack[filter].push(value);
}
T get() const {
return m_mm.get(0);
T get(int filter) const {
return m_stack[filter].get();
}
void reset() {
m_mm.reset();
}
// Convenience function that applies a given filter to an array
// in-place. Array has length n. Modifies both the filter and the
// array.
//
static void filter(MovingMedian<T> &mm, T *v, int n) {
int fn = mm.getSize();
int lag = fn / 2;
mm.reset();
int i = 0;
for (; i < lag; ++i) {
if (i < n) mm.push(v[i]);
for (auto &f: m_stack) {
f.reset();
}
for (; i < n; ++i) {
mm.push(v[i]);
v[i-lag] = mm.get();
}
for (; i < lag; ++i) {
// just for the unusual case where lag > n
mm.push(T());
(void)mm.get();
}
for (; i < n + lag; ++i) {
mm.push(T());
v[i-lag] = mm.get();
}
}
// As above but with a vector argument
//
static void filter(MovingMedian<T> &mm, std::vector<T> &v) {
filter(mm, v.data(), v.size());
}
private:
MovingMedianStack<T> m_mm;
MovingMedian(const MovingMedian &) =delete;
MovingMedian &operator=(const MovingMedian &) =delete;
std::vector<MovingMedian<T>> m_stack;
};
}