Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CC Curve support for standard EG's #1194

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion external/st_audiofile/thirdparty/wavpack
Submodule wavpack updated 109 files
12 changes: 6 additions & 6 deletions src/sfizz/ADSREnvelope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, int de
void ADSREnvelope::updateValues(int delay) noexcept
{
if (currentState == State::Delay)
this->delay = delay + secondsToSamples(desc_->getDelay(midiState_, triggerVelocity_, delay));
this->attackStep = secondsToLinRate(desc_->getAttack(midiState_, triggerVelocity_, delay));
this->decayRate = secondsToExpRate(desc_->getDecay(midiState_, triggerVelocity_, delay));
this->releaseRate = secondsToExpRate(desc_->getRelease(midiState_, triggerVelocity_, delay));
this->hold = secondsToSamples(desc_->getHold(midiState_, triggerVelocity_, delay));
this->sustain = clamp(desc_->getSustain(midiState_, triggerVelocity_, delay), 0.0f, 1.0f);
this->delay = delay + secondsToSamples(desc_->getDelay(midiState_, curveSet_, triggerVelocity_, delay));
this->attackStep = secondsToLinRate(desc_->getAttack(midiState_, curveSet_, triggerVelocity_, delay));
this->decayRate = secondsToExpRate(desc_->getDecay(midiState_, curveSet_, triggerVelocity_, delay));
this->releaseRate = secondsToExpRate(desc_->getRelease(midiState_, curveSet_, triggerVelocity_, delay));
this->hold = secondsToSamples(desc_->getHold(midiState_, curveSet_, triggerVelocity_, delay));
this->sustain = clamp(desc_->getSustain(midiState_, curveSet_, triggerVelocity_, delay), 0.0f, 1.0f);
this->start = clamp(desc_->getStart(midiState_, triggerVelocity_, delay), 0.0f, 1.0f);
sustainThreshold = this->sustain + config::virtuallyZero;
}
Expand Down
6 changes: 4 additions & 2 deletions src/sfizz/ADSREnvelope.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ namespace sfz {
* @brief Describe an attack/delay/sustain/release envelope that can
* produce its coefficient in a blockwise manner for SIMD-type operations.
*/

class ADSREnvelope {
public:
using Float = float;

ADSREnvelope(const MidiState& state)
: midiState_(state) {}
ADSREnvelope(const MidiState& state, CurveSet& curveSet)
: midiState_(state), curveSet_(curveSet) {}
/**
* @brief Resets the ADSR envelope given a Region, the current midi state, and a delay and
* trigger velocity
Expand Down Expand Up @@ -104,6 +105,7 @@ class ADSREnvelope {
Float currentValue { 0.0 };
const EGDescription* desc_ { nullptr };
const MidiState& midiState_;
CurveSet& curveSet_;
float triggerVelocity_ { 0.0f };
bool dynamic_ { false };
int delay { 0 };
Expand Down
51 changes: 33 additions & 18 deletions src/sfizz/EGDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include "utility/LeakDetector.h"
#include <absl/types/optional.h>

#include "Curve.h"

namespace sfz {
/**
* @brief A description for an SFZ envelope generator, with its envelope parameters
Expand Down Expand Up @@ -66,13 +68,20 @@ struct EGDescription {
float vel2sustain { Default::egPercentMod };
float vel2depth { Default::egVel2Depth };

CCMap<float> ccAttack;
CCMap<float> ccDecay;
CCMap<float> ccDelay;
CCMap<float> ccHold;
CCMap<float> ccRelease;
CCMap<ModifierCurvePair<float>> ccAttack { ModifierCurvePair<float>{ Default::egTime, Default::curveCC } };
CCMap<ModifierCurvePair<float>> ccDecay { ModifierCurvePair<float>{ Default::egTime, Default::curveCC } };
CCMap<ModifierCurvePair<float>> ccDelay { ModifierCurvePair<float>{ Default::egTime, Default::curveCC } };
CCMap<ModifierCurvePair<float>> ccHold { ModifierCurvePair<float>{ Default::egTime, Default::curveCC } };
CCMap<ModifierCurvePair<float>> ccRelease { ModifierCurvePair<float>{ Default::egTime, Default::curveCC } };
CCMap<float> ccStart;
CCMap<float> ccSustain;
CCMap<ModifierCurvePair<float>> ccSustain { ModifierCurvePair<float>{ Default::egPercent, Default::curveCC } };
//CCMap<float> ccAttack;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could probably go away.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're referring to what I commented out, fair point. I just kept it in case revertng was desired.

//CCMap<float> ccDecay;
//CCMap<float> ccDelay;
//CCMap<float> ccHold;
//CCMap<float> ccRelease;
//CCMap<float> ccStart;
//CCMap<float> ccSustain;
bool dynamic { false };

/**
Expand All @@ -82,12 +91,13 @@ struct EGDescription {
* @param velocity
* @return float
*/
float getAttack(const MidiState& state, float velocity, int delay = 0) const noexcept
float getAttack(const MidiState& state, CurveSet& curveSet, float velocity, int delay = 0) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float returnedValue { attack + velocity * vel2attack };
for (auto& mod: ccAttack) {
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
const auto& curve = curveSet.getCurve(mod.data.curve);
returnedValue += curve.evalNormalized(state.getCCValueAt(mod.cc, delay)) * mod.data.modifier;
}
return returnedValue;
}
Expand All @@ -98,12 +108,13 @@ struct EGDescription {
* @param velocity
* @return float
*/
float getDecay(const MidiState& state, float velocity, int delay = 0) const noexcept
float getDecay(const MidiState& state, CurveSet& curveSet, float velocity, int delay = 0) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float returnedValue { decay + velocity * vel2decay };
for (auto& mod: ccDecay) {
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
const auto& curve = curveSet.getCurve(mod.data.curve);
returnedValue += curve.evalNormalized(state.getCCValueAt(mod.cc, delay)) * mod.data.modifier;
}
return returnedValue;
}
Expand All @@ -114,12 +125,13 @@ struct EGDescription {
* @param velocity
* @return float
*/
float getDelay(const MidiState& state, float velocity, int delay = 0) const noexcept
float getDelay(const MidiState& state, CurveSet& curveSet, float velocity, int delay = 0) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float returnedValue { this->delay + velocity * vel2delay };
for (auto& mod: ccDelay) {
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
const auto& curve = curveSet.getCurve(mod.data.curve);
returnedValue += curve.evalNormalized(state.getCCValueAt(mod.cc, delay)) * mod.data.modifier;
}
return returnedValue;
}
Expand All @@ -130,12 +142,13 @@ struct EGDescription {
* @param velocity
* @return float
*/
float getHold(const MidiState& state, float velocity, int delay = 0) const noexcept
float getHold(const MidiState& state, CurveSet& curveSet, float velocity, int delay = 0) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float returnedValue { hold + velocity * vel2hold };
for (auto& mod: ccHold) {
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
const auto& curve = curveSet.getCurve(mod.data.curve);
returnedValue += curve.evalNormalized(state.getCCValueAt(mod.cc, delay)) * mod.data.modifier;
}
return returnedValue;
}
Expand All @@ -146,12 +159,13 @@ struct EGDescription {
* @param velocity
* @return float
*/
float getRelease(const MidiState& state, float velocity, int delay = 0) const noexcept
float getRelease(const MidiState& state, CurveSet& curveSet, float velocity, int delay = 0) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float returnedValue { release + velocity * vel2release };
for (auto& mod: ccRelease) {
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
const auto& curve = curveSet.getCurve(mod.data.curve);
returnedValue += curve.evalNormalized(state.getCCValueAt(mod.cc, delay)) * mod.data.modifier;
}
return returnedValue;
}
Expand All @@ -178,12 +192,13 @@ struct EGDescription {
* @param velocity
* @return float
*/
float getSustain(const MidiState& state, float velocity, int delay = 0) const noexcept
float getSustain(const MidiState& state, CurveSet& curveSet, float velocity, int delay = 0) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float returnedValue { sustain + velocity * vel2sustain };
for (auto& mod: ccSustain) {
returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data;
const auto& curve = curveSet.getCurve(mod.data.curve);
returnedValue += curve.evalNormalized(state.getCCValueAt(mod.cc, delay)) * mod.data.modifier;
}
return returnedValue;
}
Expand Down
12 changes: 6 additions & 6 deletions src/sfizz/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1101,35 +1101,35 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg)
if (opcode.parameters.back() >= config::numCCs)
return false;

eg.ccAttack[opcode.parameters.back()] = opcode.read(Default::egTimeMod);
eg.ccAttack[opcode.parameters.back()].modifier = opcode.read(Default::egTimeMod);

break;
case_any_eg("decay_oncc&"): // also decaycc&
if (opcode.parameters.back() >= config::numCCs)
return false;

eg.ccDecay[opcode.parameters.back()] = opcode.read(Default::egTimeMod);
eg.ccDecay[opcode.parameters.back()].modifier = opcode.read(Default::egTimeMod);

break;
case_any_eg("delay_oncc&"): // also delaycc&
if (opcode.parameters.back() >= config::numCCs)
return false;

eg.ccDelay[opcode.parameters.back()] = opcode.read(Default::egTimeMod);
eg.ccDelay[opcode.parameters.back()].modifier = opcode.read(Default::egTimeMod);

break;
case_any_eg("hold_oncc&"): // also holdcc&
if (opcode.parameters.back() >= config::numCCs)
return false;

eg.ccHold[opcode.parameters.back()] = opcode.read(Default::egTimeMod);
eg.ccHold[opcode.parameters.back()].modifier = opcode.read(Default::egTimeMod);

break;
case_any_eg("release_oncc&"): // also releasecc&
if (opcode.parameters.back() >= config::numCCs)
return false;

eg.ccRelease[opcode.parameters.back()] = opcode.read(Default::egTimeMod);
eg.ccRelease[opcode.parameters.back()].modifier = opcode.read(Default::egTimeMod);

break;
case_any_eg("start_oncc&"): // also startcc&
Expand All @@ -1143,7 +1143,7 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg)
if (opcode.parameters.back() >= config::numCCs)
return false;

eg.ccSustain[opcode.parameters.back()] = opcode.read(Default::egPercentMod);
eg.ccSustain[opcode.parameters.back()].modifier = opcode.read(Default::egPercentMod);

break;

Expand Down
24 changes: 12 additions & 12 deletions src/sfizz/SynthMessaging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,32 +1321,32 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co

MATCH("/region&/ampeg_attack_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
float value = region.amplitudeEG.ccAttack.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, value);
const auto& cc = region.amplitudeEG.ccAttack.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, cc.modifier);
} break;

MATCH("/region&/ampeg_decay_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
float value = region.amplitudeEG.ccDecay.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, value);
const auto& cc = region.amplitudeEG.ccDecay.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, cc.modifier);
} break;

MATCH("/region&/ampeg_delay_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
float value = region.amplitudeEG.ccDelay.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, value);
const auto& cc = region.amplitudeEG.ccDelay.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, cc.modifier);
} break;

MATCH("/region&/ampeg_hold_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
float value = region.amplitudeEG.ccHold.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, value);
const auto& cc = region.amplitudeEG.ccHold.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, cc.modifier);
} break;

MATCH("/region&/ampeg_release_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
float value = region.amplitudeEG.ccRelease.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, value);
const auto& cc = region.amplitudeEG.ccRelease.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, cc.modifier);
} break;

MATCH("/region&/ampeg_start_cc&", "") {
Expand All @@ -1357,8 +1357,8 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co

MATCH("/region&/ampeg_sustain_cc&", "") {
GET_REGION_OR_BREAK(indices[0])
float value = region.amplitudeEG.ccSustain.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, value * 100.0f);
const auto& cc = region.amplitudeEG.ccSustain.getWithDefault(indices[1]);
client.receive<'f'>(delay, path, cc.modifier * 100.0f);
} break;

MATCH("/region&/filter&/cutoff", "") {
Expand Down
6 changes: 3 additions & 3 deletions src/sfizz/Voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ struct Voice::Impl
std::unique_ptr<LFO> lfoPitch_;
std::unique_ptr<LFO> lfoFilter_;

ADSREnvelope egAmplitude_ { resources_.getMidiState() };
ADSREnvelope egAmplitude_ { resources_.getMidiState(), resources_.getCurves() };
std::unique_ptr<ADSREnvelope> egPitch_;
std::unique_ptr<ADSREnvelope> egFilter_;

Expand Down Expand Up @@ -1846,7 +1846,7 @@ void Voice::setPitchEGEnabledPerVoice(bool havePitchEG)
{
Impl& impl = *impl_;
if (havePitchEG)
impl.egPitch_.reset(new ADSREnvelope(impl.resources_.getMidiState()));
impl.egPitch_.reset(new ADSREnvelope(impl.resources_.getMidiState(), impl.resources_.getCurves()));
else
impl.egPitch_.reset();
}
Expand All @@ -1855,7 +1855,7 @@ void Voice::setFilterEGEnabledPerVoice(bool haveFilterEG)
{
Impl& impl = *impl_;
if (haveFilterEG)
impl.egFilter_.reset(new ADSREnvelope(impl.resources_.getMidiState()));
impl.egFilter_.reset(new ADSREnvelope(impl.resources_.getMidiState(), impl.resources_.getCurves()));
else
impl.egFilter_.reset();
}
Expand Down
Loading