Skip to content

Commit

Permalink
Complete Multi-Sel Consistency (LFO and Routing) (#1467)
Browse files Browse the repository at this point in the history
Closes #761

If LFO shapes or Routing shapes are different pop a consistent
button like we do for processors or mod matrix rows
  • Loading branch information
baconpaul authored Nov 27, 2024
1 parent 60c788f commit d6df8b3
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 24 deletions.
59 changes: 53 additions & 6 deletions src-ui/app/edit-screen/components/LFOPane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "modulation/modulators/steplfo.h"
#include "app/edit-screen/EditScreen.h"
#include "app/shared/MenuValueTypein.h"
#include "sst/jucegui/components/TextPushButton.h"

namespace scxt::ui::app::edit_screen
{
Expand Down Expand Up @@ -744,7 +745,45 @@ struct MSEGLFOPane : juce::Component
}
};

// TODO: A Million things of course
struct ConsistencyLFOPane : juce::Component
{
LfoPane *parent{nullptr};
std::unique_ptr<jcmp::Label> conLabel;
std::unique_ptr<jcmp::TextPushButton> conButton;
ConsistencyLFOPane(LfoPane *p) : parent(p)
{
conLabel = std::make_unique<jcmp::Label>();
conLabel->setText("LFO Type is Inconsistent across zone selection");
addAndMakeVisible(*conLabel);

conButton = std::make_unique<jcmp::TextPushButton>();
conButton->setLabel("Label This");
conButton->setOnCallback([w = juce::Component::SafePointer(this)]() {
if (!w)
return;
w->parent->modulatorShapeA->setValueFromGUI(w->parent->modulatorShapeA->getValue());
});
addAndMakeVisible(*conButton);
}

void resized() override
{
auto b = getLocalBounds().reduced(10).withTrimmedTop(20).withHeight(24);
conLabel->setBounds(b);
b = b.translated(0, 30);
conButton->setBounds(b);
}

void visibilityChanged() override
{
auto &ms = parent->modulatorStorageData[parent->selectedTab];
if (isVisible())
{
conButton->setLabel("Set all LFO shapes to " +
parent->modulatorShapeA->getValueAsString());
}
}
};

LfoPane::LfoPane(SCXTEditor *e, bool fz)
: sst::jucegui::components::NamedPanel(""), HasEditor(e), forZone(fz)
Expand Down Expand Up @@ -839,6 +878,9 @@ void LfoPane::rebuildPanelComponents()
curveLfoPane = std::make_unique<CurveLFOPane>(this);
getContentAreaComponent()->addChildComponent(*curveLfoPane);

consistencyLfoPane = std::make_unique<ConsistencyLFOPane>(this);
getContentAreaComponent()->addChildComponent(*consistencyLfoPane);

sfac::attach(ms, ms.modulatorShape, this, modulatorShapeA, modulatorShape, forZone,
selectedTab);
connectors::addGuiStep(*modulatorShapeA,
Expand Down Expand Up @@ -896,20 +938,25 @@ void LfoPane::repositionContentAreaComponents()
envLfoPane->setBounds(paneArea);
msegLfoPane->setBounds(paneArea);
curveLfoPane->setBounds(paneArea);
consistencyLfoPane->setBounds(paneArea);
modulatorShape->setBounds(mg, 0, paneArea.getWidth() / 4, ht);
}

void LfoPane::setSubPaneVisibility()
{
if (!stepLfoPane || !msegLfoPane || !curveLfoPane)
if (!stepLfoPane || !msegLfoPane || !curveLfoPane || !consistencyLfoPane || !envLfoPane)
return;

auto &ms = modulatorStorageData[selectedTab];

stepLfoPane->setVisible(ms.isStep());
msegLfoPane->setVisible(ms.isMSEG());
curveLfoPane->setVisible(ms.isCurve());
envLfoPane->setVisible(ms.isEnv());
auto con = ms.modulatorConsistent;

stepLfoPane->setVisible(ms.isStep() && con);
msegLfoPane->setVisible(ms.isMSEG() && con);
curveLfoPane->setVisible(ms.isCurve() && con);
envLfoPane->setVisible(ms.isEnv() && con);
consistencyLfoPane->setVisible(!con);
modulatorShape->setVisible(con);
}

void LfoPane::pickPresets()
Expand Down
2 changes: 2 additions & 0 deletions src-ui/app/edit-screen/components/LFOPane.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct StepLFOPane;
struct CurveLFOPane;
struct ENVLFOPane;
struct MSEGLFOPane;
struct ConsistencyLFOPane;

struct LfoPane : sst::jucegui::components::NamedPanel, app::HasEditor
{
Expand All @@ -76,6 +77,7 @@ struct LfoPane : sst::jucegui::components::NamedPanel, app::HasEditor
std::unique_ptr<CurveLFOPane> curveLfoPane;
std::unique_ptr<ENVLFOPane> envLfoPane;
std::unique_ptr<MSEGLFOPane> msegLfoPane;
std::unique_ptr<ConsistencyLFOPane> consistencyLfoPane;

bool forZone{true};

Expand Down
49 changes: 40 additions & 9 deletions src-ui/app/edit-screen/components/OutputPane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ template <typename OTTraits> struct ProcTab : juce::Component, HasEditor
{
OutputPane<OTTraits> *outputPane{nullptr};
std::unique_ptr<jcmp::MenuButton> procRouting;
std::unique_ptr<jcmp::TextPushButton> consistentButton;
std::unique_ptr<jcmp::Label> consistentLabel;

ProcTab(SCXTEditor *e, OutputPane<OTTraits> *pane)
: HasEditor(e), outputPane(pane), info(OTTraits::outputInfo(e))
Expand All @@ -70,18 +72,29 @@ template <typename OTTraits> struct ProcTab : juce::Component, HasEditor
w->selectNewProcRouting();
});
addAndMakeVisible(*procRouting);

consistentLabel = std::make_unique<jcmp::Label>();
consistentLabel->setText("Routing inconsistent across selection");
addChildComponent(*consistentLabel);
consistentButton = std::make_unique<jcmp::TextPushButton>();
consistentButton->setLabel("Make Consistent");
consistentButton->setOnCallback([w = juce::Component::SafePointer(this)]() {
if (w)
{
w->template sendSingleToSerialization<typename OTTraits::int16RefreshMsg_t>(
w->info, w->info.procRouting);
}
});
addChildComponent(*consistentButton);
}
void paint(juce::Graphics &g)

void resized()
{
auto ft = editor->style()->getFont(jcmp::Label::Styles::styleClass,
jcmp::Label::Styles::labelfont);
g.setFont(ft.withHeight(14));
g.setColour(juce::Colours::white);
g.drawText("Proc Routing", getLocalBounds().withHeight(30), juce::Justification::centred);
procRouting->setBounds(getLocalBounds().reduced(3, 5).withHeight(24));
consistentLabel->setBounds(procRouting->getBounds());
consistentButton->setBounds(procRouting->getBounds().translated(0, 30));
}

void resized() { procRouting->setBounds(getLocalBounds().reduced(3, 5).withHeight(24)); }

std::string getRoutingName(typename OTTraits::route_t r)
{
auto zn = std::string();
Expand All @@ -91,6 +104,24 @@ template <typename OTTraits> struct ProcTab : juce::Component, HasEditor
zn = scxt::engine::Group::getProcRoutingPathDisplayName(r);
return zn;
}
void updateProcRoutingFromInfo()
{
updateProcRoutingLabel();
if constexpr (OTTraits::forZone)
{
auto c = info.procRoutingConsistent;
procRouting->setVisible(c);
for (int i = 0; i < nOuts; ++i)
levelK[i]->setVisible(c);
consistentButton->setVisible(!c);
consistentLabel->setVisible(!c);

if (!c)
{
consistentButton->setLabel("Set to " + getRoutingName(info.procRouting));
}
}
}
void updateProcRoutingLabel()
{
procRouting->setLabel(getRoutingName(info.procRouting));
Expand Down Expand Up @@ -350,7 +381,7 @@ template <typename OTTraits> void OutputPane<OTTraits>::updateFromOutputInfo()
output->updateRoutingLabel();
output->repaint();

proc->updateProcRoutingLabel();
proc->updateProcRoutingFromInfo();
proc->repaint();
}

Expand Down
2 changes: 2 additions & 0 deletions src-ui/app/edit-screen/components/OutputPane.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct OutPaneZoneTraits

using floatMsg_t = scxt::messaging::client::UpdateZoneOutputFloatValue;
using int16Msg_t = scxt::messaging::client::UpdateZoneOutputInt16TValue;
using int16RefreshMsg_t = scxt::messaging::client::UpdateZoneOutputInt16TValueThenRefresh;

static engine::Zone::ZoneOutputInfo &outputInfo(SCXTEditor *e);
};
Expand All @@ -60,6 +61,7 @@ struct OutPaneGroupTraits

using floatMsg_t = scxt::messaging::client::UpdateGroupOutputFloatValue;
using int16Msg_t = scxt::messaging::client::UpdateGroupOutputInt16TValue;
using int16RefreshMsg_t = int16Msg_t; // for now.

static engine::Group::GroupOutputInfo &outputInfo(SCXTEditor *e);
};
Expand Down
1 change: 1 addition & 0 deletions src/engine/zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ struct Zone : MoveableOnly<Zone>, HasGroupZoneProcessors<Zone>, SampleRateSuppor
float amplitude{1.f}, pan{0.f};
bool muted{false};
ProcRoutingPath procRouting{procRoute_linear};
bool procRoutingConsistent{true};
BusAddress routeTo{DEFAULT_BUS};
} outputInfo;
static_assert(std::is_standard_layout<ZoneOutputInfo>::value);
Expand Down
2 changes: 2 additions & 0 deletions src/json/engine_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ SC_STREAMDEF(scxt::engine::Group, SC_FROM({

SC_STREAMDEF(scxt::engine::Zone::ZoneOutputInfo, SC_FROM({
v = {{"amp", t.amplitude}, {"pan", t.pan}, {"to", (int)t.routeTo}};
addUnlessDefault<val_t>(v, "prc", true, t.procRoutingConsistent);
addUnlessDefault<val_t>(v, "prt", engine::Zone::ProcRoutingPath::procRoute_linear,
t.procRouting);
addUnlessDefault<val_t>(v, "muted", false, t.muted);
Expand All @@ -406,6 +407,7 @@ SC_STREAMDEF(scxt::engine::Zone::ZoneOutputInfo, SC_FROM({
findIf(v, {"amp", "amplitude"}, zo.amplitude);
findIf(v, "pan", zo.pan);
findOrSet(v, "muted", false, zo.muted);
findOrSet(v, "prc", true, zo.procRoutingConsistent);
findOrSet(v, {"prt", "procRouting"},
engine::Zone::ProcRoutingPath::procRoute_linear, zo.procRouting);
int rt{engine::BusAddress::DEFAULT_BUS};
Expand Down
3 changes: 3 additions & 0 deletions src/json/modulation_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ SC_STREAMDEF(scxt::modulation::ModulatorStorage, SC_FROM({
{"curveLfoStorage", t.curveLfoStorage},
{"stepLfoStorage", t.stepLfoStorage},
{"envLfoStorage", t.envLfoStorage}};

addUnlessDefault<val_t>(v, "cn", true, t.modulatorConsistent);
}),
SC_TO({
const auto &object = v.get_object();
Expand All @@ -147,6 +149,7 @@ SC_STREAMDEF(scxt::modulation::ModulatorStorage, SC_FROM({
findIf(v, "curveLfoStorage", result.curveLfoStorage);
findIf(v, "stepLfoStorage", result.stepLfoStorage);
findIf(v, "envLfoStorage", result.envLfoStorage);
findOrSet(v, "cn", true, result.modulatorConsistent);

result.configureCalculatedState();
}))
Expand Down
3 changes: 3 additions & 0 deletions src/messaging/client/client_serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ enum ClientToSerializationMessagesIds

c2s_update_zone_output_float_value,
c2s_update_zone_output_int16_t_value,
c2s_update_zone_output_int16_t_value_then_refresh,

c2s_update_zone_routing_row,

Expand All @@ -108,6 +109,8 @@ enum ClientToSerializationMessagesIds

c2s_update_group_trigger_conditions,

c2s_request_zone_data_refresh,

// #1141 done up until here. Below this point the name rubric above isn't confirmed in place
c2s_request_pgz_structure, // ?

Expand Down
10 changes: 4 additions & 6 deletions src/messaging/client/group_or_zone_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,13 @@ CLIENT_TO_SERIAL_CONSTRAINED(
auto forZone = std::get<0>(payload);
if (forZone)
{
// ToDo: Have to do the group side of this later
// ToDo: This is a wee bit heavy handed. Refactor so we can just send lfo
// and mod matrix (look at the git history before this for matrix).
auto lz = eng.getSelectionManager()->currentLeadZone(eng);
if (lz.has_value())
{
auto &z =
eng.getPatch()->getPart(lz->part)->getGroup(lz->group)->getZone(lz->zone);
serializationSendToClient(messaging::client::s2c_update_zone_matrix_metadata,
voice::modulation::getVoiceMatrixMetadata(*z),
*(eng.getMessageController()));
eng.getSelectionManager()->sendDisplayDataForZonesBasedOnLead(
lz->part, lz->group, lz->zone);
}
}
else
Expand Down
13 changes: 13 additions & 0 deletions src/messaging/client/structure_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,19 @@ inline void doDeactivatePart(int part, messaging::MessageController &cont)
}
CLIENT_TO_SERIAL(DeactivatePart, c2s_deactivate_part, int32_t, doDeactivatePart(payload, cont));

inline void doRequestZoneDataRefresh(const engine::Engine &eng, messaging::MessageController &cont)
{
auto lz = eng.getSelectionManager()->currentLeadZone(eng);
if (lz.has_value())
{
eng.getSelectionManager()->sendDisplayDataForZonesBasedOnLead(lz->part, lz->group,
lz->zone);
}
}

CLIENT_TO_SERIAL(RequestZoneDataRefresh, c2s_request_zone_data_refresh, bool,
doRequestZoneDataRefresh(engine, cont));

} // namespace scxt::messaging::client

#endif // SHORTCIRCUIT_STRUCTURE_MESSAGES_H
13 changes: 13 additions & 0 deletions src/messaging/client/zone_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ CLIENT_TO_SERIAL_CONSTRAINED(UpdateZoneOutputInt16TValue, c2s_update_zone_output
detail::updateZoneMemberValue(&engine::Zone::outputInfo, payload,
engine, cont));

CLIENT_TO_SERIAL_CONSTRAINED(
UpdateZoneOutputInt16TValueThenRefresh, c2s_update_zone_output_int16_t_value_then_refresh,
detail::diffMsg_t<int16_t>, engine::Zone::ZoneOutputInfo,
detail::updateZoneMemberValue(
&engine::Zone::outputInfo, payload, engine, cont, [](auto const &eng) {
auto lz = eng.getSelectionManager()->currentLeadZone(eng);
if (lz.has_value())
{
eng.getSelectionManager()->sendDisplayDataForZonesBasedOnLead(lz->part, lz->group,
lz->zone);
}
}));

using addBlankZonePayload_t = std::array<int, 6>;
inline void doAddBlankZone(const addBlankZonePayload_t &payload, engine::Engine &engine,
MessageController &cont)
Expand Down
2 changes: 2 additions & 0 deletions src/modulation/modulator_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ struct ModulatorStorage
inline bool isMSEG() const { return modulatorShape == MSEG; }
inline bool isEnv() const { return modulatorShape == LFO_ENV; }
inline bool isCurve() const { return !isStep() && !isEnv() && !isMSEG(); }

bool modulatorConsistent{true};
};

inline double secondsToNormalizedEnvTime(double s)
Expand Down
45 changes: 42 additions & 3 deletions src/selection/selection_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,27 @@ void SelectionManager::sendDisplayDataForZonesBasedOnLead(int p, int g, int z)

for (int i = 0; i < engine::lfosPerZone; ++i)
{
auto rsh = zp->modulatorStorage[i].modulatorShape;
auto con = true;
if (allSelectedZones[selectedPart].size() > 1)
{
for (const auto &sz : allSelectedZones[selectedPart])
{
const auto &zsh = engine.getPatch()
->getPart(sz.part)
->getGroup(sz.group)
->getZone(sz.zone)
->modulatorStorage[i]
.modulatorShape;
if (zsh != rsh)
{
con = false;
break;
}
}
}

zp->modulatorStorage[i].modulatorConsistent = con;
serializationSendToClient(
cms::s2c_update_group_or_zone_individual_modulator_storage,
cms::indexedModulatorStorageUpdate_t{true, true, i, zp->modulatorStorage[i]},
Expand Down Expand Up @@ -549,9 +570,27 @@ void SelectionManager::sendDisplayDataForZonesBasedOnLead(int p, int g, int z)

configureAndSendZoneModMatrixMetadata(p, g, z);

serializationSendToClient(cms::s2c_update_zone_output_info,
cms::zoneOutputInfoUpdate_t{true, zp->outputInfo},
*(engine.getMessageController()));
// Update across selections here to see if the routing is consistent
auto rt = zp->outputInfo.procRouting;
auto con = true;
if (allSelectedZones[selectedPart].size() > 1)
{
for (const auto &sz : allSelectedZones[selectedPart])
{
const auto &zpr = engine.getPatch()
->getPart(sz.part)
->getGroup(sz.group)
->getZone(sz.zone)
->outputInfo.procRouting;

if (zpr != rt)
{
con = false;
break;
}
}
}
zp->outputInfo.procRoutingConsistent = con;

serializationSendToClient(cms::s2c_update_zone_output_info,
cms::zoneOutputInfoUpdate_t{true, zp->outputInfo},
Expand Down

0 comments on commit d6df8b3

Please sign in to comment.