Negative group IDs
This commit is contained in:
parent
6b1a7cd259
commit
c382b8894e
9 changed files with 53 additions and 31 deletions
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>() };
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue