ampeg_XX_oncc can now contain multiple modifiers

This commit is contained in:
Paul Ferrand 2020-07-01 17:37:44 +02:00
parent 792b62cc78
commit 0e2ebe6d46
4 changed files with 227 additions and 77 deletions

View file

@ -80,13 +80,13 @@ struct EGDescription {
float vel2sustain { Default::vel2sustain };
int vel2depth { Default::depth };
absl::optional<CCData<float>> ccAttack;
absl::optional<CCData<float>> ccDecay;
absl::optional<CCData<float>> ccDelay;
absl::optional<CCData<float>> ccHold;
absl::optional<CCData<float>> ccRelease;
absl::optional<CCData<float>> ccStart;
absl::optional<CCData<float>> ccSustain;
CCMap<float> ccAttack;
CCMap<float> ccDecay;
CCMap<float> ccDelay;
CCMap<float> ccHold;
CCMap<float> ccRelease;
CCMap<float> ccStart;
CCMap<float> ccSustain;
/**
* @brief Get the attack with possibly a CC modifier and a velocity modifier
@ -98,7 +98,11 @@ struct EGDescription {
float getAttack(const MidiState& state, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + velocity * vel2attack);
float returnedValue { attack + velocity * vel2attack };
for (auto& mod: ccAttack) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
}
/**
* @brief Get the decay with possibly a CC modifier and a velocity modifier
@ -110,7 +114,11 @@ struct EGDescription {
float getDecay(const MidiState& state, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + velocity * vel2decay);
float returnedValue { decay + velocity * vel2decay };
for (auto& mod: ccDecay) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
}
/**
* @brief Get the delay with possibly a CC modifier and a velocity modifier
@ -122,7 +130,11 @@ struct EGDescription {
float getDelay(const MidiState& state, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + velocity * vel2delay);
float returnedValue { delay + velocity * vel2delay };
for (auto& mod: ccDelay) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
}
/**
* @brief Get the holding duration with possibly a CC modifier and a velocity modifier
@ -134,7 +146,11 @@ struct EGDescription {
float getHold(const MidiState& state, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + velocity * vel2hold);
float returnedValue { hold + velocity * vel2hold };
for (auto& mod: ccHold) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
}
/**
* @brief Get the release duration with possibly a CC modifier and a velocity modifier
@ -146,7 +162,11 @@ struct EGDescription {
float getRelease(const MidiState& state, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + velocity * vel2release);
float returnedValue { release + velocity * vel2release };
for (auto& mod: ccRelease) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egTimeRange.clamp(returnedValue);
}
/**
* @brief Get the starting level with possibly a CC modifier and a velocity modifier
@ -158,7 +178,11 @@ struct EGDescription {
float getStart(const MidiState& state, float velocity) const noexcept
{
UNUSED(velocity);
return Default::egPercentRange.clamp(ccSwitchedValue(state, ccStart, start));
float returnedValue { start };
for (auto& mod: ccStart) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egPercentRange.clamp(returnedValue);
}
/**
* @brief Get the sustain level with possibly a CC modifier and a velocity modifier
@ -170,7 +194,11 @@ struct EGDescription {
float getSustain(const MidiState& state, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + velocity * vel2sustain);
float returnedValue { sustain + velocity * vel2sustain };
for (auto& mod: ccSustain) {
returnedValue += state.getCCValue(mod.cc) * mod.data;
}
return Default::egPercentRange.clamp(returnedValue);
}
LEAK_DETECTOR(EGDescription);
};

View file

@ -816,25 +816,60 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
setValueFromOpcode(opcode, amplitudeEG.vel2sustain, Default::egOnCCPercentRange);
break;
case hash("ampeg_attack_oncc&"): // also ampeg_attackcc&
setCCPairFromOpcode(opcode, amplitudeEG.ccAttack, Default::egOnCCTimeRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange))
amplitudeEG.ccAttack[opcode.parameters.back()] = *value;
break;
case hash("ampeg_decay_oncc&"): // also ampeg_decaycc&
setCCPairFromOpcode(opcode, amplitudeEG.ccDecay, Default::egOnCCTimeRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange))
amplitudeEG.ccDecay[opcode.parameters.back()] = *value;
break;
case hash("ampeg_delay_oncc&"): // also ampeg_delaycc&
setCCPairFromOpcode(opcode, amplitudeEG.ccDelay, Default::egOnCCTimeRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange))
amplitudeEG.ccDelay[opcode.parameters.back()] = *value;
break;
case hash("ampeg_hold_oncc&"): // also ampeg_holdcc&
setCCPairFromOpcode(opcode, amplitudeEG.ccHold, Default::egOnCCTimeRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange))
amplitudeEG.ccHold[opcode.parameters.back()] = *value;
break;
case hash("ampeg_release_oncc&"): // also ampeg_releasecc&
setCCPairFromOpcode(opcode, amplitudeEG.ccRelease, Default::egOnCCTimeRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCTimeRange))
amplitudeEG.ccRelease[opcode.parameters.back()] = *value;
break;
case hash("ampeg_start_oncc&"): // also ampeg_startcc&
setCCPairFromOpcode(opcode, amplitudeEG.ccStart, Default::egOnCCPercentRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCPercentRange))
amplitudeEG.ccStart[opcode.parameters.back()] = *value;
break;
case hash("ampeg_sustain_oncc&"): // also ampeg_sustaincc&
setCCPairFromOpcode(opcode, amplitudeEG.ccSustain, Default::egOnCCPercentRange);
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::egOnCCPercentRange))
amplitudeEG.ccSustain[opcode.parameters.back()] = *value;
break;
case hash("effect&"):

View file

@ -17,14 +17,21 @@ TEST_CASE("[EGDescription] Attack range")
sfz::MidiState state;
eg.attack = 1;
eg.vel2attack = -1.27f;
eg.ccAttack = { 63, 1.27f };
eg.ccAttack[63] = 1.27f;
REQUIRE(eg.getAttack(state, 0_norm) == 1.0f);
REQUIRE(eg.getAttack(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getAttack(state, 127_norm) == 1.0f);
REQUIRE(eg.getAttack(state, 0_norm) == 2.27f);
eg.ccAttack = { 63, 127.0f };
eg.ccAttack[63] = 127.0f;
REQUIRE(eg.getAttack(state, 0_norm) == 100.0f);
eg.ccAttack[63] = 1.27f;
eg.ccAttack[65] = 1.0f;
REQUIRE(eg.getAttack(state, 0_norm) == 2.27f);
REQUIRE(eg.getAttack(state, 127_norm) == 1.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getAttack(state, 0_norm) == 3.27f);
REQUIRE(eg.getAttack(state, 127_norm) == 2.0f);
}
TEST_CASE("[EGDescription] Delay range")
@ -33,14 +40,21 @@ TEST_CASE("[EGDescription] Delay range")
sfz::MidiState state;
eg.delay = 1;
eg.vel2delay = -1.27f;
eg.ccDelay = { 63, 1.27f };
eg.ccDelay[63] = 1.27f;
REQUIRE(eg.getDelay(state, 0_norm) == 1.0f);
REQUIRE(eg.getDelay(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getDelay(state, 127_norm) == 1.0f);
REQUIRE(eg.getDelay(state, 0_norm) == 2.27f);
eg.ccDelay = { 63, 127.0f };
eg.ccDelay[63] = 127.0f;
REQUIRE(eg.getDelay(state, 0_norm) == 100.0f);
eg.ccDelay[63] = 1.27f;
eg.ccDelay[65] = 1.0f;
REQUIRE(eg.getDelay(state, 0_norm) == 2.27f);
REQUIRE(eg.getDelay(state, 127_norm) == 1.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getDelay(state, 0_norm) == 3.27f);
REQUIRE(eg.getDelay(state, 127_norm) == 2.0f);
}
TEST_CASE("[EGDescription] Decay range")
@ -49,14 +63,21 @@ TEST_CASE("[EGDescription] Decay range")
sfz::MidiState state;
eg.decay = 1.0f;
eg.vel2decay = -1.27f;
eg.ccDecay = { 63, 1.27f };
eg.ccDecay[63] = 1.27f;
REQUIRE(eg.getDecay(state, 0_norm) == 1.0f);
REQUIRE(eg.getDecay(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getDecay(state, 127_norm) == 1.0f);
REQUIRE(eg.getDecay(state, 0_norm) == 2.27f);
eg.ccDecay = { 63, 127.0f };
eg.ccDecay[63] = 127.0f;
REQUIRE(eg.getDecay(state, 0_norm) == 100.0f);
eg.ccDecay[63] = 1.27f;
eg.ccDecay[65] = 1.0f;
REQUIRE(eg.getDecay(state, 0_norm) == 2.27f);
REQUIRE(eg.getDecay(state, 127_norm) == 1.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getDecay(state, 0_norm) == 3.27f);
REQUIRE(eg.getDecay(state, 127_norm) == 2.0f);
}
TEST_CASE("[EGDescription] Release range")
@ -65,14 +86,21 @@ TEST_CASE("[EGDescription] Release range")
sfz::MidiState state;
eg.release = 1;
eg.vel2release = -1.27f;
eg.ccRelease = { 63, 1.27f };
eg.ccRelease[63] = 1.27f;
REQUIRE(eg.getRelease(state, 0_norm) == 1.0f);
REQUIRE(eg.getRelease(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getRelease(state, 127_norm) == 1.0f);
REQUIRE(eg.getRelease(state, 0_norm) == 2.27f);
eg.ccRelease = { 63, 127.0f };
eg.ccRelease[63] = 127.0f;
REQUIRE(eg.getRelease(state, 0_norm) == 100.0f);
eg.ccRelease[63] = 1.27f;
eg.ccRelease[65] = 1.0f;
REQUIRE(eg.getRelease(state, 0_norm) == 2.27f);
REQUIRE(eg.getRelease(state, 127_norm) == 1.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getRelease(state, 0_norm) == 3.27f);
REQUIRE(eg.getRelease(state, 127_norm) == 2.0f);
}
TEST_CASE("[EGDescription] Hold range")
@ -81,14 +109,21 @@ TEST_CASE("[EGDescription] Hold range")
sfz::MidiState state;
eg.hold = 1;
eg.vel2hold = -1.27f;
eg.ccHold = { 63, 1.27f };
eg.ccHold[63] = 1.27f;
REQUIRE(eg.getHold(state, 0_norm) == 1.0f);
REQUIRE(eg.getHold(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getHold(state, 127_norm) == 1.0f);
REQUIRE(eg.getHold(state, 0_norm) == 2.27f);
eg.ccHold = { 63, 127.0f };
eg.ccHold[63] = 127.0f;
REQUIRE(eg.getHold(state, 0_norm) == 100.0f);
eg.ccHold[63] = 1.27f;
eg.ccHold[65] = 1.0f;
REQUIRE(eg.getHold(state, 0_norm) == 2.27f);
REQUIRE(eg.getHold(state, 127_norm) == 1.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getHold(state, 0_norm) == 3.27f);
REQUIRE(eg.getHold(state, 127_norm) == 2.0f);
}
TEST_CASE("[EGDescription] Sustain level")
@ -97,13 +132,21 @@ TEST_CASE("[EGDescription] Sustain level")
sfz::MidiState state;
eg.sustain = 50;
eg.vel2sustain = -100;
eg.ccSustain = { 63, 100.0f };
eg.ccSustain[63] = 100.0f;
REQUIRE(eg.getSustain(state, 0_norm) == 50.0f);
REQUIRE(eg.getSustain(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getSustain(state, 127_norm) == 50.0f);
eg.ccSustain = { 63, 200.0f };
eg.ccSustain[63] = 200.0f;
REQUIRE(eg.getSustain(state, 0_norm) == 100.0f);
eg.sustain = 0;
eg.ccSustain[63] = 50.0f;
eg.ccSustain[65] = 50.0f;
REQUIRE(eg.getSustain(state, 0_norm) == 50.0f);
REQUIRE(eg.getSustain(state, 127_norm) == 0.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getSustain(state, 0_norm) == 100.0f);
REQUIRE(eg.getSustain(state, 127_norm) == 0.0f);
}
TEST_CASE("[EGDescription] Start level")
@ -111,11 +154,19 @@ TEST_CASE("[EGDescription] Start level")
sfz::EGDescription eg;
sfz::MidiState state;
eg.start = 0;
eg.ccStart = { 63, 127.0f };
eg.ccStart[63] = 127.0f;
REQUIRE(eg.getStart(state, 0_norm) == 0.0f);
REQUIRE(eg.getStart(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm);
REQUIRE(eg.getStart(state, 0_norm) == 100.0f);
eg.ccStart = { 63, -127.0f };
eg.ccStart[63] = -127.0f;
REQUIRE(eg.getStart(state, 0_norm) == 0.0f);
eg.start = 0;
eg.ccStart[63] = 50.0f;
eg.ccStart[65] = 50.0f;
REQUIRE(eg.getStart(state, 0_norm) == 50.0f);
REQUIRE(eg.getStart(state, 127_norm) == 50.0f);
state.ccEvent(0, 65, 127_norm);
REQUIRE(eg.getStart(state, 0_norm) == 100.0f);
REQUIRE(eg.getStart(state, 127_norm) == 100.0f);
}

View file

@ -1083,13 +1083,13 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("ampeg_XX_onccNN")
{
// Defaults
REQUIRE(!region.amplitudeEG.ccAttack);
REQUIRE(!region.amplitudeEG.ccDecay);
REQUIRE(!region.amplitudeEG.ccDelay);
REQUIRE(!region.amplitudeEG.ccHold);
REQUIRE(!region.amplitudeEG.ccRelease);
REQUIRE(!region.amplitudeEG.ccStart);
REQUIRE(!region.amplitudeEG.ccSustain);
REQUIRE(region.amplitudeEG.ccAttack.empty());
REQUIRE(region.amplitudeEG.ccDecay.empty());
REQUIRE(region.amplitudeEG.ccDelay.empty());
REQUIRE(region.amplitudeEG.ccHold.empty());
REQUIRE(region.amplitudeEG.ccRelease.empty());
REQUIRE(region.amplitudeEG.ccStart.empty());
REQUIRE(region.amplitudeEG.ccSustain.empty());
//
region.parseOpcode({ "ampeg_attack_oncc1", "1" });
region.parseOpcode({ "ampeg_decay_oncc2", "2" });
@ -1098,27 +1098,20 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "ampeg_release_oncc5", "5" });
region.parseOpcode({ "ampeg_start_oncc6", "6" });
region.parseOpcode({ "ampeg_sustain_oncc7", "7" });
REQUIRE(region.amplitudeEG.ccAttack);
REQUIRE(region.amplitudeEG.ccDecay);
REQUIRE(region.amplitudeEG.ccDelay);
REQUIRE(region.amplitudeEG.ccHold);
REQUIRE(region.amplitudeEG.ccRelease);
REQUIRE(region.amplitudeEG.ccStart);
REQUIRE(region.amplitudeEG.ccSustain);
REQUIRE(region.amplitudeEG.ccAttack->cc == 1);
REQUIRE(region.amplitudeEG.ccDecay->cc == 2);
REQUIRE(region.amplitudeEG.ccDelay->cc == 3);
REQUIRE(region.amplitudeEG.ccHold->cc == 4);
REQUIRE(region.amplitudeEG.ccRelease->cc == 5);
REQUIRE(region.amplitudeEG.ccStart->cc == 6);
REQUIRE(region.amplitudeEG.ccSustain->cc == 7);
REQUIRE(region.amplitudeEG.ccAttack->data == 1.0f);
REQUIRE(region.amplitudeEG.ccDecay->data == 2.0f);
REQUIRE(region.amplitudeEG.ccDelay->data == 3.0f);
REQUIRE(region.amplitudeEG.ccHold->data == 4.0f);
REQUIRE(region.amplitudeEG.ccRelease->data == 5.0f);
REQUIRE(region.amplitudeEG.ccStart->data == 6.0f);
REQUIRE(region.amplitudeEG.ccSustain->data == 7.0f);
REQUIRE(region.amplitudeEG.ccAttack.contains(1));
REQUIRE(region.amplitudeEG.ccDecay.contains(2));
REQUIRE(region.amplitudeEG.ccDelay.contains(3));
REQUIRE(region.amplitudeEG.ccHold.contains(4));
REQUIRE(region.amplitudeEG.ccRelease.contains(5));
REQUIRE(region.amplitudeEG.ccStart.contains(6));
REQUIRE(region.amplitudeEG.ccSustain.contains(7));
REQUIRE(region.amplitudeEG.ccAttack[1] == 1.0f);
REQUIRE(region.amplitudeEG.ccDecay[2] == 2.0f);
REQUIRE(region.amplitudeEG.ccDelay[3] == 3.0f);
REQUIRE(region.amplitudeEG.ccHold[4] == 4.0f);
REQUIRE(region.amplitudeEG.ccRelease[5] == 5.0f);
REQUIRE(region.amplitudeEG.ccStart[6] == 6.0f);
REQUIRE(region.amplitudeEG.ccSustain[7] == 7.0f);
//
region.parseOpcode({ "ampeg_attack_oncc1", "101" });
region.parseOpcode({ "ampeg_decay_oncc2", "101" });
@ -1127,13 +1120,13 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "ampeg_release_oncc5", "101" });
region.parseOpcode({ "ampeg_start_oncc6", "101" });
region.parseOpcode({ "ampeg_sustain_oncc7", "101" });
REQUIRE(region.amplitudeEG.ccAttack->data == 100.0f);
REQUIRE(region.amplitudeEG.ccDecay->data == 100.0f);
REQUIRE(region.amplitudeEG.ccDelay->data == 100.0f);
REQUIRE(region.amplitudeEG.ccHold->data == 100.0f);
REQUIRE(region.amplitudeEG.ccRelease->data == 100.0f);
REQUIRE(region.amplitudeEG.ccStart->data == 100.0f);
REQUIRE(region.amplitudeEG.ccSustain->data == 100.0f);
REQUIRE(region.amplitudeEG.ccAttack[1] == 100.0f);
REQUIRE(region.amplitudeEG.ccDecay[2] == 100.0f);
REQUIRE(region.amplitudeEG.ccDelay[3] == 100.0f);
REQUIRE(region.amplitudeEG.ccHold[4] == 100.0f);
REQUIRE(region.amplitudeEG.ccRelease[5] == 100.0f);
REQUIRE(region.amplitudeEG.ccStart[6] == 100.0f);
REQUIRE(region.amplitudeEG.ccSustain[7] == 100.0f);
//
region.parseOpcode({ "ampeg_attack_oncc1", "-101" });
region.parseOpcode({ "ampeg_decay_oncc2", "-101" });
@ -1142,13 +1135,56 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "ampeg_release_oncc5", "-101" });
region.parseOpcode({ "ampeg_start_oncc6", "-101" });
region.parseOpcode({ "ampeg_sustain_oncc7", "-101" });
REQUIRE(region.amplitudeEG.ccAttack->data == -100.0f);
REQUIRE(region.amplitudeEG.ccDecay->data == -100.0f);
REQUIRE(region.amplitudeEG.ccDelay->data == -100.0f);
REQUIRE(region.amplitudeEG.ccHold->data == -100.0f);
REQUIRE(region.amplitudeEG.ccRelease->data == -100.0f);
REQUIRE(region.amplitudeEG.ccStart->data == -100.0f);
REQUIRE(region.amplitudeEG.ccSustain->data == -100.0f);
REQUIRE(region.amplitudeEG.ccAttack[1] == -100.0f);
REQUIRE(region.amplitudeEG.ccDecay[2] == -100.0f);
REQUIRE(region.amplitudeEG.ccDelay[3] == -100.0f);
REQUIRE(region.amplitudeEG.ccHold[4] == -100.0f);
REQUIRE(region.amplitudeEG.ccRelease[5] == -100.0f);
REQUIRE(region.amplitudeEG.ccStart[6] == -100.0f);
REQUIRE(region.amplitudeEG.ccSustain[7] == -100.0f);
//
region.parseOpcode({ "ampeg_attack_oncc1", "1" });
region.parseOpcode({ "ampeg_decay_oncc2", "2" });
region.parseOpcode({ "ampeg_delay_oncc3", "3" });
region.parseOpcode({ "ampeg_hold_oncc4", "4" });
region.parseOpcode({ "ampeg_release_oncc5", "5" });
region.parseOpcode({ "ampeg_start_oncc6", "6" });
region.parseOpcode({ "ampeg_sustain_oncc7", "7" });
region.parseOpcode({ "ampeg_attack_oncc2", "2" });
region.parseOpcode({ "ampeg_decay_oncc3", "3" });
region.parseOpcode({ "ampeg_delay_oncc4", "4" });
region.parseOpcode({ "ampeg_hold_oncc5", "5" });
region.parseOpcode({ "ampeg_release_oncc6", "6" });
region.parseOpcode({ "ampeg_start_oncc7", "7" });
region.parseOpcode({ "ampeg_sustain_oncc8", "8" });
REQUIRE(region.amplitudeEG.ccAttack.contains(1));
REQUIRE(region.amplitudeEG.ccDecay.contains(2));
REQUIRE(region.amplitudeEG.ccDelay.contains(3));
REQUIRE(region.amplitudeEG.ccHold.contains(4));
REQUIRE(region.amplitudeEG.ccRelease.contains(5));
REQUIRE(region.amplitudeEG.ccStart.contains(6));
REQUIRE(region.amplitudeEG.ccSustain.contains(7));
REQUIRE(region.amplitudeEG.ccAttack.contains(2));
REQUIRE(region.amplitudeEG.ccDecay.contains(3));
REQUIRE(region.amplitudeEG.ccDelay.contains(4));
REQUIRE(region.amplitudeEG.ccHold.contains(5));
REQUIRE(region.amplitudeEG.ccRelease.contains(6));
REQUIRE(region.amplitudeEG.ccStart.contains(7));
REQUIRE(region.amplitudeEG.ccSustain.contains(8));
REQUIRE(region.amplitudeEG.ccAttack[1] == 1.0f);
REQUIRE(region.amplitudeEG.ccDecay[2] == 2.0f);
REQUIRE(region.amplitudeEG.ccDelay[3] == 3.0f);
REQUIRE(region.amplitudeEG.ccHold[4] == 4.0f);
REQUIRE(region.amplitudeEG.ccRelease[5] == 5.0f);
REQUIRE(region.amplitudeEG.ccStart[6] == 6.0f);
REQUIRE(region.amplitudeEG.ccSustain[7] == 7.0f);
REQUIRE(region.amplitudeEG.ccAttack[2] == 2.0f);
REQUIRE(region.amplitudeEG.ccDecay[3] == 3.0f);
REQUIRE(region.amplitudeEG.ccDelay[4] == 4.0f);
REQUIRE(region.amplitudeEG.ccHold[5] == 5.0f);
REQUIRE(region.amplitudeEG.ccRelease[6] == 6.0f);
REQUIRE(region.amplitudeEG.ccStart[7] == 7.0f);
REQUIRE(region.amplitudeEG.ccSustain[8] == 8.0f);
}
SECTION("sustain_sw and sostenuto_sw")