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
|
static constexpr int loopXfadeCurve = 2; // 0: linear
|
||||||
// 1: use curves 5 & 6
|
// 1: use curves 5 & 6
|
||||||
// 2: use S-shaped curve
|
// 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 config
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
|
|
@ -53,3 +53,8 @@ unsigned sfz::RegionSet::numPlayingVoices() const noexcept
|
||||||
return !v->releasedOrFree();
|
return !v->releasedOrFree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::removeAllVoices() noexcept
|
||||||
|
{
|
||||||
|
voices.clear();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,11 @@ public:
|
||||||
* @return const std::vector<RegionSet*>&
|
* @return const std::vector<RegionSet*>&
|
||||||
*/
|
*/
|
||||||
const std::vector<RegionSet*>& getSubsets() const noexcept { return subsets; }
|
const std::vector<RegionSet*>& getSubsets() const noexcept { return subsets; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove all voices from the set
|
||||||
|
*/
|
||||||
|
void removeAllVoices() noexcept;
|
||||||
private:
|
private:
|
||||||
RegionSet* parent { nullptr };
|
RegionSet* parent { nullptr };
|
||||||
OpcodeScope level { kOpcodeScopeGeneric };
|
OpcodeScope level { kOpcodeScopeGeneric };
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ sfz::Synth::Synth(int numVoices)
|
||||||
initializeSIMDDispatchers();
|
initializeSIMDDispatchers();
|
||||||
|
|
||||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
engineSet = absl::make_unique<RegionSet>(nullptr, OpcodeScope::kOpcodeScopeGeneric);
|
||||||
parser.setListener(this);
|
parser.setListener(this);
|
||||||
effectFactory.registerStandardEffectTypes();
|
effectFactory.registerStandardEffectTypes();
|
||||||
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
||||||
|
|
@ -387,6 +388,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
||||||
default:
|
default:
|
||||||
DBG("Unsupported value for hint_stealing: " << member.value);
|
DBG("Unsupported value for hint_stealing: " << member.value);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Unsupported control opcode
|
// Unsupported control opcode
|
||||||
DBG("Unsupported control opcode: " << member.opcode);
|
DBG("Unsupported control opcode: " << member.opcode);
|
||||||
|
|
@ -742,23 +744,8 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
|
||||||
if (freeVoice != voices.end())
|
if (freeVoice != voices.end())
|
||||||
return freeVoice->get();
|
return freeVoice->get();
|
||||||
|
|
||||||
// Engine polyphony reached
|
DBG("Engine polyphony reached");
|
||||||
Voice* stolenVoice = stealer.steal(absl::MakeSpan(voiceViewArray));
|
return {};
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sfz::Synth::getNumActiveVoices(bool recompute) const noexcept
|
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
|
int sfz::Synth::getNumVoices() const noexcept
|
||||||
{
|
{
|
||||||
return numVoices;
|
return numRequiredVoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
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 };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if (numVoices == this->numVoices)
|
if (numVoices == this->numRequiredVoices)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
resetVoices(numVoices);
|
resetVoices(numVoices);
|
||||||
|
|
@ -1528,16 +1515,25 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||||
|
|
||||||
void sfz::Synth::resetVoices(int numVoices)
|
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.clear();
|
||||||
voices.reserve(numVoices);
|
voices.reserve(numActualVoices);
|
||||||
|
|
||||||
voiceViewArray.clear();
|
voiceViewArray.clear();
|
||||||
voiceViewArray.reserve(numVoices);
|
voiceViewArray.reserve(numActualVoices);
|
||||||
|
|
||||||
tempPolyphonyArray.clear();
|
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);
|
auto voice = absl::make_unique<Voice>(i, resources);
|
||||||
voice->setStateListener(this);
|
voice->setStateListener(this);
|
||||||
voiceViewArray.push_back(voice.get());
|
voiceViewArray.push_back(voice.get());
|
||||||
|
|
@ -1549,8 +1545,6 @@ void sfz::Synth::resetVoices(int numVoices)
|
||||||
voice->setSamplesPerBlock(this->samplesPerBlock);
|
voice->setSamplesPerBlock(this->samplesPerBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->numVoices = numVoices;
|
|
||||||
|
|
||||||
applySettingsPerVoice();
|
applySettingsPerVoice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -819,6 +819,9 @@ private:
|
||||||
// These are more general "groups" than sfz and encapsulates the full hierarchy
|
// These are more general "groups" than sfz and encapsulates the full hierarchy
|
||||||
RegionSet* currentSet { nullptr };
|
RegionSet* currentSet { nullptr };
|
||||||
std::vector<RegionSetPtr> sets;
|
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
|
// These are the `group=` groups where you can off voices
|
||||||
std::vector<PolyphonyGroup> polyphonyGroups;
|
std::vector<PolyphonyGroup> polyphonyGroups;
|
||||||
|
|
@ -902,7 +905,8 @@ private:
|
||||||
int samplesPerBlock { config::defaultSamplesPerBlock };
|
int samplesPerBlock { config::defaultSamplesPerBlock };
|
||||||
float sampleRate { config::defaultSampleRate };
|
float sampleRate { config::defaultSampleRate };
|
||||||
float volume { Default::globalVolume };
|
float volume { Default::globalVolume };
|
||||||
int numVoices { config::numVoices };
|
int numRequiredVoices { config::numVoices };
|
||||||
|
int numActualVoices { static_cast<int>(config::numVoices * config::overflowVoiceMultiplier) };
|
||||||
int activeVoices { 0 };
|
int activeVoices { 0 };
|
||||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue