From 507ddc8e01ec00a0827482b7d6dfb8c786df1d30 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Sun, 29 Mar 2020 20:35:15 +0200 Subject: [PATCH] Reworking the modifiers separately --- src/sfizz/Voice.cpp | 251 ++++++++++++++++++++++++++++++++++++-------- src/sfizz/Voice.h | 7 ++ 2 files changed, 213 insertions(+), 45 deletions(-) diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 8b22323a..f10679df 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -253,7 +253,7 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept } template -void getLinearEnvelope(const sfz::CCMap& ccMods, const sfz::MidiState& state, absl::Span temp, std::function function) +void getLinearEnvelope(const sfz::CCMap& ccMods, const sfz::MidiState& state, absl::Span output, absl::Span temp, std::function function) { for (auto& mod : ccMods) { const auto eventList = state.getEvents(mod.cc); @@ -270,9 +270,203 @@ void getLinearEnvelope(const sfz::CCMap& ccMods, const sfz::MidiState& state, lastDelay += length; } sfz::fill(temp.subspan(lastDelay), lastValue); + sfz::applyGain(temp, output); } } +void sfz::Voice::amplitudeModulation(absl::Span modulationSpan) noexcept +{ + fill(modulationSpan, 1.0f); + if (!region) + return; + + const auto numSamples = modulationSpan.size(); + auto tempBuffer = resources.bufferPool.getBuffer(numSamples); + if (!tempBuffer) + return; + auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples); + + for (auto& mod : region->amplitudeCC) { + const auto eventList = resources.midiState.getEvents(mod.cc); + ASSERT(eventList.size() > 0); + ASSERT(eventList[0].delay == 0); + + auto lastValue = mod.value * eventList[0].value; + auto lastDelay = eventList[0].delay; + for (unsigned i = 1; i < eventList.size(); ++ i) { + const auto event = eventList[i]; + const auto length = event.delay - lastDelay; + const auto step = (Default::amplitudeRange.clamp(mod.value * event.value) - lastValue)/ length; + lastValue = linearRamp(tempSpan.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(tempSpan.subspan(lastDelay), lastValue); + applyGain(tempSpan, modulationSpan); + } + applyGain(baseGain, modulationSpan); +} + +void sfz::Voice::crossfadeModulation(absl::Span modulationSpan) noexcept +{ + fill(modulationSpan, 1.0f); + if (!region) + return; + + const auto numSamples = modulationSpan.size(); + auto tempBuffer = resources.bufferPool.getBuffer(numSamples); + if (!tempBuffer) + return; + auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples); + + for (auto& mod : region->crossfadeCCInRange) { + const auto eventList = resources.midiState.getEvents(mod.cc); + ASSERT(eventList.size() > 0); + ASSERT(eventList[0].delay == 0); + + auto lastValue = crossfadeIn(mod.value, eventList[0].value, region->crossfadeCCCurve); + auto lastDelay = eventList[0].delay; + for (unsigned i = 1; i < eventList.size(); ++ i) { + const auto event = eventList[i]; + const auto length = event.delay - lastDelay; + const auto step = (crossfadeIn(mod.value, event.value, region->crossfadeCCCurve) - lastValue)/ length; + lastValue = linearRamp(tempSpan.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(tempSpan.subspan(lastDelay), lastValue); + applyGain(tempSpan, modulationSpan); + } + + for (auto& mod : region->crossfadeCCOutRange) { + const auto eventList = resources.midiState.getEvents(mod.cc); + ASSERT(eventList.size() > 0); + ASSERT(eventList[0].delay == 0); + + auto lastValue = crossfadeOut(mod.value, eventList[0].value, region->crossfadeCCCurve); + auto lastDelay = eventList[0].delay; + for (unsigned i = 1; i < eventList.size(); ++ i) { + const auto event = eventList[i]; + const auto length = event.delay - lastDelay; + const auto step = (crossfadeOut(mod.value, event.value, region->crossfadeCCCurve) - lastValue)/ length; + lastValue = linearRamp(tempSpan.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(tempSpan.subspan(lastDelay), lastValue); + applyGain(tempSpan, modulationSpan); + } +} + +void sfz::Voice::panningModulation(absl::Span modulationSpan) noexcept +{ + if (!region) + return; + + if (region->panCC.empty()) { + fill(modulationSpan, region->pan); + return; + } + + fill(modulationSpan, 1.0f); + const auto numSamples = modulationSpan.size(); + auto tempBuffer = resources.bufferPool.getBuffer(numSamples); + if (!tempBuffer) + return; + auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples); + + for (auto& mod : region->panCC) { + const auto eventList = resources.midiState.getEvents(mod.cc); + ASSERT(eventList.size() > 0); + ASSERT(eventList[0].delay == 0); + + auto lastValue = mod.value * eventList[0].value; + auto lastDelay = eventList[0].delay; + for (unsigned i = 1; i < eventList.size(); ++ i) { + const auto event = eventList[i]; + const auto length = event.delay - lastDelay; + const auto step = (Default::panRange.clamp(mod.value * event.value) - lastValue)/ length; + lastValue = linearRamp(tempSpan.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(tempSpan.subspan(lastDelay), lastValue); + applyGain(tempSpan, modulationSpan); + } + + add(region->pan, modulationSpan); +} + +void sfz::Voice::widthModulation(absl::Span modulationSpan) noexcept +{ + if (!region) + return; + + if (region->widthCC.empty()) { + fill(modulationSpan, region->width); + return; + } + + fill(modulationSpan, 1.0f); + const auto numSamples = modulationSpan.size(); + auto tempBuffer = resources.bufferPool.getBuffer(numSamples); + if (!tempBuffer) + return; + auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples); + + for (auto& mod : region->widthCC) { + const auto eventList = resources.midiState.getEvents(mod.cc); + ASSERT(eventList.size() > 0); + ASSERT(eventList[0].delay == 0); + + auto lastValue = mod.value * eventList[0].value; + auto lastDelay = eventList[0].delay; + for (unsigned i = 1; i < eventList.size(); ++ i) { + const auto event = eventList[i]; + const auto length = event.delay - lastDelay; + const auto step = (Default::widthRange.clamp(mod.value * event.value) - lastValue)/ length; + lastValue = linearRamp(tempSpan.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(tempSpan.subspan(lastDelay), lastValue); + applyGain(tempSpan, modulationSpan); + } + add(region->width, modulationSpan); +} + +void sfz::Voice::positionModulation(absl::Span modulationSpan) noexcept +{ + if (!region) + return; + + if (region->positionCC.empty()) { + fill(modulationSpan, region->position); + return; + } + + fill(modulationSpan, 1.0f); + const auto numSamples = modulationSpan.size(); + auto tempBuffer = resources.bufferPool.getBuffer(numSamples); + if (!tempBuffer) + return; + auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples); + + for (auto& mod : region->positionCC) { + const auto eventList = resources.midiState.getEvents(mod.cc); + ASSERT(eventList.size() > 0); + ASSERT(eventList[0].delay == 0); + + auto lastValue = mod.value * eventList[0].value; + auto lastDelay = eventList[0].delay; + for (unsigned i = 1; i < eventList.size(); ++ i) { + const auto event = eventList[i]; + const auto length = event.delay - lastDelay; + const auto step = (Default::positionRange.clamp(mod.value * event.value) - lastValue)/ length; + lastValue = linearRamp(tempSpan.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(tempSpan.subspan(lastDelay), lastValue); + applyGain(tempSpan, modulationSpan); + } + add(region->position, modulationSpan); +} + void sfz::Voice::processMono(AudioSpan buffer) noexcept { const auto numSamples = buffer.getNumFrames(); @@ -288,30 +482,15 @@ void sfz::Voice::processMono(AudioSpan buffer) noexcept ScopedTiming logger { amplitudeDuration }; // Amplitude envelope - fill(modulationSpan, 0.0f); - getLinearEnvelope(region->amplitudeCC, resources.midiState, modulationSpan, [](const float& modifier, float value){ - return value * modifier; - }); - // DBG("Amplitude curve back: " << modulationSpan.back()); - add(baseGain, modulationSpan); - // DBG("Final gain: " << modulationSpan.back()); + amplitudeModulation(modulationSpan); + DBG("Final gain: " << modulationSpan.back()); applyGain(modulationSpan, leftBuffer); // Crossfade envelopes // crossfadeEnvelope.getBlock(modulationSpan); - fill(modulationSpan, 1.0f); - getLinearEnvelope>(region->crossfadeCCInRange, resources.midiState, modulationSpan, [this](const Range& range, float value){ - return crossfadeIn(range, value, region->crossfadeCCCurve); - }); + crossfadeModulation(modulationSpan); + DBG("XF: " << modulationSpan.back()); applyGain(modulationSpan, leftBuffer); - // DBG("XFin: " << modulationSpan.back()); - - fill(modulationSpan, 1.0f); - getLinearEnvelope>(region->crossfadeCCOutRange, resources.midiState, modulationSpan, [this](const Range& range, float value){ - return crossfadeOut(range, value, region->crossfadeCCCurve); - }); - applyGain(modulationSpan, leftBuffer); - // DBG("XFout: " << modulationSpan.back()); // Volume envelope volumeEnvelope.getBlock(modulationSpan); @@ -343,9 +522,8 @@ void sfz::Voice::processMono(AudioSpan buffer) noexcept copy(leftBuffer, rightBuffer); // Apply panning - fill(modulationSpan, 0.0f); - getLinearEnvelope(region->panCC, resources.midiState, modulationSpan, [](float modifier, float value) { return value * modifier; }); - add(region->pan, modulationSpan); + panningModulation(modulationSpan); + DBG("Pan: " << modulationSpan.back()); pan(modulationSpan, leftBuffer, rightBuffer); } } @@ -365,22 +543,11 @@ void sfz::Voice::processStereo(AudioSpan buffer) noexcept ScopedTiming logger { amplitudeDuration }; // Amplitude envelope - fill(modulationSpan, 0.0f); - getLinearEnvelope(region->amplitudeCC, resources.midiState, modulationSpan, [](float modifier, float value) { return value * modifier; }); - add(baseGain, modulationSpan); + amplitudeModulation(modulationSpan); buffer.applyGain(modulationSpan); // Crossfade envelopes - fill(modulationSpan, 1.0f); - getLinearEnvelope>(region->crossfadeCCInRange, resources.midiState, modulationSpan, [this](const Range& range, float value){ - return crossfadeIn(range, value, region->crossfadeCCCurve); - }); - buffer.applyGain(modulationSpan); - - fill(modulationSpan, 1.0f); - getLinearEnvelope>(region->crossfadeCCOutRange, resources.midiState, modulationSpan, [this](const Range& range, float value){ - return crossfadeOut(range, value, region->crossfadeCCCurve); - }); + crossfadeModulation(modulationSpan); buffer.applyGain(modulationSpan); // Volume envelope @@ -396,20 +563,14 @@ void sfz::Voice::processStereo(AudioSpan buffer) noexcept ScopedTiming logger { panningDuration }; // Apply panning - fill(modulationSpan, 0.0f); - getLinearEnvelope(region->panCC, resources.midiState, modulationSpan, [](float modifier, float value) { return value * modifier; }); - add(region->pan, modulationSpan); + panningModulation(modulationSpan); pan(modulationSpan, leftBuffer, rightBuffer); // Apply the width/position process - fill(modulationSpan, 0.0f); - getLinearEnvelope(region->widthCC, resources.midiState, modulationSpan, [](float modifier, float value) { return value * modifier; }); - add(region->width, modulationSpan); + widthModulation(modulationSpan); width(modulationSpan, leftBuffer, rightBuffer); - fill(modulationSpan, 0.0f); - getLinearEnvelope(region->positionCC, resources.midiState, modulationSpan, [](float modifier, float value) { return value * modifier; }); - add(region->position, modulationSpan); + positionModulation(modulationSpan); pan(modulationSpan, leftBuffer, rightBuffer); } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 0d51cb3e..996bcab2 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -250,6 +250,13 @@ private: * @param buffer */ void processStereo(AudioSpan buffer) noexcept; + + void amplitudeModulation(absl::Span modulationSpan) noexcept; + void crossfadeModulation(absl::Span modulationSpan) noexcept; + void panningModulation(absl::Span modulationSpan) noexcept; + void widthModulation(absl::Span modulationSpan) noexcept; + void positionModulation(absl::Span modulationSpan) noexcept; + Region* region { nullptr }; enum class State {