Add more voices than necessary
These will naturally be used for "dying" voices beyond the engine polyphony
This commit is contained in:
parent
4e13493ab4
commit
8071cd0131
5 changed files with 41 additions and 26 deletions
|
|
@ -131,6 +131,13 @@ namespace config {
|
|||
static constexpr int loopXfadeCurve = 2; // 0: linear
|
||||
// 1: use curves 5 & 6
|
||||
// 2: use S-shaped curve
|
||||
/**
|
||||
* @brief Overflow voices in the engine, relative to the required voices.
|
||||
* These are additional voices that more or less hold the "dying" voices
|
||||
* due to engine polyphony being reached.
|
||||
*/
|
||||
static constexpr float overflowVoiceMultiplier { 1.5f };
|
||||
static_assert(overflowVoiceMultiplier >= 1.0f);
|
||||
} // namespace config
|
||||
|
||||
} // namespace sfz
|
||||
|
|
|
|||
|
|
@ -53,3 +53,8 @@ unsigned sfz::RegionSet::numPlayingVoices() const noexcept
|
|||
return !v->releasedOrFree();
|
||||
});
|
||||
}
|
||||
|
||||
void sfz::RegionSet::removeAllVoices() noexcept
|
||||
{
|
||||
voices.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,11 @@ public:
|
|||
* @return const std::vector<RegionSet*>&
|
||||
*/
|
||||
const std::vector<RegionSet*>& getSubsets() const noexcept { return subsets; }
|
||||
|
||||
/**
|
||||
* @brief Remove all voices from the set
|
||||
*/
|
||||
void removeAllVoices() noexcept;
|
||||
private:
|
||||
RegionSet* parent { nullptr };
|
||||
OpcodeScope level { kOpcodeScopeGeneric };
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ sfz::Synth::Synth(int numVoices)
|
|||
initializeSIMDDispatchers();
|
||||
|
||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
engineSet = absl::make_unique<RegionSet>(nullptr, OpcodeScope::kOpcodeScopeGeneric);
|
||||
parser.setListener(this);
|
||||
effectFactory.registerStandardEffectTypes();
|
||||
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
||||
|
|
@ -387,6 +388,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
|||
default:
|
||||
DBG("Unsupported value for hint_stealing: " << member.value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Unsupported control opcode
|
||||
DBG("Unsupported control opcode: " << member.opcode);
|
||||
|
|
@ -742,23 +744,8 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
|
|||
if (freeVoice != voices.end())
|
||||
return freeVoice->get();
|
||||
|
||||
// Engine polyphony reached
|
||||
Voice* stolenVoice = stealer.steal(absl::MakeSpan(voiceViewArray));
|
||||
if (stolenVoice == nullptr)
|
||||
return {};
|
||||
|
||||
// Never kill age 0 voices
|
||||
if (stolenVoice->getAge() == 0)
|
||||
return {};
|
||||
|
||||
|
||||
auto tempSpan = resources.bufferPool.getStereoBuffer(samplesPerBlock);
|
||||
SisterVoiceRing::applyToRing(stolenVoice, [&] (Voice* v) {
|
||||
renderVoiceToOutputs(*v, *tempSpan);
|
||||
v->reset();
|
||||
});
|
||||
|
||||
return stolenVoice;
|
||||
DBG("Engine polyphony reached");
|
||||
return {};
|
||||
}
|
||||
|
||||
int sfz::Synth::getNumActiveVoices(bool recompute) const noexcept
|
||||
|
|
@ -1511,7 +1498,7 @@ void sfz::Synth::setVolume(float volume) noexcept
|
|||
|
||||
int sfz::Synth::getNumVoices() const noexcept
|
||||
{
|
||||
return numVoices;
|
||||
return numRequiredVoices;
|
||||
}
|
||||
|
||||
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||
|
|
@ -1520,7 +1507,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
|||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||
|
||||
// fast path
|
||||
if (numVoices == this->numVoices)
|
||||
if (numVoices == this->numRequiredVoices)
|
||||
return;
|
||||
|
||||
resetVoices(numVoices);
|
||||
|
|
@ -1528,16 +1515,25 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
|||
|
||||
void sfz::Synth::resetVoices(int numVoices)
|
||||
{
|
||||
numActualVoices =
|
||||
static_cast<int>(config::overflowVoiceMultiplier * numVoices);
|
||||
numRequiredVoices = numVoices;
|
||||
|
||||
for (auto& set : sets)
|
||||
set->removeAllVoices();
|
||||
engineSet->removeAllVoices();
|
||||
engineSet->setPolyphonyLimit(numRequiredVoices);
|
||||
|
||||
voices.clear();
|
||||
voices.reserve(numVoices);
|
||||
voices.reserve(numActualVoices);
|
||||
|
||||
voiceViewArray.clear();
|
||||
voiceViewArray.reserve(numVoices);
|
||||
voiceViewArray.reserve(numActualVoices);
|
||||
|
||||
tempPolyphonyArray.clear();
|
||||
tempPolyphonyArray.reserve(numVoices);
|
||||
tempPolyphonyArray.reserve(numActualVoices);
|
||||
|
||||
for (int i = 0; i < numVoices; ++i) {
|
||||
for (int i = 0; i < numActualVoices; ++i) {
|
||||
auto voice = absl::make_unique<Voice>(i, resources);
|
||||
voice->setStateListener(this);
|
||||
voiceViewArray.push_back(voice.get());
|
||||
|
|
@ -1549,8 +1545,6 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
voice->setSamplesPerBlock(this->samplesPerBlock);
|
||||
}
|
||||
|
||||
this->numVoices = numVoices;
|
||||
|
||||
applySettingsPerVoice();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -819,6 +819,9 @@ private:
|
|||
// These are more general "groups" than sfz and encapsulates the full hierarchy
|
||||
RegionSet* currentSet { nullptr };
|
||||
std::vector<RegionSetPtr> sets;
|
||||
// This region set holds the engine set of voices, which tries to respect the required
|
||||
// engine polyphony
|
||||
RegionSetPtr engineSet;
|
||||
|
||||
// These are the `group=` groups where you can off voices
|
||||
std::vector<PolyphonyGroup> polyphonyGroups;
|
||||
|
|
@ -902,7 +905,8 @@ private:
|
|||
int samplesPerBlock { config::defaultSamplesPerBlock };
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
float volume { Default::globalVolume };
|
||||
int numVoices { config::numVoices };
|
||||
int numRequiredVoices { config::numVoices };
|
||||
int numActualVoices { static_cast<int>(config::numVoices * config::overflowVoiceMultiplier) };
|
||||
int activeVoices { 0 };
|
||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue