Skip to content

Commit eda89ba

Browse files
authored
Merge pull request #3231 from tleb/spectrum-precompute-hann
Optimise spectrum's realtime code
2 parents dc2c964 + afc43cb commit eda89ba

File tree

2 files changed

+30
-34
lines changed

2 files changed

+30
-34
lines changed

include/spectrum.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,7 @@ class Spectrum : public PluginBase {
6565

6666
uint n_bands = 8192U;
6767

68-
std::deque<float> deque_in_mono;
68+
std::vector<float> latest_samples_mono;
69+
70+
std::vector<float> hann_window;
6971
};

src/spectrum.cpp

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <glib.h>
2525
#include <sys/types.h>
2626
#include <algorithm>
27+
#include <cassert>
2728
#include <cmath>
2829
#include <cstddef>
2930
#include <mutex>
@@ -45,6 +46,16 @@ Spectrum::Spectrum(const std::string& tag,
4546
real_input.resize(n_bands);
4647
output.resize(n_bands / 2U + 1U);
4748

49+
latest_samples_mono.resize(n_bands);
50+
51+
// Precompute the Hann window, which is an expensive operation.
52+
// https://en.wikipedia.org/wiki/Hann_function
53+
hann_window.resize(n_bands);
54+
for (size_t n = 0; n < n_bands; n++) {
55+
hann_window[n] = 0.5F * (1.0F - std::cos(2.0F * std::numbers::pi_v<float> *
56+
static_cast<float>(n) / static_cast<float>(n_bands-1)));
57+
}
58+
4859
complex_output = fftwf_alloc_complex(n_bands);
4960

5061
plan = fftwf_plan_dft_r2c_1d(static_cast<int>(n_bands), real_input.data(), complex_output, FFTW_ESTIMATE);
@@ -78,21 +89,8 @@ Spectrum::~Spectrum() {
7889
}
7990

8091
void Spectrum::setup() {
81-
deque_in_mono.resize(0U);
82-
8392
std::ranges::fill(real_input, 0.0F);
84-
85-
/*
86-
real_input size is hardcoded to 8192. THe same maxium buffer size hardcoded in PipeWire
87-
https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/src/pipewire/filter.c#L48. If we reevei a smaller
88-
array we have to insert some zeros in the beginning.
89-
*/
90-
91-
if (n_samples < real_input.size()) {
92-
while (deque_in_mono.size() != real_input.size() - n_samples) {
93-
deque_in_mono.push_back(0.0F);
94-
}
95-
}
93+
std::ranges::fill(latest_samples_mono, 0.0F);
9694
}
9795

9896
void Spectrum::process(std::span<float>& left_in,
@@ -108,27 +106,23 @@ void Spectrum::process(std::span<float>& left_in,
108106
return;
109107
}
110108

111-
for (uint n = 0U; n < left_in.size(); n++) {
112-
deque_in_mono.push_back(0.5F * (left_in[n] + right_in[n]));
109+
if (n_samples < n_bands) {
110+
// Drop the oldest quantum.
111+
for (size_t n = 0; n < n_bands - n_samples; n++)
112+
latest_samples_mono[n] = latest_samples_mono[n_samples + n];
113+
114+
// Copy the new quantum.
115+
for (size_t n = 0; n < n_samples; n++)
116+
latest_samples_mono[n_bands - n_samples + n] = 0.5F * (left_in[n] + right_in[n]);
117+
} else {
118+
// Copy the latest n_bands samples.
119+
for (size_t n = 0; n < n_bands; n++)
120+
latest_samples_mono[n] = 0.5F * (left_in[n_samples - n_bands + n] +
121+
right_in[n_samples - n_bands + n]);
113122
}
114123

115-
for (size_t n = 0; n < deque_in_mono.size(); n++) {
116-
if (n < real_input.size()) {
117-
// https : // en.wikipedia.org/wiki/Hann_function
118-
119-
const float w = 0.5F * (1.0F - std::cos(2.0F * std::numbers::pi_v<float> * static_cast<float>(n) /
120-
static_cast<float>(real_input.size() - 1U)));
121-
122-
real_input[n] = deque_in_mono[n] * w;
123-
}
124-
}
125-
126-
size_t count = 0U;
127-
128-
while (count < n_samples && !deque_in_mono.empty()) {
129-
deque_in_mono.pop_front();
130-
131-
count++;
124+
for (size_t n = 0; n < n_bands; n++) {
125+
real_input[n] = latest_samples_mono[n] * hann_window[n];
132126
}
133127

134128
if (send_notifications) {

0 commit comments

Comments
 (0)