diff --git a/sfizz/MidiState.h b/sfizz/MidiState.h index fd575529..b6725533 100644 --- a/sfizz/MidiState.h +++ b/sfizz/MidiState.h @@ -1,10 +1,13 @@ +#pragma once #include #include +#include "SfzHelpers.h" #include "compat/utils.h" + namespace sfz { - SFZ_INLINE std::array noteOnTimes { }; - SFZ_INLINE std::array lastNoteVelocities { }; +struct MidiState +{ inline void noteOn(int noteNumber, uint8_t velocity) { if (noteNumber >= 0 && noteNumber < 128) { @@ -13,7 +16,7 @@ namespace sfz } } - inline float getNoteDuration(int noteNumber) + inline float getNoteDuration(int noteNumber) const { if (noteNumber >= 0 && noteNumber < 128) { const auto noteOffTime = std::chrono::steady_clock::now(); @@ -24,11 +27,15 @@ namespace sfz return 0.0f; } - inline uint8_t getNoteVelocity(int noteNumber) + inline uint8_t getNoteVelocity(int noteNumber) const { if (noteNumber >= 0 && noteNumber < 128) return lastNoteVelocities[noteNumber]; return 0; } + std::array noteOnTimes { }; + std::array lastNoteVelocities { }; + CCValueArray cc; +}; } diff --git a/sfizz/Region.cpp b/sfizz/Region.cpp index e6126be5..39ae614f 100644 --- a/sfizz/Region.cpp +++ b/sfizz/Region.cpp @@ -635,7 +635,7 @@ float sfz::Region::getBaseVolumedB(int noteNumber) noexcept { auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator); if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key) - baseVolumedB -= rtDecay * getNoteDuration(noteNumber); + baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber); return baseVolumedB; } diff --git a/sfizz/Region.h b/sfizz/Region.h index 61a9a6e1..f4173460 100644 --- a/sfizz/Region.h +++ b/sfizz/Region.h @@ -28,6 +28,7 @@ #include "EGDescription.h" #include "Opcode.h" #include "AudioBuffer.h" +#include "MidiState.h" #include #include #include @@ -36,7 +37,8 @@ namespace sfz { struct Region { - Region() + Region(const MidiState& midiState) + : midiState(midiState) { ccSwitched.set(); } @@ -154,6 +156,7 @@ struct Region { double sampleRate { config::defaultSampleRate }; std::shared_ptr> preloadedData { nullptr }; private: + const MidiState& midiState; bool keySwitched { true }; bool previousKeySwitched { true }; bool sequenceSwitched { true }; diff --git a/sfizz/SfzHelpers.h b/sfizz/SfzHelpers.h index 10151814..87df290f 100644 --- a/sfizz/SfzHelpers.h +++ b/sfizz/SfzHelpers.h @@ -44,7 +44,7 @@ inline constexpr float centsFactor(T cents, T centsPerOctave = 1200) template inline constexpr float normalizeCC(T ccValue) { - static_assert(std::is_integral::value); + static_assert(std::is_integral::value, "Requires an integral T"); return static_cast(std::min(std::max(ccValue, static_cast(0)), static_cast(127))) / 127.0f; } diff --git a/sfizz/Synth.cpp b/sfizz/Synth.cpp index 87a647c2..4f9875bc 100644 --- a/sfizz/Synth.cpp +++ b/sfizz/Synth.cpp @@ -37,7 +37,7 @@ using namespace std::literals; sfz::Synth::Synth() { for (int i = 0; i < config::numVoices; ++i) - voices.push_back(std::make_unique(ccState)); + voices.push_back(std::make_unique(midiState)); voiceViewArray.reserve(config::numVoices); } @@ -83,7 +83,7 @@ void sfz::Synth::callback(absl::string_view header, const std::vector& m void sfz::Synth::buildRegion(const std::vector& regionOpcodes) { - auto lastRegion = std::make_unique(); + auto lastRegion = std::make_unique(midiState); auto parseOpcodes = [&](const auto& opcodes) { for (auto& opcode : opcodes) { @@ -114,7 +114,7 @@ void sfz::Synth::clear() numCurves = 0; fileTicket = -1; defaultSwitch = absl::nullopt; - for (auto& state : ccState) + for (auto& state : midiState.cc) state = 0; ccNames.clear(); globalOpcodes.clear(); @@ -142,7 +142,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Set_cc"): [[fallthrough]]; case hash("set_cc"): if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter)) - setValueFromOpcode(member, ccState[*member.parameter], Default::ccRange); + setValueFromOpcode(member, midiState.cc[*member.parameter], Default::ccRange); break; case hash("Label_cc"): [[fallthrough]]; case hash("label_cc"): @@ -230,7 +230,7 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename) // Defaults for (int ccIndex = 1; ccIndex < 128; ccIndex++) - region->registerCC(region->channelRange.getStart(), ccIndex, ccState[ccIndex]); + region->registerCC(region->channelRange.getStart(), ccIndex, midiState.cc[ccIndex]); if (defaultSwitch) { region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0); @@ -333,7 +333,7 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - sfz::noteOn(noteNumber, velocity); + midiState.noteOn(noteNumber, velocity); auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { @@ -361,7 +361,7 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - auto replacedVelocity = velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity; + auto replacedVelocity = velocity == 0 ? midiState.getNoteVelocity(noteNumber) : velocity; auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& voice : voices) voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity); @@ -389,7 +389,7 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc for (auto& voice : voices) voice->registerCC(delay, channel, ccNumber, ccValue); - ccState[ccNumber] = ccValue; + midiState.cc[ccNumber] = ccValue; for (auto& region : ccActivationLists[ccNumber]) { if (region->registerCC(channel, ccNumber, ccValue)) { diff --git a/sfizz/Synth.h b/sfizz/Synth.h index 80f45343..763415e4 100644 --- a/sfizz/Synth.h +++ b/sfizz/Synth.h @@ -26,6 +26,7 @@ #include "Parser.h" #include "Region.h" #include "LeakDetector.h" +#include "MidiState.h" #include "AudioSpan.h" #include "absl/types/span.h" #include @@ -80,7 +81,7 @@ private: std::vector groupOpcodes; FilePool filePool; - CCValueArray ccState; + MidiState midiState; Voice* findFreeVoice() noexcept; std::vector ccNames; absl::optional defaultSwitch; diff --git a/sfizz/Voice.cpp b/sfizz/Voice.cpp index 5aef90ff..8beee826 100644 --- a/sfizz/Voice.cpp +++ b/sfizz/Voice.cpp @@ -31,8 +31,8 @@ #include "absl/algorithm/container.h" #include -sfz::Voice::Voice(const CCValueArray& ccState) - : ccState(ccState) +sfz::Voice::Voice(const MidiState& midiState) + : midiState(midiState) { } @@ -59,39 +59,39 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, auto volumedB { baseVolumedB }; if (region->volumeCC) - volumedB += normalizeCC(ccState[region->volumeCC->first]) * region->volumeCC->second; + volumedB += normalizeCC(midiState.cc[region->volumeCC->first]) * region->volumeCC->second; volumeEnvelope.reset(db2mag(volumedB)); // DBG("Base volume: " << baseVolumedB << " dB - with modifier: " << volumedB << " dB"); baseGain = region->getBaseGain(); - baseGain *= region->getCrossfadeGain(ccState); + baseGain *= region->getCrossfadeGain(midiState.cc); if (triggerType != TriggerType::CC) baseGain *= region->getNoteGain(number, value); float gain { baseGain }; if (region->amplitudeCC) - gain *= normalizeCC(ccState[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second); + gain *= normalizeCC(midiState.cc[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second); amplitudeEnvelope.reset(gain); // DBG("Base gain: " << baseGain << " - with modifier: " << gain); basePan = normalizeNegativePercents(region->pan); auto pan { basePan }; if (region->panCC) - pan += normalizeCC(ccState[region->panCC->first]) * normalizeNegativePercents(region->panCC->second); + pan += normalizeCC(midiState.cc[region->panCC->first]) * normalizeNegativePercents(region->panCC->second); panEnvelope.reset(pan); // DBG("Base pan: " << basePan << " - with modifier: " << pan); basePosition = normalizeNegativePercents(region->position); auto position { basePosition }; if (region->positionCC) - position += normalizeCC(ccState[region->positionCC->first]) * normalizeNegativePercents(region->positionCC->second); + position += normalizeCC(midiState.cc[region->positionCC->first]) * normalizeNegativePercents(region->positionCC->second); positionEnvelope.reset(position); // DBG("Base position: " << basePosition << " - with modifier: " << position); baseWidth = normalizeNegativePercents(region->width); auto width { baseWidth }; if (region->widthCC) - width += normalizeCC(ccState[region->widthCC->first]) * normalizeNegativePercents(region->widthCC->second); + width += normalizeCC(midiState.cc[region->widthCC->first]) * normalizeNegativePercents(region->widthCC->second); widthEnvelope.reset(width); // DBG("Base width: " << baseWidth << " - with modifier: " << width); @@ -109,13 +109,13 @@ void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept }; egEnvelope.reset( - secondsToSamples(region->amplitudeEG.getAttack(ccState, velocity)), - secondsToSamples(region->amplitudeEG.getRelease(ccState, velocity)), - normalizePercents(region->amplitudeEG.getSustain(ccState, velocity)), - delay + secondsToSamples(region->amplitudeEG.getDelay(ccState, velocity)), - secondsToSamples(region->amplitudeEG.getDecay(ccState, velocity)), - secondsToSamples(region->amplitudeEG.getHold(ccState, velocity)), - normalizePercents(region->amplitudeEG.getStart(ccState, velocity))); + secondsToSamples(region->amplitudeEG.getAttack(midiState.cc, velocity)), + secondsToSamples(region->amplitudeEG.getRelease(midiState.cc, velocity)), + normalizePercents(region->amplitudeEG.getSustain(midiState.cc, velocity)), + delay + secondsToSamples(region->amplitudeEG.getDelay(midiState.cc, velocity)), + secondsToSamples(region->amplitudeEG.getDecay(midiState.cc, velocity)), + secondsToSamples(region->amplitudeEG.getHold(midiState.cc, velocity)), + normalizePercents(region->amplitudeEG.getStart(midiState.cc, velocity))); } void sfz::Voice::setFileData(std::shared_ptr> file, unsigned ticket) noexcept @@ -154,7 +154,7 @@ void sfz::Voice::registerNoteOff(int delay, int channel, int noteNumber, uint8_t if (region->loopMode == SfzLoopMode::one_shot) return; - if (!region->checkSustain || ccState[config::sustainCC] < config::halfCCThreshold) + if (!region->checkSustain || midiState.cc[config::sustainCC] < config::halfCCThreshold) release(delay); } } diff --git a/sfizz/Voice.h b/sfizz/Voice.h index 5ed9cd14..ae58b756 100644 --- a/sfizz/Voice.h +++ b/sfizz/Voice.h @@ -28,6 +28,7 @@ #include "HistoricalBuffer.h" #include "Region.h" #include "AudioBuffer.h" +#include "MidiState.h" #include "AudioSpan.h" #include "LeakDetector.h" #include @@ -38,7 +39,7 @@ namespace sfz { class Voice { public: Voice() = delete; - Voice(const CCValueArray& ccState); + Voice(const MidiState& midiState); enum class TriggerType { NoteOn, NoteOff, @@ -124,7 +125,7 @@ private: int samplesPerBlock { config::defaultSamplesPerBlock }; float sampleRate { config::defaultSampleRate }; - const CCValueArray& ccState; + const MidiState& midiState; ADSREnvelope egEnvelope; LinearEnvelope volumeEnvelope; // dB events but the envelope output is linear gain LinearEnvelope amplitudeEnvelope; // linear events diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 34e3d8b4..259cd399 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -27,7 +27,9 @@ using namespace Catch::literals; TEST_CASE("Region activation", "Region tests") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; + region.parseOpcode({ "sample", "*sine" }); SECTION("Basic state") { diff --git a/tests/RegionCrossfadesT.cpp b/tests/RegionCrossfadesT.cpp index e933c661..eec5597c 100644 --- a/tests/RegionCrossfadesT.cpp +++ b/tests/RegionCrossfadesT.cpp @@ -32,7 +32,8 @@ using namespace Catch::literals; TEST_CASE("[Region] Crossfade in on key") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); @@ -43,7 +44,8 @@ TEST_CASE("[Region] Crossfade in on key") TEST_CASE("[Region] Crossfade in on key - 2") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); @@ -57,7 +59,8 @@ TEST_CASE("[Region] Crossfade in on key - 2") TEST_CASE("[Region] Crossfade in on key - gain") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); @@ -71,7 +74,8 @@ TEST_CASE("[Region] Crossfade in on key - gain") TEST_CASE("[Region] Crossfade out on key") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); @@ -86,7 +90,8 @@ TEST_CASE("[Region] Crossfade out on key") TEST_CASE("[Region] Crossfade out on key - gain") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); @@ -102,7 +107,8 @@ TEST_CASE("[Region] Crossfade out on key - gain") TEST_CASE("[Region] Crossfade in on velocity") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); @@ -118,7 +124,8 @@ TEST_CASE("[Region] Crossfade in on velocity") TEST_CASE("[Region] Crossfade in on vel - gain") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); @@ -135,7 +142,8 @@ TEST_CASE("[Region] Crossfade in on vel - gain") TEST_CASE("[Region] Crossfade out on vel") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); @@ -151,7 +159,8 @@ TEST_CASE("[Region] Crossfade out on vel") TEST_CASE("[Region] Crossfade out on vel - gain") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); @@ -168,76 +177,77 @@ TEST_CASE("[Region] Crossfade out on vel - gain") TEST_CASE("[Region] Crossfade in on CC") { - sfz::Region region {}; - sfz::CCValueArray ccState; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); - ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); - ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a ); - ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.70711_a ); - ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.86603_a ); - ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); - ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); + midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); + midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.70711_a ); + midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.86603_a ); + midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on CC - gain") { - sfz::Region region {}; - sfz::CCValueArray ccState; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); - ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); - ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.25_a ); - ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a ); - ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.75_a ); - ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); - ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); + midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.25_a ); + midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); + midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.75_a ); + midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on CC") { - sfz::Region region {}; - sfz::CCValueArray ccState; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); - ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); - ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.86603_a ); - ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.70711_a ); - ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a ); - ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); - ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); + midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.86603_a ); + midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.70711_a ); + midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); + midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on CC - gain") { - sfz::Region region {}; - sfz::CCValueArray ccState; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); - ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a ); - ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.75_a ); - ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a ); - ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.25_a ); - ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); - ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a ); + midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a ); + midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.75_a ); + midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a ); + midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.25_a ); + midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); + midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a ); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); REQUIRE( region.getNoteGain(64, 127) == 1.0_a ); @@ -247,7 +257,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); REQUIRE( region.getNoteGain(64, 127) == 1.0_a ); @@ -256,7 +267,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) ); @@ -265,19 +277,20 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") TEST_CASE("[Region] rt_decay") { - sfz::Region region {}; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "rt_decay", "10" }); - sfz::noteOn(64, 64); + midiState.noteOn(64, 64); std::this_thread::sleep_for(std::chrono::milliseconds(100)); REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) ); region.parseOpcode({ "rt_decay", "20" }); - sfz::noteOn(64, 64); + midiState.noteOn(64, 64); std::this_thread::sleep_for(std::chrono::milliseconds(100)); REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) ); region.parseOpcode({ "trigger", "attack" }); - sfz::noteOn(64, 64); + midiState.noteOn(64, 64); std::this_thread::sleep_for(std::chrono::milliseconds(100)); REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) ); } \ No newline at end of file diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 0f52cc79..ab9f8cdd 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -21,13 +21,16 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "MidiState.h" #include "Region.h" #include "catch2/catch.hpp" using namespace Catch::literals; TEST_CASE("[Region] Parsing opcodes") { - sfz::Region region; + sfz::MidiState midiState; + sfz::Region region { midiState }; + SECTION("sample") { REQUIRE(region.sample == ""); @@ -168,10 +171,10 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(!region.offBy); region.parseOpcode({ "off_by", "5" }); REQUIRE(region.offBy); - REQUIRE(region.offBy == 5); + REQUIRE(*region.offBy == 5); region.parseOpcode({ "off_by", "-1" }); REQUIRE(region.offBy); - REQUIRE(region.offBy == 0); + REQUIRE(*region.offBy == 0); } SECTION("off_mode") @@ -1036,7 +1039,8 @@ TEST_CASE("[Region] Parsing opcodes") // Specific region bugs TEST_CASE("[Region] Non-conforming floating point values in integer opcodes") { - sfz::Region region; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "offset", "2014.5" }); REQUIRE(region.offset == 2014); region.parseOpcode({ "pitch_keytrack", "-2.1" }); diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index a9ffa9e9..21d773f6 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -27,7 +27,9 @@ using namespace Catch::literals; TEST_CASE("Basic triggers", "Region triggers") { - sfz::Region region; + sfz::MidiState midiState; + sfz::Region region { midiState }; + region.parseOpcode({ "sample", "*sine" }); SECTION("key") { @@ -136,7 +138,8 @@ TEST_CASE("Basic triggers", "Region triggers") TEST_CASE("Legato triggers", "Region triggers") { - sfz::Region region; + sfz::MidiState midiState; + sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); SECTION("First note playing") {