diff --git a/sfizz/Region.cpp b/sfizz/Region.cpp index 0ea7bb1c..ebf8e258 100644 --- a/sfizz/Region.cpp +++ b/sfizz/Region.cpp @@ -284,7 +284,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("amp_random"): setValueFromOpcode(opcode, ampRandom, Default::ampRandomRange); - gainDistribution.param(std::uniform_real_distribution::param_type(-ampRandom, ampRandom)); + volumeDistribution.param(std::uniform_real_distribution::param_type(-ampRandom, ampRandom)); break; case hash("amp_velcurve_"): if (opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter)) { @@ -620,11 +620,14 @@ float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexc return centsFactor(pitchVariationInCents); } +float sfz::Region::getBaseVolumedB() noexcept +{ + return volume + volumeDistribution(Random::randomGenerator); +} + float sfz::Region::getBaseGain() noexcept { - float baseGaindB { volume }; - baseGaindB += gainDistribution(Random::randomGenerator); - return db2mag(baseGaindB); + return amplitude; } uint32_t sfz::Region::getOffset() noexcept @@ -692,20 +695,22 @@ float crossfadeOut(const Range& crossfadeRange, U value, SfzCrossfadeCurve cu float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept { - float baseGain { 1.0f }; + float baseGain { 1.0 }; + + // Amplitude key tracking + baseGain *= db2mag(ampKeytrack * static_cast(noteNumber - ampKeycenter)); + + // Crossfades related to the note number + baseGain *= crossfadeIn(crossfadeKeyInRange, noteNumber, crossfadeKeyCurve); + baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); // Amplitude velocity tracking if (trigger == SfzTrigger::release_key) - baseGain *= velocityGain(lastNoteVelocities[noteNumber]); + baseGain *= velocityCurve(lastNoteVelocities[noteNumber]); else - baseGain *= velocityGain(velocity); + baseGain *= velocityCurve(velocity); - // Amplitude key tracking - baseGain *= db2pow(ampKeytrack * static_cast(noteNumber - pitchKeycenter)); - - // Crossfades related to key and velocity - baseGain *= crossfadeIn(crossfadeKeyInRange, noteNumber, crossfadeKeyCurve); - baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); + // Crossfades related to velocity baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve); baseGain *= crossfadeOut(crossfadeVelOutRange, velocity, crossfadeVelCurve); @@ -732,9 +737,9 @@ float sfz::Region::getCrossfadeGain(const sfz::CCValueArray& ccState) noexcept return gain; } -float sfz::Region::velocityGain(uint8_t velocity) const noexcept +float sfz::Region::velocityCurve(uint8_t velocity) const noexcept { - float gaindB { 0.0 }; + float gain { 1.0 }; if (velocityPoints.size() > 0) { // Custom velocity curve auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](auto& val) { return val.first >= velocity; }); @@ -742,17 +747,17 @@ float sfz::Region::velocityGain(uint8_t velocity) const noexcept // Linear interpolation float relativePositionInSegment { static_cast(velocity - before->first) / (after->first - before->first) }; float segmentEndpoints { after->second - before->second }; - gaindB = db2pow(relativePositionInSegment * segmentEndpoints); + gain *= relativePositionInSegment * segmentEndpoints; } else { // Standard velocity curve - float floatVelocity { static_cast(velocity) / 127 }; - if (ampVeltrack > 0) - gaindB = 40 * std::log(floatVelocity) / std::log(10.0f); + const float floatVelocity { static_cast(velocity) / 127 }; + const float gaindB = [&]() { + if (ampVeltrack > 0) + return 40 * std::log(floatVelocity) / std::log(10.0f); else - gaindB = 40 * std::log(1 - floatVelocity) / std::log(10.0f); + return 40 * std::log(1 - floatVelocity) / std::log(10.0f); + }(); + gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd()); } - // Velocity tracking - gaindB *= std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd(); - - return db2pow(gaindB); + return gain; } \ No newline at end of file diff --git a/sfizz/Region.h b/sfizz/Region.h index 1560b3dd..d373b685 100644 --- a/sfizz/Region.h +++ b/sfizz/Region.h @@ -57,8 +57,9 @@ struct Region { float getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept; float getNoteGain(int noteNumber, uint8_t velocity) noexcept; float getCrossfadeGain(const CCValueArray& ccState) noexcept; + float getBaseVolumedB() noexcept; float getBaseGain() noexcept; - float velocityGain(uint8_t velocity) const noexcept; + float velocityCurve(uint8_t velocity) const noexcept; uint32_t getOffset() noexcept; uint32_t getDelay() noexcept; uint32_t trueSampleEnd() const noexcept; @@ -161,7 +162,7 @@ private: int activeNotesInRange { -1 }; int sequenceCounter { 0 }; - std::uniform_real_distribution gainDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom }; + std::uniform_real_distribution volumeDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom }; std::uniform_real_distribution delayDistribution { 0, sfz::Default::delayRandom }; std::uniform_int_distribution offsetDistribution { 0, sfz::Default::offsetRandom }; std::uniform_int_distribution pitchDistribution { -sfz::Default::pitchRandom, sfz::Default::pitchRandom }; diff --git a/sfizz/Voice.cpp b/sfizz/Voice.cpp index 408eef2f..d79cf30f 100644 --- a/sfizz/Voice.cpp +++ b/sfizz/Voice.cpp @@ -24,6 +24,7 @@ #include "Voice.h" #include "AudioSpan.h" #include "Defaults.h" +#include "MathHelpers.h" #include "SIMDHelpers.h" #include "SfzHelpers.h" #include "absl/algorithm/container.h" @@ -52,13 +53,21 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, speedRatio = static_cast(region->sampleRate / this->sampleRate); pitchRatio = region->getBasePitchVariation(number, value); + baseVolumedB = region->getBaseVolumedB(); + auto volumedB { baseVolumedB }; + if (region->volumeCC) + volumedB += normalizeCC(ccState[region->volumeCC->first]) * region->volumeCC->second; + volumeEnvelope.reset(db2mag(volumedB)); + baseGain = region->getBaseGain(); + baseGain *= region->getCrossfadeGain(ccState); if (triggerType != TriggerType::CC) baseGain *= region->getNoteGain(number, value); - baseGain *= region->getCrossfadeGain(ccState); + + float gain { baseGain }; if (region->amplitudeCC) - baseGain *= normalizeCC(ccState[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second); - amplitudeEnvelope.reset(baseGain); + gain *= normalizeCC(ccState[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second); + amplitudeEnvelope.reset(gain); basePan = normalizeNegativePercents(region->pan); auto pan = basePan; @@ -152,14 +161,29 @@ void sfz::Voice::registerCC(int delay, int channel [[maybe_unused]], int ccNumbe release(delay); if (region->amplitudeCC && ccNumber == region->amplitudeCC->first) { - const float newGain { normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->second) * baseGain }; + const float newGain { baseGain * normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->second) }; amplitudeEnvelope.registerEvent(delay, newGain); } + if (region->volumeCC && ccNumber == region->volumeCC->first) { + const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->second }; + amplitudeEnvelope.registerEvent(delay, db2mag(newVolumedB)); + } + if (region->panCC && ccNumber == region->panCC->first) { const float newPan { basePan + normalizeCC(ccValue) * normalizeNegativePercents(region->panCC->second)}; panEnvelope.registerEvent(delay, newPan); } + + if (region->positionCC && ccNumber == region->positionCC->first) { + const float newPosition { basePosition + normalizeCC(ccValue) * normalizeNegativePercents(region->positionCC->second)}; + positionEnvelope.registerEvent(delay, newPosition); + } + + if (region->widthCC && ccNumber == region->widthCC->first) { + const float newWidth { baseWidth + normalizeCC(ccValue) * normalizeNegativePercents(region->widthCC->second)}; + widthEnvelope.registerEvent(delay, newWidth); + } } void sfz::Voice::registerPitchWheel(int delay [[maybe_unused]], int channel [[maybe_unused]], int pitch [[maybe_unused]]) noexcept @@ -233,7 +257,11 @@ void sfz::Voice::processMono(AudioSpan buffer) noexcept // AmpEG envelope egEnvelope.getBlock(span1); ::applyGain(span1, leftBuffer); - + + // Volume envelope + volumeEnvelope.getBlock(span1); + ::applyGain(span1, leftBuffer); + // Prepare for stereo output ::copy(leftBuffer, rightBuffer); @@ -258,12 +286,18 @@ void sfz::Voice::processStereo(AudioSpan buffer) noexcept auto leftBuffer = buffer.getSpan(0); auto rightBuffer = buffer.getSpan(1); + // Amplitude envelope amplitudeEnvelope.getBlock(span1); buffer.applyGain(span1); + // AmpEG envelope egEnvelope.getBlock(span1); buffer.applyGain(span1); + // Volume envelope + volumeEnvelope.getBlock(span1); + buffer.applyGain(span1); + // Create mid/side from left/right in the output buffer ::copy(rightBuffer, span1); ::add(leftBuffer, rightBuffer); diff --git a/sfizz/Voice.h b/sfizz/Voice.h index 37fbccf3..9dc7119f 100644 --- a/sfizz/Voice.h +++ b/sfizz/Voice.h @@ -90,6 +90,7 @@ private: float speedRatio { 1.0 }; float pitchRatio { 1.0 }; + float baseVolumedB{ 0.0 }; float baseGain { 1.0 }; float basePan { 0.0 }; float basePosition { 0.0 }; @@ -119,7 +120,8 @@ private: const CCValueArray& ccState; ADSREnvelope egEnvelope; - LinearEnvelope amplitudeEnvelope; + LinearEnvelope volumeEnvelope; // dB events but the envelope output is linear gain + LinearEnvelope amplitudeEnvelope; // linear events LinearEnvelope panEnvelope; LinearEnvelope positionEnvelope; LinearEnvelope widthEnvelope;