92 halp_meta(name,
"Spectrum")
93 halp_meta(c_name,
"SpectrumAnalyser")
94 halp_meta(category,
"Monitoring/Audio")
95 halp_meta(author,
"ossia score")
96 halp_meta(manual_url,
"")
99 "Spectrum analyser, third-octave RTA and spectrogram, calibrated "
100 "so that a full-scale sine reads 0 dBFS")
101 halp_meta(uuid,
"277e8d4c-afea-490f-bf45-21ac2180871c")
102 halp_flag(fully_custom_item);
107 halp::dynamic_audio_bus<
"Audio",
double> audio;
108 struct : halp::combobox_t<
"Mode", Mode>
112 std::string_view values[3]{mode_names[0], mode_names[1], mode_names[2]};
113 Mode init{Mode::Analyser};
116 struct : halp::combobox_t<
"FFT size", Size>
118 halp_meta(description,
"Larger: finer frequency resolution, slower response")
121 std::string_view values[6]{
122 size_names[0], size_names[1], size_names[2],
123 size_names[3], size_names[4], size_names[5]};
124 Size init{Size::S8192};
127 struct : halp::combobox_t<
"Window", Ui::Monitor::Window>
129 halp_meta(description,
"Hann for general use, Blackman-Harris for dynamic range, flat top for exact levels")
132 std::string_view values[3]{window_names[0], window_names[1], window_names[2]};
133 Ui::Monitor::Window init{Ui::Monitor::Window::Hann};
136 struct : halp::spinbox_f32<
"Averaging", halp::range{0., 10., 0.3}>
138 halp_meta(description,
"Seconds of exponential averaging")
140 struct : halp::combobox_t<
"Slope", Slope>
142 halp_meta(description,
"Tilt around 1 kHz: 3 dB/oct shows pink noise flat")
145 std::string_view values[3]{slope_names[0], slope_names[1], slope_names[2]};
146 Slope init{Slope::Flat};
149 struct : halp::combobox_t<
"Smoothing", Smoothing>
153 std::string_view values[4]{
154 smoothing_names[0], smoothing_names[1], smoothing_names[2], smoothing_names[3]};
155 Smoothing init{Smoothing::Off};
158 struct : halp::spinbox_f32<
"Floor", halp::range{-160., -20., -100.}>
161 struct : halp::spinbox_f32<
"Ceiling", halp::range{-60., 24., 0.}>
164 struct : halp::combobox_t<
"Channels", Channels>
168 std::string_view values[5]{
169 channels_names[0], channels_names[1], channels_names[2], channels_names[3],
171 Channels init{Channels::Sum};
174 halp::toggle<
"Peak hold"> peak_hold;
179 struct : halp::val_port<
"Peak",
float>
181 halp_meta(description,
"Frequency of the strongest component, in Hz")
183 struct : halp::val_port<
"Centroid",
float>
185 halp_meta(description,
"Spectral centroid, in Hz")
188 struct : halp::val_port<
"Bands", std::optional<std::vector<float>>>
190 halp_meta(description,
"Levels of the 31 third-octave bands from 20 Hz to 20 kHz, in dBFS")
194 std::shared_ptr<SpectrumMonitor> monitor = std::make_shared<SpectrumMonitor>();
196 std::vector<double> history;
197 std::vector<double> ordered;
206 const double r = s.rate > 0 ? s.rate : 48000.;
210 monitor->ring.set_rate(rate);
211 analysis.prepare(4096, rate, Ui::Monitor::Window::Hann);
212 history.assign(4096, 0.);
213 ordered.assign(4096, 0.);
217 void operator()(
int frames)
219 const int nch = inputs.audio.channels;
220 if(nch <= 0 || frames <= 0 || history.empty())
222 if(monitor->watched())
223 monitor->ring.write(nch, frames, [&](
int c) {
return inputs.audio[c]; });
226 const int n = int(history.size());
227 for(
int i = 0; i < frames; i++)
230 for(
int c = 0; c < nch; c++)
231 x += inputs.audio[c][i];
232 history[write_pos] = x / nch;
233 write_pos = (write_pos + 1) % n;
236 if(since < rate / 20.)
239 for(
int i = 0; i < n; i++)
240 ordered[i] = history[(write_pos + i) % n];
241 analysis.
analyse(ordered.data());
243 const auto& p = analysis.
power;
245 double num = 0, den = 0;
246 for(
int b = 1; b < int(p.size()); b++)
250 num += p[b] * analysis.binFrequency(b);
253 outputs.peak.value = float(analysis.binFrequency(best));
254 outputs.centroid.value = den > 0 ? float(num / den) : 0.f;
255 auto& bands = outputs.bands.value.emplace(Ui::Monitor::ThirdOctave::count);
256 for(
int i = 0; i < Ui::Monitor::ThirdOctave::count; i++)
257 bands[i] =
float(10. * std::log10(std::max(
259 Ui::Monitor::ThirdOctave::lower(i), Ui::Monitor::ThirdOctave::upper(i)),
266 static constexpr double fmin = 20.;
267 static constexpr double left_margin = 30.;
268 static constexpr double bottom_margin = 14.;
274 std::vector<double> avg;
275 std::vector<double> peak;
285 Smoothing smoothing{};
291 QGraphicsItem* parent);
293 void hoverMoveEvent(QGraphicsSceneHoverEvent* ev)
override;
294 void hoverLeaveEvent(QGraphicsSceneHoverEvent*)
override;
295 void paint_impl(QPainter* p)
const override;
302 double floorDb()
const;
303 double ceilingDb()
const;
304 double fmax()
const {
return std::min(20000., m_rate / 2.); }
305 Display display()
const;
308 double levelAt(
const Display& d,
const std::vector<double>& power,
double f0,
double f1)
const;
309 QRectF plotRect()
const;
310 double xOfFreq(
const QRectF& r,
double f)
const;
311 double freqOfX(
const QRectF& r,
double x)
const;
312 double yOfDb(
const QRectF& r,
double db)
const;
313 void addSonogramColumns(
int count);
315 void paintGrid(QPainter* p,
const QRectF& r)
const;
316 void paintAnalyser(QPainter* p)
const;
317 void paintRta(QPainter* p)
const;
318 void paintSonogram(QPainter* p)
const;
335 std::vector<std::vector<float>> m_in;
336 std::vector<float> m_mix;
337 std::vector<Curve> m_curves;
338 QElapsedTimer m_clock;
344 uint64_t m_column_written{};
346 double m_rate{48000.};
347 std::optional<QPointF> m_hover;
Definition ProcessContext.hpp:12