diff --git a/resources/surge-shared/paramdocumentation.xml b/resources/surge-shared/paramdocumentation.xml index d6e2e3a5a66..71746b7e6db 100644 --- a/resources/surge-shared/paramdocumentation.xml +++ b/resources/surge-shared/paramdocumentation.xml @@ -34,6 +34,7 @@ + diff --git a/src/surge-xt/gui/overlays/AboutScreen.cpp b/src/surge-xt/gui/overlays/AboutScreen.cpp index 20418764ae6..3b727fbcab6 100644 --- a/src/surge-xt/gui/overlays/AboutScreen.cpp +++ b/src/surge-xt/gui/overlays/AboutScreen.cpp @@ -408,6 +408,12 @@ void AboutScreen::resized() "by Émilie Gillet, licensed under MIT license", 600); + yp += lblvs; + + addLabel("Oscilloscope code based on s(m)exoscope by Bram @ Smartelectronix, licensed " + "under GNU GPL v3 license", + 600); + auto img = associatedBitmapStore->getImage(IDB_ABOUT_LOGOS); auto idxes = {0, 4, 3, 6, 1, 2, 5}; diff --git a/src/surge-xt/gui/overlays/Oscilloscope.cpp b/src/surge-xt/gui/overlays/Oscilloscope.cpp index f8cefadc478..632181ba613 100644 --- a/src/surge-xt/gui/overlays/Oscilloscope.cpp +++ b/src/surge-xt/gui/overlays/Oscilloscope.cpp @@ -27,6 +27,8 @@ #include "RuntimeFont.h" #include "SkinColors.h" +#include "widgets/MenuCustomComponents.h" + using namespace std::chrono_literals; using std::placeholders::_1; @@ -534,7 +536,9 @@ Oscilloscope::Oscilloscope(SurgeGUIEditor *e, SurgeStorage *s) setOpaque(true); background_.updateBackgroundType(WAVEFORM); + auto onToggle = std::bind(std::mem_fn(&Oscilloscope::toggleChannel), this); + left_chan_button_.setStorage(storage_); left_chan_button_.setToggleState(true); left_chan_button_.onToggle = onToggle; @@ -543,6 +547,8 @@ Oscilloscope::Oscilloscope(SurgeGUIEditor *e, SurgeStorage *s) left_chan_button_.setTitle("Left Channel"); left_chan_button_.setDescription("Enable input from left channel."); left_chan_button_.setWantsKeyboardFocus(false); + left_chan_button_.setTag(tag_input_l); + left_chan_button_.addListener(this); right_chan_button_.setStorage(storage_); right_chan_button_.setToggleState(true); right_chan_button_.onToggle = onToggle; @@ -551,20 +557,26 @@ Oscilloscope::Oscilloscope(SurgeGUIEditor *e, SurgeStorage *s) right_chan_button_.setTitle("Right Channel"); right_chan_button_.setDescription("Enable input from right channel."); right_chan_button_.setWantsKeyboardFocus(false); + right_chan_button_.setTag(tag_input_r); + right_chan_button_.addListener(this); scope_mode_button_.setStorage(storage_); scope_mode_button_.setRows(1); scope_mode_button_.setColumns(2); scope_mode_button_.setLabels({"Waveform", "Spectrum"}); scope_mode_button_.setWantsKeyboardFocus(false); scope_mode_button_.setValue(0.f); + scope_mode_button_.setTag(tag_scope_mode); + scope_mode_button_.setDraggable(true); + scope_mode_button_.addListener(this); spectrum_parameters_.setOpaque(true); waveform_parameters_.setOpaque(true); addAndMakeVisible(background_); addAndMakeVisible(left_chan_button_); addAndMakeVisible(right_chan_button_); addAndMakeVisible(scope_mode_button_); - addAndMakeVisible(spectrum_); - addAndMakeVisible(spectrum_parameters_); + + addChildComponent(spectrum_); + addChildComponent(spectrum_parameters_); addChildComponent(waveform_); addChildComponent(waveform_parameters_); @@ -609,6 +621,120 @@ void Oscilloscope::onSkinChanged() void Oscilloscope::paint(juce::Graphics &g) {} +int32_t Oscilloscope::controlModifierClicked(Surge::GUI::IComponentTagValue *pControl, + const juce::ModifierKeys &button, + bool isDoubleClickEvent) +{ + if (isDoubleClickEvent) + { + return 0; + } + + int tag = pControl->getTag(); + + // Basically all the menus are a list of options with values + std::vector> options; + std::string menuName = ""; + + switch (tag) + { + case tag_scope_mode: + menuName = "Oscilloscope Mode"; + options.push_back(std::make_pair("Waveform", 0)); + options.push_back(std::make_pair("Spectrum", 1)); + break; + case tag_input_l: + menuName = "Left Input"; + break; + case tag_input_r: + menuName = "Right Input"; + break; + case tag_wf_dc_block: + menuName = "DC Block"; + break; + case tag_wf_freeze: + case tag_sp_freeze: + menuName = "Freeze"; + break; + case tag_wf_sync: + menuName = "Sync Redraw"; + break; + case tag_wf_time_scaling: + menuName = "Time Scaling"; + break; + case tag_wf_amp_scaling: + menuName = "Amplitude Scaling"; + break; + case tag_wf_trigger_mode: + menuName = "Trigger Mode"; + options.push_back(std::make_pair("Freerun", 0.0)); + options.push_back(std::make_pair("Rising Edge", 0.25)); + options.push_back(std::make_pair("Falling Edge", 0.5)); + options.push_back(std::make_pair("Internal Trigger", 1.0)); + break; + case tag_wf_trigger_level: + menuName = "Trigger Level"; + break; + case tag_wf_retrigger_threshold: + menuName = "Retrigger Threshold"; + break; + case tag_wf_int_trigger_freq: + menuName = "Trigger Frequency"; + break; + case tag_sp_min_level: + menuName = "Minimum Level"; + break; + case tag_sp_max_level: + menuName = "Maximum Level"; + break; + case tag_sp_decay_rate: + menuName = "Spectrum Decay Rate"; + break; + default: + break; + } + + auto contextMenu = juce::PopupMenu(); + + auto msurl = SurgeGUIEditor::helpURLForSpecial(storage_, "oscilloscope"); + auto hurl = SurgeGUIEditor::fullyResolvedHelpURL(msurl); + auto tcomp = std::make_unique(menuName, hurl); + + tcomp->setSkin(skin, associatedBitmapStore); + + auto hment = tcomp->getTitle(); + + contextMenu.addCustomItem(-1, std::move(tcomp), nullptr, hment); + + if (!options.empty()) + { + contextMenu.addSeparator(); + + for (auto op : options) + { + auto val = op.second; + + contextMenu.addItem(op.first, true, (val == pControl->getValue()), + [val, pControl, this]() { + pControl->setValue(val); + valueChanged(pControl); + + auto iv = pControl->asJuceComponent(); + + if (iv) + { + iv->repaint(); + } + }); + } + } + + contextMenu.showMenuAsync(editor_->popupMenuOptions(), + Surge::GUI::makeEndHoverCallback(pControl)); + + return 1; +} + void Oscilloscope::resized() { // Scope looks like the following picture. @@ -634,7 +760,7 @@ void Oscilloscope::resized() left_chan_button_.setBounds(rhs - 21, 4, buttonSize, buttonSize); right_chan_button_.setBounds(rhs - 5, 4, buttonSize, buttonSize); - scope_mode_button_.setBounds(8, 4, 105, buttonSize); + scope_mode_button_.setBounds(8, 4, 116, buttonSize); spectrum_.setBounds(scopeRect); waveform_.setBounds(scopeRect); @@ -648,9 +774,9 @@ void Oscilloscope::resized() } Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeStorage *s, - juce::Component *parent) + Oscilloscope *parent) : editor_(e), storage_(s), parent_(parent), freeze_("Freeze"), dc_kill_("DC Block"), - sync_draw_("Sync") + sync_draw_("Sync Redraw") { // Initialize parameters from the DAW state. auto *state = &s->getPatch().dawExtraState.editor.oscilloscopeOverlayState; @@ -688,7 +814,7 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto time_window_.setQuantitizedDisplayValue(params_.time_window); amp_window_.setQuantitizedDisplayValue(params_.amp_window); - trigger_speed_.setLabel("Internal Trigger Freq"); + trigger_speed_.setLabel("Trigger Frequency"); trigger_level_.setLabel("Trigger Level"); trigger_limit_.setLabel("Retrigger Threshold"); time_window_.setLabel("Time Scaling"); @@ -718,6 +844,22 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto time_window_.setPrecision(2); amp_window_.setPrecision(2); + trigger_type_.setTag(tag_wf_trigger_mode); + trigger_speed_.setTag(tag_wf_int_trigger_freq); + trigger_level_.setTag(tag_wf_trigger_level); + trigger_limit_.setTag(tag_wf_retrigger_threshold); + time_window_.setTag(tag_wf_time_scaling); + amp_window_.setTag(tag_wf_amp_scaling); + + trigger_type_.addListener(this); + trigger_speed_.addListener(this); + trigger_level_.addListener(this); + trigger_limit_.addListener(this); + time_window_.addListener(this); + amp_window_.addListener(this); + + trigger_type_.setDraggable(true); + auto updateParameter = [this](float ¶m, float &backer, float value) { std::lock_guard l(params_lock_); params_changed_ = true; @@ -750,7 +892,7 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto time_window_.setRootWindow(parent_); amp_window_.setRootWindow(parent_); - // These are not visible by default, since the default trigger type is "free". + // These are not visible by default, since the default trigger type is Freerun trigger_speed_.setVisible(false); trigger_level_.setVisible(false); trigger_limit_.setVisible(false); @@ -761,10 +903,9 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto addAndMakeVisible(time_window_); addAndMakeVisible(amp_window_); - // The multiswitch. trigger_type_.setRows(4); trigger_type_.setColumns(1); - trigger_type_.setLabels({"Free", "Rising", "Falling", "Internal"}); + trigger_type_.setLabels({"Freerun", "Rising Edge", "Falling Edge", "Internal Trigger"}); trigger_type_.setIntegerValue(static_cast(params_.trigger_type)); trigger_type_.setWantsKeyboardFocus(false); trigger_type_.setOnUpdate([this, state](int value) { @@ -820,6 +961,7 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto dc_kill_.setValue(static_cast(params_.dc_kill)); sync_draw_.setValue(static_cast(params_.sync_draw)); + dc_kill_.isToggled = params_.dc_kill; sync_draw_.isToggled = params_.sync_draw; @@ -827,6 +969,14 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto dc_kill_.setWantsKeyboardFocus(false); sync_draw_.setWantsKeyboardFocus(false); + freeze_.setTag(tag_wf_freeze); + dc_kill_.setTag(tag_wf_dc_block); + sync_draw_.setTag(tag_wf_sync); + + freeze_.addListener(this); + dc_kill_.addListener(this); + sync_draw_.addListener(this); + freeze_.onToggle = std::bind(toggleParam, std::ref(params_.freeze), nullptr); dc_kill_.onToggle = std::bind(toggleParam, std::ref(params_.dc_kill), &state->dc_kill); sync_draw_.onToggle = std::bind(toggleParam, std::ref(params_.sync_draw), &state->sync_draw); @@ -839,11 +989,14 @@ Oscilloscope::WaveformParameters::WaveformParameters(SurgeGUIEditor *e, SurgeSto std::optional Oscilloscope::WaveformParameters::getParamsIfDirty() { std::lock_guard l(params_lock_); + if (params_changed_) { params_changed_ = false; + return params_; } + return std::nullopt; } @@ -878,25 +1031,25 @@ void Oscilloscope::WaveformParameters::resized() auto t = getTransform().inverted(); auto h = getHeight(); auto w = getWidth(); - auto buttonWidth = 49; + auto buttonWidth = 58; int labelHeight = 12; - trigger_type_.setBounds(222, 14, buttonWidth, 52); + trigger_type_.setBounds(227, 14, 68, 52); - trigger_speed_.setBounds(283, 28, 140, 26); - trigger_level_.setBounds(283, 14, 140, 26); - trigger_limit_.setBounds(283, 42, 140, 26); + trigger_speed_.setBounds(307, 28, 140, 26); + trigger_level_.setBounds(307, 14, 140, 26); + trigger_limit_.setBounds(307, 42, 140, 26); - time_window_.setBounds(73, 14, 140, 26); - amp_window_.setBounds(73, 42, 140, 26); + time_window_.setBounds(78, 14, 140, 26); + amp_window_.setBounds(78, 42, 140, 26); - dc_kill_.setBounds(12, 14, buttonWidth, 14); - freeze_.setBounds(12, 33, buttonWidth, 14); - sync_draw_.setBounds(12, 52, buttonWidth, 14); + dc_kill_.setBounds(8, 14, buttonWidth, 14); + freeze_.setBounds(8, 33, buttonWidth, 14); + sync_draw_.setBounds(8, 52, buttonWidth, 14); } Oscilloscope::SpectrumParameters::SpectrumParameters(SurgeGUIEditor *e, SurgeStorage *s, - juce::Component *parent) + Oscilloscope *parent) : editor_(e), storage_(s), parent_(parent), freeze_("Freeze") { // Initialize parameters from the DAW state. @@ -941,6 +1094,14 @@ Oscilloscope::SpectrumParameters::SpectrumParameters(SurgeGUIEditor *e, SurgeSto max_db_.setUnit(" dB"); decay_rate_.setUnit(" %"); + noise_floor_.setTag(tag_sp_min_level); + max_db_.setTag(tag_sp_max_level); + decay_rate_.setTag(tag_sp_decay_rate); + + noise_floor_.addListener(this); + max_db_.addListener(this); + decay_rate_.addListener(this); + auto updateParameter = [this](float ¶m, float &backer, float value) { std::lock_guard l(params_lock_); params_changed_ = true; @@ -971,6 +1132,8 @@ Oscilloscope::SpectrumParameters::SpectrumParameters(SurgeGUIEditor *e, SurgeSto }; freeze_.setWantsKeyboardFocus(false); + freeze_.setTag(tag_sp_freeze); + freeze_.addListener(this); freeze_.onToggle = std::bind(toggleParam, std::ref(params_.freeze)); addAndMakeVisible(freeze_); @@ -1013,13 +1176,13 @@ void Oscilloscope::SpectrumParameters::resized() auto t = getTransform().inverted(); auto h = getHeight(); auto w = getWidth(); - auto buttonWidth = 49; + auto buttonWidth = 58; - noise_floor_.setBounds(73, 14, 140, 26); - max_db_.setBounds(73, 42, 140, 26); - decay_rate_.setBounds(214, 28, 140, 26); + noise_floor_.setBounds(78, 14, 140, 26); + max_db_.setBounds(78, 42, 140, 26); + decay_rate_.setBounds(219, 28, 140, 26); - freeze_.setBounds(12, 33, buttonWidth, 14); + freeze_.setBounds(8, 33, buttonWidth, 14); } void Oscilloscope::updateDrawing() diff --git a/src/surge-xt/gui/overlays/Oscilloscope.h b/src/surge-xt/gui/overlays/Oscilloscope.h index d153b8f420b..6e91d7ad12e 100644 --- a/src/surge-xt/gui/overlays/Oscilloscope.h +++ b/src/surge-xt/gui/overlays/Oscilloscope.h @@ -194,6 +194,7 @@ class SpectrumDisplay : public juce::Component, public Surge::GUI::SkinConsuming class Oscilloscope : public OverlayComponent, public Surge::GUI::SkinConsumingComponent, + public Surge::GUI::IComponentTagValue::Listener, public Surge::GUI::Hoverable { public: @@ -206,7 +207,38 @@ class Oscilloscope : public OverlayComponent, void updateDrawing(); void visibilityChanged() override; + void valueChanged(GUI::IComponentTagValue *p) override{}; + int32_t controlModifierClicked(Surge::GUI::IComponentTagValue *pControl, + const juce::ModifierKeys &button, + bool isDoubleClickEvent) override; + private: + enum ControlTags + { + tag_scope_mode = 567898765, // Just to push outside any ID range + + tag_input_l, + tag_input_r, + + tag_wf_dc_block, + tag_wf_freeze, + tag_wf_sync, + + tag_wf_time_scaling, + tag_wf_amp_scaling, + + tag_wf_trigger_mode, + tag_wf_trigger_level, + tag_wf_retrigger_threshold, + tag_wf_int_trigger_freq, + + tag_sp_freeze, + + tag_sp_min_level, + tag_sp_max_level, + tag_sp_decay_rate, + }; + enum ChannelSelect { LEFT = 1, @@ -247,22 +279,36 @@ class Oscilloscope : public OverlayComponent, WaveformDisplay::Parameters waveform_params_; }; - class SpectrumParameters : public juce::Component, public Surge::GUI::SkinConsumingComponent + class SpectrumParameters : public juce::Component, + public Surge::GUI::SkinConsumingComponent, + public Surge::GUI::IComponentTagValue::Listener { public: - SpectrumParameters(SurgeGUIEditor *e, SurgeStorage *s, juce::Component *parent); + SpectrumParameters(SurgeGUIEditor *e, SurgeStorage *s, Oscilloscope *parent); std::optional getParamsIfDirty(); void onSkinChanged() override; void paint(juce::Graphics &g) override; void resized() override; + void valueChanged(GUI::IComponentTagValue *p) override{}; + int32_t controlModifierClicked(Surge::GUI::IComponentTagValue *pControl, + const juce::ModifierKeys &button, + bool isDoubleClickEvent) override + { + if (parent_) + { + return parent_->controlModifierClicked(pControl, button, isDoubleClickEvent); + } + + return 0; + } private: SurgeGUIEditor *editor_; SurgeStorage *storage_; - juce::Component - *parent_; // Saved here so we can provide it to the children at construction time. + // Saved here so we can provide it to the children at construction time. + Oscilloscope *parent_; SpectrumDisplay::Parameters params_; bool params_changed_; std::mutex params_lock_; @@ -273,22 +319,36 @@ class Oscilloscope : public OverlayComponent, Surge::Widgets::SelfDrawToggleButton freeze_; }; - class WaveformParameters : public juce::Component, public Surge::GUI::SkinConsumingComponent + class WaveformParameters : public juce::Component, + public Surge::GUI::SkinConsumingComponent, + public Surge::GUI::IComponentTagValue::Listener { public: - WaveformParameters(SurgeGUIEditor *e, SurgeStorage *s, juce::Component *parent); + WaveformParameters(SurgeGUIEditor *e, SurgeStorage *s, Oscilloscope *parent); std::optional getParamsIfDirty(); void onSkinChanged() override; void paint(juce::Graphics &g) override; void resized() override; + void valueChanged(GUI::IComponentTagValue *p) override{}; + int32_t controlModifierClicked(Surge::GUI::IComponentTagValue *pControl, + const juce::ModifierKeys &button, + bool isDoubleClickEvent) override + { + if (parent_) + { + return parent_->controlModifierClicked(pControl, button, isDoubleClickEvent); + } + + return 0; + } private: SurgeGUIEditor *editor_; SurgeStorage *storage_; - juce::Component - *parent_; // Saved here so we can provide it to the children at construction time. + // Saved here so we can provide it to the children at construction time. + Oscilloscope *parent_; WaveformDisplay::Parameters params_; bool params_changed_; std::mutex params_lock_;