commit 1fbc6328ec12d8017d0873f15173f920b004d513
parent 27d811e8fcbedec3d70747c2dd0dd1b57b158aeb
Author: Matt Demanett <matt@demanett.net>
Date: Tue, 5 Dec 2017 01:32:34 -0500
Analyzer: pull fft averaging out into separate class; add benchmark on it; optimization.
Diffstat:
3 files changed, 131 insertions(+), 30 deletions(-)
diff --git a/benchmarks/buffer.cpp b/benchmarks/buffer.cpp
@@ -18,3 +18,31 @@ static void BM_OverlappingBuffer(benchmark::State& state) {
}
}
BENCHMARK(BM_OverlappingBuffer);
+
+
+static void _averagingBuffer(benchmark::State& state, int n, int m) {
+ AveragingBuffer<float> b(n, m);
+ for (int i = 0; i < m; ++i) {
+ float* frame = b.getInputFrame();
+ std::fill_n(frame, n, M_PI);
+ b.commitInputFrame();
+ }
+ float pi = 0.0;
+ for (auto _ : state) {
+ b.getInputFrame();
+ b.commitInputFrame();
+ pi = b.getAverages()[0];
+ }
+ const float e = 0.00001;
+ assert(pi > M_PI - e && pi < M_PI + e);
+}
+
+static void BM_AveragingBufferSmallN(benchmark::State& state) {
+ _averagingBuffer(state, 1024, 3);
+}
+BENCHMARK(BM_AveragingBufferSmallN);
+
+static void BM_AveragingBufferLargeN(benchmark::State& state) {
+ _averagingBuffer(state, 1024, 100);
+}
+BENCHMARK(BM_AveragingBufferLargeN);
diff --git a/src/Analyzer.cpp b/src/Analyzer.cpp
@@ -7,11 +7,9 @@
using namespace bogaudio::dsp;
struct ChannelAnalyzer : SpectrumAnalyzer {
- const int _averageN;
- const int _binsN;
+ int _binsN;
float* _bins;
- float* _frames;
- int _currentFrame;
+ AveragingBuffer<float>* _averagedBins;
ChannelAnalyzer(
SpectrumAnalyzer::Size size,
@@ -22,16 +20,27 @@ struct ChannelAnalyzer : SpectrumAnalyzer {
int binSize
)
: SpectrumAnalyzer(size, overlap, windowType, sampleRate)
- , _averageN(averageN)
, _binsN(size / binSize)
- , _bins(new float[size] {})
- , _frames(new float[_averageN * _binsN] {})
- , _currentFrame(0)
+ , _bins(averageN == 1 ? new float[_binsN] {} : NULL)
+ , _averagedBins(averageN == 1 ? NULL : new AveragingBuffer<float>(_binsN, averageN))
{
+ assert(averageN >= 1);
+ assert(binSize >= 1);
}
virtual ~ChannelAnalyzer() {
- delete[] _bins;
- delete[] _frames;
+ if (_bins) {
+ delete[] _bins;
+ }
+ if (_averagedBins) {
+ delete _averagedBins;
+ }
+ }
+
+ const float* getBins() {
+ if (_bins) {
+ return _bins;
+ }
+ return _averagedBins->getAverages();
}
virtual bool step(float sample) override;
@@ -40,18 +49,14 @@ struct ChannelAnalyzer : SpectrumAnalyzer {
bool ChannelAnalyzer::step(float sample) {
if (SpectrumAnalyzer::step(sample)) {
- float* frame = _frames + _currentFrame*_binsN;
- getMagnitudes(frame, _binsN);
-
- for (int bin = 0; bin < _binsN; ++bin) {
- _bins[bin] = 0.0;
- for (int i = 0; i < _averageN; ++i) {
- _bins[bin] += _frames[i*_binsN + bin];
- }
- _bins[bin] /= (float)_averageN;
- // FIXME: still unclear if should or should not take the root here: _bins[bin] = sqrtf(_bins[bin]);
+ if (_bins) {
+ getMagnitudes(_bins, _binsN);
+ }
+ else {
+ float* frame = _averagedBins->getInputFrame();
+ getMagnitudes(frame, _binsN);
+ _averagedBins->commitInputFrame();
}
- _currentFrame = (_currentFrame + 1) % _averageN;
return true;
}
return false;
@@ -61,12 +66,13 @@ float ChannelAnalyzer::getPeak() {
float max = 0.0;
float sum = 0.0;
int maxBin = 0;
+ const float* bins = getBins();
for (int bin = 0; bin < _binsN; ++bin) {
- if( _bins[bin] > max) {
- max = _bins[bin];
+ if (bins[bin] > max) {
+ max = bins[bin];
maxBin = bin;
}
- sum += _bins[bin];
+ sum += bins[bin];
}
const float fWidth = _sampleRate / (float)(_size / (_size / _binsN));
return (maxBin + 1)*fWidth - fWidth/2.0;
@@ -262,7 +268,7 @@ struct AnalyzerDisplay : TransparentWidget {
void drawYAxis(NVGcontext* vg, float strokeWidth);
void drawXAxis(NVGcontext* vg, float strokeWidth);
void drawXAxisLine(NVGcontext* vg, float hz, float maxHz);
- void drawGraph(NVGcontext* vg, float* bins, int binsN, NVGcolor color, float strokeWidth);
+ void drawGraph(NVGcontext* vg, const float* bins, int binsN, NVGcolor color, float strokeWidth);
void drawText(NVGcontext* vg, const char* s, float x, float y, float rotation = 0.0, const NVGcolor* color = NULL);
int binValueToHeight(float value);
};
@@ -279,16 +285,16 @@ void AnalyzerDisplay::draw(NVGcontext* vg) {
drawXAxis(vg, strokeWidth);
if (_module->_channelA) {
- drawGraph(vg, _module->_channelA->_bins, _module->_channelA->_binsN, _channelAColor, strokeWidth);
+ drawGraph(vg, _module->_channelA->getBins(), _module->_channelA->_binsN, _channelAColor, strokeWidth);
}
if (_module->_channelB) {
- drawGraph(vg, _module->_channelB->_bins, _module->_channelB->_binsN, _channelBColor, strokeWidth);
+ drawGraph(vg, _module->_channelB->getBins(), _module->_channelB->_binsN, _channelBColor, strokeWidth);
}
if (_module->_channelC) {
- drawGraph(vg, _module->_channelC->_bins, _module->_channelC->_binsN, _channelCColor, strokeWidth);
+ drawGraph(vg, _module->_channelC->getBins(), _module->_channelC->_binsN, _channelCColor, strokeWidth);
}
if (_module->_channelD) {
- drawGraph(vg, _module->_channelD->_bins, _module->_channelD->_binsN, _channelDColor, strokeWidth);
+ drawGraph(vg, _module->_channelD->getBins(), _module->_channelD->_binsN, _channelDColor, strokeWidth);
}
nvgRestore(vg);
}
@@ -453,7 +459,7 @@ void AnalyzerDisplay::drawXAxisLine(NVGcontext* vg, float hz, float maxHz) {
}
}
-void AnalyzerDisplay::drawGraph(NVGcontext* vg, float* bins, int binsN, NVGcolor color, float strokeWidth) {
+void AnalyzerDisplay::drawGraph(NVGcontext* vg, const float* bins, int binsN, NVGcolor color, float strokeWidth) {
const int pointsN = roundf(_module->_range*(_module->size()/2));
nvgSave(vg);
nvgScissor(vg, _insetLeft, _insetTop, _graphSize.x, _graphSize.y);
diff --git a/src/dsp/buffer.hpp b/src/dsp/buffer.hpp
@@ -51,5 +51,72 @@ struct OverlappingBuffer {
}
};
+
+template<typename T>
+struct AveragingBuffer {
+ const int _size;
+ const int _framesN;
+ const float _inverseFramesN;
+ T* _sums;
+ T* _averages;
+ T* _frames;
+ int _currentFrame;
+ const int _resetsPerCommit;
+ int _currentReset;
+
+ AveragingBuffer(
+ int size,
+ int framesToAverage
+ )
+ : _size(size)
+ , _framesN(framesToAverage)
+ , _inverseFramesN(1.0 / (float)_framesN)
+ , _sums(new T[_size] {})
+ , _averages(new T[_size] {})
+ , _frames(new T[_size * _framesN] {})
+ , _currentFrame(0)
+ , _resetsPerCommit(_size / 1000)
+ , _currentReset(0)
+ {
+ assert(framesToAverage > 0);
+ }
+ ~AveragingBuffer() {
+ delete[] _sums;
+ delete[] _averages;
+ delete[] _frames;
+ }
+
+ T* getInputFrame() {
+ float* frame = _frames + _currentFrame*_size;
+ for (int i = 0; i < _size; ++i) {
+ _sums[i] -= frame[i];
+ }
+ return frame;
+ }
+
+ void commitInputFrame() {
+ float* frame = _frames + _currentFrame*_size;
+ for (int i = 0; i < _size; ++i) {
+ _sums[i] += frame[i];
+ _averages[i] = _sums[i] * _inverseFramesN;
+ }
+
+ // Reset the average for some bins, such that reset overhead is even between calls -- avoids buildup of floating point error.
+ for (int i = 0; i < _resetsPerCommit; ++i) {
+ _sums[_currentReset] = 0.0;
+ for (int j = 0; j < _framesN; ++j) {
+ _sums[_currentReset] += _frames[j*_size + _currentReset];
+ }
+ _currentReset = (_currentReset + 1) % _size;
+ }
+
+ _currentFrame = (_currentFrame + 1) % _framesN;
+ }
+
+ const T* getAverages() {
+ return _averages;
+ }
+};
+
} // namespace dsp
} // namespace bogaudio