Ensure to only generate per-voice modulations of the same region
This commit is contained in:
parent
652d0c898d
commit
0507dab7c4
3 changed files with 67 additions and 38 deletions
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue