Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
audio_widgets.hpp
Go to the documentation of this file.
1#pragma once
2
8
9namespace pulp::audio {
11}
12namespace pulp::view {
13// ── Meter ────────────────────────────────────────────────────────────────────
14// Audio level meter with peak hold
15
16class Meter : public View,
18 public InspectorFacts,
19 public LevelDisplay {
20 public:
22
26
27 // ── AccessibilityValueInterface (read-only gauge, normalized 0..1) ───
28 // `meter` maps to a UIA ProgressBar / AT-SPI LEVEL_BAR, both of which
29 // advertise a range. Without a source they report 0 in a 0..0 range.
30 double get_current_value() const override {
31 return display_peak();
32 }
33 void set_current_value(double) override {} // a meter is not writable
34 double get_minimum_value() const override {
35 return 0.0;
36 }
37 double get_maximum_value() const override {
38 return 1.0;
39 }
40
42 render_style_ = s;
43 }
45 return render_style_;
46 }
47
48 // Set levels directly (normalized 0-1)
49 void set_level(float rms, float peak);
50 void set_levels(float primary, float secondary) override {
51 set_level(primary, secondary);
52 }
57 void set_level_fill(std::optional<canvas::Color> background,
58 std::vector<canvas::Color> gradient) override {
59 if (background)
60 set_skin_background_color(*background);
61 set_skin_gradient(std::move(gradient));
62 }
63 void clear_level_fill() override {
64 clear_skin();
65 }
66 void set_level_bar_ratio(float ratio) override {
67 set_bar_fill_ratio(ratio);
68 }
72
73 // Update from MeterBallistics (call once per frame with dt)
74 void update(float raw_peak, float raw_rms, float dt);
75
77 orientation_ = o;
78 }
80 return orientation_;
81 }
82
83 // Ballistics accessors for testing
84 float display_rms() const {
85 return ballistics_.display_rms;
86 }
87 float display_peak() const {
88 return ballistics_.display_peak;
89 }
90
91 std::string_view inspector_type_name() const override {
92 return "Meter";
93 }
94 void inspector_facts(std::vector<InspectorFacts::Fact>& out) const override;
95 float held_peak() const {
96 return ballistics_.held_peak;
97 }
98
99 // ── Live host→view source ────────────────────────────────────────────────
100 // Bind a lock-free MeterSource whose `channel` this meter reads once per
101 // frame, driving the ballistic `update()` path from `on_meter_frame`. The
102 // subscription lifecycle (attach, re-point on a clock change, self-healing
103 // teardown) and the lifetime contract live on View::set_meter_source; these
104 // are the meter-shaped spelling of that seam.
105 void set_source(std::shared_ptr<MeterSource> source, int channel = 0) {
106 set_meter_source(std::move(source), channel);
107 }
108 bool has_source() const {
109 return has_meter_source();
110 }
111 int source_channel() const {
112 return meter_source_channel();
113 }
114
115 void paint(canvas::Canvas& canvas) override;
116
117 // ── Skin overrides ────────────────────────────────────────────────────
118 // Per-widget gradient + background, generalising the knob sprite-strip
119 // path to the meter. The importer samples the captured Figma PNG's
120 // gradient (green→orange→red) and hands the stops here; the meter redraws
121 // that gradient procedurally, CLIPPED to the current level, so the fill
122 // still animates with set_level()/update() instead of freezing the
123 // captured image. Stops are ordered low→high (bottom→top for vertical).
124 // Unset → today's theme-token threshold behavior (back-compat).
125 void set_skin_gradient(std::vector<canvas::Color> stops) {
126 gradient_stops_ = std::move(stops);
128 }
130 background_color_ = c;
131 has_skin_background_ = true;
133 }
134 void clear_skin() {
135 gradient_stops_.clear();
136 has_skin_background_ = false;
137 bar_fill_ratio_ = 1.0f;
139 }
140 // Fraction of the widget's cross-axis width occupied by the colored bar
141 // (0..1). The captured meter draws a narrow colored fill recessed inside a
142 // wider dark housing slot; this ratio reproduces that inset so the rendered
143 // bar isn't edge-to-edge paint. Derived from the captured asset
144 // (colored-bar width / housing width); defaults to 1.0 (full width) when the
145 // importer didn't supply it.
146 void set_bar_fill_ratio(float r) {
147 bar_fill_ratio_ = std::clamp(r, 0.05f, 1.0f);
149 }
150 float bar_fill_ratio() const {
151 return bar_fill_ratio_;
152 }
153 bool has_skin_gradient() const {
154 return gradient_stops_.size() >= 2;
155 }
157 return has_skin_background_;
158 }
159 const std::vector<canvas::Color>& skin_gradient() const {
160 return gradient_stops_;
161 }
163 return background_color_;
164 }
165
166 // Sample the skin gradient at normalized position t (0=low/bottom,
167 // 1=high/top). Linear interpolation across the supplied stops.
168 //
169 // Nothing when no stops were set: a meter with no gradient has no colour
170 // to give, and the caller must draw nothing rather than receive one. This
171 // used to answer a built-in green, which is a colour from nowhere on any
172 // design that never chose it — and returning a transparent Color instead
173 // would be no safer, since a caller that ignores alpha paints it black.
174 std::optional<canvas::Color> gradient_color_at(float t) const;
175
176 private:
177 // Drive the ballistic path from the frame the View base snapshotted for us.
178 void on_meter_frame(const MeterFrame& frame, float dt) override;
181 void on_meter_snapshot_changed() override {}
182
183 Orientation orientation_ = Orientation::vertical;
184 MeterBallistics ballistics_;
185 float current_rms_ = 0;
186 float current_peak_ = 0;
188 std::vector<canvas::Color> gradient_stops_;
189 canvas::Color background_color_{};
190 bool has_skin_background_ = false;
191 float bar_fill_ratio_ = 1.0f;
192};
193
194// ── WaveformView ─────────────────────────────────────────────────────────────
195// Displays audio waveform data
196//
197// Two backing sources are supported:
198//
199// set_data(...) — raw normalized samples, redrawn directly. The
200// existing behavior, used by oscilloscope-style
201// displays of live audio.
202// set_thumbnail(...) — points the widget at a pre-decoded
203// `pulp::audio::WaveformOverview`. Paint reads peak
204// pairs from the cached level instead of decoding
205// per redraw, wiring WaveformOverview into the existing
206// WaveformView without inventing a new widget.
207//
208// This remains a display-only waveform widget. Sampler/editor behaviors
209// such as trim handles, fade curves, loop regions, slice markers, playheads,
210// hit testing, and snapping should live in dedicated editor/viewport modules
211// that compose this renderer instead of expanding WaveformView itself.
212
213} // namespace pulp::view
214namespace pulp::audio {
215class WaveformOverview;
216}
217namespace pulp::view {
218
219class WaveformView : public View, public InspectorFacts, public SeriesDisplay {
220 public:
221 // Triggering mode for periodic signal display.
222 // free_run: no triggering — caller is responsible for continuity
223 // rising_zero: align to the first rising-edge zero crossing
224 // falling_zero: align to the first falling-edge zero crossing
227
228 WaveformView() = default;
229
230 // Set waveform data (normalized -1 to 1). If a non-free_run trigger
231 // mode is active, the buffer is rotated so the first matching
232 // crossing becomes index 0, producing a stable display for periodic
233 // signals. If no crossing is found, the buffer is stored as-is.
234 //
235 // Calling set_data clears any active thumbnail source.
236 void set_data(const float* samples, size_t count);
237 void set_data(std::vector<float> samples);
238 void set_series(const float* values, std::size_t count) override {
239 set_data(values, count);
240 }
241 void set_series_preview(std::string_view token) override {
242 set_preview_shape(token);
243 }
244
245 // Wire the widget to read peak (min, max) pairs from a pre-decoded
246 // `WaveformOverview`. The shared_ptr overload is the
247 // editor/cache-safe path: the widget retains the CPU thumbnail summary
248 // while it may paint from it. Setting a thumbnail clears any raw sample
249 // buffer.
250 //
251 // `channel == UINT32_MAX` folds all channels into one display row.
252 void set_thumbnail(std::shared_ptr<const pulp::audio::WaveformOverview> thumb,
253 uint32_t channel = static_cast<uint32_t>(-1));
254 [[deprecated("set_thumbnail(const WaveformOverview*) is borrowed; prefer shared_ptr overload "
255 "or set_thumbnail_borrowed()")]]
257 uint32_t channel = static_cast<uint32_t>(-1));
258 // Explicit borrowed escape hatch for stack-owned tests or owners that
259 // manage lifetime externally. Prefer set_thumbnail(shared_ptr) for editor
260 // code and cache-backed thumbnails.
262 uint32_t channel = static_cast<uint32_t>(-1));
264
265 bool has_thumbnail() const noexcept {
266 return thumbnail_ != nullptr;
267 }
268 const pulp::audio::WaveformOverview* thumbnail() const noexcept {
269 return thumbnail_;
270 }
271
272 size_t sample_count() const {
273 return samples_.size();
274 }
275
276 std::string_view inspector_type_name() const override {
277 return "WaveformView";
278 }
279 void inspector_facts(std::vector<InspectorFacts::Fact>& out) const override;
280
281 // Static oscillator-preview lane for imported synth-style designs.
282 // Live sample data and thumbnails still take precedence when present.
283 void set_preview_shape(std::string_view shape);
285 return preview_shape_;
286 }
287
289 trigger_mode_ = mode;
290 }
292 return trigger_mode_;
293 }
294
295 // Locate the first crossing of `mode` in the buffer without mutating
296 // it. Returns the crossing index, or 0 if none is found or
297 // mode is free_run.
298 static size_t find_trigger_index(const float* samples, size_t count, TriggerMode mode);
299
300 void paint(canvas::Canvas& canvas) override;
301
302 private:
303 void apply_trigger();
304
305 std::vector<float> samples_;
306 TriggerMode trigger_mode_ = TriggerMode::free_run;
307 PreviewShape preview_shape_ = PreviewShape::none;
308
309 std::shared_ptr<const pulp::audio::WaveformOverview> thumbnail_owner_;
310 const pulp::audio::WaveformOverview* thumbnail_ = nullptr;
311 std::vector<float> thumbnail_min_max_;
312 std::vector<float> thumbnail_envelope_;
313 uint32_t thumbnail_channel_ = static_cast<uint32_t>(-1);
314};
315
316// ── SpectrumView ─────────────────────────────────────────────────────────────
317// Displays frequency spectrum (magnitude data from FFT)
318
319class SpectrumView : public View, public InspectorFacts, public BinDisplay {
320 public:
321 enum class Style { bars, line, filled };
322
323 SpectrumView() = default;
324
325 // Set spectrum magnitudes (dB, typically -100 to 0)
326 void set_spectrum(const float* magnitudes_db, size_t bin_count);
327 void set_spectrum(std::vector<float> magnitudes_db);
328 void set_bins(const float* values, std::size_t count) override {
329 set_spectrum(values, count);
330 }
331
332 size_t bin_count() const {
333 return bins_.size();
334 }
335
336 std::string_view inspector_type_name() const override {
337 return "SpectrumView";
338 }
339 void inspector_facts(std::vector<InspectorFacts::Fact>& out) const override;
340
342 style_ = s;
343 }
344 Style style() const {
345 return style_;
346 }
347
348 // dB range for display
349 void set_range(float min_db, float max_db) {
350 min_db_ = min_db;
351 max_db_ = max_db;
352 }
353
354 void paint(canvas::Canvas& canvas) override;
355
356 private:
357 std::vector<float> bins_;
358 Style style_ = Style::filled;
359 float min_db_ = -80.0f;
360 float max_db_ = 0.0f;
361};
362
363} // namespace pulp::view
Definition waveform_overview.hpp:89
Definition canvas.hpp:240
Interface for accessible values (sliders, knobs, progress bars).
Definition accessibility.hpp:16
Definition data_display.hpp:47
Lets a widget describe itself to the component inspector (ViewInspector) and to view-core's layout an...
Definition inspector_facts.hpp:15
Generic level, series and bin display seams owned by view-core.
Definition data_display.hpp:24
Orientation
Definition data_display.hpp:26
@ horizontal
Definition data_display.hpp:26
Meter()
Definition audio_widgets.hpp:23
void set_levels(float primary, float secondary) override
Definition audio_widgets.hpp:50
Orientation orientation() const
Definition audio_widgets.hpp:79
float held_peak() const
Definition audio_widgets.hpp:95
double get_minimum_value() const override
Minimum value.
Definition audio_widgets.hpp:34
bool has_source() const
Definition audio_widgets.hpp:108
void set_bar_fill_ratio(float r)
Definition audio_widgets.hpp:146
Orientation
Definition audio_widgets.hpp:21
@ horizontal
Definition audio_widgets.hpp:21
@ vertical
Definition audio_widgets.hpp:21
void set_level_render_style(WidgetRenderStyle s) override
Definition audio_widgets.hpp:69
float bar_fill_ratio() const
Definition audio_widgets.hpp:150
void paint(canvas::Canvas &canvas) override
void set_skin_gradient(std::vector< canvas::Color > stops)
Definition audio_widgets.hpp:125
void set_level_bar_ratio(float ratio) override
Definition audio_widgets.hpp:66
const std::vector< canvas::Color > & skin_gradient() const
Definition audio_widgets.hpp:159
void set_level_fill(std::optional< canvas::Color > background, std::vector< canvas::Color > gradient) override
Installs a fill gradient (stops low to high).
Definition audio_widgets.hpp:57
void set_orientation(Orientation o)
Definition audio_widgets.hpp:76
void set_current_value(double) override
Set the value (if editable).
Definition audio_widgets.hpp:33
float display_rms() const
Definition audio_widgets.hpp:84
void set_level_orientation(LevelDisplay::Orientation o) override
Definition audio_widgets.hpp:53
void set_source(std::shared_ptr< MeterSource > source, int channel=0)
Definition audio_widgets.hpp:105
void inspector_facts(std::vector< InspectorFacts::Fact > &out) const override
bool has_skin_gradient() const
Definition audio_widgets.hpp:153
bool has_skin_background_color() const
Definition audio_widgets.hpp:156
int source_channel() const
Definition audio_widgets.hpp:111
void clear_level_fill() override
Definition audio_widgets.hpp:63
double get_maximum_value() const override
Maximum value.
Definition audio_widgets.hpp:37
std::string_view inspector_type_name() const override
Definition audio_widgets.hpp:91
void update(float raw_peak, float raw_rms, float dt)
canvas::Color skin_background_color() const
Definition audio_widgets.hpp:162
WidgetRenderStyle render_style() const
Definition audio_widgets.hpp:44
void set_skin_background_color(canvas::Color c)
Definition audio_widgets.hpp:129
void set_level(float rms, float peak)
double get_current_value() const override
Current value as a number.
Definition audio_widgets.hpp:30
std::optional< canvas::Color > gradient_color_at(float t) const
float display_peak() const
Definition audio_widgets.hpp:87
void set_render_style(WidgetRenderStyle s)
Definition audio_widgets.hpp:41
void clear_skin()
Definition audio_widgets.hpp:134
Definition data_display.hpp:39
void paint(canvas::Canvas &canvas) override
std::string_view inspector_type_name() const override
Definition audio_widgets.hpp:336
void set_spectrum(std::vector< float > magnitudes_db)
void set_bins(const float *values, std::size_t count) override
Values use the producer's convention for units.
Definition audio_widgets.hpp:328
void set_range(float min_db, float max_db)
Definition audio_widgets.hpp:349
Style style() const
Definition audio_widgets.hpp:344
void set_style(Style s)
Definition audio_widgets.hpp:341
void inspector_facts(std::vector< InspectorFacts::Fact > &out) const override
size_t bin_count() const
Definition audio_widgets.hpp:332
void set_spectrum(const float *magnitudes_db, size_t bin_count)
Style
Definition audio_widgets.hpp:321
@ bars
Definition audio_widgets.hpp:321
@ line
Definition audio_widgets.hpp:321
@ filled
Definition audio_widgets.hpp:321
bool has_meter_source() const
void request_repaint()
Request that this view's host invalidate and schedule a repaint.
@ meter
Definition view.hpp:989
void set_meter_source(std::shared_ptr< MeterSource > source, int channel=0)
Bind a MeterSource.
int meter_source_channel() const
void set_access_role(AccessRole role)
Definition view.hpp:995
virtual void on_meter_snapshot_changed()
meter_frame() changed: repaint this view (a live binding alone does not composite).
Definition view.hpp:598
void paint(canvas::Canvas &canvas) override
bool has_thumbnail() const noexcept
Definition audio_widgets.hpp:265
void inspector_facts(std::vector< InspectorFacts::Fact > &out) const override
void set_preview_shape(std::string_view shape)
const pulp::audio::WaveformOverview * thumbnail() const noexcept
Definition audio_widgets.hpp:268
void set_data(const float *samples, size_t count)
void set_thumbnail(std::shared_ptr< const pulp::audio::WaveformOverview > thumb, uint32_t channel=static_cast< uint32_t >(-1))
PreviewShape preview_shape() const
Definition audio_widgets.hpp:284
void set_thumbnail(const pulp::audio::WaveformOverview *thumb, uint32_t channel=static_cast< uint32_t >(-1))
static size_t find_trigger_index(const float *samples, size_t count, TriggerMode mode)
void set_data(std::vector< float > samples)
PreviewShape
Definition audio_widgets.hpp:226
@ square
Definition audio_widgets.hpp:226
@ none
Definition audio_widgets.hpp:226
@ sine
Definition audio_widgets.hpp:226
@ saw
Definition audio_widgets.hpp:226
@ triangle
Definition audio_widgets.hpp:226
TriggerMode
Definition audio_widgets.hpp:225
@ free_run
Definition audio_widgets.hpp:225
@ rising_zero
Definition audio_widgets.hpp:225
@ falling_zero
Definition audio_widgets.hpp:225
void set_series(const float *values, std::size_t count) override
Definition audio_widgets.hpp:238
std::string_view inspector_type_name() const override
Definition audio_widgets.hpp:276
void set_series_preview(std::string_view token) override
Opaque design-time preview token; the vocabulary belongs to the provider.
Definition audio_widgets.hpp:241
void set_thumbnail_borrowed(const pulp::audio::WaveformOverview *thumb, uint32_t channel=static_cast< uint32_t >(-1))
size_t sample_count() const
Definition audio_widgets.hpp:272
void set_trigger_mode(TriggerMode mode)
Definition audio_widgets.hpp:288
TriggerMode trigger_mode() const
Definition audio_widgets.hpp:291
Definition analyzer_provider.hpp:12
Definition affine_transform.hpp:24
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ s
Definition input_events.hpp:41
@ c
Definition input_events.hpp:39
@ r
Definition input_events.hpp:41
@ o
Definition input_events.hpp:40
@ t
Definition input_events.hpp:41
WidgetRenderStyle
Rendering style for widgets.
Definition data_display.hpp:20
@ standard
Definition data_display.hpp:20
Definition canvas.hpp:52
Definition audio_bridge.hpp:23
A single multi-channel meter reading.
Definition value_source.hpp:39
Source-compatible spelling of <pulp/dataplane/audio_bridge.hpp>.