diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 0c2ae266..ad5d0b87 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -41,6 +41,7 @@ namespace config { constexpr int numVoices { 64 }; constexpr unsigned maxVoices { 256 }; constexpr unsigned smoothingSteps { 512 }; + constexpr uint8_t xfadeSmoothing { 5 }; constexpr uint8_t gainSmoothing { 0 }; constexpr unsigned powerTableSizeExponent { 11 }; constexpr int maxFilePromises { maxVoices }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 4f23fd5d..e0799d86 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -25,6 +25,7 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources) osc.init(sampleRate); gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); + xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); for (auto & filter : channelEnvelopeFilters) filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); @@ -245,6 +246,7 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept { this->sampleRate = sampleRate; gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); + xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); for (auto & filter : channelEnvelopeFilters) filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); @@ -308,11 +310,45 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept #endif } -void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept +void sfz::Voice::applyCrossfades(absl::Span modulationSpan) noexcept { const auto numSamples = modulationSpan.size(); const auto xfCurve = region->crossfadeCCCurve; + auto tempSpan = resources.bufferPool.getBuffer(numSamples); + if (!tempSpan) + return; + + bool smoothOutput = false; + for (const auto& mod : region->crossfadeCCInRange) { + const auto events = resources.midiState.getCCEvents(mod.cc); + smoothOutput |= (events.size() > 1); + linearEnvelope(events, *tempSpan, [&](float x) { + return crossfadeIn(mod.data, x, xfCurve); + }); + applyGain(*tempSpan, modulationSpan); + } + + for (const auto& mod : region->crossfadeCCOutRange) { + const auto events = resources.midiState.getCCEvents(mod.cc); + smoothOutput |= (events.size() > 1); + linearEnvelope(events, *tempSpan, [&](float x) { + return crossfadeOut(mod.data, x, xfCurve); + }); + applyGain(*tempSpan, modulationSpan); + } + + if (smoothOutput) { + xfadeSmoother.reset(modulationSpan.front()); + xfadeSmoother.process(modulationSpan, modulationSpan); + } +} + + +void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept +{ + const auto numSamples = modulationSpan.size(); + auto tempSpan = resources.bufferPool.getBuffer(numSamples); if (!tempSpan) return; @@ -328,22 +364,6 @@ void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept applyGain(*tempSpan, modulationSpan); }); - // Crossfade envelopes - for (const auto& mod : region->crossfadeCCInRange) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&](float x) { - return crossfadeIn(mod.data, x, xfCurve); - }); - applyGain(*tempSpan, modulationSpan); - } - for (const auto& mod : region->crossfadeCCOutRange) { - const auto events = resources.midiState.getCCEvents(mod.cc); - linearEnvelope(events, *tempSpan, [&](float x) { - return crossfadeOut(mod.data, x, xfCurve); - }); - applyGain(*tempSpan, modulationSpan); - } - // Volume envelope applyGain1(db2mag(baseVolumedB), modulationSpan); forEachWithSmoother(Mod::volume, [&](const CCData& mod, Smoother& smoother) { @@ -370,6 +390,7 @@ void sfz::Voice::ampStageMono(AudioSpan buffer) noexcept return; amplitudeEnvelope(*modulationSpan); + applyCrossfades(*modulationSpan); applyGain(*modulationSpan, leftBuffer); } @@ -383,6 +404,7 @@ void sfz::Voice::ampStageStereo(AudioSpan buffer) noexcept return; amplitudeEnvelope(*modulationSpan); + applyCrossfades(*modulationSpan); buffer.applyGain(*modulationSpan); } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 40accaf1..03c83337 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -340,6 +340,14 @@ private: * @param modulationSpan */ void amplitudeEnvelope(absl::Span modulationSpan) noexcept; + + /** + * @brief Apply the crossfade envelope to a span. + * + * @param modulationSpan + */ + void applyCrossfades(absl::Span modulationSpan) noexcept; + /** * @brief Amplitude stage for a mono source * @@ -473,6 +481,7 @@ private: ModifierArray> modifierSmoothers; Smoother gainSmoother; Smoother bendSmoother; + Smoother xfadeSmoother; void resetSmoothers() noexcept; std::array, 2> channelEnvelopeFilters;