diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 645f6168..4919f01c 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -142,15 +142,6 @@ void sfz::Synth::buildRegion(const std::vector& regionOpcodes) int regionNumber = static_cast(regions.size()); auto lastRegion = absl::make_unique(regionNumber, resources.midiState, defaultPath); - // Create default connections - constexpr unsigned defaultSmoothness = 10; - lastRegion->getOrCreateConnection( - ModKey::createCC(7, 4, defaultSmoothness, 0), - ModKey::createNXYZ(ModId::Amplitude, lastRegion->id)).sourceDepth = 100.0f; - lastRegion->getOrCreateConnection( - ModKey::createCC(10, 1, defaultSmoothness, 0), - ModKey::createNXYZ(ModId::Pan, lastRegion->id)).sourceDepth = 100.0f; - // auto parseOpcodes = [&](const std::vector& opcodes) { for (auto& opcode : opcodes) { @@ -626,6 +617,33 @@ void sfz::Synth::finalizeSfzLoad() } DBG("Removing " << (regions.size() - currentRegionCount) << " out of " << regions.size() << " regions"); regions.resize(currentRegionCount); + + // collect all CCs used in regions, with matrix not yet connected + std::bitset usedCCs; + for (const RegionPtr& regionPtr : regions) { + const Region& region = *regionPtr; + updateUsedCCsFromRegion(usedCCs, region); + for (const Region::Connection& connection : region.connections) { + if (connection.source.id() == ModId::Controller) + usedCCs.set(connection.source.parameters().cc); + } + } + // connect default controllers, except if these CC are already used + for (const RegionPtr& regionPtr : regions) { + Region& region = *regionPtr; + constexpr unsigned defaultSmoothness = 10; + if (!usedCCs.test(7)) { + region.getOrCreateConnection( + ModKey::createCC(7, 4, defaultSmoothness, 0), + ModKey::createNXYZ(ModId::Amplitude, region.id)).sourceDepth = 100.0f; + } + if (!usedCCs.test(10)) { + region.getOrCreateConnection( + ModKey::createCC(10, 1, defaultSmoothness, 0), + ModKey::createNXYZ(ModId::Pan, region.id)).sourceDepth = 100.0f; + } + } + modificationTime = checkModificationTime(); settingsPerVoice.maxFilters = maxFilters; diff --git a/tests/ModulationsT.cpp b/tests/ModulationsT.cpp index 9a617b48..d5dc49af 100644 --- a/tests/ModulationsT.cpp +++ b/tests/ModulationsT.cpp @@ -304,3 +304,33 @@ TEST_CASE("[Modulations] FlexEG Ampeg target with multiple EGs targeting ampeg") })); } +TEST_CASE("[Modulations] Override the default volume controller") +{ + sfz::Synth synth; + + synth.loadSfzString(fs::current_path(), R"( + sample=*sine tune_oncc7=1200 + )"); + + const std::string graph = synth.getResources().modMatrix.toDotGraph(); + REQUIRE(graph == createModulationDotGraph({ + R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")", + R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", + R"("Controller 7 {curve=0, smooth=0, step=0}" -> "Pitch {0}")", + })); +} + +TEST_CASE("[Modulations] Override the default pan controller") +{ + sfz::Synth synth; + + synth.loadSfzString(fs::current_path(), R"( + sample=*sine on_locc10=127 on_hicc10=127 + )"); + + const std::string graph = synth.getResources().modMatrix.toDotGraph(); + REQUIRE(graph == createModulationDotGraph({ + R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")", + R"("Controller 7 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", + })); +}