diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 40ce37c7..d70f3f83 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -41,7 +41,7 @@ FloatSpec oscillatorDetuneMod { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds } FloatSpec oscillatorModDepth { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec oscillatorModDepthMod { 0.0f, {0.0f, 10000.0f}, kNormalizePercent|kPermissiveBounds }; Int32Spec oscillatorQuality { 1, {0, 3}, 0 }; -UInt32Spec group { 0, {0, uint32_t_max}, 0 }; +Int64Spec group { 0, {-int32_t_max, uint32_t_max}, 0 }; FloatSpec offTime { 6e-3f, {0.0f, 100.0f}, kPermissiveBounds }; UInt32Spec polyphony { config::maxVoices, {0, config::maxVoices}, kEnforceBounds }; UInt32Spec notePolyphony { config::maxVoices, {1, config::maxVoices}, kEnforceBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 7d6c54fa..05fc5a37 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -153,7 +153,7 @@ namespace Default extern const OpcodeSpec oscillatorModDepth; extern const OpcodeSpec oscillatorModDepthMod; extern const OpcodeSpec oscillatorQuality; - extern const OpcodeSpec group; + extern const OpcodeSpec group; extern const OpcodeSpec offTime; extern const OpcodeSpec polyphony; extern const OpcodeSpec notePolyphony; diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 3cbd4161..39a387af 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -95,8 +95,6 @@ void FlexEnvelope::configure(const FlexEGDescription* desc) void FlexEnvelope::start(unsigned triggerDelay) { Impl& impl = *impl_; - const FlexEGDescription& desc = *impl.desc_; - impl.delayFramesLeft_ = triggerDelay; impl.currentFramesUntilRelease_ = absl::nullopt; impl.advanceToStage(0); @@ -116,6 +114,7 @@ void FlexEnvelope::release(unsigned releaseDelay) void FlexEnvelope::cancelRelease(unsigned delay) { + UNUSED(delay); Impl& impl = *impl_; const FlexEGDescription& desc = *impl.desc_; @@ -249,8 +248,6 @@ void FlexEnvelope::Impl::process(absl::Span out) bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber) { const FlexEGDescription& desc = *desc_; - const MidiState& midiState = resources_->getMidiState(); - currentStageNumber_ = stageNumber; if (stageNumber >= desc.points.size()) diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fe407065..95dfec54 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -327,8 +327,8 @@ struct Region { absl::optional oscillatorQuality; // Instrument settings: voice lifecycle - uint32_t group { Default::group }; // group - absl::optional offBy {}; // off_by + int64_t group { Default::group }; // group + absl::optional offBy {}; // off_by OffMode offMode { Default::offMode }; // off_mode float offTime { Default::offTime }; // off_mode absl::optional notePolyphony {}; // note_polyphony diff --git a/src/sfizz/VoiceManager.cpp b/src/sfizz/VoiceManager.cpp index 862626d7..7440f5cf 100644 --- a/src/sfizz/VoiceManager.cpp +++ b/src/sfizz/VoiceManager.cpp @@ -19,7 +19,7 @@ void VoiceManager::onVoiceStateChanging(NumericId id, Voice::State state) const uint32_t group = region->group; RegionSet::removeVoiceFromHierarchy(region, voice); swapAndPopFirst(activeVoices_, [voice](const Voice* v) { return v == voice; }); - ASSERT(group < polyphonyGroups_.size()); + ASSERT(polyphonyGroups_.contains(group)); polyphonyGroups_[group].removeVoice(voice); } else if (state == Voice::State::playing) { Voice* voice = getVoiceById(id); @@ -27,7 +27,7 @@ void VoiceManager::onVoiceStateChanging(NumericId id, Voice::State state) const uint32_t group = region->group; activeVoices_.push_back(voice); RegionSet::registerVoiceInHierarchy(region, voice); - ASSERT(group < polyphonyGroups_.size()); + ASSERT(polyphonyGroups_.contains(group)); polyphonyGroups_[group].registerVoice(voice); } } @@ -61,8 +61,7 @@ void VoiceManager::reset() voice.reset(); polyphonyGroups_.clear(); - polyphonyGroups_.emplace_back(); - polyphonyGroups_.back().setPolyphonyLimit(config::maxVoices); + polyphonyGroups_.emplace(0, PolyphonyGroup{}); setStealingAlgorithm(StealingAlgorithm::Oldest); } @@ -84,29 +83,31 @@ bool VoiceManager::playingAttackVoice(const Region* releaseRegion) noexcept return true; } -void VoiceManager::ensureNumPolyphonyGroups(unsigned groupIdx) noexcept +void VoiceManager::ensureNumPolyphonyGroups(int groupIdx) noexcept { - size_t neededSize = static_cast(groupIdx) + 1; - if (polyphonyGroups_.size() < neededSize) - polyphonyGroups_.resize(neededSize); + if (!polyphonyGroups_.contains(groupIdx)) + polyphonyGroups_.emplace(groupIdx, PolyphonyGroup{}); } -void VoiceManager::setGroupPolyphony(unsigned groupIdx, unsigned polyphony) noexcept +void VoiceManager::setGroupPolyphony(int groupIdx, unsigned polyphony) noexcept { ensureNumPolyphonyGroups(groupIdx); polyphonyGroups_[groupIdx].setPolyphonyLimit(polyphony); } -const PolyphonyGroup* VoiceManager::getPolyphonyGroupView(int idx) const noexcept +const PolyphonyGroup* VoiceManager::getPolyphonyGroupView(int idx) noexcept { - return (size_t)idx < polyphonyGroups_.size() ? &polyphonyGroups_[idx] : nullptr; + if (!polyphonyGroups_.contains(idx)) + return {}; + + return &polyphonyGroups_[idx]; } void VoiceManager::clear() { - for (PolyphonyGroup& pg : polyphonyGroups_) - pg.removeAllVoices(); + for (auto& pg : polyphonyGroups_) + pg.second.removeAllVoices(); list_.clear(); activeVoices_.clear(); } diff --git a/src/sfizz/VoiceManager.h b/src/sfizz/VoiceManager.h index b4e22d4d..785f271c 100644 --- a/src/sfizz/VoiceManager.h +++ b/src/sfizz/VoiceManager.h @@ -6,6 +6,7 @@ #pragma once +#include "absl/container/flat_hash_map.h" #include "Config.h" #include "PolyphonyGroup.h" #include "Region.h" @@ -59,7 +60,7 @@ struct VoiceManager final : public Voice::StateListener * * @param groupIdx */ - void ensureNumPolyphonyGroups(unsigned groupIdx) noexcept; + void ensureNumPolyphonyGroups(int groupIdx) noexcept; /** * @brief Set the polyphony for a given group @@ -69,7 +70,7 @@ struct VoiceManager final : public Voice::StateListener * @param groupIdx * @param polyphony */ - void setGroupPolyphony(unsigned groupIdx, unsigned polyphony) noexcept; + void setGroupPolyphony(int groupIdx, unsigned polyphony) noexcept; /** * @brief Get a view into a given polyphony group @@ -77,7 +78,7 @@ struct VoiceManager final : public Voice::StateListener * @param idx * @return const PolyphonyGroup* */ - const PolyphonyGroup* getPolyphonyGroupView(int idx) const noexcept; + const PolyphonyGroup* getPolyphonyGroupView(int idx) noexcept; /** * @brief Clear all voices and polyphony groups. @@ -138,7 +139,7 @@ private: std::vector activeVoices_; std::vector temp_; // These are the `group=` groups where you can off voices - std::vector polyphonyGroups_; + absl::flat_hash_map polyphonyGroups_; std::unique_ptr stealer_ { absl::make_unique() }; /** diff --git a/tests/PolyphonyT.cpp b/tests/PolyphonyT.cpp index 0a966da9..f07e6c91 100644 --- a/tests/PolyphonyT.cpp +++ b/tests/PolyphonyT.cpp @@ -57,7 +57,7 @@ TEST_CASE("[Polyphony] Polyphony groups") group=4 key=62 sample=*sine )"); - REQUIRE( synth.getNumPolyphonyGroups() == 5 ); + REQUIRE( synth.getNumPolyphonyGroups() == 4 ); REQUIRE( synth.getNumRegions() == 5 ); REQUIRE( synth.getRegionView(0)->group == 0 ); REQUIRE( synth.getRegionView(1)->group == 1 ); @@ -67,7 +67,7 @@ TEST_CASE("[Polyphony] Polyphony groups") REQUIRE( synth.getRegionView(4)->group == 4 ); REQUIRE( synth.getPolyphonyGroupView(1)->getPolyphonyLimit() == 3 ); REQUIRE( synth.getPolyphonyGroupView(2)->getPolyphonyLimit() == 4 ); - REQUIRE( synth.getPolyphonyGroupView(3)->getPolyphonyLimit() == sfz::config::maxVoices ); + REQUIRE( !synth.getPolyphonyGroupView(3) ); REQUIRE( synth.getPolyphonyGroupView(4)->getPolyphonyLimit() == 5 ); } diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 970a259e..633c6afe 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -426,7 +426,7 @@ TEST_CASE("[Values] Group") synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( sample=kick.wav sample=kick.wav group=5 - sample=kick.wav group=-1 + sample=kick.wav group=-2 )"); synth.dispatchMessage(client, 0, "/region0/group", "", nullptr); synth.dispatchMessage(client, 0, "/region1/group", "", nullptr); @@ -434,7 +434,7 @@ TEST_CASE("[Values] Group") std::vector expected { "/region0/group,h : { 0 }", "/region1/group,h : { 5 }", - "/region2/group,h : { 0 }", + "/region2/group,h : { -2 }", }; REQUIRE(messageList == expected); } @@ -448,7 +448,7 @@ TEST_CASE("[Values] Off by") synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( sample=kick.wav sample=kick.wav off_by=5 - sample=kick.wav off_by=-1 + sample=kick.wav off_by=-2 )"); synth.dispatchMessage(client, 0, "/region0/off_by", "", nullptr); synth.dispatchMessage(client, 0, "/region1/off_by", "", nullptr); @@ -456,7 +456,7 @@ TEST_CASE("[Values] Off by") std::vector expected { "/region0/off_by,N : { }", "/region1/off_by,h : { 5 }", - "/region2/off_by,N : { }", + "/region2/off_by,h : { -2 }", }; REQUIRE(messageList == expected); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 639acefa..ad099fde 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -1595,6 +1595,29 @@ TEST_CASE("[Synth] Off by standard") REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(60) ); } +TEST_CASE("[Synth] Off by negative groups") +{ + sfz::Synth synth; + sfz::AudioBuffer buffer { 2, static_cast(synth.getSamplesPerBlock()) }; + + synth.loadSfzString(fs::current_path(), R"( + group=-1 off_by=-2 sample=*saw transpose=12 key=60 + group=-2 off_by=-1 sample=*triangle key=62 + )"); + synth.noteOn(0, 60, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + synth.noteOn(10, 62, 85); + synth.renderBlock(buffer); + REQUIRE( numPlayingVoices(synth) == 1 ); + auto playingVoices = getPlayingVoices(synth); + REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(62) ); + synth.noteOn(10, 60, 85); + synth.renderBlock(buffer); + playingVoices = getPlayingVoices(synth); + REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(60) ); +} + TEST_CASE("[Synth] Off by same group") { sfz::Synth synth;