diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 40357c71..07b327fa 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1724,19 +1724,6 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec spec, return true; } -float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - fast_real_distribution pitchDistribution { 0.0f, pitchRandom }; - float pitchVariationInCents = pitchKeytrack * (noteNumber - float(pitchKeycenter)); // note difference with pitch center - pitchVariationInCents += pitch; // sample tuning - pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose - pitchVariationInCents += velocity * pitchVeltrack; // track velocity - pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes - return centsFactor(pitchVariationInCents); -} - float sfz::Region::getBaseGain() const noexcept { float baseGain = amplitude; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index ab1e76d8..d0abf16d 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -99,15 +99,6 @@ struct Region { */ bool shouldLoop() const noexcept { return (loopMode == LoopMode::loop_continuous || loopMode == LoopMode::loop_sustain); } - /** - * @brief Get the base pitch of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getBasePitchVariation(float noteNumber, float velocity) const noexcept; /** * @brief Get the base gain of the region. * diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp index 90963d7d..e0f7bf86 100644 --- a/src/sfizz/RegionStateful.cpp +++ b/src/sfizz/RegionStateful.cpp @@ -125,10 +125,40 @@ float velocityCurve(const Region& region, float velocity, const MidiState& midiS else gain = velocity * velocity; - gain = std::fabs(region.ampVeltrack) * (1.0f - gain); - gain = (region.ampVeltrack < 0) ? gain : (1.0f - gain); + float veltrack = region.ampVeltrack; + + for (const auto& mod : region.ampVeltrackCC) { + const auto& curve = curveSet.getCurve(mod.data.curve); + const float value = midiState.getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + gain = std::fabs(veltrack) * (1.0f - gain); + gain = (veltrack < 0) ? gain : (1.0f - gain); return gain; } +float basePitchVariation(const Region& region, float noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + fast_real_distribution pitchDistribution { 0.0f, region.pitchRandom }; + float pitchVariationInCents = region.pitchKeytrack * (noteNumber - float(region.pitchKeycenter)); // note difference with pitch center + pitchVariationInCents += region.pitch; // sample tuning + pitchVariationInCents += config::centPerSemitone * region.transpose; // sample transpose + + float veltrack = region.pitchVeltrack; + + for (const auto& mod : region.pitchVeltrackCC) { + const auto& curve = curveSet.getCurve(mod.data.curve); + const float value = midiState.getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + pitchVariationInCents += velocity * veltrack; // track velocity + pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes + return centsFactor(pitchVariationInCents); +} + } diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h index aa33b932..83031265 100644 --- a/src/sfizz/RegionStateful.h +++ b/src/sfizz/RegionStateful.h @@ -94,4 +94,17 @@ uint32_t loopStart(const Region& region, MidiState& midiState) noexcept; */ uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept; +/** + * @brief Get the base pitch of the region depending on which note has been + * pressed and at which velocity. + * + * @param region + * @param noteNumber + * @param velocity + * @param midiState + * @param curveSet + * @return float + */ +float basePitchVariation(const Region& region, float noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fd7c80a2..7841cacd 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -483,7 +483,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc Tuning& tuning = resources.getTuning(); const float numberRetuned = tuning.getKeyFractional12TET(impl.triggerEvent_.number); - impl.pitchRatio_ = region.getBasePitchVariation(numberRetuned, impl.triggerEvent_.value); + impl.pitchRatio_ = basePitchVariation(region, numberRetuned, impl.triggerEvent_.value, midiState, curveSet); // apply stretch tuning if set if (absl::optional& stretch = resources.getStretch()) diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index d468b8c8..30eef25b 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -360,14 +360,16 @@ TEST_CASE("[Region] Offsets with CCs") TEST_CASE("[Region] Pitch variation with veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; - REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 0_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 64_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 127_norm, midiState, curveSet) == 1.0); region.parseOpcode({ "pitch_veltrack", "1200" }); - REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == Approx(centsFactor(600.0)).margin(0.01f)); - REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == Approx(centsFactor(1200.0)).margin(0.01f)); + REQUIRE(basePitchVariation(region, 60.0, 0_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 64_norm, midiState, curveSet) == Approx(centsFactor(600.0)).margin(0.01f)); + REQUIRE(basePitchVariation(region, 60.0, 127_norm, midiState, curveSet) == Approx(centsFactor(1200.0)).margin(0.01f)); } TEST_CASE("[Synth] velcurve")