Check all used CCs and output them in the MIDNAM file if they have no name

This commit is contained in:
Paul Ferrand 2020-07-19 19:36:27 +02:00 committed by Jean Pierre Cimalando
parent 75b068d6e9
commit e660b80042
5 changed files with 229 additions and 0 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -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_;

View file

@ -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] );
}