Files
serumalt/Source/ModulationMatrix.cpp
T
biggy e2e66457c2 refactor(mod): validate modulation matrix and macro inputs
Add bounds, finiteness, and range clamping to addConnection and
addAssignment. Validate ValueTree types and property values in
fromValueTree before inserting, rejecting non-integer indices and
unknown source names. Run toValueTree through a validated copy to
ensure persisted state is always within constraints.
2026-09-09 14:33:06 +02:00

77 lines
2.5 KiB
C++

#include "ModulationMatrix.h"
namespace serum
{
bool ModulationMatrix::addConnection (ModSource source, ModTarget target, float depth, bool bipolar)
{
if ((int) source < 0 || (int) source >= kNumModSources
|| (int) target < 0 || (int) target >= kNumModTargets
|| ! std::isfinite (depth) || (int) connections.size() >= kMaxConnections)
return false;
connections.push_back ({ source, target, juce::jlimit (-1.0f, 1.0f, depth), bipolar });
return true;
}
void ModulationMatrix::removeConnection (int index)
{
if (index >= 0 && index < (int) connections.size())
connections.erase (connections.begin() + index);
}
void ModulationMatrix::removeAllWithTarget (ModTarget target)
{
connections.erase (std::remove_if (connections.begin(), connections.end(),
[target] (const ModConnection& c) { return c.target == target; }),
connections.end());
}
juce::ValueTree ModulationMatrix::toValueTree() const
{
ModulationMatrix validated;
for (const auto& c : connections)
{
if (validated.size() >= kMaxConnections)
break;
if (! validated.addConnection (c.source, c.target, c.depth, c.bipolar))
continue;
}
juce::ValueTree tree ("MODMATRIX");
for (const auto& c : validated.connections)
{
juce::ValueTree con ("CONNECTION");
con.setProperty ("source", modSourceToString (c.source), nullptr);
con.setProperty ("target", modTargetToString (c.target), nullptr);
con.setProperty ("depth", c.depth, nullptr);
con.setProperty ("bipolar", c.bipolar, nullptr);
tree.appendChild (con, nullptr);
}
return tree;
}
void ModulationMatrix::fromValueTree (const juce::ValueTree& tree)
{
connections.clear();
if (! tree.hasType ("MODMATRIX"))
return;
for (const auto& con : tree)
{
if ((int) connections.size() >= kMaxConnections)
break;
if (! con.hasType ("CONNECTION"))
continue;
const auto sourceName = con.getProperty ("source").toString();
const auto source = modSourceFromString (sourceName);
if (sourceName.isEmpty() || modSourceToString (source) != sourceName)
continue;
if (! addConnection (source,
modTargetFromString (con.getProperty ("target").toString()),
(float) con.getProperty ("depth", 0.0),
(bool) con.getProperty ("bipolar", false)))
continue;
}
}
} // namespace serum