diff --git a/src-ui/app/HasEditor.h b/src-ui/app/HasEditor.h index 9c594aea..c0911426 100644 --- a/src-ui/app/HasEditor.h +++ b/src-ui/app/HasEditor.h @@ -40,6 +40,7 @@ struct HasEditor { SCXTEditor *editor{nullptr}; HasEditor(SCXTEditor *e); + HasEditor(HasEditor *e); virtual ~HasEditor() = default; template void sendToSerialization(const T &msg); diff --git a/src-ui/app/edit-screen/components/GroupTriggersCard.cpp b/src-ui/app/edit-screen/components/GroupTriggersCard.cpp index 9034c451..2ef05502 100644 --- a/src-ui/app/edit-screen/components/GroupTriggersCard.cpp +++ b/src-ui/app/edit-screen/components/GroupTriggersCard.cpp @@ -27,9 +27,13 @@ #include "GroupTriggersCard.h" #include "sst/jucegui/components/ToggleButton.h" +#include "sst/jucegui/components/TextPushButton.h" #include "sst/jucegui/components/MenuButton.h" +#include "sst/jucegui/components/DraggableTextEditableValue.h" #include "app/SCXTEditor.h" +#include "messaging/client/client_messages.h" +#include "connectors/PayloadDataAttachment.h" namespace scxt::ui::app::edit_screen { @@ -37,17 +41,34 @@ namespace jcmp = sst::jucegui::components; struct GroupTriggersCard::ConditionRow : juce::Component, HasEditor { + using booleanAttachment_t = + connectors::BooleanPayloadDataAttachment; + + using floatAttachment_t = + connectors::PayloadDataAttachment; + + GroupTriggersCard *parent{nullptr}; + engine::GroupTriggerID lastID{engine::GroupTriggerID::NONE}; + int index{-1}; bool withCondition{false}; - ConditionRow(int index, bool withCond, SCXTEditor *ed) - : index(index), withCondition(withCond), HasEditor(ed) + ConditionRow(GroupTriggersCard *p, int index, bool withCond) + : parent(p), index(index), withCondition(withCond), HasEditor(p) { + activeA = std::make_unique( + "Active", + [this](const auto &a) { + setupValuesFromData(); + parent->pushUpdate(); + }, + parent->cond.active[index]); activeB = std::make_unique(); activeB->setLabel(std::to_string(index + 1)); + activeB->setSource(activeA.get()); addAndMakeVisible(*activeB); auto mkm = [this](auto tx, auto cs) { - auto res = std::make_unique(); + auto res = std::make_unique(); res->setLabel(tx); res->setOnCallback( editor->makeComingSoon(std::string() + "Trigger Condition Pane " + cs)); @@ -55,10 +76,137 @@ struct GroupTriggersCard::ConditionRow : juce::Component, HasEditor return res; }; typeM = mkm("TYPE", "Trigger Mode"); - a1M = mkm("A1", "Argument One"); - a2M = mkm("A2", "Argument Two"); + typeM->setOnCallback([w = juce::Component::SafePointer(this)]() { + if (!w) + return; + w->showTypeMenu(); + }); if (withCond) cM = mkm("&", "Conjunction"); + + setupValuesFromData(); + } + + void setupValuesFromData() + { + auto &sr = parent->cond.storage[index]; + + if (sr.id != lastID) + { + lastID = sr.id; + + auto onArgChanged = [this](const auto &a) { parent->pushUpdate(); }; + + if (sr.id == engine::GroupTriggerID::NONE) + { + SCLOG("NONE"); + a1A.reset(); + a2A.reset(); + a1M.reset(); + a2M.reset(); + } + else if ((int)sr.id >= (int)engine::GroupTriggerID::MACRO && + (int)sr.id <= (int)engine::GroupTriggerID::MACRO + scxt::macrosPerPart) + { + auto dm = datamodel::pmd() + .asFloat() + .withRange(0, 1) + .withLinearScaleFormatting("") + .withDecimalPlaces(2) + .withDefault(0.5); + a1A = std::make_unique(dm, onArgChanged, sr.args[0]); + a2A = std::make_unique(dm, onArgChanged, sr.args[1]); + + a1M = std::make_unique(); + a1M->setSource(a1A.get()); + addAndMakeVisible(*a1M); + + a2M = std::make_unique(); + a2M->setSource(a2A.get()); + addAndMakeVisible(*a2M); + } + else if ((int)sr.id >= (int)engine::GroupTriggerID::MIDICC && + (int)sr.id <= (int)engine::GroupTriggerID::LAST_MIDICC) + { + auto dm = datamodel::pmd() + .asFloat() + .withRange(0, 127) + .withLinearScaleFormatting("") + .withDecimalPlaces(0) + .withDefault(64); + a1A = std::make_unique(dm, onArgChanged, sr.args[0]); + a2A = std::make_unique(dm, onArgChanged, sr.args[1]); + + a1M = std::make_unique(); + a1M->setSource(a1A.get()); + addAndMakeVisible(*a1M); + + a2M = std::make_unique(); + a2M->setSource(a2A.get()); + addAndMakeVisible(*a2M); + } + else + { + SCLOG_UNIMPL("No midi CC for type " << (int)sr.id); + } + resized(); + } + + typeM->setLabel(engine::getGroupTriggerDisplayName(sr.id)); + auto showRest = (sr.id != engine::GroupTriggerID::NONE); + if (a1M) + a1M->setVisible(showRest); + if (a2M) + a2M->setVisible(showRest); + if (cM) + cM->setVisible(showRest); + + auto ac = parent->cond.active[index]; + typeM->setEnabled(ac); + if (a1M) + a1M->setEnabled(ac); + if (a2M) + a2M->setEnabled(ac); + if (cM) + cM->setEnabled(ac); + + repaint(); + } + + void showTypeMenu() + { + auto p = juce::PopupMenu(); + p.addSectionHeader("Trigger Mode"); + p.addSeparator(); + + auto mkv = [this](auto v) { + return [w = juce::Component::SafePointer(this), v]() { + if (!w) + return; + w->parent->cond.storage[w->index].id = (engine::GroupTriggerID)v; + w->parent->pushUpdate(); + w->setupValuesFromData(); + }; + }; + + p.addItem("NONE", mkv((int)engine::GroupTriggerID::NONE)); + + auto mcc = juce::PopupMenu(); + for (int i = 0; i < 128; ++i) + { + mcc.addItem(fmt::format("CC {:3d}", i), mkv((int)engine::GroupTriggerID::MIDICC + i)); + } + + p.addSubMenu("MIDI CC", mcc); + auto msm = juce::PopupMenu(); + for (int i = 0; i < scxt::macrosPerPart; ++i) + { + msm.addItem("Macro " + std::to_string(i + 1), + mkv((int)engine::GroupTriggerID::MACRO + i)); + } + + p.addSubMenu("Macros", msm); + p.showMenuAsync(editor->defaultPopupMenuOptions()); } void resized() override @@ -69,9 +217,11 @@ struct GroupTriggersCard::ConditionRow : juce::Component, HasEditor tb = tb.translated(tb.getWidth() + 2, 0).withWidth(72); typeM->setBounds(tb); tb = tb.translated(tb.getWidth() + 2, 0).withWidth(32); - a1M->setBounds(tb); + if (a1M) + a1M->setBounds(tb); tb = tb.translated(tb.getWidth() + 2, 0).withWidth(32); - a2M->setBounds(tb); + if (a2M) + a2M->setBounds(tb); if (cM) { @@ -80,15 +230,17 @@ struct GroupTriggersCard::ConditionRow : juce::Component, HasEditor } } + std::unique_ptr activeA; + std::unique_ptr a1A, a2A; std::unique_ptr activeB; - std::unique_ptr typeM, a1M, a2M, cM; + std::unique_ptr typeM, cM; + std::unique_ptr a1M, a2M; }; GroupTriggersCard::GroupTriggersCard(SCXTEditor *e) : HasEditor(e) { for (int i = 0; i < scxt::triggerConditionsPerGroup; ++i) { - rows[i] = - std::make_unique(i, i != scxt::triggerConditionsPerGroup - 1, editor); + rows[i] = std::make_unique(this, i, i != scxt::triggerConditionsPerGroup - 1); addAndMakeVisible(*rows[i]); } } @@ -117,4 +269,17 @@ void GroupTriggersCard::paint(juce::Graphics &g) g.drawText("TRIGGER CONDITIONS", getLocalBounds(), juce::Justification::topLeft); } +void GroupTriggersCard::setGroupTriggerConditions(const scxt::engine::GroupTriggerConditions &c) +{ + cond = c; + for (auto &r : rows) + r->setupValuesFromData(); + repaint(); +} + +void GroupTriggersCard::pushUpdate() +{ + sendToSerialization(scxt::messaging::client::UpdateGroupTriggerConditions(cond)); +} + } // namespace scxt::ui::app::edit_screen \ No newline at end of file diff --git a/src-ui/app/edit-screen/components/GroupTriggersCard.h b/src-ui/app/edit-screen/components/GroupTriggersCard.h index a90f70d4..c9bd1eaf 100644 --- a/src-ui/app/edit-screen/components/GroupTriggersCard.h +++ b/src-ui/app/edit-screen/components/GroupTriggersCard.h @@ -33,6 +33,8 @@ #include +#include "engine/group_triggers.h" + #include "configuration.h" #include "app/HasEditor.h" @@ -46,6 +48,10 @@ struct GroupTriggersCard : juce::Component, HasEditor ~GroupTriggersCard(); void paint(juce::Graphics &g) override; void resized() override; + + void setGroupTriggerConditions(const scxt::engine::GroupTriggerConditions &); + void pushUpdate(); + scxt::engine::GroupTriggerConditions cond; }; } // namespace scxt::ui::app::edit_screen #endif // GROUPTRIGGERSCARD_H diff --git a/src-ui/app/edit-screen/components/PartGroupSidebar.cpp b/src-ui/app/edit-screen/components/PartGroupSidebar.cpp index 2379c9b6..ba518f8f 100644 --- a/src-ui/app/edit-screen/components/PartGroupSidebar.cpp +++ b/src-ui/app/edit-screen/components/PartGroupSidebar.cpp @@ -240,7 +240,13 @@ struct GroupSidebar : GroupZoneSidebarBase } ~GroupSidebar() = default; - void updateSelection() { updateSelectionFrom(partGroupSidebar->editor->allGroupSelections); } + void updateSelection() + { + bool anySel = !partGroupSidebar->editor->allGroupSelections.empty(); + updateSelectionFrom(partGroupSidebar->editor->allGroupSelections); + groupSettings->setVisible(anySel); + groupTriggers->setVisible(anySel); + } void resized() override { @@ -522,4 +528,10 @@ void PartGroupSidebar::partConfigurationChanged(int i) partSidebar->parts[i]->resetFromEditorCache(); partSidebar->restackForActive(); } + +void PartGroupSidebar::groupTriggerConditionChanged(const scxt::engine::GroupTriggerConditions &c) +{ + groupSidebar->groupTriggers->setGroupTriggerConditions(c); +} + } // namespace scxt::ui::app::edit_screen \ No newline at end of file diff --git a/src-ui/app/edit-screen/components/PartGroupSidebar.h b/src-ui/app/edit-screen/components/PartGroupSidebar.h index e9b92a69..3a7ee232 100644 --- a/src-ui/app/edit-screen/components/PartGroupSidebar.h +++ b/src-ui/app/edit-screen/components/PartGroupSidebar.h @@ -59,6 +59,7 @@ struct PartGroupSidebar : sst::jucegui::components::NamedPanel, HasEditor std::unique_ptr partSidebar; void partConfigurationChanged(int i); + void groupTriggerConditionChanged(const scxt::engine::GroupTriggerConditions &); void resized() override; }; diff --git a/src-ui/app/editor-impl/HasEditor.cpp b/src-ui/app/editor-impl/HasEditor.cpp index 86553901..08970a6e 100644 --- a/src-ui/app/editor-impl/HasEditor.cpp +++ b/src-ui/app/editor-impl/HasEditor.cpp @@ -31,4 +31,5 @@ namespace scxt::ui::app { HasEditor::HasEditor(SCXTEditor *e) : editor(e) {} +HasEditor::HasEditor(HasEditor *e) : editor(e->editor) {} } // 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 91ca7f1d..cfcb5e9e 100644 --- a/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp +++ b/src-ui/app/editor-impl/SCXTEditorResponseHandlers.cpp @@ -447,6 +447,6 @@ void SCXTEditor::onMissingResolutionWorkItemList( void SCXTEditor::onGroupTriggerConditions(scxt::engine::GroupTriggerConditions const &g) { - SCLOG_ONCE("Implement: On Group Trigger Conditions"); + editScreen->partSidebar->groupTriggerConditionChanged(g); } } // namespace scxt::ui::app \ No newline at end of file diff --git a/src/engine/engine.h b/src/engine/engine.h index f199f080..7772d0e8 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -147,6 +147,9 @@ struct Engine : MoveableOnly, SampleRateSupport { for (const auto &[gidx, group] : sst::cpputils::enumerate(*part)) { + if (!group->triggerConditions.value(*this, *group)) + continue; + for (const auto &[zidx, zone] : sst::cpputils::enumerate(*group)) { if (zone->mapping.keyboardRange.includes(key) && diff --git a/src/engine/group_triggers.cpp b/src/engine/group_triggers.cpp index 270fc455..66614322 100644 --- a/src/engine/group_triggers.cpp +++ b/src/engine/group_triggers.cpp @@ -26,6 +26,9 @@ */ #include "group_triggers.h" + +#include "group.h" +#include "part.h" #include "utils.h" namespace scxt::engine @@ -33,6 +36,14 @@ namespace scxt::engine std::string toStringGroupTriggerID(const GroupTriggerID &p) { + if (p >= GroupTriggerID::MACRO && (int)p <= (int)GroupTriggerID::MACRO + scxt::macrosPerPart) + { + return fmt::format("macro{}", (int)p - (int)GroupTriggerID::MACRO); + } + if (p >= GroupTriggerID::MIDICC && p <= GroupTriggerID::LAST_MIDICC) + { + return fmt::format("midcc{}", (int)p - (int)GroupTriggerID::MIDICC); + } switch (p) { // lesson learned earlier was long names are not that handy debugging now the infra works @@ -40,19 +51,24 @@ std::string toStringGroupTriggerID(const GroupTriggerID &p) case GroupTriggerID::NONE: return "n"; case GroupTriggerID::MACRO: - return "mcr"; case GroupTriggerID::MIDICC: - return "mcc"; + case GroupTriggerID::LAST_MIDICC: + { + assert(false); + } + break; } return "n"; } GroupTriggerID fromStringGroupTriggerID(const std::string &s) { static auto inverse = makeEnumInverse( - GroupTriggerID::NONE, GroupTriggerID::MIDICC); + GroupTriggerID::NONE, GroupTriggerID::LAST_MIDICC); auto p = inverse.find(s); if (p == inverse.end()) + { return GroupTriggerID::NONE; + } return p->second; } @@ -86,51 +102,107 @@ GroupTriggerConditions::fromStringConditionsConjunction(const std::string &s) struct GTMacro : GroupTrigger { - GTMacro(GroupTriggerInstrumentState &onState, GroupTriggerStorage &onStorage) - : GroupTrigger(onState, onStorage) + int macro{0}; + float lb{0}, ub{1}; + GTMacro(GroupTriggerID id, GroupTriggerInstrumentState &onState, GroupTriggerStorage &onStorage, + int macro) + : macro(macro), GroupTrigger(id, onState, onStorage) { } - bool value(const std::unique_ptr &, const std::unique_ptr &) const override + bool value(const Engine &, const Group &g) const override { - return true; + auto mv = g.parentPart->macros[macro].value; + return mv >= lb && mv <= ub; + } + + void storageAdjusted() override + { + lb = storage.args[0]; + ub = storage.args[1]; + if (lb > ub) + std::swap(lb, ub); } - datamodel::pmd argMetadata(int argNo) const override { return {}; } }; + struct GTMIDI1CC : GroupTrigger { - GTMIDI1CC(GroupTriggerInstrumentState &onState, GroupTriggerStorage &onStorage) - : GroupTrigger(onState, onStorage) + int cc; + float lb{0}, ub{1}; + GTMIDI1CC(GroupTriggerID id, GroupTriggerInstrumentState &onState, + GroupTriggerStorage &onStorage, int cc) + : cc(cc), GroupTrigger(id, onState, onStorage) { } - bool value(const std::unique_ptr &, const std::unique_ptr &) const override + bool value(const Engine &, const Group &g) const override { - return true; + assert(g.parentPart); + auto ccv = g.parentPart->midiCCValues[cc]; + return ccv >= lb && ccv <= ub; + } + + void storageAdjusted() override + { + auto flb = storage.args[0]; + auto fub = storage.args[1]; + if (flb > fub) + std::swap(flb, fub); + lb = std::floor(flb) / 127.0; + ub = std::ceil(fub) / 127.0; } - datamodel::pmd argMetadata(int argNo) const override { return {}; } }; GroupTrigger *makeGroupTrigger(GroupTriggerID id, GroupTriggerInstrumentState &gis, GroupTriggerStorage &st, GroupTriggerBuffer &bf) { + if (id >= GroupTriggerID::MACRO && (int)id <= (int)GroupTriggerID::MACRO + scxt::macrosPerPart) + { + static_assert(sizeof(GTMacro) < sizeof(GroupTriggerBuffer)); + return new (bf) GTMacro(id, gis, st, (int)id - (int)GroupTriggerID::MACRO); + } + if (id >= GroupTriggerID::MIDICC && id <= GroupTriggerID::LAST_MIDICC) + { + static_assert(sizeof(GTMIDI1CC) < sizeof(GroupTriggerBuffer)); + return new (bf) GTMIDI1CC(id, gis, st, (int)id - (int)GroupTriggerID::MIDICC); + } + #define CS(id, tp) \ static_assert(sizeof(tp) < sizeof(GroupTriggerBuffer)); \ case id: \ - return new (bf) tp(gis, st); + return new (bf) tp(id, gis, st); switch (id) { - CS(GroupTriggerID::MIDICC, GTMIDI1CC); - CS(GroupTriggerID::MACRO, GTMacro); - case GroupTriggerID::NONE: + default: return nullptr; } return nullptr; } -// THIS NEEDS ARGUMENTS of the state and so on +std::string getGroupTriggerDisplayName(GroupTriggerID id) +{ + if (id >= GroupTriggerID::MACRO && (int)id <= (int)GroupTriggerID::MACRO + scxt::macrosPerPart) + { + return fmt::format("MACRO {}", (int)id - (int)GroupTriggerID::MACRO + 1); + } + if (id >= GroupTriggerID::MIDICC && id <= GroupTriggerID::LAST_MIDICC) + { + return fmt::format("MIDICC {}", (int)id - (int)GroupTriggerID::MIDICC); + } + + switch (id) + { + case GroupTriggerID::NONE: + return "NONE"; + default: + return "ERROR"; + } + return "ERROR"; +} + void GroupTriggerConditions::setupOnUnstream(GroupTriggerInstrumentState &gis) { + bool allNone{true}; for (int i = 0; i < triggerConditionsPerGroup; ++i) { auto &s = storage[i]; @@ -142,9 +214,32 @@ void GroupTriggerConditions::setupOnUnstream(GroupTriggerInstrumentState &gis) } else { - conditions[i] = makeGroupTrigger(s.id, gis, s, conditionBuffers[i]); + allNone = false; + + if (!conditions[i] || conditions[i]->getID() != s.id) + { + conditions[i] = makeGroupTrigger(s.id, gis, s, conditionBuffers[i]); + } } + if (conditions[i]) + conditions[i]->storageAdjusted(); + } + alwaysReturnsTrue = allNone; +} + +bool GroupTriggerConditions::value(const Engine &e, const Group &g) const +{ + if (alwaysReturnsTrue) + return true; + + auto v = true; + for (int i = 0; i < triggerConditionsPerGroup; ++i) + { + // FIXME - conjunctions + if (active[i] && conditions[i]) + v = v & conditions[i]->value(e, g); } + return v; } } // namespace scxt::engine \ No newline at end of file diff --git a/src/engine/group_triggers.h b/src/engine/group_triggers.h index 937405d1..d99fbc10 100644 --- a/src/engine/group_triggers.h +++ b/src/engine/group_triggers.h @@ -55,8 +55,10 @@ enum struct GroupTriggerID : int32_t { NONE, + // Leave these at the end please MACRO, - MIDICC // if MIDICC is no longer last, adjust fromStringGfroupTRiggerID iteration + MIDICC = MACRO + scxt::macrosPerPart, + LAST_MIDICC = MIDICC + 128 // Leave this at the end please }; std::string toStringGroupTriggerID(const GroupTriggerID &p); @@ -65,7 +67,7 @@ GroupTriggerID fromStringGroupTriggerID(const std::string &p); struct GroupTriggerStorage { GroupTriggerID id{GroupTriggerID::NONE}; - using argInterface_t = int32_t; // for now and just use ms for miliseconds and scale + using argInterface_t = float; static constexpr int32_t numArgs{2}; std::array args{0, 0}; @@ -77,13 +79,26 @@ struct GroupTrigger { GroupTriggerInstrumentState &state; GroupTriggerStorage &storage; - GroupTrigger(GroupTriggerInstrumentState &onState, GroupTriggerStorage &onStorage) - : state(onState), storage(onStorage) + GroupTriggerID id; + GroupTrigger(GroupTriggerID id, GroupTriggerInstrumentState &onState, + GroupTriggerStorage &onStorage) + : state(onState), storage(onStorage), id(id) { } + GroupTriggerID getID() const { return id; } virtual ~GroupTrigger() = default; - virtual bool value(const std::unique_ptr &, const std::unique_ptr &) const = 0; - virtual datamodel::pmd argMetadata(int argNo) const = 0; + virtual bool value(const Engine &, const Group &) const = 0; + virtual void storageAdjusted() = 0; + + /* + * At one point I had considered making these self describing but our + * trigger types are small enough I just went with an int32 as each data + * element and then set up based on type in the UI in GroupTriggersCard.cpp, + * since I don't think adding trigger types will be anything like adding + * modulator or processor types in cadence. If I'm wrong add something like + * this again. + */ + // virtual datamodel::pmd argMetadata(int argNo) const = 0; }; // FIXME - make this sized more intelligently @@ -98,11 +113,13 @@ struct GroupTriggerConditions GroupTriggerConditions() { std::fill(conditions.begin(), conditions.end(), nullptr); - std::fill(active.begin(), active.end(), false); + std::fill(active.begin(), active.end(), true); } std::array storage{}; std::array active{}; + bool alwaysReturnsTrue{true}; + enum struct Conjunction : int32_t { AND, @@ -118,6 +135,8 @@ struct GroupTriggerConditions void setupOnUnstream(GroupTriggerInstrumentState &); + bool value(const Engine &, const Group &) const; + protected: std::array conditionBuffers; std::array conditions{}; diff --git a/src/json/engine_traits.h b/src/json/engine_traits.h index 125c6495..9ecdc80f 100644 --- a/src/json/engine_traits.h +++ b/src/json/engine_traits.h @@ -298,6 +298,7 @@ SC_STREAMDEF(scxt::engine::Group, SC_FROM({ findIf(v, "outputInfo", group.outputInfo); findIf(v, "processorStorage", group.processorStorage); findIf(v, "routingTable", group.routingTable); + findIf(v, "triggerConditions", group.triggerConditions); findIfArray(v, "modulatorStorage", group.modulatorStorage); group.clearZones(); diff --git a/src/messaging/client/client_serial.h b/src/messaging/client/client_serial.h index d0f149ec..8b996a8a 100644 --- a/src/messaging/client/client_serial.h +++ b/src/messaging/client/client_serial.h @@ -103,6 +103,8 @@ enum ClientToSerializationMessagesIds c2s_update_group_output_int16_t_value, c2s_update_group_output_bool_value, + c2s_update_group_trigger_conditions, + // #1141 done up until here. Below this point the name rubric above isn't confirmed in place c2s_request_pgz_structure, // ? diff --git a/src/messaging/client/group_messages.h b/src/messaging/client/group_messages.h index 86a92dc3..73ca34d5 100644 --- a/src/messaging/client/group_messages.h +++ b/src/messaging/client/group_messages.h @@ -58,5 +58,24 @@ CLIENT_TO_SERIAL_CONSTRAINED(UpdateGroupOutputBoolValue, c2s_update_group_output detail::updateGroupMemberValue(&engine::Group::outputInfo, payload, engine, cont)); +inline void doUpdateGroupTriggerConditions(const engine::GroupTriggerConditions &payload, + const engine::Engine &engine, MessageController &cont) +{ + auto ga = engine.getSelectionManager()->currentLeadGroup(engine); + if (ga.has_value()) + { + cont.scheduleAudioThreadCallback([p = payload, g = *ga](auto &eng) { + auto &grp = eng.getPatch()->getPart(g.part)->getGroup(g.group); + grp->triggerConditions = p; + grp->triggerConditions.setupOnUnstream( + eng.getPatch()->getPart(g.part)->groupTriggerInstrumentState); + }); + } +} + +CLIENT_TO_SERIAL(UpdateGroupTriggerConditions, c2s_update_group_trigger_conditions, + scxt::engine::GroupTriggerConditions, + doUpdateGroupTriggerConditions(payload, engine, cont)); + } // namespace scxt::messaging::client #endif // SHORTCIRCUIT_GROUP_MESSAGES_H