diff --git a/src/layout_store.cpp b/src/layout_store.cpp index ebb6d42..5f6a6b2 100644 --- a/src/layout_store.cpp +++ b/src/layout_store.cpp @@ -366,7 +366,7 @@ std::string math_node_text(const MathNode& node) { std::ostringstream o; o << kind << '|' << node.source << '|' << node.in_a << '|' << node.in_b << '|' << node.gain << '|' << node.offset << '|' << node.fc << '|' << node.q << '|' << node.lo << '|' << node.hi << '|' - << node.x << '|' << node.y; + << node.x << '|' << node.y << '|' << node.med_n; return o.str(); } @@ -382,7 +382,9 @@ bool math_node_from_text(const std::string& text, MathNode& out) { } } f.push_back(cur); - if (f.size() != 12u) return false; + // Twelve fields is a session file written before the de-noise node existed; its `med_n` stays at the + // struct's default, which is what that file meant. + if (f.size() != 12u && f.size() != 13u) return false; const auto number = [&](std::size_t i, double& v) { try { v = std::stod(f[i]); @@ -402,8 +404,16 @@ bool math_node_from_text(const std::string& text, MathNode& out) { out.source = f[1]; out.in_a = static_cast(in_a); out.in_b = static_cast(in_b); - return number(4, out.gain) && number(5, out.offset) && number(6, out.fc) && number(7, out.q) && - number(8, out.lo) && number(9, out.hi) && number(10, out.x) && number(11, out.y); + if (!(number(4, out.gain) && number(5, out.offset) && number(6, out.fc) && number(7, out.q) && + number(8, out.lo) && number(9, out.hi) && number(10, out.x) && number(11, out.y))) { + return false; + } + if (f.size() == 13u) { + double med = 0.0; + if (!number(12, med)) return false; + out.med_n = static_cast(med); + } + return true; } bool load_layout(const std::string& path, LayoutState& out) { diff --git a/src/ui.cpp b/src/ui.cpp index 8768f47..8b66d41 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -2140,6 +2140,14 @@ void open_math_dialog(App& app, int edit) { gtk_box_append(GTK_BOX(form), clamp_rows); } + // Odd, 3..9. The engine rounds up to odd anyway, but a step of 2 means the widget never offers an even + // one. + d->med = gtk_spin_button_new_with_range(3.0, 9.0, 2.0); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(d->med), 0); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->med), 5.0); + g_signal_connect(d->med, "value-changed", G_CALLBACK(on_math_param_changed), d); + gtk_box_append(GTK_BOX(form), math_row("median samples", d->med, &d->rows[6])); + d->inputs = gtk_label_new(""); gtk_label_set_wrap(GTK_LABEL(d->inputs), true); gtk_widget_add_css_class(d->inputs, "dim-label"); @@ -2159,7 +2167,7 @@ void open_math_dialog(App& app, int edit) { gtk_widget_set_visible(d->problem, FALSE); gtk_box_append(GTK_BOX(form), d->problem); - // A menu of the twelve ops rather than one button that always makes a gain: building anything + // A menu of the thirteen ops rather than one button that always makes a gain: building anything // else was add-then-change-the-dropdown, which is two steps for the common case. GtkWidget* add = gtk_menu_button_new(); gtk_menu_button_set_label(GTK_MENU_BUTTON(add), "add node"); diff --git a/src/ui_app.hpp b/src/ui_app.hpp index 539beb4..fd742a5 100644 --- a/src/ui_app.hpp +++ b/src/ui_app.hpp @@ -978,9 +978,10 @@ struct MathDialog { GtkWidget* offset = nullptr; GtkWidget* fc = nullptr; GtkWidget* q = nullptr; + GtkWidget* med = nullptr; ///< MathNode::med_n GtkWidget* lo = nullptr; GtkWidget* hi = nullptr; - GtkWidget* rows[6] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + GtkWidget* rows[7] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; GtkWidget* inputs = nullptr; /// The validation line and the button it governs. Both are set from `math_dialog_sync()` and from /// nowhere else, so what the line says and what the button allows cannot disagree. diff --git a/src/ui_math.cpp b/src/ui_math.cpp index 50f606a..9a84cbe 100644 --- a/src/ui_math.cpp +++ b/src/ui_math.cpp @@ -835,6 +835,7 @@ void on_math_param_changed(GtkWidget*, gpointer user) { n.offset = gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->offset)); n.fc = gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->fc)); n.q = gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->q)); + n.med_n = static_cast(gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->med))); n.lo = gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->lo)); n.hi = gtk_spin_button_get_value(GTK_SPIN_BUTTON(d->hi)); if (d->source_drop != nullptr) { @@ -930,14 +931,18 @@ void math_dialog_sync_form(MathDialog* d) { const bool source = op == MathOp::Source; gtk_widget_set_visible(d->rows[0], source); // the source channel gtk_widget_set_visible(d->rows[1], gain); - gtk_widget_set_visible(d->rows[2], filter); + // The corner row is shown for anything that reads `fc`, which is now the three biquads *and* the + // de-noise node; `q` stays a band-pass-only row. + gtk_widget_set_visible(d->rows[2], math_op_uses_fc(op)); gtk_widget_set_visible(d->rows[3], filter && op == MathOp::BandPass); gtk_widget_set_visible(d->rows[4], clamp); gtk_widget_set_visible(d->rows[5], true); // the inputs, always: they say what is wired + gtk_widget_set_visible(d->rows[6], math_op_uses_median(op)); gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->gain), n.gain); gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->offset), n.offset); gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->fc), n.fc); gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->q), n.q); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->med), static_cast(n.med_n)); gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->lo), n.lo); gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->hi), n.hi); if (d->source_drop != nullptr) { diff --git a/src/ui_probe.cpp b/src/ui_probe.cpp index df28aa0..93a4e05 100644 --- a/src/ui_probe.cpp +++ b/src/ui_probe.cpp @@ -281,6 +281,16 @@ gboolean eventzoom_probe(gpointer user) { gboolean math_probe(gpointer user) { App* a = static_cast(user); if (a->window == nullptr) return G_SOURCE_REMOVE; + // The channel the graphs are built from is enabled *here*, for the reason the noise and measurement + // probes give: `enable_all_probe` finishes by switching one signal off, and with `sine` gone the graphs + // land on `square`, which turns the documented "2 math channels on 'sine' and 'noise'" into a different + // line for reasons that have nothing to do with the math. + { + const std::vector& signals = a->model->signals(); + for (std::size_t i = 0; i < signals.size(); ++i) { + if (signals[i].name == "sine") a->model->set_signal_enabled(i, true); + } + } std::string name; std::string late_name; {