Ensure to only generate per-voice modulations of the same region

This commit is contained in:
Jean Pierre Cimalando 2020-07-28 13:30:10 +02:00
parent 652d0c898d
commit 0507dab7c4
3 changed files with 67 additions and 38 deletions

View file

@ -744,7 +744,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
if (voice->isFree()) if (voice->isFree())
continue; continue;
mm.beginVoice(voice->getId()); mm.beginVoice(voice->getId(), voice->getRegion()->getId());
activeVoices++; activeVoices++;
renderVoiceToOutputs(*voice, *tempSpan); renderVoiceToOutputs(*voice, *tempSpan);

View file

@ -24,6 +24,7 @@ struct ModMatrix::Impl {
uint32_t numFrames_ {}; uint32_t numFrames_ {};
NumericId<Voice> voiceId_ {}; NumericId<Voice> voiceId_ {};
NumericId<Region> regionId_ {};
struct Source { struct Source {
ModKey key; ModKey key;
@ -195,7 +196,7 @@ void ModMatrix::init()
Impl& impl = *impl_; Impl& impl = *impl_;
for (Impl::Source &source : impl.sources_) { for (Impl::Source &source : impl.sources_) {
int flags = source.key.flags(); const int flags = source.key.flags();
if (flags & kModIsPerCycle) if (flags & kModIsPerCycle)
source.gen->init(source.key, {}); source.gen->init(source.key, {});
} }
@ -206,7 +207,7 @@ void ModMatrix::initVoice(NumericId<Voice> voiceId)
Impl& impl = *impl_; Impl& impl = *impl_;
for (Impl::Source &source : impl.sources_) { for (Impl::Source &source : impl.sources_) {
int flags = source.key.flags(); const int flags = source.key.flags();
if (flags & kModIsPerVoice) if (flags & kModIsPerVoice)
source.gen->init(source.key, voiceId); source.gen->init(source.key, voiceId);
} }
@ -231,20 +232,23 @@ void ModMatrix::endCycle()
for (Impl::Source &source : impl.sources_) { for (Impl::Source &source : impl.sources_) {
if (!source.bufferReady) { if (!source.bufferReady) {
int flags = source.key.flags(); const int flags = source.key.flags();
if (flags & kModIsPerCycle) { if (flags & kModIsPerCycle) {
absl::Span<float> buffer(source.buffer.data(), numFrames); absl::Span<float> buffer(source.buffer.data(), numFrames);
source.gen->generateDiscarded(source.key, {}, buffer); source.gen->generateDiscarded(source.key, {}, buffer);
} }
} }
} }
impl.numFrames_ = 0;
} }
void ModMatrix::beginVoice(NumericId<Voice> voiceId) void ModMatrix::beginVoice(NumericId<Voice> voiceId, NumericId<Region> regionId)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
impl.voiceId_ = voiceId; impl.voiceId_ = voiceId;
impl.regionId_ = regionId;
for (Impl::Source &source : impl.sources_) { for (Impl::Source &source : impl.sources_) {
const int flags = source.key.flags(); const int flags = source.key.flags();
@ -263,16 +267,20 @@ void ModMatrix::endVoice()
Impl& impl = *impl_; Impl& impl = *impl_;
const uint32_t numFrames = impl.numFrames_; const uint32_t numFrames = impl.numFrames_;
const NumericId<Voice> voiceId = impl.voiceId_; const NumericId<Voice> voiceId = impl.voiceId_;
const NumericId<Region> regionId = impl.regionId_;
for (Impl::Source &source : impl.sources_) { for (Impl::Source &source : impl.sources_) {
if (!source.bufferReady) { if (!source.bufferReady) {
int flags = source.key.flags(); const int flags = source.key.flags();
if (flags & kModIsPerVoice) { if ((flags & kModIsPerVoice) && source.key.region() == regionId) {
absl::Span<float> buffer(source.buffer.data(), numFrames); absl::Span<float> buffer(source.buffer.data(), numFrames);
source.gen->generateDiscarded(source.key, voiceId, buffer); source.gen->generateDiscarded(source.key, voiceId, buffer);
} }
} }
} }
impl.voiceId_ = {};
impl.regionId_ = {};
} }
float* ModMatrix::getModulation(TargetId targetId) float* ModMatrix::getModulation(TargetId targetId)
@ -281,13 +289,18 @@ float* ModMatrix::getModulation(TargetId targetId)
return nullptr; return nullptr;
Impl& impl = *impl_; Impl& impl = *impl_;
const NumericId<Region> regionId = impl.regionId_;
const uint32_t targetIndex = targetId.number(); const uint32_t targetIndex = targetId.number();
Impl::Target &target = impl.targets_[targetIndex]; Impl::Target &target = impl.targets_[targetIndex];
const int flags = target.key.flags(); const int targetFlags = target.key.flags();
const uint32_t numFrames = impl.numFrames_; const uint32_t numFrames = impl.numFrames_;
absl::Span<float> buffer(target.buffer.data(), numFrames); absl::Span<float> buffer(target.buffer.data(), numFrames);
// only accept per-voice targets of the same region
if ((targetFlags & kModIsPerVoice) && regionId != target.key.region())
return nullptr;
// check if already processed // check if already processed
if (target.bufferReady) if (target.bufferReady)
return buffer.data(); return buffer.data();
@ -295,45 +308,59 @@ float* ModMatrix::getModulation(TargetId targetId)
// set the ready flag to prevent a cycle // set the ready flag to prevent a cycle
// in case there is, be sure to initialize the buffer // in case there is, be sure to initialize the buffer
target.bufferReady = true; target.bufferReady = true;
if (flags & kModIsMultiplicative)
sfz::fill(buffer, 1.0f);
else if (flags & kModIsPercentMultiplicative)
sfz::fill(buffer, 100.0f);
else {
ASSERT(flags & kModIsAdditive);
sfz::fill(buffer, 0.0f);
}
auto sourcesPos = target.connectedSources.begin(); auto sourcesPos = target.connectedSources.begin();
auto sourcesEnd = target.connectedSources.end(); auto sourcesEnd = target.connectedSources.end();
bool isFirstSource = true;
// generate the first source in buffer // generate first source in output buffer, next sources in temporary buffer
if (sourcesPos != sourcesEnd) { // then add or multiply, depending on target flags
while (sourcesPos != sourcesEnd) {
Impl::Source &source = impl.sources_[sourcesPos->first]; Impl::Source &source = impl.sources_[sourcesPos->first];
source.gen->generate(source.key, impl.voiceId_, buffer); const int sourceFlags = source.key.flags();
// only accept per-voice sources of the same region
bool useThisSource = true;
if (sourceFlags & kModIsPerVoice)
useThisSource = (regionId == source.key.region());
if (useThisSource) {
if (isFirstSource) {
source.gen->generate(source.key, impl.voiceId_, buffer);
isFirstSource = false;
}
else {
absl::Span<float> temp(impl.temp_.data(), numFrames);
source.gen->generate(source.key, impl.voiceId_, temp);
if (targetFlags & kModIsMultiplicative) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= temp[i];
}
else if (targetFlags & kModIsPercentMultiplicative) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= 0.01f * temp[i];
}
else {
ASSERT(targetFlags & kModIsAdditive);
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] += temp[i];
}
}
}
++sourcesPos; ++sourcesPos;
} }
// generate next sources in temporary buffer // if there were no source, fill output with the neutral element
// then add or multiply, depending on target flags if (isFirstSource) {
absl::Span<float> temp(impl.temp_.data(), numFrames); if (targetFlags & kModIsMultiplicative)
while (sourcesPos != sourcesEnd) { sfz::fill(buffer, 1.0f);
Impl::Source &source = impl.sources_[sourcesPos->first]; else if (targetFlags & kModIsPercentMultiplicative)
source.gen->generate(source.key, impl.voiceId_, temp); sfz::fill(buffer, 100.0f);
if (flags & kModIsMultiplicative) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= temp[i];
}
else if (flags & kModIsPercentMultiplicative) {
for (uint32_t i = 0; i < numFrames; ++i)
buffer[i] *= 0.01f * temp[i];
}
else { else {
ASSERT(flags & kModIsAdditive); ASSERT(targetFlags & kModIsAdditive);
for (uint32_t i = 0; i < numFrames; ++i) sfz::fill(buffer, 0.0f);
buffer[i] += temp[i];
} }
++sourcesPos;
} }
return buffer.data(); return buffer.data();

View file

@ -14,6 +14,7 @@ namespace sfz {
class ModKey; class ModKey;
class ModGenerator; class ModGenerator;
class Voice; class Voice;
struct Region;
/** /**
* @brief Modulation matrix * @brief Modulation matrix
@ -124,8 +125,9 @@ public:
* This clears all the buffers which are per-voice. * This clears all the buffers which are per-voice.
* *
* @param voiceId the identifier of the current voice * @param voiceId the identifier of the current voice
* @param regionId the identifier of the region of the current voice
*/ */
void beginVoice(NumericId<Voice> voiceId); void beginVoice(NumericId<Voice> voiceId, NumericId<Region> regionId);
/** /**
* @brief End modulation processing for a given voice. * @brief End modulation processing for a given voice.