From 0cd622d0da9cdc37ae48ad4f05e12da30c5b9f47 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Tue, 7 Jun 2022 09:50:33 +0100 Subject: [PATCH] Create a MovingMedianStack to contain a stack of filters with contiguous addressing - does not appear to be notably beneficial, though I quite like the api --- src/common/MovingMedian.h | 129 ++++++++++++++++++++++++++++++++++++ src/finer/BinClassifier.h | 18 ++--- src/finer/R3StretcherImpl.h | 1 + 3 files changed, 140 insertions(+), 8 deletions(-) diff --git a/src/common/MovingMedian.h b/src/common/MovingMedian.h index 37a2d98..b1eee68 100644 --- a/src/common/MovingMedian.h +++ b/src/common/MovingMedian.h @@ -25,6 +25,7 @@ #define RUBBERBAND_MOVING_MEDIAN_H #include "SampleFilter.h" +#include "FixedVector.h" #include "Allocators.h" #include @@ -35,6 +36,134 @@ namespace RubberBand { +template +class MovingMedianStack +{ +public: + MovingMedianStack(int nfilters, int filterSize, float percentile = 50.f) : + m_buffer(nfilters * filterSize * 2, {}), + m_size(filterSize) + { + setPercentile(percentile); + } + + ~MovingMedianStack() { + } + + void setPercentile(float p) { + m_index = int((m_size * p) / 100.f); + if (m_index >= m_size) m_index = m_size-1; + if (m_index < 0) m_index = 0; + } + + void push(int filter, T value) { + if (value != value) { + std::cerr << "WARNING: MovingMedian: NaN encountered" << std::endl; + value = T(); + } + T *frame = frameFor(filter); + T toDrop = frame[0]; + v_move(frame, frame+1, m_size-1); + frame[m_size-1] = value; + dropAndPut(filter, toDrop, value); + } + + T get(int filter) const { + const T *sorted = sortedFor(filter); + return sorted[m_index]; + } + + void reset() { + v_zero(m_buffer.data(), m_buffer.size()); + } + +private: + FixedVector m_buffer; + int m_size; + int m_index; + + const T *frameFor(int filter) const { + return m_buffer.data() + filter * m_size * 2; + } + T *frameFor(int filter) { + return m_buffer.data() + filter * m_size * 2; + } + const T *sortedFor(int filter) const { + return frameFor(filter) + m_size; + } + T *sortedFor(int filter) { + return frameFor(filter) + m_size; + } + + void dropAndPut(int filter, const T &toDrop, const T &toPut) { + // precondition: sorted contains m_size values, one of which is toDrop + // postcondition: sorted contains m_size values, one of which is toPut + // (and one instance of toDrop has been removed) + int n = m_size; + int dropIx; + T *sorted = sortedFor(filter); + if (toDrop <= sorted[0]) { + // this is quite a common short-circuit in situations + // where many values can be (the equivalent of) 0 + dropIx = 0; + } else { + dropIx = std::lower_bound(sorted, sorted + n, toDrop) - sorted; + } + +#ifdef DEBUG_MM + std::cout << "\nbefore: ["; + for (int i = 0; i < m_size; ++i) { + if (i > 0) std::cout << ","; + std::cout << sorted[i]; + } + std::cout << "]" << std::endl; + + std::cout << "toDrop = " << toDrop << ", dropIx = " << dropIx << std::endl; + std::cout << "toPut = " << toPut << std::endl; + if (sorted[dropIx] != toDrop) { + throw std::runtime_error("element not found"); + } +#endif + + if (toPut > toDrop) { + int i = dropIx; + while (i+1 < n) { + if (sorted[i+1] > toPut) { + break; + } + sorted[i] = sorted[i+1]; + ++i; + } + sorted[i] = toPut; + } else if (toPut < toDrop) { + int i = dropIx; + while (true) { + if (--i < 0 || sorted[i] < toPut) { + break; + } + sorted[i+1] = sorted[i]; + } + sorted[i+1] = toPut; + } + +#ifdef DEBUG_MM + std::cout << "after: ["; + for (int i = 0; i < m_size; ++i) { + if (i > 0) std::cout << ","; + std::cout << sorted[i]; + } + std::cout << "]" << std::endl; + + if (!std::is_sorted(sorted, sorted + n)) { + throw std::runtime_error("array is not sorted"); + } +#endif + } + + MovingMedianStack(const MovingMedianStack &) =delete; + MovingMedianStack &operator=(const MovingMedianStack &) =delete; +}; + template class MovingMedian : public SampleFilter { diff --git a/src/finer/BinClassifier.h b/src/finer/BinClassifier.h index 73febee..105fcf8 100644 --- a/src/finer/BinClassifier.h +++ b/src/finer/BinClassifier.h @@ -66,16 +66,13 @@ public: BinClassifier(Parameters parameters) : m_parameters(parameters), + m_hFilters(new MovingMedianStack(m_parameters.binCount, + m_parameters.horizontalFilterLength)), m_vFilter(new MovingMedian(m_parameters.verticalFilterLength)), m_vfQueue(parameters.horizontalFilterLag) { int n = m_parameters.binCount; - for (int i = 0; i < n; ++i) { - m_hFilters.push_back(std::make_shared> - (m_parameters.horizontalFilterLength)); - } - m_hf = allocate_and_zero(n); m_vf = allocate_and_zero(n); @@ -96,12 +93,17 @@ public: deallocate(m_vf); } + void reset() + { + m_hFilters->reset(); + } + void classify(const double *const mag, Classification *classification) { const int n = m_parameters.binCount; for (int i = 0; i < n; ++i) { - m_hFilters[i]->push(mag[i]); - m_hf[i] = m_hFilters[i]->get(); + m_hFilters->push(i, mag[i]); + m_hf[i] = m_hFilters->get(i); } v_copy(m_vf, mag, n); @@ -134,7 +136,7 @@ public: protected: Parameters m_parameters; - std::vector>> m_hFilters; + std::unique_ptr> m_hFilters; std::unique_ptr> m_vFilter; // We manage the queued frames through pointer swapping, hence // bare pointers here diff --git a/src/finer/R3StretcherImpl.h b/src/finer/R3StretcherImpl.h index 7079934..80e9bc9 100644 --- a/src/finer/R3StretcherImpl.h +++ b/src/finer/R3StretcherImpl.h @@ -201,6 +201,7 @@ protected: formant(new FormantData(segmenterParameters.fftSize)) { } void reset() { haveReadahead = false; + classifier->reset(); segmentation = BinSegmenter::Segmentation(); prevSegmentation = BinSegmenter::Segmentation(); nextSegmentation = BinSegmenter::Segmentation();