EchoMap 2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Loading...
Searching...
No Matches
echomap::SignalWaveformPanel Class Referencefinal

The SignalWaveformPanel provides a preview of downsampled time-series waveforms of each Signal in the active Project. More...

#include <SignalWaveformPanel.hpp>

Inheritance diagram for echomap::SignalWaveformPanel:
[legend]

Classes

struct  CallbackData

Public Member Functions

 SignalWaveformPanel (Worker *parent_worker, WorkerResultDespatcher &despatcher, const Project *initial_project=nullptr)
 Create a new SignalWaveformPanel to display downsampled Signal waveforms in the time domain.
 SignalWaveformPanel (const SignalWaveformPanel &)=delete
SignalWaveformPaneloperator= (const SignalWaveformPanel &)=delete
 SignalWaveformPanel (SignalWaveformPanel &&) noexcept
SignalWaveformPaneloperator= (SignalWaveformPanel &&) noexcept=delete
const char * get_imgui_name () const noexcept override
 Retrieves the name of the IPanel for Dear ImGui API functions.
void draw () noexcept override
 Draw the panel to the active rendering context.
void change_active_project (const Project *new_project) override
 Updates the active Project being described by the IPanel.
Public Member Functions inherited from echomap::IPanel
virtual void update_gpu (const wgpu::CommandEncoder &command_encoder)
 Delegate work to the GPU via the WebGPU platform.

Static Public Member Functions

static const char * get_imgui_stable_name () noexcept

Private Member Functions

void handle_downsampled_result (DownsampleResult &&result)
void update_bounding_box (const Signal &signal) noexcept
void update_bounding_box () noexcept
const Signalget_downsampled_signal (std::shared_ptr< Signal > signal)
 Retrieves a downsampled Signal from the cache.

Static Private Member Functions

static ImPlotPoint get_indexed_signal_point (int index, void *user_data) noexcept

Private Attributes

std::unordered_map< id_type, std::unique_ptr< Signal > > downsample_cache
 The duplicated Signal objects, downsampled for visualisation.
ImPlotRect bounding_box
 The maximum bounding box of an LTTB-downsampled wave form plot.
std::string panel_name
ImPlotSpec plotting_spec_2d
Workerparent_worker
const Projectactive_project = nullptr
std::vector< sigc::scoped_connection > connections

Static Private Attributes

static constexpr float default_downsample_factor = 50.0f

Additional Inherited Members

Static Protected Attributes inherited from echomap::IPanel
static constexpr ImVec2 button_size {80.0f, 20.0f}
static constexpr auto table_flags

Detailed Description

The SignalWaveformPanel provides a preview of downsampled time-series waveforms of each Signal in the active Project.

Additionally, the panel owns the downsampled time series in a cache used exclusively for visualisation.

Definition at line 32 of file SignalWaveformPanel.hpp.

Constructor & Destructor Documentation

◆ SignalWaveformPanel()

echomap::SignalWaveformPanel::SignalWaveformPanel ( Worker * parent_worker,
WorkerResultDespatcher & despatcher,
const Project * initial_project = nullptr )
explicit

Create a new SignalWaveformPanel to display downsampled Signal waveforms in the time domain.

The SignalWaveformPanel consumes the DownsampleResult message.

Parameters
parent_workerThe Worker to receive ITask commands over the command bus.
despatcherThe despatcher to expose the result buses.
initial_projectAn optional initial Project for the IPanel to display.

Definition at line 23 of file SignalWaveformPanel.cpp.

27 :
33 },
34 panel_name(std::string("Signal Waveform Preview") + get_imgui_stable_name()),
35 parent_worker(parent_worker),
36 active_project(initial_project)
37{
38 connections.emplace_back(despatcher.downsample_finished_channel.nominate_consumer(
39 sigc::mem_fun(*this, &SignalWaveformPanel::handle_downsampled_result)
40 ));
41}
ImPlotRect bounding_box
The maximum bounding box of an LTTB-downsampled wave form plot.
static constexpr std::pair< Sample::AmplitudeT, Sample::AmplitudeT > normalised_range
The range within which the amplitude values are normalised.
Definition Signal.hpp:74
T lowest(T... args)
T max(T... args)

Member Function Documentation

◆ change_active_project()

void echomap::SignalWaveformPanel::change_active_project ( const Project * new_project)
overridevirtual

Updates the active Project being described by the IPanel.

Parameters
new_projectAn observing pointer to the new active Project.

Implements echomap::IProjectPanel.

Definition at line 96 of file SignalWaveformPanel.cpp.

99{
100 active_project = new_project;
101 downsample_cache.clear();
102 update_bounding_box();
103}
std::unordered_map< id_type, std::unique_ptr< Signal > > downsample_cache
The duplicated Signal objects, downsampled for visualisation.

◆ draw()

void echomap::SignalWaveformPanel::draw ( )
overridevirtualnoexcept

Draw the panel to the active rendering context.

Implements echomap::IPanel.

Definition at line 52 of file SignalWaveformPanel.cpp.

53{
54 if (ImGui::Begin(panel_name.c_str())) {
55 if (active_project == nullptr)
56 ImGui::Text("No project is loaded.");
57 else if (ImPlot::BeginAlignedPlots("##WaveformAlignedGroup")) {
58 ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
59 bool drawn_any = false;
60
61 for (const auto& signal : active_project->share_signals()) {
62 drawn_any = true;
63
64 if (const auto* const downsampled = get_downsampled_signal(signal); downsampled == nullptr)
65 ImGui::Text("Loading downsampled variant of %s...", signal->get_imgui_name());
66 else if (ImPlot::BeginPlot(downsampled->get_imgui_name())) {
67
68 ImPlot::SetupAxes("Time (seconds)", "Amplitude");
69 ImPlot::SetupAxisLinks(ImAxis_X1, &bounding_box.X.Min, &bounding_box.X.Max);
70 ImPlot::SetupAxisLinks(ImAxis_Y1, &bounding_box.Y.Min, &bounding_box.Y.Max);
71
72 CallbackData callback_data = {.signal = downsampled};
73 ImPlot::PlotLineG(
74 "",
75 &SignalWaveformPanel::get_indexed_signal_point,
76 &callback_data,
77 static_cast<int>(downsampled->get_sample_count()),
78 plotting_spec_2d
79 );
80
81 ImPlot::EndPlot();
82 }
83 }
84
85 ImPlot::EndAlignedPlots();
86 ImPlot::PopStyleColor();
87
88 if (!drawn_any)
89 ImGui::Text("No signals are available.");
90 }
91 }
92
93 ImGui::End();
94}
const Signal * get_downsampled_signal(std::shared_ptr< Signal > signal)
Retrieves a downsampled Signal from the cache.
T signal(T... args)

◆ get_downsampled_signal()

const Signal * echomap::SignalWaveformPanel::get_downsampled_signal ( std::shared_ptr< Signal > signal)
private

Retrieves a downsampled Signal from the cache.

If the downsampled variant is not present, it is computed and cached.

Parameters
signalThe original Signal to downsample.
Returns
A stable observing pointer to the downsampled Signal, or null if it could not be produced at this time.
Precondition
The given Signal container must detain a valid Signal object.

Definition at line 163 of file SignalWaveformPanel.cpp.

166{
167 assert(signal != nullptr);
168 const auto downsampled_it = downsample_cache.find(signal->get_id());
169
170 if (downsampled_it == downsample_cache.end()) {
171 /*
172 * If the source signal hasn't already been downsampled and cached, submit a job to do it ASAP.
173 *
174 * The result won't be picked up on this render cycle, so return nullptr to indicate the "Loading" state, but it
175 * should come through shortly. We emplace a nullptr in the slot to indicate that the work has been requested,
176 * but not yet completed.
177 */
178
179 downsample_cache.emplace(signal->get_id(), nullptr);
180 parent_worker->submit(std::make_unique<DownsampleTask>(std::move(signal)));
181 return nullptr;
182 }
183
184 return downsampled_it->second.get();
185}
T make_unique(T... args)

◆ get_imgui_name()

const char * echomap::SignalWaveformPanel::get_imgui_name ( ) const
nodiscardoverridevirtualnoexcept

Retrieves the name of the IPanel for Dear ImGui API functions.

Returns
The human-readable name of the window as a NULL-terminated C string.

Implements echomap::IPanel.

Definition at line 47 of file SignalWaveformPanel.cpp.

48{
49 return panel_name.c_str();
50}

◆ get_imgui_stable_name()

const char * echomap::SignalWaveformPanel::get_imgui_stable_name ( )
staticnoexcept

Definition at line 105 of file SignalWaveformPanel.cpp.

106{
107 return "###SignalWaveformPanel";
108}

◆ get_indexed_signal_point()

ImPlotPoint echomap::SignalWaveformPanel::get_indexed_signal_point ( int index,
void * user_data )
staticprivatenoexcept

Definition at line 135 of file SignalWaveformPanel.cpp.

139{
140 const auto* const signal = static_cast<CallbackData*>(user_data)->signal;
141 return {signal->get_time_at_index(index), signal->begin()[index]};
142}

◆ handle_downsampled_result()

void echomap::SignalWaveformPanel::handle_downsampled_result ( DownsampleResult && result)
private

Definition at line 110 of file SignalWaveformPanel.cpp.

113{
114 auto ds_slot_it = downsample_cache.find(result.get_source_id());
115 auto signal = std::move(result).take_downsampled();
116
117 if (ds_slot_it == downsample_cache.end()) {
118 LOG_F_WARN("Received an unexpected result for the downsampled Signal {}.", signal->get_name());
119 const auto signal_name_view = signal->get_name();
120 auto [it, success] = downsample_cache.emplace(result.get_source_id(), std::move(signal));
121
122 if (!success) {
123 LOG_F_ERROR("Could not store the downsampled Signal {} in the cache.", signal_name_view);
124 return;
125 }
126
127 ds_slot_it = it;
128 } else
129 ds_slot_it->second = std::move(signal);
130
131 // Update the bounding box for the signal graphical representation.
132 update_bounding_box(*ds_slot_it->second);
133}
#define LOG_F_WARN(msg,...)
Logs a formatted warning-level message using echomap::Logger::log_f.
Definition Logger.hpp:115
#define LOG_F_ERROR(msg,...)
Logs a formatted error-level message using echomap::Logger::log_f.
Definition Logger.hpp:125

◆ update_bounding_box() [1/2]

void echomap::SignalWaveformPanel::update_bounding_box ( )
privatenoexcept

◆ update_bounding_box() [2/2]

void echomap::SignalWaveformPanel::update_bounding_box ( const Signal & signal)
privatenoexcept

Definition at line 144 of file SignalWaveformPanel.cpp.

147{
148 if (signal.get_sample_count() > 0) {
149 bounding_box.X.Min = std::min<double>(signal.get_time_at_index(0), bounding_box.X.Min);
150 bounding_box.X.Max =
151 std::max<double>(signal.get_time_at_index(signal.get_sample_count() - 1), bounding_box.X.Max);
152 }
153}
T min(T... args)

Member Data Documentation

◆ active_project

const Project* echomap::SignalWaveformPanel::active_project = nullptr
private

Definition at line 122 of file SignalWaveformPanel.hpp.

◆ bounding_box

ImPlotRect echomap::SignalWaveformPanel::bounding_box
private

The maximum bounding box of an LTTB-downsampled wave form plot.

The Y axis (amplitude) is fixed, since our PCM-normalised values, given as a constraint from the Signal samples, are within a fixed range. The X range is variable and should be updated as new Signal objects are received.

Definition at line 117 of file SignalWaveformPanel.hpp.

◆ connections

std::vector<sigc::scoped_connection> echomap::SignalWaveformPanel::connections
private

Definition at line 123 of file SignalWaveformPanel.hpp.

◆ default_downsample_factor

float echomap::SignalWaveformPanel::default_downsample_factor = 50.0f
staticconstexprprivate

Definition at line 67 of file SignalWaveformPanel.hpp.

◆ downsample_cache

std::unordered_map<id_type, std::unique_ptr<Signal> > echomap::SignalWaveformPanel::downsample_cache
private

The duplicated Signal objects, downsampled for visualisation.

This map associates the IDs of the original Signal objects with owning containers of their downsampled counterparts.

It is typically undesirable for the panels to own any non-trivial state. In this case, however, the data is generated exclusively for the purposes of visualisation on this panel, and the elements' construction and destruction is at the whim of the renderer such that downsampled time series are only created ad-hoc when they need to be visualised. Therefore, the SignalWaveformPanel seems to be the natural owner.

Definition at line 109 of file SignalWaveformPanel.hpp.

◆ panel_name

std::string echomap::SignalWaveformPanel::panel_name
private

Definition at line 119 of file SignalWaveformPanel.hpp.

◆ parent_worker

Worker* echomap::SignalWaveformPanel::parent_worker
private

Definition at line 121 of file SignalWaveformPanel.hpp.

◆ plotting_spec_2d

ImPlotSpec echomap::SignalWaveformPanel::plotting_spec_2d
private

Definition at line 120 of file SignalWaveformPanel.hpp.


The documentation for this class was generated from the following files: