Merge pull request #313 from paulfd/xfade-smoothers
Crossfade smoothers
This commit is contained in:
commit
f2ffad44bf
7 changed files with 92 additions and 21 deletions
|
|
@ -4,6 +4,7 @@ set -ex
|
|||
|
||||
sudo ln -s /usr/local /opt/local
|
||||
brew update
|
||||
brew upgrade python || brew link --overwrite python
|
||||
brew upgrade cmake
|
||||
brew install jack
|
||||
brew install dylibbundler
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class OnePoleFilter {
|
|||
public:
|
||||
OnePoleFilter() = default;
|
||||
|
||||
Type current() const { return state; }
|
||||
|
||||
void setGain(Type gain)
|
||||
{
|
||||
G = gain / (1 + gain);
|
||||
|
|
|
|||
|
|
@ -25,12 +25,22 @@ void Smoother::reset(float value)
|
|||
filter.reset(value);
|
||||
}
|
||||
|
||||
void Smoother::process(absl::Span<const float> input, absl::Span<float> output)
|
||||
void Smoother::process(absl::Span<const float> input, absl::Span<float> output, bool canShortcut)
|
||||
{
|
||||
if (smoothing)
|
||||
CHECK_SPAN_SIZES(input, output);
|
||||
if (input.size() == 0)
|
||||
return;
|
||||
|
||||
if (canShortcut && std::abs(input.front() - current()) < config::virtuallyZero) {
|
||||
if (input.data() != output.data())
|
||||
copy<float>(input, output);
|
||||
|
||||
filter.reset(input.back());
|
||||
} else if (smoothing) {
|
||||
filter.processLowpass(input, output);
|
||||
else
|
||||
} else if (input.data() != output.data()) {
|
||||
copy<float>(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,9 +42,13 @@ public:
|
|||
*
|
||||
* @param input
|
||||
* @param output
|
||||
* @param canShortcut whether we can have a fast path if the filter is within
|
||||
* a reasonable range around the first value of the input
|
||||
* span.
|
||||
*/
|
||||
void process(absl::Span<const float> input, absl::Span<float> output);
|
||||
void process(absl::Span<const float> input, absl::Span<float> output, bool canShortcut = false);
|
||||
|
||||
float current() const { return filter.current(); }
|
||||
private:
|
||||
bool smoothing { false };
|
||||
OnePoleFilter<float> filter {};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
@ -109,6 +110,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
|
|||
if (triggerType != TriggerType::CC)
|
||||
baseGain *= region->getNoteGain(number, value);
|
||||
gainSmoother.reset();
|
||||
resetCrossfades();
|
||||
|
||||
// Check that we can handle the number of filters; filters should be cleared here
|
||||
ASSERT((filters.capacity() - filters.size()) >= region->filters.size());
|
||||
|
|
@ -248,6 +250,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));
|
||||
|
|
@ -311,11 +314,65 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
#endif
|
||||
}
|
||||
|
||||
void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
|
||||
void sfz::Voice::resetCrossfades() noexcept
|
||||
{
|
||||
float xfadeValue { 1.0f };
|
||||
const auto xfCurve = region->crossfadeCCCurve;
|
||||
|
||||
for (const auto& mod : region->crossfadeCCInRange) {
|
||||
const auto value = resources.midiState.getCCValue(mod.cc);
|
||||
xfadeValue *= crossfadeIn(mod.data, value, xfCurve);
|
||||
}
|
||||
|
||||
for (const auto& mod : region->crossfadeCCOutRange) {
|
||||
const auto value = resources.midiState.getCCValue(mod.cc);
|
||||
xfadeValue *= crossfadeOut(mod.data, value, xfCurve);
|
||||
}
|
||||
|
||||
xfadeSmoother.reset(xfadeValue);
|
||||
}
|
||||
|
||||
void sfz::Voice::applyCrossfades(absl::Span<float> modulationSpan) noexcept
|
||||
{
|
||||
const auto numSamples = modulationSpan.size();
|
||||
const auto xfCurve = region->crossfadeCCCurve;
|
||||
|
||||
auto tempSpan = resources.bufferPool.getBuffer(numSamples);
|
||||
auto xfadeSpan = resources.bufferPool.getBuffer(numSamples);
|
||||
|
||||
if (!tempSpan || !xfadeSpan)
|
||||
return;
|
||||
|
||||
fill<float>(*xfadeSpan, 1.0f);
|
||||
|
||||
bool canShortcut = true;
|
||||
for (const auto& mod : region->crossfadeCCInRange) {
|
||||
const auto events = resources.midiState.getCCEvents(mod.cc);
|
||||
canShortcut &= (events.size() == 1);
|
||||
linearEnvelope(events, *tempSpan, [&](float x) {
|
||||
return crossfadeIn(mod.data, x, xfCurve);
|
||||
});
|
||||
applyGain<float>(*tempSpan, *xfadeSpan);
|
||||
}
|
||||
|
||||
for (const auto& mod : region->crossfadeCCOutRange) {
|
||||
const auto events = resources.midiState.getCCEvents(mod.cc);
|
||||
canShortcut &= (events.size() == 1);
|
||||
linearEnvelope(events, *tempSpan, [&](float x) {
|
||||
return crossfadeOut(mod.data, x, xfCurve);
|
||||
});
|
||||
applyGain<float>(*tempSpan, *xfadeSpan);
|
||||
}
|
||||
|
||||
xfadeSmoother.process(*xfadeSpan, *xfadeSpan, canShortcut);
|
||||
applyGain<float>(*xfadeSpan, modulationSpan);
|
||||
}
|
||||
|
||||
|
||||
void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
|
||||
{
|
||||
const auto numSamples = modulationSpan.size();
|
||||
|
||||
auto tempSpan = resources.bufferPool.getBuffer(numSamples);
|
||||
if (!tempSpan)
|
||||
return;
|
||||
|
|
@ -331,22 +388,6 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
|
|||
applyGain<float>(*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<float>(*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<float>(*tempSpan, modulationSpan);
|
||||
}
|
||||
|
||||
// Volume envelope
|
||||
applyGain1<float>(db2mag(baseVolumedB), modulationSpan);
|
||||
forEachWithSmoother(Mod::volume, [&](const CCData<Modifier>& mod, Smoother& smoother) {
|
||||
|
|
@ -373,6 +414,7 @@ void sfz::Voice::ampStageMono(AudioSpan<float> buffer) noexcept
|
|||
return;
|
||||
|
||||
amplitudeEnvelope(*modulationSpan);
|
||||
applyCrossfades(*modulationSpan);
|
||||
applyGain<float>(*modulationSpan, leftBuffer);
|
||||
}
|
||||
|
||||
|
|
@ -386,6 +428,7 @@ void sfz::Voice::ampStageStereo(AudioSpan<float> buffer) noexcept
|
|||
return;
|
||||
|
||||
amplitudeEnvelope(*modulationSpan);
|
||||
applyCrossfades(*modulationSpan);
|
||||
buffer.applyGain(*modulationSpan);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -340,6 +340,15 @@ private:
|
|||
* @param modulationSpan
|
||||
*/
|
||||
void amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Apply the crossfade envelope to a span.
|
||||
*
|
||||
* @param modulationSpan
|
||||
*/
|
||||
void applyCrossfades(absl::Span<float> modulationSpan) noexcept;
|
||||
void resetCrossfades() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Amplitude stage for a mono source
|
||||
*
|
||||
|
|
@ -473,6 +482,7 @@ private:
|
|||
ModifierArray<std::vector<Smoother>> modifierSmoothers;
|
||||
Smoother gainSmoother;
|
||||
Smoother bendSmoother;
|
||||
Smoother xfadeSmoother;
|
||||
void resetSmoothers() noexcept;
|
||||
|
||||
std::array<OnePoleFilter<float>, 2> channelEnvelopeFilters;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue