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,