Add modulations for Flex EG time and level

This commit is contained in:
Jean Pierre Cimalando 2021-06-26 18:56:42 +02:00
parent 78673d683a
commit 503ff04f9b
9 changed files with 61 additions and 11 deletions

View file

@ -152,7 +152,9 @@ BoolSpec flexEGAmpeg { false, {0, 1}, kEnforceBounds };
BoolSpec flexEGDynamic { 0, {0, 1}, kEnforceBounds };
Int32Spec flexEGSustain { 0, {0, 100}, kEnforceLowerBound|kPermissiveUpperBound };
FloatSpec flexEGPointTime { 0.0f, {0.0f, 100.0f}, kPermissiveBounds };
FloatSpec flexEGPointTimeMod { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds };
FloatSpec flexEGPointLevel { 0.0f, {-1.0f, 1.0f}, kPermissiveBounds };
FloatSpec flexEGPointLevelMod { 0.0f, {-1.0f, 1.0f}, kPermissiveBounds };
FloatSpec flexEGPointShape { 0.0f, {-100.0f, 100.0f}, kPermissiveBounds };
Int32Spec sampleQuality { 2, {0, 10}, 0 };
Int32Spec octaveOffset { 0, {-10, 10}, kPermissiveBounds };

View file

@ -264,7 +264,9 @@ namespace Default
extern const OpcodeSpec<bool> flexEGDynamic;
extern const OpcodeSpec<int32_t> flexEGSustain;
extern const OpcodeSpec<float> flexEGPointTime;
extern const OpcodeSpec<float> flexEGPointTimeMod;
extern const OpcodeSpec<float> flexEGPointLevel;
extern const OpcodeSpec<float> flexEGPointLevelMod;
extern const OpcodeSpec<float> flexEGPointShape;
extern const OpcodeSpec<int32_t> sampleQuality;
extern const OpcodeSpec<int32_t> octaveOffset;

View file

@ -6,6 +6,7 @@
#pragma once
#include "Defaults.h"
#include "CCMap.h"
#include <vector>
#include <memory>
@ -21,6 +22,9 @@ struct FlexEGPoint {
float time { Default::flexEGPointTime }; // duration until next step (s)
float level { Default::flexEGPointLevel }; // normalized amplitude
CCMap<float> ccTime;
CCMap<float> ccLevel;
void setShape(float shape);
float shape() const noexcept { return shape_; }
const Curve& curve() const;

View file

@ -9,8 +9,11 @@
- [ ] egN_points (purpose unknown)
- [x] egN_timeX
- [x] egN_timeX_onccY
- [x] egN_levelX
- [x] egN_levelX_onccY
- [x] egN_shapeX
- [ ] egN_shapeX_onccY
- [x] egN_sustain
- [ ] egN_dynamic
- [ ] egN_loop
@ -21,6 +24,8 @@
#include "FlexEnvelope.h"
#include "FlexEGDescription.h"
#include "Curve.h"
#include "MidiState.h"
#include "Resources.h"
#include "Config.h"
#include "SIMDHelpers.h"
#include <absl/types/optional.h>
@ -28,6 +33,7 @@
namespace sfz {
struct FlexEnvelope::Impl {
const Resources* resources_ { nullptr };
const FlexEGDescription* desc_ { nullptr };
float samplePeriod_ { 1.0 / config::defaultSampleRate };
size_t delayFramesLeft_ { 0 };
@ -53,9 +59,11 @@ struct FlexEnvelope::Impl {
bool advanceToNextStage();
};
FlexEnvelope::FlexEnvelope()
FlexEnvelope::FlexEnvelope(Resources &resources)
: impl_(new Impl)
{
Impl& impl = *impl_;
impl.resources_ = &resources;
}
FlexEnvelope::~FlexEnvelope()
@ -249,6 +257,7 @@ void FlexEnvelope::Impl::process(absl::Span<float> out)
bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber)
{
const FlexEGDescription& desc = *desc_;
const MidiState& midiState = resources_->getMidiState();
currentStageNumber_ = stageNumber;
@ -258,7 +267,11 @@ bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber)
const FlexEGPoint& point = desc.points[stageNumber];
stageSourceLevel_ = currentLevel_;
stageTargetLevel_ = point.level;
for (const CCData<float>& mod : point.ccLevel)
stageTargetLevel_ += midiState.getCCValue(mod.cc) * mod.data;
stageTime_ = point.time;
for (const CCData<float>& mod : point.ccTime)
stageTime_ += midiState.getCCValue(mod.cc) * mod.data;
stageSustained_ = int(stageNumber) == desc.sustain;
stageCurve_ = &point.curve();

View file

@ -10,13 +10,14 @@
namespace sfz {
struct FlexEGDescription;
class Resources;
/**
Flex envelope generator (according to ARIA)
*/
class FlexEnvelope {
public:
FlexEnvelope();
explicit FlexEnvelope(Resources &resources);
~FlexEnvelope();
/**

View file

@ -1452,12 +1452,32 @@ bool sfz::Region::parseEGOpcodeV2(const Opcode& opcode)
else
return false;
break;
case hash("eg&_time&_oncc&"):
if (FlexEGPoint* point = getOrCreateEGPoint()) {
auto ccNumber = opcode.parameters.back();
if (ccNumber >= config::numCCs)
return false;
point->ccTime[ccNumber] = opcode.read(Default::flexEGPointTimeMod);
}
else
return false;
break;
case hash("eg&_level&"):
if (FlexEGPoint* point = getOrCreateEGPoint())
point->level = opcode.read(Default::flexEGPointLevel);
else
return false;
break;
case hash("eg&_level&_oncc&"):
if (FlexEGPoint* point = getOrCreateEGPoint()) {
auto ccNumber = opcode.parameters.back();
if (ccNumber >= config::numCCs)
return false;
point->ccLevel[ccNumber] = opcode.read(Default::flexEGPointLevelMod);
}
else
return false;
break;
case hash("eg&_shape&"):
if (FlexEGPoint* point = getOrCreateEGPoint())
point->setShape(opcode.read(Default::flexEGPointShape));

View file

@ -2068,6 +2068,12 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, co
collectUsedCCsFromCCMap(usedCCs, lfo.delayCC);
collectUsedCCsFromCCMap(usedCCs, lfo.fadeCC);
}
for (const FlexEGDescription& flexEG : region.flexEGs) {
for (const FlexEGPoint& point : flexEG.points) {
collectUsedCCsFromCCMap(usedCCs, point.ccTime);
collectUsedCCsFromCCMap(usedCCs, point.ccLevel);
}
}
collectUsedCCsFromCCMap(usedCCs, region.ccConditions);
collectUsedCCsFromCCMap(usedCCs, region.ccTriggers);
collectUsedCCsFromCCMap(usedCCs, region.crossfadeCCInRange);

View file

@ -1817,10 +1817,12 @@ void Voice::setMaxLFOsPerVoice(size_t numLFOs)
void Voice::setMaxFlexEGsPerVoice(size_t numFlexEGs)
{
Impl& impl = *impl_;
Resources& resources = impl.resources_;
impl.flexEGs_.resize(numFlexEGs);
for (size_t i = 0; i < numFlexEGs; ++i) {
auto eg = absl::make_unique<FlexEnvelope>();
auto eg = absl::make_unique<FlexEnvelope>(resources);
eg->setSampleRate(impl.sampleRate_);
impl.flexEGs_[i] = std::move(eg);
}

View file

@ -107,7 +107,7 @@ TEST_CASE("[FlexEG] Coarse numerical envelope test (No release)")
eg1_time2=0.5 eg1_level2=1
eg1_sustain=2
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -132,7 +132,7 @@ TEST_CASE("[FlexEG] Detailed numerical envelope test")
eg1_time2=0.5 eg1_level2=1
eg1_sustain=2
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -155,7 +155,7 @@ TEST_CASE("[FlexEG] Coarse numerical envelope test (with release)")
eg1_time2=0.5 eg1_level2=1
eg1_sustain=2
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -184,7 +184,7 @@ TEST_CASE("[FlexEG] Detailed numerical envelope test (with release and release r
eg1_time3=0.5 eg1_level3=0
eg1_sustain=2
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -216,7 +216,7 @@ TEST_CASE("[FlexEG] Coarse numerical envelope test (with shapes)")
eg1_sustain=2
eg1_time3=0.5 eg1_level3=0 eg1_shape3=4
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -244,7 +244,7 @@ TEST_CASE("[FlexEG] Detailed numerical envelope test (with shapes)")
eg1_time3=0.5 eg1_level3=0 eg1_shape3=4
eg1_sustain=2
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -276,7 +276,7 @@ TEST_CASE("[FlexEG] Zero delay transitions")
eg1_time3=1 eg1_level3=.5 eg1_sustain=3
eg1_time4=1 eg1_level4=1
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->flexEGs.size() == 1);
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);
@ -303,7 +303,7 @@ TEST_CASE("[FlexEG] Early release")
eg1_time2=1.0 eg1_level2=1.0 eg1_sustain=2
eg1_time3=1.0 eg1_level3=0.0
)");
sfz::FlexEnvelope envelope;
sfz::FlexEnvelope envelope(synth.getResources());
REQUIRE(synth.getNumRegions() == 1);
REQUIRE(synth.getRegionView(0)->flexEGs.size() == 1);
envelope.configure(&synth.getRegionView(0)->flexEGs[0]);