Add comments and a helper function to test ring validity
This commit is contained in:
parent
1e6cb8245e
commit
b8a23ee205
3 changed files with 72 additions and 2 deletions
|
|
@ -12,6 +12,14 @@ namespace sfz
|
||||||
{
|
{
|
||||||
|
|
||||||
struct SisterVoiceRing {
|
struct SisterVoiceRing {
|
||||||
|
/**
|
||||||
|
* @brief Apply a lambda function to all sisters in a ring.
|
||||||
|
* This function should be robust enough to be able to kill the voice
|
||||||
|
* in the lambda.
|
||||||
|
*
|
||||||
|
* @param voice
|
||||||
|
* @param lambda
|
||||||
|
*/
|
||||||
template<class F, class T,
|
template<class F, class T,
|
||||||
absl::enable_if_t<std::is_same<Voice, absl::remove_const_t<T>>::value, int> = 0>
|
absl::enable_if_t<std::is_same<Voice, absl::remove_const_t<T>>::value, int> = 0>
|
||||||
static void applyToRing(T* voice, F&& lambda) noexcept
|
static void applyToRing(T* voice, F&& lambda) noexcept
|
||||||
|
|
@ -25,6 +33,12 @@ struct SisterVoiceRing {
|
||||||
lambda(voice);
|
lambda(voice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count the number of sister voices in a ring
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* @return unsigned
|
||||||
|
*/
|
||||||
static unsigned countSisterVoices(const Voice* start) noexcept
|
static unsigned countSisterVoices(const Voice* start) noexcept
|
||||||
{
|
{
|
||||||
if (!start)
|
if (!start)
|
||||||
|
|
@ -41,6 +55,54 @@ struct SisterVoiceRing {
|
||||||
ASSERT(count < config::maxVoices);
|
ASSERT(count < config::maxVoices);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a sister voice ring is well formed
|
||||||
|
*
|
||||||
|
* @param start
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
static bool checkRingValidity(const Voice* start) noexcept
|
||||||
|
{
|
||||||
|
if (start == nullptr)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
unsigned idx { 0 };
|
||||||
|
const Voice* ring[config::maxVoices];
|
||||||
|
ring[idx] = start;
|
||||||
|
while (idx < config::maxVoices) {
|
||||||
|
const auto* newVoice = ring[idx]->getNextSisterVoice();
|
||||||
|
|
||||||
|
if (newVoice == nullptr) {
|
||||||
|
DBG("Error in ring: " << static_cast<const void*>(ring[idx])
|
||||||
|
<< " next sister is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newVoice->getPreviousSisterVoice() != ring[idx]) {
|
||||||
|
DBG("Error in ring: " << static_cast<const void*>(newVoice)
|
||||||
|
<< " refers " << static_cast<const void*>(newVoice->getPreviousSisterVoice())
|
||||||
|
<< " as previous sister voice instead of "
|
||||||
|
<< static_cast<const void*>(ring[idx]));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newVoice == start)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (unsigned i = 1; i < idx; ++i) {
|
||||||
|
if (ring[i] == newVoice) {
|
||||||
|
DBG("Error in ring: " << static_cast<const void*>(newVoice)
|
||||||
|
<< " already present in ring at index " << i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ring[++idx] = newVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -730,7 +730,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
callbackBreakdown.panning += voice->getLastPanningDuration();
|
callbackBreakdown.panning += voice->getLastPanningDuration();
|
||||||
|
|
||||||
if (voice->toBeCleanedUp())
|
if (voice->toBeCleanedUp())
|
||||||
voice->reset();
|
voice->reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,24 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
|
||||||
const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
|
const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
|
||||||
return sum + v->getAverageEnvelope();
|
return sum + v->getAverageEnvelope();
|
||||||
});
|
});
|
||||||
|
// We are checking the envelope to try and kill voices with relative low contribution
|
||||||
|
// to the output compared to the rest.
|
||||||
const auto envThreshold = sumEnvelope
|
const auto envThreshold = sumEnvelope
|
||||||
/ static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff;
|
/ static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff;
|
||||||
|
// We are checking the age so that voices have the time to build up attack
|
||||||
|
// This is not perfect because pad-type voices will take a long time to output
|
||||||
|
// their sound, but it's reasonable for sounds with a quick attack and longer
|
||||||
|
// release.
|
||||||
const auto ageThreshold = voices.front()->getAge() * config::stealingAgeCoeff;
|
const auto ageThreshold = voices.front()->getAge() * config::stealingAgeCoeff;
|
||||||
|
// This needs to be positive
|
||||||
|
ASSERT(ageThreshold >= 0);
|
||||||
|
|
||||||
Voice* returnedVoice = voices.front();
|
Voice* returnedVoice = voices.front();
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
while (idx < voices.size()) {
|
while (idx < voices.size()) {
|
||||||
const auto ref = voices[idx];
|
const auto ref = voices[idx];
|
||||||
|
|
||||||
if (ref->getAge() < ageThreshold) {
|
if (ref->getAge() <= ageThreshold) {
|
||||||
// Went too far, we'll kill the oldest note.
|
// Went too far, we'll kill the oldest note.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue