From 186719a3c73787e5f638fa5d704153348a0f22b2 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 17 Oct 2024 08:00:49 -0400 Subject: [PATCH] Add a data cache; add a cross-editor subscription (#1424) * Add a data cache; add a cross-editor subscription Basically make it so shared ui data has a place to live and that editors on it can detect they are multiple and show you the changes. Applied right now to group pan and amplitude only but its all generalizable. * Duh - do that properly --- libs/sst/sst-jucegui | 2 +- src-ui/CMakeLists.txt | 1 + src-ui/app/HasEditor.h | 2 + src-ui/app/SCXTEditor.h | 26 ++++- src-ui/app/SCXTEditorDataCache.h | 91 ++++++++++++++++++ .../components/GroupSettingsCard.cpp | 11 ++- .../components/GroupSettingsCard.h | 10 ++ .../app/edit-screen/components/OutputPane.cpp | 25 +++-- .../app/edit-screen/components/OutputPane.h | 7 +- .../app/editor-impl/SCXTEditorDataCache.cpp | 95 +++++++++++++++++++ .../SCXTEditorResponseHandlers.cpp | 6 +- src-ui/connectors/PayloadDataAttachment.h | 21 +++- src/engine/group.h | 2 +- 13 files changed, 279 insertions(+), 20 deletions(-) create mode 100644 src-ui/app/SCXTEditorDataCache.h create mode 100644 src-ui/app/editor-impl/SCXTEditorDataCache.cpp diff --git a/libs/sst/sst-jucegui b/libs/sst/sst-jucegui index bfa96278..ec5f74a0 160000 --- a/libs/sst/sst-jucegui +++ b/libs/sst/sst-jucegui @@ -1 +1 @@ -Subproject commit bfa962785b720e786b9d88a4c1bcfd354ca585e8 +Subproject commit ec5f74a0ebfe1cf590fdb93129473e60a5965aee diff --git a/src-ui/CMakeLists.txt b/src-ui/CMakeLists.txt index 089e5bc8..ca8dbc30 100644 --- a/src-ui/CMakeLists.txt +++ b/src-ui/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(${PROJECT_NAME} STATIC app/editor-impl/SCXTEditor.cpp app/editor-impl/SCXTEditorMenus.cpp app/editor-impl/SCXTEditorResponseHandlers.cpp + app/editor-impl/SCXTEditorDataCache.cpp app/edit-screen/EditScreen.cpp app/edit-screen/components/AdsrPane.cpp diff --git a/src-ui/app/HasEditor.h b/src-ui/app/HasEditor.h index c0911426..3857383b 100644 --- a/src-ui/app/HasEditor.h +++ b/src-ui/app/HasEditor.h @@ -57,6 +57,8 @@ struct HasEditor template void updateValueTooltip(const T &attachment); template void setupWidgetForValueTooltip(W *widget, const A &attachment); + + template void addSubscription(const P &, A &); }; } // namespace scxt::ui::app #endif // SHORTCIRCUIT_HASEDITOR_H diff --git a/src-ui/app/SCXTEditor.h b/src-ui/app/SCXTEditor.h index b08bfa2f..bd1e00b5 100644 --- a/src-ui/app/SCXTEditor.h +++ b/src-ui/app/SCXTEditor.h @@ -34,7 +34,6 @@ #include "app/editor-impl/SCXTJuceLookAndFeel.h" #include "engine/engine.h" -#include "engine/patch.h" #include "messaging/client/selection_messages.h" #include "messaging/messaging.h" #include "selection/selection_manager.h" @@ -46,10 +45,13 @@ #include "messaging/client/zone_messages.h" #include "browser/browser.h" +#include "SCXTEditorDataCache.h" #include "HasEditor.h" #include "theme/ThemeApplier.h" +#include "connectors/PayloadDataAttachment.h" + namespace melatonin { class Inspector; @@ -125,6 +127,12 @@ struct SCXTEditor : sst::jucegui::components::WindowPanel, juce::DragAndDropCont juce::Colour themeColor(scxt::ui::theme::ColorMap::Colors, float alpha = 1.f) const; void resetColorsFromUserPreferences(); + /* + * See the comment in SCXTEditorDataCache for more on this class. Shared space to + * cache the UI data types + */ + SCXTEditorDataCache editorDataCache; + sst::basic_blocks::dsp::RNG rng; static constexpr int edWidth{1186}, edHeight{816}; @@ -367,6 +375,22 @@ inline void HasEditor::setupWidgetForValueTooltip(W *w, const A &a) }; } } + +template void HasEditor::addSubscription(const P &p, A &a) +{ + auto *dc = &editor->editorDataCache; + auto dce = dc + sizeof(SCXTEditorDataCache); + if ((size_t)&p >= (size_t)dc && (size_t)&p < (size_t)dce) + { + auto *edCopy = editor; + auto *vp = (void *)&p; + + connectors::addGuiStep(*a, [edCopy, vp, f = a.get()](auto &) { + edCopy->editorDataCache.fireSubscription(vp, f); + }); + editor->editorDataCache.addSubscription(vp, a.get()); + } +} } // namespace scxt::ui::app #endif // SCXT_SRC_UI_COMPONENTS_SCXTEDITOR_H diff --git a/src-ui/app/SCXTEditorDataCache.h b/src-ui/app/SCXTEditorDataCache.h new file mode 100644 index 00000000..9fc25f98 --- /dev/null +++ b/src-ui/app/SCXTEditorDataCache.h @@ -0,0 +1,91 @@ +/* + * Shortcircuit XT - a Surge Synth Team product + * + * A fully featured creative sampler, available as a standalone + * and plugin for multiple platforms. + * + * Copyright 2019 - 2024, Various authors, as described in the github + * transaction log. + * + * ShortcircuitXT is released under the Gnu General Public Licence + * V3 or later (GPL-3.0-or-later). The license is found in the file + * "LICENSE" in the root of this repository or at + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * Individual sections of code which comprises ShortcircuitXT in this + * repository may also be used under an MIT license. Please see the + * section "Licensing" in "README.md" for details. + * + * ShortcircuitXT is inspired by, and shares code with, the + * commercial product Shortcircuit 1 and 2, released by VemberTech + * in the mid 2000s. The code for Shortcircuit 2 was opensourced in + * 2020 at the outset of this project. + * + * All source for ShortcircuitXT is available at + * https://github.com/surge-synthesizer/shortcircuit-xt + */ + +#ifndef SCXT_SRC_UI_APP_SCXTEDITORDATACACHE_H +#define SCXT_SRC_UI_APP_SCXTEDITORDATACACHE_H + +#include +#include + +#include "engine/bus.h" +#include "engine/part.h" +#include "engine/group.h" +#include "engine/zone.h" +#include "engine/patch.h" + +#include "sst/jucegui/data/Continuous.h" +#include "sst/jucegui/data/Discrete.h" + +namespace scxt::ui::app +{ +/** + * The iriginal design of the SC UI (which is still partly that way) has local copies + * of the data cache right next to the particular panel. This works well until you want + * to share data across more than one panel, and then it gets clumsy, or if your heirarchy + * gets deep. + * + * So this class exists to have a place for the SCXTEditorResonder thingy to push data + * copies and for hte UI to bind to, since the UI will reliably be components which + * haveeditor. + */ +struct SCXTEditorDataCache +{ + // Displayed group tab output info + scxt::engine::Group::GroupOutputInfo groupOutputInfo; + // Displayed zone tab output info + scxt::engine::Zone::ZoneOutputInfo zoneOutputInfo; + + void addSubscription(void *el, sst::jucegui::data::Continuous *); + void addSubscription(void *el, sst::jucegui::data::Discrete *); + void fireSubscription(void *el, sst::jucegui::data::Continuous *); + void fireSubscription(void *el, sst::jucegui::data::Discrete *); + + private: + std::unordered_map> csubs; + std::unordered_map> dsubs; + + struct CListener : sst::jucegui::data::Continuous::DataListener + { + SCXTEditorDataCache &cache; + CListener(SCXTEditorDataCache &dc) : cache(dc){}; + virtual ~CListener() = default; + void dataChanged() override{}; + void sourceVanished(sst::jucegui::data::Continuous *c) override; + } clistener{*this}; + + struct DListener : sst::jucegui::data::Discrete::DataListener + { + SCXTEditorDataCache &cache; + DListener(SCXTEditorDataCache &dc) : cache(dc){}; + virtual ~DListener() = default; + void dataChanged() override{}; + void sourceVanished(sst::jucegui::data::Discrete *c) override; + } dlistener{*this}; +}; +} // namespace scxt::ui::app + +#endif // SCXTEDITORDATACACHE_H diff --git a/src-ui/app/edit-screen/components/GroupSettingsCard.cpp b/src-ui/app/edit-screen/components/GroupSettingsCard.cpp index 9d038159..6850f34d 100644 --- a/src-ui/app/edit-screen/components/GroupSettingsCard.cpp +++ b/src-ui/app/edit-screen/components/GroupSettingsCard.cpp @@ -27,14 +27,16 @@ #include "GroupSettingsCard.h" #include "app/SCXTEditor.h" -#include "connectors/PayloadDataAttachment.h" namespace scxt::ui::app::edit_screen { namespace jcmp = sst::jucegui::components; -GroupSettingsCard::GroupSettingsCard(SCXTEditor *e) : HasEditor(e) +GroupSettingsCard::GroupSettingsCard(SCXTEditor *e) + : HasEditor(e), info(e->editorDataCache.groupOutputInfo) { + using fac = connectors::SingleValueFactory; + auto mkg = [this](auto gl) { auto res = std::make_unique(gl); addAndMakeVisible(*res); @@ -67,9 +69,10 @@ GroupSettingsCard::GroupSettingsCard(SCXTEditor *e) : HasEditor(e) glideDrag = mkd('gdrg', "Glide"); volGlyph = mkg(jcmp::GlyphPainter::GlyphType::VOLUME); - volDrag = mkd('grvl', "Volume"); + fac::attachAndAdd(info, info.amplitude, this, volAttachment, volDrag); panGlyph = mkg(jcmp::GlyphPainter::GlyphType::PAN); - panDrag = mkd('grpn', "Pan"); + fac::attachAndAdd(info, info.pan, this, panAttachment, panDrag); + tuneGlyph = mkg(jcmp::GlyphPainter::GlyphType::TUNING); tuneDrag = mkd('grtn', "Tune"); } diff --git a/src-ui/app/edit-screen/components/GroupSettingsCard.h b/src-ui/app/edit-screen/components/GroupSettingsCard.h index 11586d66..50f093ca 100644 --- a/src-ui/app/edit-screen/components/GroupSettingsCard.h +++ b/src-ui/app/edit-screen/components/GroupSettingsCard.h @@ -35,6 +35,9 @@ #include "sst/jucegui/components/Label.h" #include "sst/jucegui/components/GlyphButton.h" +#include "messaging/messaging.h" +#include "connectors/PayloadDataAttachment.h" + #include "app/HasEditor.h" namespace scxt::ui::app::edit_screen @@ -52,6 +55,13 @@ struct GroupSettingsCard : juce::Component, HasEditor glideMenu, srcMenu; std::unique_ptr pbDnVal, pbUpDrag, glideDrag, volDrag, panDrag, tuneDrag; + + using floatMsg_t = scxt::messaging::client::UpdateGroupOutputFloatValue; + typedef connectors::PayloadDataAttachment attachment_t; + + std::unique_ptr volAttachment, panAttachment; + + engine::Group::GroupOutputInfo &info; }; } // namespace scxt::ui::app::edit_screen #endif // GROUPSETTINGSCARD_H diff --git a/src-ui/app/edit-screen/components/OutputPane.cpp b/src-ui/app/edit-screen/components/OutputPane.cpp index bf96193f..deac3396 100644 --- a/src-ui/app/edit-screen/components/OutputPane.cpp +++ b/src-ui/app/edit-screen/components/OutputPane.cpp @@ -44,12 +44,23 @@ namespace scxt::ui::app::edit_screen namespace jcmp = sst::jucegui::components; namespace cmsg = scxt::messaging::client; +engine::Zone::ZoneOutputInfo &OutPaneZoneTraits::outputInfo(SCXTEditor *e) +{ + return e->editorDataCache.zoneOutputInfo; +} + +engine::Group::GroupOutputInfo &OutPaneGroupTraits::outputInfo(SCXTEditor *e) +{ + return e->editorDataCache.groupOutputInfo; +} + template struct ProcTab : juce::Component, HasEditor { OutputPane *outputPane{nullptr}; std::unique_ptr procRouting; - ProcTab(SCXTEditor *e, OutputPane *pane) : HasEditor(e), outputPane(pane) + ProcTab(SCXTEditor *e, OutputPane *pane) + : HasEditor(e), outputPane(pane), info(OTTraits::outputInfo(e)) { procRouting = std::make_unique(); procRouting->setLabel(OTTraits::defaultRoutingLocationName); @@ -148,7 +159,7 @@ template struct ProcTab : juce::Component, HasEditor } } - typename OTTraits::info_t info; + typename OTTraits::info_t &info; }; template struct OutputTab : juce::Component, HasEditor @@ -165,7 +176,8 @@ template struct OutputTab : juce::Component, HasEditor std::unique_ptr oversampleAttachment; std::unique_ptr oversampleButton; - OutputTab(SCXTEditor *e, OutputPane *p) : HasEditor(e), parent(p) + OutputTab(SCXTEditor *e, OutputPane *p) + : HasEditor(e), parent(p), info(OTTraits::outputInfo(e)) { using fac = connectors::SingleValueFactory; @@ -272,7 +284,7 @@ template struct OutputTab : juce::Component, HasEditor } p.showMenuAsync(editor->defaultPopupMenuOptions()); } - typename OTTraits::info_t info; + typename OTTraits::info_t &info; }; template @@ -333,14 +345,11 @@ template void OutputPane::setActive(bool b) onTabSelected(selectedTab); } -template -void OutputPane::setOutputData(const typename OTTraits::info_t &d) +template void OutputPane::updateFromOutputInfo() { - output->info = d; output->updateRoutingLabel(); output->repaint(); - proc->info = d; proc->updateProcRoutingLabel(); proc->repaint(); } diff --git a/src-ui/app/edit-screen/components/OutputPane.h b/src-ui/app/edit-screen/components/OutputPane.h index 83ef041d..1c03dad2 100644 --- a/src-ui/app/edit-screen/components/OutputPane.h +++ b/src-ui/app/edit-screen/components/OutputPane.h @@ -47,6 +47,8 @@ struct OutPaneZoneTraits using floatMsg_t = scxt::messaging::client::UpdateZoneOutputFloatValue; using int16Msg_t = scxt::messaging::client::UpdateZoneOutputInt16TValue; + + static engine::Zone::ZoneOutputInfo &outputInfo(SCXTEditor *e); }; struct OutPaneGroupTraits @@ -58,6 +60,8 @@ struct OutPaneGroupTraits using floatMsg_t = scxt::messaging::client::UpdateGroupOutputFloatValue; using int16Msg_t = scxt::messaging::client::UpdateGroupOutputInt16TValue; + + static engine::Group::GroupOutputInfo &outputInfo(SCXTEditor *e); }; template struct OutputTab; template struct ProcTab; @@ -69,7 +73,8 @@ template struct OutputPane : sst::jucegui::components::Named void resized() override; void setActive(bool); - void setOutputData(const typename OTTraits::info_t &); + + void updateFromOutputInfo(); std::unique_ptr> output; std::unique_ptr> proc; diff --git a/src-ui/app/editor-impl/SCXTEditorDataCache.cpp b/src-ui/app/editor-impl/SCXTEditorDataCache.cpp new file mode 100644 index 00000000..a6d42048 --- /dev/null +++ b/src-ui/app/editor-impl/SCXTEditorDataCache.cpp @@ -0,0 +1,95 @@ +/* + * Shortcircuit XT - a Surge Synth Team product + * + * A fully featured creative sampler, available as a standalone + * and plugin for multiple platforms. + * + * Copyright 2019 - 2024, Various authors, as described in the github + * transaction log. + * + * ShortcircuitXT is released under the Gnu General Public Licence + * V3 or later (GPL-3.0-or-later). The license is found in the file + * "LICENSE" in the root of this repository or at + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * Individual sections of code which comprises ShortcircuitXT in this + * repository may also be used under an MIT license. Please see the + * section "Licensing" in "README.md" for details. + * + * ShortcircuitXT is inspired by, and shares code with, the + * commercial product Shortcircuit 1 and 2, released by VemberTech + * in the mid 2000s. The code for Shortcircuit 2 was opensourced in + * 2020 at the outset of this project. + * + * All source for ShortcircuitXT is available at + * https://github.com/surge-synthesizer/shortcircuit-xt + */ + +#include "app/SCXTEditorDataCache.h" +#include "utils.h" + +namespace scxt::ui::app +{ + +void SCXTEditorDataCache::addSubscription(void *el, sst::jucegui::data::Continuous *c) +{ + csubs[el].insert(c); + c->addGUIDataListener(&clistener); +} +void SCXTEditorDataCache::addSubscription(void *el, sst::jucegui::data::Discrete *d) +{ + dsubs[el].insert(d); + d->addGUIDataListener(&dlistener); +} + +void SCXTEditorDataCache::fireSubscription(void *el, sst::jucegui::data::Continuous *c) +{ + auto cp = csubs.find(el); + if (cp != csubs.end()) + { + for (auto &ca : cp->second) + { + if (ca != c) + { + ca->setValueFromModel(c->getValue()); + } + } + } +} + +void SCXTEditorDataCache::fireSubscription(void *el, sst::jucegui::data::Discrete *c) +{ + auto cp = dsubs.find(el); + if (cp != dsubs.end()) + { + for (auto &ca : cp->second) + { + if (ca != c) + { + ca->setValueFromModel(c->getValue()); + } + } + } +} + +void SCXTEditorDataCache::CListener::sourceVanished(sst::jucegui::data::Continuous *c) +{ + for (auto &cs : cache.csubs) + { + auto p = cs.second.find(c); + if (p != cs.second.end()) + cs.second.erase(p); + } +} + +void SCXTEditorDataCache::DListener::sourceVanished(sst::jucegui::data::Discrete *c) +{ + for (auto &cs : cache.dsubs) + { + auto p = cs.second.find(c); + if (p != cs.second.end()) + cs.second.erase(p); + } +} + +} // namespace scxt::ui::app \ No newline at end of file diff --git a/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp b/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp index cfcb5e9e..bc390170 100644 --- a/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp +++ b/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp @@ -203,20 +203,22 @@ void SCXTEditor::onGroupOrZoneModulatorStorageUpdated( void SCXTEditor::onZoneOutputInfoUpdated(const scxt::messaging::client::zoneOutputInfoUpdate_t &p) { auto [active, inf] = p; + editorDataCache.zoneOutputInfo = inf; editScreen->getZoneElements()->outPane->setActive(active); if (active) { - editScreen->getZoneElements()->outPane->setOutputData(inf); + editScreen->getZoneElements()->outPane->updateFromOutputInfo(); } } void SCXTEditor::onGroupOutputInfoUpdated(const scxt::messaging::client::groupOutputInfoUpdate_t &p) { auto [active, inf] = p; + editorDataCache.groupOutputInfo = inf; editScreen->getGroupElements()->outPane->setActive(active); if (active) { - editScreen->getGroupElements()->outPane->setOutputData(inf); + editScreen->getGroupElements()->outPane->updateFromOutputInfo(); } } diff --git a/src-ui/connectors/PayloadDataAttachment.h b/src-ui/connectors/PayloadDataAttachment.h index 3837032e..6a60d6f3 100644 --- a/src-ui/connectors/PayloadDataAttachment.h +++ b/src-ui/connectors/PayloadDataAttachment.h @@ -231,6 +231,9 @@ struct PayloadDataAttachment : sst::jucegui::data::Continuous { prevValue = value; value = (ValueType)f; + + for (auto *l : guilisteners) + l->dataChanged(); } float getMin() const override { return description.minVal; } @@ -309,6 +312,9 @@ struct DiscretePayloadDataAttachment : sst::jucegui::data::Discrete { prevValue = value; value = (ValueType)f; + + for (auto *l : guilisteners) + l->dataChanged(); } int getMin() const override { return (int)description.minVal; } @@ -403,7 +409,12 @@ struct DirectBooleanPayloadDataAttachment : sst::jucegui::data::Discrete value = f; callback(f); } - void setValueFromModel(const int &f) override { value = f; } + void setValueFromModel(const int &f) override + { + value = f; + for (auto *l : guilisteners) + l->dataChanged(); + } int getMin() const override { return (int)0; } int getMax() const override { return (int)1; } @@ -448,7 +459,12 @@ struct SamplePointDataAttachment : sst::jucegui::data::Continuous float getMin() const override { return -1; } float getMax() const override { return sampleCount; } float getDefaultValue() const override { return 0; } - void setValueFromModel(const float &f) override { value = (int64_t)f; } + void setValueFromModel(const float &f) override + { + value = (int64_t)f; + for (auto *l : guilisteners) + l->dataChanged(); + } }; template struct SingleValueFactory @@ -470,6 +486,7 @@ template struct SingleValueFactor { e->setupWidgetForValueTooltip(wid.get(), att.get()); } + e->addSubscription(val, att); return {std::move(att), std::move(wid)}; } diff --git a/src/engine/group.h b/src/engine/group.h index 89f9a369..f4083ec5 100644 --- a/src/engine/group.h +++ b/src/engine/group.h @@ -260,7 +260,7 @@ struct Group : MoveableOnly, SC_DESCRIBE(scxt::engine::Group::GroupOutputInfo, SC_FIELD(amplitude, pmd().asCubicDecibelAttenuationWithUpperDBBound(12).withName("Amplitude")); - SC_FIELD(pan, pmd().asPercentBipolar().withName("Pan")); + SC_FIELD(pan, pmd().asPercentBipolar().withName("Pan").withDecimalPlaces(0)); SC_FIELD(procRouting, pmd().asInt().withRange(0, 1)); SC_FIELD(oversample, pmd().asBool().withName("Oversample")); SC_FIELD(velocitySensitivity,