Check all used CCs and output them in the MIDNAM file if they have no name
This commit is contained in:
parent
75b068d6e9
commit
e660b80042
5 changed files with 229 additions and 0 deletions
|
|
@ -1195,14 +1195,26 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
}
|
||||
|
||||
{
|
||||
auto anonymousCCs = getUsedCCs();
|
||||
|
||||
pugi::xml_node cns = device.append_child("ControlNameList");
|
||||
cns.append_attribute("Name").set_value("Controls");
|
||||
for (const auto& pair : ccLabels) {
|
||||
anonymousCCs.set(pair.first, false);
|
||||
pugi::xml_node cn = cns.append_child("Control");
|
||||
cn.append_attribute("Type").set_value("7bit");
|
||||
cn.append_attribute("Number").set_value(std::to_string(pair.first).c_str());
|
||||
cn.append_attribute("Name").set_value(pair.second.c_str());
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < anonymousCCs.size(); ++i) {
|
||||
if (anonymousCCs[i]) {
|
||||
pugi::xml_node cn = cns.append_child("Control");
|
||||
cn.append_attribute("Type").set_value("7bit");
|
||||
cn.append_attribute("Number").set_value(std::to_string(i).c_str());
|
||||
cn.append_attribute("Name").set_value(("Unnamed CC " + std::to_string(i)).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -1581,3 +1593,64 @@ void sfz::Synth::setGroupPolyphony(unsigned groupIdx, unsigned polyphony) noexce
|
|||
|
||||
polyphonyGroups[groupIdx].setPolyphonyLimit(polyphony);
|
||||
}
|
||||
|
||||
std::bitset<sfz::config::numCCs> sfz::Synth::getUsedCCs() const noexcept
|
||||
{
|
||||
std::bitset<sfz::config::numCCs> used;
|
||||
for (const RegionPtr& region : regions)
|
||||
updateUsedCCsFromRegion(used, *region);
|
||||
updateUsedCCsFromModulations(used, resources.modMatrix);
|
||||
return used;
|
||||
}
|
||||
|
||||
void sfz::Synth::updateUsedCCsFromRegion(std::bitset<sfz::config::numCCs>& usedCCs, const Region& region)
|
||||
{
|
||||
updateUsedCCsFromCCMap(usedCCs, region.offsetCC);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccAttack);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccRelease);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccDecay);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccDelay);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccHold);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccStart);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccSustain);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccAttack);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccRelease);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccDecay);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccDelay);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccHold);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccStart);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.pitchEG.ccSustain);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccAttack);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccRelease);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccDecay);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccDelay);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccHold);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccStart);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.filterEG.ccSustain);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.ccConditions);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.ccTriggers);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.crossfadeCCInRange);
|
||||
updateUsedCCsFromCCMap(usedCCs, region.crossfadeCCOutRange);
|
||||
}
|
||||
|
||||
void sfz::Synth::updateUsedCCsFromModulations(std::bitset<sfz::config::numCCs>& usedCCs, const ModMatrix& mm)
|
||||
{
|
||||
class CCSourceCollector : public ModMatrix::KeyVisitor {
|
||||
public:
|
||||
explicit CCSourceCollector(std::bitset<sfz::config::numCCs>& used)
|
||||
: used_(used)
|
||||
{
|
||||
}
|
||||
|
||||
bool visit(const ModKey& key) override
|
||||
{
|
||||
if (key.id() == ModId::Controller)
|
||||
used_.set(key.parameters().cc);
|
||||
return true;
|
||||
}
|
||||
std::bitset<sfz::config::numCCs>& used_;
|
||||
};
|
||||
|
||||
CCSourceCollector vtor(usedCCs);
|
||||
mm.visitSources(vtor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -575,6 +575,13 @@ public:
|
|||
*/
|
||||
const std::vector<CCNamePair>& getCCLabels() const noexcept { return ccLabels; }
|
||||
|
||||
/**
|
||||
* @brief Get the used CCs
|
||||
*
|
||||
* @return const std::bitset<config::numCCs>&
|
||||
*/
|
||||
std::bitset<config::numCCs> getUsedCCs() const noexcept;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The voice callback which is called during a change of state.
|
||||
|
|
@ -697,6 +704,15 @@ private:
|
|||
void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept;
|
||||
void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept;
|
||||
|
||||
template<class T>
|
||||
static void updateUsedCCsFromCCMap(std::bitset<sfz::config::numCCs>& usedCCs, const CCMap<T> map)
|
||||
{
|
||||
for (auto& mod : map)
|
||||
usedCCs[mod.cc] = true;
|
||||
}
|
||||
static void updateUsedCCsFromRegion(std::bitset<sfz::config::numCCs>& usedCCs, const Region& region);
|
||||
static void updateUsedCCsFromModulations(std::bitset<sfz::config::numCCs>& usedCCs, const ModMatrix& mm);
|
||||
|
||||
// Opcode memory; these are used to build regions, as a new region
|
||||
// will integrate opcodes from the group, master and global block
|
||||
std::vector<Opcode> globalOpcodes;
|
||||
|
|
|
|||
|
|
@ -428,4 +428,28 @@ std::string ModMatrix::toDotGraph() const
|
|||
return dot;
|
||||
}
|
||||
|
||||
bool ModMatrix::visitSources(KeyVisitor& vtor) const
|
||||
{
|
||||
const Impl& impl = *impl_;
|
||||
|
||||
for (const Impl::Source& item : impl.sources_) {
|
||||
if (!vtor.visit(item.key))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModMatrix::visitTargets(KeyVisitor& vtor) const
|
||||
{
|
||||
const Impl& impl = *impl_;
|
||||
|
||||
for (const Impl::Target& item : impl.targets_) {
|
||||
if (!vtor.visit(item.key))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace sfz
|
||||
|
|
|
|||
|
|
@ -173,6 +173,34 @@ public:
|
|||
*/
|
||||
std::string toDotGraph() const;
|
||||
|
||||
class KeyVisitor {
|
||||
public:
|
||||
virtual ~KeyVisitor() {}
|
||||
/**
|
||||
* @brief Visit a key of the modulation matrix.
|
||||
*
|
||||
* @param key
|
||||
* @return true to continue visiting, false to stop
|
||||
*/
|
||||
virtual bool visit(const ModKey& key) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Visit the keys of all the sources in the matrix.
|
||||
*
|
||||
* @param vtor a visitor object
|
||||
* @return last return code from the visitor
|
||||
*/
|
||||
bool visitSources(KeyVisitor& vtor) const;
|
||||
|
||||
/**
|
||||
* @brief Visit the keys of all the sources in the matrix.
|
||||
*
|
||||
* @param vtor a visitor object
|
||||
* @return last return code from the visitor
|
||||
*/
|
||||
bool visitTargets(KeyVisitor& vtor) const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
|
|
|||
|
|
@ -981,3 +981,91 @@ TEST_CASE("[Synth] sw_default works at a group level")
|
|||
synth.noteOn(0, 62, 85);
|
||||
REQUIRE( synth.getNumActiveVoices(true) == 1 );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Used CCs")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
REQUIRE( !synth.getUsedCCs().any() );
|
||||
synth.loadSfzString(fs::current_path(), R"(
|
||||
<global> amplitude_cc1=100
|
||||
<group> volume_oncc2=5
|
||||
<region> locc4=64 hicc67=32 pan_cc5=200 sample=*sine
|
||||
<region> width_cc98=200 sample=*sine
|
||||
<region> position_cc42=200 pitch_oncc56=200 sample=*sine
|
||||
<region> start_locc44=200 hikey=-1 sample=*sine
|
||||
)");
|
||||
auto usedCCs = synth.getUsedCCs();
|
||||
REQUIRE( usedCCs[1] );
|
||||
REQUIRE( usedCCs[2] );
|
||||
REQUIRE( !usedCCs[3] );
|
||||
REQUIRE( usedCCs[4] );
|
||||
REQUIRE( usedCCs[5] );
|
||||
REQUIRE( !usedCCs[6] );
|
||||
REQUIRE( usedCCs[42] );
|
||||
REQUIRE( usedCCs[44] );
|
||||
REQUIRE( usedCCs[56] );
|
||||
REQUIRE( usedCCs[67] );
|
||||
REQUIRE( usedCCs[98] );
|
||||
REQUIRE( !usedCCs[127] );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Used CCs EGs")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
REQUIRE( !synth.getUsedCCs().any() );
|
||||
synth.loadSfzString(fs::current_path(), R"(
|
||||
<region>
|
||||
ampeg_attack_oncc1=1
|
||||
ampeg_sustain_oncc2=2
|
||||
ampeg_start_oncc3=3
|
||||
ampeg_hold_oncc4=4
|
||||
ampeg_decay_oncc5=5
|
||||
ampeg_delay_oncc6=6
|
||||
ampeg_release_oncc7=7
|
||||
sample=*sine
|
||||
<region>
|
||||
pitcheg_attack_oncc11=11
|
||||
pitcheg_sustain_oncc12=12
|
||||
pitcheg_start_oncc13=13
|
||||
pitcheg_hold_oncc14=14
|
||||
pitcheg_decay_oncc15=15
|
||||
pitcheg_delay_oncc16=16
|
||||
pitcheg_release_oncc17=17
|
||||
sample=*sine
|
||||
<region>
|
||||
fileg_attack_oncc21=21
|
||||
fileg_sustain_oncc22=22
|
||||
fileg_start_oncc23=23
|
||||
fileg_hold_oncc24=24
|
||||
fileg_decay_oncc25=25
|
||||
fileg_delay_oncc26=26
|
||||
fileg_release_oncc27=27
|
||||
sample=*sine
|
||||
)");
|
||||
auto usedCCs = synth.getUsedCCs();
|
||||
REQUIRE( usedCCs[1] );
|
||||
REQUIRE( usedCCs[2] );
|
||||
REQUIRE( usedCCs[3] );
|
||||
REQUIRE( usedCCs[4] );
|
||||
REQUIRE( usedCCs[5] );
|
||||
REQUIRE( usedCCs[6] );
|
||||
REQUIRE( usedCCs[7] );
|
||||
// FIXME: enable when supported
|
||||
// REQUIRE( !usedCCs[8] );
|
||||
// REQUIRE( usedCCs[11] );
|
||||
// REQUIRE( usedCCs[12] );
|
||||
// REQUIRE( usedCCs[13] );
|
||||
// REQUIRE( usedCCs[14] );
|
||||
// REQUIRE( usedCCs[15] );
|
||||
// REQUIRE( usedCCs[16] );
|
||||
// REQUIRE( usedCCs[17] );
|
||||
// REQUIRE( !usedCCs[18] );
|
||||
// REQUIRE( usedCCs[21] );
|
||||
// REQUIRE( usedCCs[22] );
|
||||
// REQUIRE( usedCCs[23] );
|
||||
// REQUIRE( usedCCs[24] );
|
||||
// REQUIRE( usedCCs[25] );
|
||||
// REQUIRE( usedCCs[26] );
|
||||
// REQUIRE( usedCCs[27] );
|
||||
// REQUIRE( !usedCCs[28] );
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue