Parse EQ and filter parameters in the Regions

This commit is contained in:
Paul Ferrand 2020-02-04 23:49:39 +01:00
parent bc72240bed
commit 3c0cfac953
7 changed files with 617 additions and 0 deletions

View file

@ -124,6 +124,41 @@ namespace Default
constexpr float rtDecay { 0.0f };
constexpr Range<float> rtDecayRange { 0.0f, 200.0f };
// Performance parameters: Filters
constexpr float filterCutoff { 0 };
constexpr float filterResonance { 0 };
constexpr int filterKeytrack { 0 };
constexpr uint8_t filterKeycenter { 60 };
constexpr int filterRandom { 60 };
constexpr int filterVeltrack { 0 };
constexpr int filterCutoffCC { 0 };
constexpr float filterResonanceCC { 0 };
constexpr Range<float> filterCutoffRange { 0.0f, 96000.0f };
constexpr Range<int> filterCutoffModRange { -9600, 9600 };
constexpr Range<int> filterKeytrackRange { 0, 1200 };
constexpr Range<int> filterRandomRange { 0, 9600 };
constexpr Range<int> filterVeltrackRange { -9600, 9600 };
constexpr Range<float> filterResonanceRange { 0, 40 };
// Performance parameters: EQ
constexpr float eqBandwidth { 1.0f };
constexpr float eqBandwidthCC { 0.0f };
constexpr float eqFrequencyUnset { 0.0f };
constexpr float eqFrequency1 { 50.0f };
constexpr float eqFrequency2 { 500.0f };
constexpr float eqFrequency3 { 5000.0f };
constexpr float eqFrequencyCC { 0.0f };
constexpr float eqGain { 0.0f };
constexpr float eqGainCC { 0.0f };
constexpr float eqVel2frequency { 0.0f };
constexpr float eqVel2gain { 0.0f };
constexpr Range<float> eqBandwidthRange { 0.001f, 4.0f };
constexpr Range<float> eqBandwidthModRange { -4.0f, 4.0f };
constexpr Range<float> eqFrequencyRange { 0.0f, 30000.0f };
constexpr Range<float> eqFrequencyModRange { -30000.0f, 30000.0f };
constexpr Range<float> eqGainRange { -96.0f, 24.0f };
constexpr Range<float> eqGainModRange { -96.0f, 24.0f };
// Performance parameters: pitch
constexpr uint8_t pitchKeycenter { 60 };
constexpr int pitchKeytrack { 100 };

19
src/sfizz/EQDescription.h Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include "Config.h"
#include "Defaults.h"
#include "CCMap.h"
namespace sfz
{
struct EQDescription
{
float bandwidth { Default::eqBandwidth };
float frequency { Default::eqFrequencyUnset };
float gain { Default::eqGain };
float vel2frequency { Default::eqVel2frequency };
float vel2gain { Default::eqVel2gain };
CCMap<float> bandwidthCC { Default::eqBandwidthCC };
CCMap<float> frequencyCC { Default::eqFrequencyCC };
CCMap<float> gainCC { Default::eqGainCC };
};
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "Config.h"
#include "Defaults.h"
#include "SfzFilter.h"
#include "CCMap.h"
namespace sfz
{
struct FilterDescription
{
float cutoff { Default::filterCutoff };
float resonance { Default::filterCutoff };
int keytrack { Default::filterKeytrack };
uint8_t keycenter { Default::filterKeycenter };
int veltrack { Default::filterVeltrack };
int random { Default::filterRandom };
FilterType type { FilterType::kFilterLpf2p };
CCMap<int> cutoffCC { Default::filterCutoffCC };
CCMap<float> resonanceCC { Default::filterResonanceCC };
};
}

View file

@ -15,6 +15,18 @@
#include "absl/strings/str_cat.h"
#include <random>
template<class T>
bool extendIfNecessary(std::vector<T>& vec, unsigned size)
{
if (size == 0)
return false;
if (vec.size() < size)
vec.resize(size);
return true;
}
bool sfz::Region::parseOpcode(const Opcode& opcode)
{
const auto backParameter = opcode.backParameter();
@ -381,6 +393,226 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, rtDecay, Default::rtDecayRange);
break;
// Performance parameters: filters
case hash("cutoff"):
{
const auto filterIndex { backParameter.value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(opcode, filters[filterIndex].cutoff, Default::filterCutoffRange);
}
break;
case hash("resonance"):
{
const auto filterIndex { backParameter.value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(opcode, filters[filterIndex].resonance, Default::filterResonanceRange);
}
break;
case hash("cutoff_cc"):
{
if (!backParameter)
return false;
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(
opcode,
filters[filterIndex].cutoffCC[*backParameter],
Default::filterCutoffModRange
);
}
break;
case hash("resonance_cc"):
{
if (!backParameter)
return false;
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(
opcode,
filters[filterIndex].resonanceCC[*backParameter],
Default::filterResonanceRange
);
}
break;
case hash("fil_keytrack"):
{
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(opcode, filters[filterIndex].keytrack, Default::filterKeytrackRange);
}
break;
case hash("fil_keycenter"):
{
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(opcode, filters[filterIndex].keycenter, Default::keyRange);
}
break;
case hash("fil_veltrack"):
{
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(opcode, filters[filterIndex].veltrack, Default::filterVeltrackRange);
}
break;
case hash("fil_random"):
{
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
setValueFromOpcode(opcode, filters[filterIndex].random, Default::filterRandomRange);
}
break;
case hash("fil_type"):
{
const auto filterIndex { opcode.firstParameter().value_or(1) - 1 };
if (!extendIfNecessary(filters, filterIndex + 1))
return false;
switch (hash(opcode.value)) {
case hash("lpf_1p"): filters[filterIndex].type = FilterType::kFilterLpf1p; break;
case hash("hpf_1p"): filters[filterIndex].type = FilterType::kFilterHpf1p; break;
case hash("lpf_2p"): filters[filterIndex].type = FilterType::kFilterLpf2p; break;
case hash("hpf_2p"): filters[filterIndex].type = FilterType::kFilterHpf2p; break;
case hash("bpf_2p"): filters[filterIndex].type = FilterType::kFilterBpf2p; break;
case hash("brf_2p"): filters[filterIndex].type = FilterType::kFilterBrf2p; break;
case hash("bpf_1p"): filters[filterIndex].type = FilterType::kFilterBpf1p; break;
case hash("brf_1p"): filters[filterIndex].type = FilterType::kFilterBrf1p; break;
case hash("apf_1p"): filters[filterIndex].type = FilterType::kFilterApf1p; break;
case hash("lpf_2p_sv"): filters[filterIndex].type = FilterType::kFilterLpf2pSv; break;
case hash("hpf_2p_sv"): filters[filterIndex].type = FilterType::kFilterHpf2pSv; break;
case hash("bpf_2p_sv"): filters[filterIndex].type = FilterType::kFilterBpf2pSv; break;
case hash("brf_2p_sv"): filters[filterIndex].type = FilterType::kFilterBrf2pSv; break;
case hash("lpf_4p"): filters[filterIndex].type = FilterType::kFilterLpf4p; break;
case hash("hpf_4p"): filters[filterIndex].type = FilterType::kFilterHpf4p; break;
case hash("lpf_6p"): filters[filterIndex].type = FilterType::kFilterLpf6p; break;
case hash("hpf_6p"): filters[filterIndex].type = FilterType::kFilterHpf6p; break;
case hash("pink"): filters[filterIndex].type = FilterType::kFilterPink; break;
case hash("lsh"): filters[filterIndex].type = FilterType::kFilterLsh; break;
case hash("hsh"): filters[filterIndex].type = FilterType::kFilterHsh; break;
case hash("peq"): filters[filterIndex].type = FilterType::kFilterPeq; break;
default:
filters[filterIndex].type = FilterType::kFilterNone;
DBG("Unknown filter type: " << std::string(opcode.value));
}
}
break;
// Performance parameters: EQ
case hash("eq_bw"):
{
const auto eqNumber = opcode.firstParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].bandwidth, Default::eqBandwidthRange);
}
break;
case hash("eq_bw_oncc"): [[fallthrough]];
case hash("eq_bwcc"):
{
const auto eqNumber = opcode.firstParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!backParameter)
return false;
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].bandwidthCC[*backParameter], Default::eqBandwidthModRange);
}
break;
case hash("eq_freq"):
{
const auto eqNumber = opcode.firstParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].frequency, Default::eqFrequencyRange);
}
break;
case hash("eq_freq_oncc"): [[fallthrough]];
case hash("eq_freqcc"):
{
const auto eqNumber = opcode.firstParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!backParameter)
return false;
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].frequencyCC[*backParameter], Default::eqFrequencyModRange);
}
break;
case hash("eq_velfreq"):
{
const auto eqNumber = opcode.firstParameter();
const auto check2 = opcode.middleParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!check2 || *check2 != 2 || opcode.parameterPositions[1] != 6)
return false; // was eqN_vel3freq or something else than eqN_vel2freq
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].vel2frequency, Default::eqFrequencyModRange);
}
break;
case hash("eq_gain"):
{
const auto eqNumber = opcode.firstParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].gain, Default::eqGainRange);
}
break;
case hash("eq_gain_oncc"): [[fallthrough]];
case hash("eq_gaincc"):
{
const auto eqNumber = opcode.firstParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!backParameter)
return false;
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].gainCC[*backParameter], Default::eqGainModRange);
}
break;
case hash("eq_velgain"):
{
const auto eqNumber = opcode.firstParameter();
const auto check2 = opcode.middleParameter();
if (!eqNumber || *eqNumber == 0)
return false;
if (!check2 || *check2 != 2 || opcode.parameterPositions[1] != 6)
return false; // was eqN_vel3gain or something else than eqN_vel2gain
if (!extendIfNecessary(equalizers, *eqNumber))
return false;
setValueFromOpcode(opcode, equalizers[*eqNumber - 1].vel2gain, Default::eqGainModRange);
}
break;
// Performance parameters: pitch
case hash("pitch_keycenter"):
setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange);

View file

@ -9,6 +9,8 @@
#include "LeakDetector.h"
#include "Defaults.h"
#include "EGDescription.h"
#include "EQDescription.h"
#include "FilterDescription.h"
#include "Opcode.h"
#include "AudioBuffer.h"
#include "MidiState.h"
@ -274,6 +276,9 @@ struct Region {
CCMap<Range<uint8_t>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
float rtDecay { Default::rtDecay }; // rt_decay
// Filters and EQs
std::vector<EQDescription> equalizers;
std::vector<FilterDescription> filters;
// Performance parameters: pitch
uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter

View file

@ -295,6 +295,23 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
region->registerNoteOff(*defaultSwitch, 0, 1.0);
}
// Set the default frequencies on equalizers if needed
if (region->equalizers.size() > 0
&& region->equalizers[0].frequency == Default::eqFrequencyUnset)
{
region->equalizers[0].frequency = Default::eqFrequency1;
if (region->equalizers.size() > 1
&& region->equalizers[1].frequency == Default::eqFrequencyUnset)
{
region->equalizers[1].frequency = Default::eqFrequency2;
if (region->equalizers.size() > 2
&& region->equalizers[2].frequency == Default::eqFrequencyUnset)
{
region->equalizers[2].frequency = Default::eqFrequency3;
}
}
}
addEndpointsToVelocityCurve(*region);
region->registerPitchWheel(0);
region->registerAftertouch(0);

View file

@ -1029,6 +1029,294 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "sostenuto_sw", "obladi" });
REQUIRE(region.checkSostenuto);
}
SECTION("Filter stacking and cutoffs")
{
REQUIRE(region.filters.empty());
region.parseOpcode({ "cutoff", "500" });
REQUIRE(region.filters.size() == 1);
REQUIRE(region.filters[0].cutoff == 500.0f);
// Check filter defaults
REQUIRE(region.filters[0].keycenter == 60);
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p);
REQUIRE(region.filters[0].keytrack == 0);
REQUIRE(region.filters[0].veltrack == 0);
REQUIRE(region.filters[0].resonance == 0.0f);
REQUIRE(region.filters[0].cutoffCC.empty());
REQUIRE(region.filters[0].resonanceCC.empty());
region.parseOpcode({ "cutoff2", "5000" });
REQUIRE(region.filters.size() == 2);
REQUIRE(region.filters[1].cutoff == 5000.0f);
// Check filter defaults
REQUIRE(region.filters[1].keycenter == 60);
REQUIRE(region.filters[1].type == sfz::FilterType::kFilterLpf2p);
REQUIRE(region.filters[1].keytrack == 0);
REQUIRE(region.filters[1].veltrack == 0);
REQUIRE(region.filters[1].resonance == 0.0f);
REQUIRE(region.filters[1].cutoffCC.empty());
REQUIRE(region.filters[1].resonanceCC.empty());
region.parseOpcode({ "cutoff4", "50" });
REQUIRE(region.filters.size() == 4);
REQUIRE(region.filters[2].cutoff == 0.0f);
REQUIRE(region.filters[3].cutoff == 50.0f);
// Check filter defaults
REQUIRE(region.filters[2].keycenter == 60);
REQUIRE(region.filters[2].type == sfz::FilterType::kFilterLpf2p);
REQUIRE(region.filters[2].keytrack == 0);
REQUIRE(region.filters[2].veltrack == 0);
REQUIRE(region.filters[2].resonance == 0.0f);
REQUIRE(region.filters[2].cutoffCC.empty());
REQUIRE(region.filters[2].resonanceCC.empty());
REQUIRE(region.filters[3].keycenter == 60);
REQUIRE(region.filters[3].type == sfz::FilterType::kFilterLpf2p);
REQUIRE(region.filters[3].keytrack == 0);
REQUIRE(region.filters[3].veltrack == 0);
REQUIRE(region.filters[3].resonance == 0.0f);
REQUIRE(region.filters[3].cutoffCC.empty());
REQUIRE(region.filters[3].resonanceCC.empty());
}
SECTION("Filter parameter dispatch")
{
region.parseOpcode({ "cutoff3", "50" });
REQUIRE(region.filters.size() == 3);
REQUIRE(region.filters[2].cutoff == 50.0f);
region.parseOpcode({ "resonance2", "3" });
REQUIRE(region.filters[1].resonance == 3.0f);
region.parseOpcode({ "fil2_veltrack", "-100" });
REQUIRE(region.filters[1].veltrack == -100);
region.parseOpcode({ "fil3_keytrack", "100" });
REQUIRE(region.filters[2].keytrack == 100);
REQUIRE(region.filters[0].cutoffCC.empty());
region.parseOpcode({ "cutoff1_cc15", "210" });
REQUIRE(region.filters[0].cutoffCC.contains(15));
REQUIRE(region.filters[0].cutoffCC[15] == 210);
region.parseOpcode({ "resonance3_cc24", "10" });
REQUIRE(region.filters[2].resonanceCC.contains(24));
REQUIRE(region.filters[2].resonanceCC[24] == 10);
}
SECTION("Filter values")
{
REQUIRE(region.filters.empty());
region.parseOpcode({ "cutoff", "500" });
REQUIRE(region.filters.size() == 1);
REQUIRE(region.filters[0].cutoff == 500.0f);
region.parseOpcode({ "cutoff", "-100" });
REQUIRE(region.filters[0].cutoff == 0.0f);
region.parseOpcode({ "cutoff", "2000000" });
REQUIRE(region.filters[0].cutoff == 96000.0f);
REQUIRE(region.filters[0].resonance == 0.0f);
region.parseOpcode({ "resonance", "5" });
REQUIRE(region.filters[0].resonance == 5.0f);
region.parseOpcode({ "resonance", "-5" });
REQUIRE(region.filters[0].resonance == 0.0f);
region.parseOpcode({ "resonance", "50" });
REQUIRE(region.filters[0].resonance == 40.0f);
REQUIRE(region.filters[0].veltrack == 0);
region.parseOpcode({ "fil_veltrack", "50" });
REQUIRE(region.filters[0].veltrack == 50);
region.parseOpcode({ "fil_veltrack", "-5" });
REQUIRE(region.filters[0].veltrack == -5);
region.parseOpcode({ "fil_veltrack", "10000" });
REQUIRE(region.filters[0].veltrack == 9600);
region.parseOpcode({ "fil_veltrack", "-10000" });
REQUIRE(region.filters[0].veltrack == -9600);
REQUIRE(region.filters[0].keycenter == 60);
region.parseOpcode({ "fil_keycenter", "50" });
REQUIRE(region.filters[0].keycenter == 50);
region.parseOpcode({ "fil_keycenter", "-2" });
REQUIRE(region.filters[0].keycenter == 0);
region.parseOpcode({ "fil_keycenter", "1000" });
REQUIRE(region.filters[0].keycenter == 127);
region.parseOpcode({ "fil_keycenter", "c4" });
REQUIRE(region.filters[0].keycenter == 60);
}
SECTION("Filter types")
{
REQUIRE(region.filters.empty());
region.parseOpcode({ "fil_type", "lpf_1p" });
REQUIRE(region.filters.size() == 1);
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf1p);
region.parseOpcode({ "fil_type", "lpf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2p);
region.parseOpcode({ "fil_type", "hpf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf1p);
region.parseOpcode({ "fil_type", "hpf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2p);
region.parseOpcode({ "fil_type", "bpf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2p);
region.parseOpcode({ "fil_type", "brf_2p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2p);
region.parseOpcode({ "fil_type", "bpf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf1p);
region.parseOpcode({ "fil_type", "brf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf1p);
region.parseOpcode({ "fil_type", "apf_1p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterApf1p);
region.parseOpcode({ "fil_type", "lpf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf2pSv);
region.parseOpcode({ "fil_type", "hpf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf2pSv);
region.parseOpcode({ "fil_type", "bpf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBpf2pSv);
region.parseOpcode({ "fil_type", "brf_2p_sv" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterBrf2pSv);
region.parseOpcode({ "fil_type", "lpf_4p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf4p);
region.parseOpcode({ "fil_type", "hpf_4p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf4p);
region.parseOpcode({ "fil_type", "lpf_6p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLpf6p);
region.parseOpcode({ "fil_type", "hpf_6p" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHpf6p);
region.parseOpcode({ "fil_type", "pink" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPink);
region.parseOpcode({ "fil_type", "lsh" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterLsh);
region.parseOpcode({ "fil_type", "hsh" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterHsh);
region.parseOpcode({ "fil_type", "peq" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterPeq);
region.parseOpcode({ "fil_type", "unknown" });
REQUIRE(region.filters[0].type == sfz::FilterType::kFilterNone);
}
SECTION("EQ stacking and gains")
{
REQUIRE(region.equalizers.empty());
region.parseOpcode({ "eq1_gain", "6" });
REQUIRE(region.equalizers.size() == 1);
REQUIRE(region.equalizers[0].gain == 6.0f);
// Check defaults
REQUIRE(region.equalizers[0].bandwidth == 1.0f);
REQUIRE(region.equalizers[0].frequency == 0.0f);
REQUIRE(region.equalizers[0].vel2frequency == 0);
REQUIRE(region.equalizers[0].vel2gain == 0);
REQUIRE(region.equalizers[0].frequencyCC.empty());
REQUIRE(region.equalizers[0].bandwidthCC.empty());
REQUIRE(region.equalizers[0].gainCC.empty());
region.parseOpcode({ "eq2_gain", "-400" });
REQUIRE(region.equalizers.size() == 2);
REQUIRE(region.equalizers[1].gain == -96.0f);
// Check defaults
REQUIRE(region.equalizers[1].bandwidth == 1.0f);
REQUIRE(region.equalizers[1].frequency == 0.0f);
REQUIRE(region.equalizers[1].vel2frequency == 0);
REQUIRE(region.equalizers[1].vel2gain == 0);
REQUIRE(region.equalizers[1].frequencyCC.empty());
REQUIRE(region.equalizers[1].bandwidthCC.empty());
REQUIRE(region.equalizers[1].gainCC.empty());
region.parseOpcode({ "eq4_gain", "50" });
REQUIRE(region.equalizers.size() == 4);
REQUIRE(region.equalizers[2].gain == 0.0f);
REQUIRE(region.equalizers[3].gain == 24.0f);
// Check defaults
REQUIRE(region.equalizers[2].bandwidth == 1.0f);
REQUIRE(region.equalizers[2].frequency == 0.0f);
REQUIRE(region.equalizers[2].vel2frequency == 0);
REQUIRE(region.equalizers[2].vel2gain == 0);
REQUIRE(region.equalizers[2].frequencyCC.empty());
REQUIRE(region.equalizers[2].bandwidthCC.empty());
REQUIRE(region.equalizers[2].gainCC.empty());
REQUIRE(region.equalizers[3].bandwidth == 1.0f);
REQUIRE(region.equalizers[3].frequency == 0.0f);
REQUIRE(region.equalizers[3].vel2frequency == 0);
REQUIRE(region.equalizers[3].vel2gain == 0);
REQUIRE(region.equalizers[3].frequencyCC.empty());
REQUIRE(region.equalizers[3].bandwidthCC.empty());
REQUIRE(region.equalizers[3].gainCC.empty());
}
SECTION("EQ parameter dispatch")
{
region.parseOpcode({ "eq3_bw", "2" });
REQUIRE(region.equalizers.size() == 3);
REQUIRE(region.equalizers[2].bandwidth == 2.0f);
region.parseOpcode({ "eq1_gain", "-25" });
REQUIRE(region.equalizers[0].gain == -25.0f);
region.parseOpcode({ "eq2_freq", "300" });
REQUIRE(region.equalizers[1].frequency == 300.0f);
region.parseOpcode({ "eq3_vel2gain", "10" });
REQUIRE(region.equalizers[2].vel2gain == 10.0f);
region.parseOpcode({ "eq1_vel2freq", "100" });
REQUIRE(region.equalizers[0].vel2frequency == 100.0f);
REQUIRE(region.equalizers[0].bandwidthCC.empty());
region.parseOpcode({ "eq1_bwcc24", "0.5" });
REQUIRE(region.equalizers[0].bandwidthCC.contains(24));
REQUIRE(region.equalizers[0].bandwidthCC[24] == 0.5f);
region.parseOpcode({ "eq1_bw_oncc24", "1.5" });
REQUIRE(region.equalizers[0].bandwidthCC[24] == 1.5f);
region.parseOpcode({ "eq3_freqcc15", "10" });
REQUIRE(region.equalizers[2].frequencyCC.contains(15));
REQUIRE(region.equalizers[2].frequencyCC[15] == 10.0f);
region.parseOpcode({ "eq3_freq_oncc15", "20" });
REQUIRE(region.equalizers[2].frequencyCC[15] == 20.0f);
region.parseOpcode({ "eq2_gaincc123", "2" });
REQUIRE(region.equalizers[1].gainCC.contains(123));
REQUIRE(region.equalizers[1].gainCC[123] == 2.0f);
region.parseOpcode({ "eq2_gain_oncc123", "-2" });
REQUIRE(region.equalizers[1].gainCC[123] == -2.0f);
}
SECTION("EQ parameter values")
{
region.parseOpcode({ "eq1_bw", "2" });
REQUIRE(region.equalizers.size() == 1);
REQUIRE(region.equalizers[0].bandwidth == 2.0f);
region.parseOpcode({ "eq1_bw", "5" });
REQUIRE(region.equalizers[0].bandwidth == 4.0f);
region.parseOpcode({ "eq1_bw", "0" });
REQUIRE(region.equalizers[0].bandwidth == 0.001f);
region.parseOpcode({ "eq1_freq", "300" });
REQUIRE(region.equalizers[0].frequency == 300.0f);
region.parseOpcode({ "eq1_freq", "-300" });
REQUIRE(region.equalizers[0].frequency == 0.0f);
region.parseOpcode({ "eq1_freq", "35000" });
REQUIRE(region.equalizers[0].frequency == 30000.0f);
region.parseOpcode({ "eq1_vel2gain", "4" });
REQUIRE(region.equalizers[0].vel2gain == 4.0f);
region.parseOpcode({ "eq1_vel2gain", "25" });
REQUIRE(region.equalizers[0].vel2gain == 24.0f);
region.parseOpcode({ "eq1_vel2gain", "-123" });
REQUIRE(region.equalizers[0].vel2gain == -96.0f);
region.parseOpcode({ "eq1_vel2freq", "40" });
REQUIRE(region.equalizers[0].vel2frequency == 40.0f);
region.parseOpcode({ "eq1_vel2freq", "35000" });
REQUIRE(region.equalizers[0].vel2frequency == 30000.0f);
region.parseOpcode({ "eq1_vel2freq", "-35000" });
REQUIRE(region.equalizers[0].vel2frequency == -30000.0f);
region.parseOpcode({ "eq1_bwcc15", "2" });
REQUIRE(region.equalizers[0].bandwidthCC[15] == 2.0f);
region.parseOpcode({ "eq1_bwcc15", "-5" });
REQUIRE(region.equalizers[0].bandwidthCC[15] == -4.0f);
region.parseOpcode({ "eq1_bwcc15", "5" });
REQUIRE(region.equalizers[0].bandwidthCC[15] == 4.0f);
region.parseOpcode({ "eq1_gaincc15", "2" });
REQUIRE(region.equalizers[0].gainCC[15] == 2.0f);
region.parseOpcode({ "eq1_gaincc15", "-500" });
REQUIRE(region.equalizers[0].gainCC[15] == -96.0f);
region.parseOpcode({ "eq1_gaincc15", "50" });
REQUIRE(region.equalizers[0].gainCC[15] == 24.0f);
region.parseOpcode({ "eq1_freqcc15", "200" });
REQUIRE(region.equalizers[0].frequencyCC[15] == 200.0f);
region.parseOpcode({ "eq1_freqcc15", "-50000" });
REQUIRE(region.equalizers[0].frequencyCC[15] == -30000.0f);
region.parseOpcode({ "eq1_freqcc15", "50000" });
REQUIRE(region.equalizers[0].frequencyCC[15] == 30000.0f);
}
}
// Specific region bugs