diff --git a/src/sfizz/Layer.cpp b/src/sfizz/Layer.cpp index 146d2181..a90261da 100644 --- a/src/sfizz/Layer.cpp +++ b/src/sfizz/Layer.cpp @@ -129,7 +129,7 @@ bool Layer::registerNoteOff(int noteNumber, float velocity, float randValue) noe return false; } -bool Layer::registerCC(int ccNumber, float ccValue) noexcept +bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept { const Region& region = region_; @@ -152,14 +152,16 @@ bool Layer::registerCC(int ccNumber, float ccValue) noexcept else ccSwitched_.set(ccNumber, false); - if (!isSwitchedOn()) - return false; - - if (!region.triggerOnCC) + if (dontTrigger || !region.triggerOnCC) return false; if (auto triggerRange = region.ccTriggers.get(ccNumber)) { - if (triggerRange->containsWithEnd(ccValue)) + if (!triggerRange->containsWithEnd(ccValue)) + return false; + + sequenceSwitched_ = + ((sequenceCounter_++ % region.sequenceLength) == region.sequencePosition - 1); + if (isSwitchedOn()) return true; } diff --git a/src/sfizz/Layer.h b/src/sfizz/Layer.h index ede1490a..ec7a7806 100644 --- a/src/sfizz/Layer.h +++ b/src/sfizz/Layer.h @@ -88,7 +88,7 @@ public: * @return true if the region should trigger on this event * @return false */ - bool registerCC(int ccNumber, float ccValue) noexcept; + bool registerCC(int ccNumber, float ccValue, bool dontTrigger = false) noexcept; /** * @brief Register a new pitch wheel event. * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 1e200b58..7a993090 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -581,6 +581,8 @@ void Synth::Impl::finalizeSfzLoad() size_t currentRegionIndex = 0; size_t currentRegionCount = layers_.size(); + absl::flat_hash_map filesToLoad; + auto removeCurrentRegion = [this, ¤tRegionIndex, ¤tRegionCount]() { const Region& region = layers_[currentRegionIndex]->getRegion(); DBG("Removing the region with sample " << *region.sampleId); @@ -679,10 +681,8 @@ void Synth::Impl::finalizeSfzLoad() return Default::offsetMod.bounds.clamp(sumOffsetCC); }(); - if (!filePool.preloadFile(*region.sampleId, maxOffset)) { - removeCurrentRegion(); - continue; - } + auto& toLoad = filesToLoad[*region.sampleId]; + toLoad = max(toLoad, maxOffset); } else if (!region.isGenerator()) { if (!wavePool.createFileWave(filePool, std::string(region.sampleId->filename()))) { @@ -726,7 +726,7 @@ void Synth::Impl::finalizeSfzLoad() // Defaults MidiState& midiState = resources_.getMidiState(); for (int cc = 0; cc < config::numCCs; cc++) { - layer.registerCC(cc, midiState.getCCValue(cc)); + layer.registerCC(cc, midiState.getCCValue(cc), true); } @@ -763,6 +763,11 @@ void Synth::Impl::finalizeSfzLoad() ++currentRegionIndex; } + + for (const auto& toLoad: filesToLoad) { + filePool.preloadFile(toLoad.first, toLoad.second); + } + if (currentRegionCount < layers_.size()) { DBG("Removing " << (layers_.size() - currentRegionCount) << " out of " << layers_.size() << " regions"); @@ -1946,7 +1951,7 @@ void Synth::Impl::resetAllControllers(int delay) noexcept for (const LayerPtr& layerPtr : layers_) { Layer& layer = *layerPtr; for (int cc = 0; cc < config::numCCs; ++cc) - layer.registerCC(cc, defaultCCValues_[cc]); + layer.registerCC(cc, defaultCCValues_[cc], true); } } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index ad099fde..03d3c4eb 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1983,3 +1983,29 @@ TEST_CASE("[Synth] Resets all controllers to default values") REQUIRE( synth.getHdcc(56) == 64_norm ); REQUIRE( synth.getHdcc(78) == 0.0f ); } + +TEST_CASE("[Synth] Sequences also work on cc triggers") +{ + sfz::Synth synth; + std::vector messageList; + sfz::Client client(&messageList); + client.setReceiveCallback(&simpleMessageReceiver); + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sequence_cc_triggers.sfz", R"( + seq_length=3 + sample=*sine hikey=-1 start_locc61=0 start_hicc61=64 seq_position=1 + sample=*saw hikey=-1 start_locc61=0 start_hicc61=64 seq_position=2 + )"); + synth.cc(0, 61, 10); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine" } ); + synth.cc(0, 61, 20); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw" } ); + synth.cc(0, 61, 20); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw" } ); + synth.cc(0, 61, 20); + synth.renderBlock(buffer); + REQUIRE( playingSamples(synth) == std::vector { "*sine", "*saw", "*sine" } ); +}