Negative group IDs

This commit is contained in:
Paul Fd 2021-06-27 22:35:41 +02:00
parent 6b1a7cd259
commit c382b8894e
9 changed files with 53 additions and 31 deletions

View file

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

View file

@ -153,7 +153,7 @@ namespace Default
extern const OpcodeSpec<float> oscillatorModDepth;
extern const OpcodeSpec<float> oscillatorModDepthMod;
extern const OpcodeSpec<int32_t> oscillatorQuality;
extern const OpcodeSpec<uint32_t> group;
extern const OpcodeSpec<int64_t> group;
extern const OpcodeSpec<float> offTime;
extern const OpcodeSpec<uint32_t> polyphony;
extern const OpcodeSpec<uint32_t> notePolyphony;

View file

@ -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<float> out)
bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber)
{
const FlexEGDescription& desc = *desc_;
const MidiState& midiState = resources_->getMidiState();
currentStageNumber_ = stageNumber;
if (stageNumber >= desc.points.size())

View file

@ -327,8 +327,8 @@ struct Region {
absl::optional<int> oscillatorQuality;
// Instrument settings: voice lifecycle
uint32_t group { Default::group }; // group
absl::optional<uint32_t> offBy {}; // off_by
int64_t group { Default::group }; // group
absl::optional<int64_t> offBy {}; // off_by
OffMode offMode { Default::offMode }; // off_mode
float offTime { Default::offTime }; // off_mode
absl::optional<uint32_t> notePolyphony {}; // note_polyphony

View file

@ -19,7 +19,7 @@ void VoiceManager::onVoiceStateChanging(NumericId<Voice> 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<Voice> 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<size_t>(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();
}

View file

@ -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<Voice*> activeVoices_;
std::vector<Voice*> temp_;
// These are the `group=` groups where you can off voices
std::vector<PolyphonyGroup> polyphonyGroups_;
absl::flat_hash_map<int, PolyphonyGroup> polyphonyGroups_;
std::unique_ptr<VoiceStealer> stealer_ { absl::make_unique<OldestStealer>() };
/**

View file

@ -57,7 +57,7 @@ TEST_CASE("[Polyphony] Polyphony groups")
<group> group=4
<region> 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 );
}

View file

@ -426,7 +426,7 @@ TEST_CASE("[Values] Group")
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
<region> sample=kick.wav
<region> sample=kick.wav group=5
<region> sample=kick.wav group=-1
<region> 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<std::string> 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"(
<region> sample=kick.wav
<region> sample=kick.wav off_by=5
<region> sample=kick.wav off_by=-1
<region> 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<std::string> expected {
"/region0/off_by,N : { }",
"/region1/off_by,h : { 5 }",
"/region2/off_by,N : { }",
"/region2/off_by,h : { -2 }",
};
REQUIRE(messageList == expected);
}

View file

@ -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<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) };
synth.loadSfzString(fs::current_path(), R"(
<region> group=-1 off_by=-2 sample=*saw transpose=12 key=60
<region> 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;