From 0507dab7c4dc3fb7bf30c159792efc07873d92fd Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Tue, 28 Jul 2020 13:30:10 +0200 Subject: [PATCH] Ensure to only generate per-voice modulations of the same region --- src/sfizz/Synth.cpp | 2 +- src/sfizz/modulations/ModMatrix.cpp | 99 ++++++++++++++++++----------- src/sfizz/modulations/ModMatrix.h | 4 +- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index cba7e139..61fb532d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -744,7 +744,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept if (voice->isFree()) continue; - mm.beginVoice(voice->getId()); + mm.beginVoice(voice->getId(), voice->getRegion()->getId()); activeVoices++; renderVoiceToOutputs(*voice, *tempSpan); diff --git a/src/sfizz/modulations/ModMatrix.cpp b/src/sfizz/modulations/ModMatrix.cpp index 9dd94893..74fa25cd 100644 --- a/src/sfizz/modulations/ModMatrix.cpp +++ b/src/sfizz/modulations/ModMatrix.cpp @@ -24,6 +24,7 @@ struct ModMatrix::Impl { uint32_t numFrames_ {}; NumericId voiceId_ {}; + NumericId regionId_ {}; struct Source { ModKey key; @@ -195,7 +196,7 @@ void ModMatrix::init() Impl& impl = *impl_; for (Impl::Source &source : impl.sources_) { - int flags = source.key.flags(); + const int flags = source.key.flags(); if (flags & kModIsPerCycle) source.gen->init(source.key, {}); } @@ -206,7 +207,7 @@ void ModMatrix::initVoice(NumericId voiceId) Impl& impl = *impl_; for (Impl::Source &source : impl.sources_) { - int flags = source.key.flags(); + const int flags = source.key.flags(); if (flags & kModIsPerVoice) source.gen->init(source.key, voiceId); } @@ -231,20 +232,23 @@ void ModMatrix::endCycle() for (Impl::Source &source : impl.sources_) { if (!source.bufferReady) { - int flags = source.key.flags(); + const int flags = source.key.flags(); if (flags & kModIsPerCycle) { absl::Span buffer(source.buffer.data(), numFrames); source.gen->generateDiscarded(source.key, {}, buffer); } } } + + impl.numFrames_ = 0; } -void ModMatrix::beginVoice(NumericId voiceId) +void ModMatrix::beginVoice(NumericId voiceId, NumericId regionId) { Impl& impl = *impl_; impl.voiceId_ = voiceId; + impl.regionId_ = regionId; for (Impl::Source &source : impl.sources_) { const int flags = source.key.flags(); @@ -263,16 +267,20 @@ void ModMatrix::endVoice() Impl& impl = *impl_; const uint32_t numFrames = impl.numFrames_; const NumericId voiceId = impl.voiceId_; + const NumericId regionId = impl.regionId_; for (Impl::Source &source : impl.sources_) { if (!source.bufferReady) { - int flags = source.key.flags(); - if (flags & kModIsPerVoice) { + const int flags = source.key.flags(); + if ((flags & kModIsPerVoice) && source.key.region() == regionId) { absl::Span buffer(source.buffer.data(), numFrames); source.gen->generateDiscarded(source.key, voiceId, buffer); } } } + + impl.voiceId_ = {}; + impl.regionId_ = {}; } float* ModMatrix::getModulation(TargetId targetId) @@ -281,13 +289,18 @@ float* ModMatrix::getModulation(TargetId targetId) return nullptr; Impl& impl = *impl_; + const NumericId regionId = impl.regionId_; const uint32_t targetIndex = targetId.number(); Impl::Target &target = impl.targets_[targetIndex]; - const int flags = target.key.flags(); + const int targetFlags = target.key.flags(); const uint32_t numFrames = impl.numFrames_; absl::Span 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 if (target.bufferReady) return buffer.data(); @@ -295,45 +308,59 @@ float* ModMatrix::getModulation(TargetId targetId) // set the ready flag to prevent a cycle // in case there is, be sure to initialize the buffer 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 sourcesEnd = target.connectedSources.end(); + bool isFirstSource = true; - // generate the first source in buffer - if (sourcesPos != sourcesEnd) { + // generate first source in output buffer, next sources in temporary buffer + // then add or multiply, depending on target flags + while (sourcesPos != sourcesEnd) { 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 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; } - // generate next sources in temporary buffer - // then add or multiply, depending on target flags - absl::Span temp(impl.temp_.data(), numFrames); - while (sourcesPos != sourcesEnd) { - Impl::Source &source = impl.sources_[sourcesPos->first]; - source.gen->generate(source.key, impl.voiceId_, temp); - 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]; - } + // if there were no source, fill output with the neutral element + if (isFirstSource) { + if (targetFlags & kModIsMultiplicative) + sfz::fill(buffer, 1.0f); + else if (targetFlags & kModIsPercentMultiplicative) + sfz::fill(buffer, 100.0f); else { - ASSERT(flags & kModIsAdditive); - for (uint32_t i = 0; i < numFrames; ++i) - buffer[i] += temp[i]; + ASSERT(targetFlags & kModIsAdditive); + sfz::fill(buffer, 0.0f); } - ++sourcesPos; } return buffer.data(); diff --git a/src/sfizz/modulations/ModMatrix.h b/src/sfizz/modulations/ModMatrix.h index e3381930..7cf51e6f 100644 --- a/src/sfizz/modulations/ModMatrix.h +++ b/src/sfizz/modulations/ModMatrix.h @@ -14,6 +14,7 @@ namespace sfz { class ModKey; class ModGenerator; class Voice; +struct Region; /** * @brief Modulation matrix @@ -124,8 +125,9 @@ public: * This clears all the buffers which are per-voice. * * @param voiceId the identifier of the current voice + * @param regionId the identifier of the region of the current voice */ - void beginVoice(NumericId voiceId); + void beginVoice(NumericId voiceId, NumericId regionId); /** * @brief End modulation processing for a given voice.