diff --git a/benchmarks/BM_mapVsArray.cpp b/benchmarks/BM_mapVsArray.cpp new file mode 100644 index 00000000..59e62b27 --- /dev/null +++ b/benchmarks/BM_mapVsArray.cpp @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include +#include +#include +#include +#include "../src/sfizz/CCMap.h" +#include +#include + +constexpr int maxCC { 256 }; + +class MyFixture : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State& state) + { + std::random_device rd {}; + std::mt19937 gen { rd() }; + + std::array sourceCC; + absl::c_iota(sourceCC, 0); + absl::c_shuffle(sourceCC, gen); + ccs.resize(state.range(0)); + std::copy(sourceCC.begin(), sourceCC.begin() + state.range(0), ccs.begin()); + std::uniform_real_distribution distFloat { 0.1f, 1.0f }; + vector.resize(maxCC); + absl::c_generate(vector, [&]() { + return distFloat(gen); + }); + for (unsigned i = 0; i < state.range(0); ++i) { + map[ccs[i]] = vector[ccs[i]]; + } + } + + void TearDown(const ::benchmark::State& /* state */) + { + } + + std::vector ccs; + std::vector vector; + sfz::CCMap map { 1 }; +}; + +BENCHMARK_DEFINE_F(MyFixture, ArraySearch) +(benchmark::State& state) +{ + float value { 1 }; + for (auto _ : state) { + for (unsigned i = 0; i < state.range(0); ++i) { + value *= vector[ccs[i]]; + } + benchmark::DoNotOptimize(value); + } +} + +BENCHMARK_DEFINE_F(MyFixture, MapSearch) +(benchmark::State& state) +{ + float value { 0 }; + for (auto _ : state) { + for (unsigned i = 0; i < state.range(0); ++i) { + value *= map[ccs[i]]; + } + benchmark::DoNotOptimize(value); + } +} + +BENCHMARK_REGISTER_F(MyFixture, ArraySearch)->Range(4, maxCC); +BENCHMARK_REGISTER_F(MyFixture, MapSearch)->Range(4, maxCC); +BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 0aa090ae..fb666f0c 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -63,6 +63,7 @@ sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp) sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp) sfizz_add_benchmark(bm_maps BM_maps.cpp) target_link_libraries(bm_maps PRIVATE absl::flat_hash_map) +sfizz_add_benchmark(bm_mapVsArray BM_mapVsArray.cpp) sfizz_add_benchmark(bm_logger BM_logger.cpp) target_link_libraries(bm_logger PRIVATE sfizz::sfizz) diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 96333be2..0acbdbe3 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -12,21 +12,20 @@ namespace sfz { template -void ADSREnvelope::reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept +void ADSREnvelope::reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept { auto secondsToSamples = [sampleRate](Type timeInSeconds) { return static_cast(timeInSeconds * sampleRate); }; - const auto ccArray = state.getCCArray(); - this->delay = delay + secondsToSamples(region.amplitudeEG.getDelay(ccArray, velocity)); - this->attack = secondsToSamples(region.amplitudeEG.getAttack(ccArray, velocity)); - this->decay = secondsToSamples(region.amplitudeEG.getDecay(ccArray, velocity)); - this->release = secondsToSamples(region.amplitudeEG.getRelease(ccArray, velocity)); - this->hold = secondsToSamples(region.amplitudeEG.getHold(ccArray, velocity)); + this->delay = delay + secondsToSamples(region.amplitudeEG.getDelay(state, velocity)); + this->attack = secondsToSamples(region.amplitudeEG.getAttack(state, velocity)); + this->decay = secondsToSamples(region.amplitudeEG.getDecay(state, velocity)); + this->release = secondsToSamples(region.amplitudeEG.getRelease(state, velocity)); + this->hold = secondsToSamples(region.amplitudeEG.getHold(state, velocity)); this->peak = 1.0; - this->sustain = normalizePercents(region.amplitudeEG.getSustain(ccArray, velocity)); - this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(ccArray, velocity)); + this->sustain = normalizePercents(region.amplitudeEG.getSustain(state, velocity)); + this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(state, velocity)); releaseDelay = 0; shouldRelease = false; diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index f2468a09..6a647d03 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -29,7 +29,7 @@ public: * @param delay * @param velocity */ - void reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept; + void reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept; /** * @brief Get the next value for the envelope * diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index e1bab309..0af2263f 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -43,7 +43,7 @@ namespace config { constexpr int allNotesOffCC { 123 }; constexpr int omniOffCC { 124 }; constexpr int omniOnCC { 125 }; - constexpr int halfCCThreshold { 64 }; + constexpr float halfCCThreshold { 0.5f }; constexpr int centPerSemitone { 100 }; constexpr float virtuallyZero { 0.00005f }; constexpr float fastReleaseDuration { 0.01f }; @@ -52,7 +52,7 @@ namespace config { constexpr float A440 { 440.0 }; constexpr size_t powerHistoryLength { 16 }; constexpr float voiceStealingThreshold { 0.00001f }; - constexpr uint8_t numCCs { 143 }; + constexpr uint16_t numCCs { 512 }; constexpr int chunkSize { 1024 }; constexpr float defaultAmpEGRelease { 0.02f }; constexpr int filtersInPool { maxVoices * 2 }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 71741b5a..9e6f0d84 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -53,6 +53,10 @@ namespace Default constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop }; constexpr Range loopRange { 0, std::numeric_limits::max() }; + // Global ranges + constexpr Range midi7Range { 0, 127 }; + constexpr Range normalizedRange { 0.0f, 1.0f }; + // Wavetable oscillator constexpr float oscillatorPhase { 0.0 }; constexpr Range oscillatorPhaseRange { -1.0, 360.0 }; @@ -64,14 +68,13 @@ namespace Default // Region logic: key mapping constexpr Range keyRange { 0, 127 }; - constexpr Range velocityRange { 0, 127 }; + constexpr auto velocityRange = normalizedRange; // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; - constexpr Range ccNumberRange { 0, config::numCCs }; - constexpr Range ccValueRange { 0, 127 }; - constexpr uint8_t cc { 0 }; + constexpr Range ccNumberRange { 0, config::numCCs }; + constexpr auto ccValueRange = normalizedRange; constexpr Range bendRange { -8192, 8192 }; constexpr int bend { 0 }; constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current }; @@ -88,7 +91,7 @@ namespace Default // Region logic: Triggers constexpr SfzTrigger trigger { SfzTrigger::attack }; - constexpr Range ccTriggerValueRange{ 0, 127 }; + constexpr Range ccTriggerValueRange = normalizedRange; // Performance parameters: amplifier constexpr float globalVolume { -7.35f }; @@ -97,7 +100,6 @@ namespace Default constexpr Range volumeCCRange { -144.0, 48.0 }; constexpr float amplitude { 100.0 }; constexpr Range amplitudeRange { 0.0, 100.0 }; - constexpr Range normalizedRange { 0.0, 1.0 }; constexpr float pan { 0.0 }; constexpr Range panRange { -100.0, 100.0 }; constexpr Range panCCRange { -200.0, 200.0 }; @@ -118,10 +120,10 @@ namespace Default constexpr Range ampRandomRange { 0.0, 24.0 }; constexpr Range crossfadeKeyInRange { 0, 0 }; constexpr Range crossfadeKeyOutRange { 127, 127 }; - constexpr Range crossfadeVelInRange { 0, 0 }; - constexpr Range crossfadeVelOutRange { 127, 127 }; - constexpr Range crossfadeCCInRange { 0, 0 }; - constexpr Range crossfadeCCOutRange { 127, 127 }; + constexpr Range crossfadeVelInRange { 0.0f, 0.0f }; + constexpr Range crossfadeVelOutRange { 1.0f, 1.0f }; + constexpr Range crossfadeCCInRange { 0.0f, 0.0f }; + constexpr Range crossfadeCCOutRange { 1.0f, 1.0f }; constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power }; diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index f9c422f7..ee44f754 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -29,11 +29,10 @@ #include "Macros.h" #include "LeakDetector.h" #include "SfzHelpers.h" +#include "MidiState.h" #include - -namespace sfz -{ +namespace sfz { /** * @brief A description for an SFZ envelope generator, with its envelope parameters * and possible CC modulation. This is a structure to be integrated directly in a @@ -42,114 +41,136 @@ namespace sfz * TODO: should be updated for SFZ v2 * */ -struct EGDescription + +/** + * @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone. + * + * @param ccValues + * @param ccSwitch + * @param value + * @return float + */ +inline float ccSwitchedValue(const MidiState& state, const absl::optional>& ccSwitch, float value) noexcept { + if (ccSwitch) + return value + ccSwitch->value * state.getCCValue(ccSwitch->cc); + else + return value; +} + +struct EGDescription { EGDescription() = default; EGDescription(const EGDescription&) = default; EGDescription(EGDescription&&) = default; ~EGDescription() = default; - float attack { Default::attack }; - float decay { Default::decay }; - float delay { Default::delayEG }; - float hold { Default::hold }; - float release { Default::release }; - float start { Default::start }; - float sustain { Default::sustain }; - int depth { Default::depth }; - float vel2attack { Default::attack }; - float vel2decay { Default::decay }; - float vel2delay { Default::delayEG }; - float vel2hold { Default::hold }; - float vel2release { Default::vel2release }; - float vel2sustain { Default::vel2sustain }; - int vel2depth { Default::depth }; + float attack { Default::attack }; + float decay { Default::decay }; + float delay { Default::delayEG }; + float hold { Default::hold }; + float release { Default::release }; + float start { Default::start }; + float sustain { Default::sustain }; + int depth { Default::depth }; + float vel2attack { Default::attack }; + float vel2decay { Default::decay }; + float vel2delay { Default::delayEG }; + float vel2hold { Default::hold }; + float vel2release { Default::vel2release }; + float vel2sustain { Default::vel2sustain }; + int vel2depth { Default::depth }; - absl::optional> ccAttack; - absl::optional> ccDecay; - absl::optional> ccDelay; - absl::optional> ccHold; - absl::optional> ccRelease; - absl::optional> ccStart; - absl::optional> ccSustain; + absl::optional> ccAttack; + absl::optional> ccDecay; + absl::optional> ccDelay; + absl::optional> ccHold; + absl::optional> ccRelease; + absl::optional> ccStart; + absl::optional> ccSustain; /** * @brief Get the attack with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getAttack(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getAttack(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccAttack, attack) + normalizeVelocity(velocity)*vel2attack); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + velocity * vel2attack); } /** * @brief Get the decay with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getDecay(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getDecay(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDecay, decay) + normalizeVelocity(velocity)*vel2decay); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + velocity * vel2decay); } /** * @brief Get the delay with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getDelay(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getDelay(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDelay, delay) + normalizeVelocity(velocity)*vel2delay); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + velocity * vel2delay); } /** * @brief Get the holding duration with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getHold(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getHold(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccHold, hold) + normalizeVelocity(velocity)*vel2hold); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + velocity * vel2hold); } /** * @brief Get the release duration with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getRelease(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getRelease(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccRelease, release) + normalizeVelocity(velocity)*vel2release); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + velocity * vel2release); } /** * @brief Get the starting level with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getStart(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getStart(const MidiState& state, float velocity) const noexcept { UNUSED(velocity); - return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccStart, start)); + return Default::egPercentRange.clamp(ccSwitchedValue(state, ccStart, start)); } /** * @brief Get the sustain level with possibly a CC modifier and a velocity modifier * - * @param ccValues + * @param state * @param velocity * @return float */ - float getSustain(const SfzCCArray &ccValues, uint8_t velocity) const noexcept + float getSustain(const MidiState& state, float velocity) const noexcept { - return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain); + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + velocity * vel2sustain); } LEAK_DETECTOR(EGDescription); }; diff --git a/src/sfizz/EQPool.cpp b/src/sfizz/EQPool.cpp index da8c9c2b..c3883124 100644 --- a/src/sfizz/EQPool.cpp +++ b/src/sfizz/EQPool.cpp @@ -15,17 +15,17 @@ void sfz::EQHolder::reset() eq.clear(); } -void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels, uint8_t velocity) +void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels, float velocity) { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); eq.setType(description.type); eq.setChannels(numChannels); this->description = &description; - const auto normalizedVelocity = normalizeVelocity(velocity); // Setup the base values - baseFrequency = description.frequency + normalizedVelocity * description.vel2frequency; + baseFrequency = description.frequency + velocity * description.vel2frequency; baseBandwidth = description.bandwidth; - baseGain = description.gain + normalizedVelocity * description.vel2gain; + baseGain = description.gain + velocity * description.vel2gain; // Setup the modulated values lastFrequency = midiState.modulate(baseFrequency, description.frequencyCC, Default::eqFrequencyRange); @@ -84,7 +84,7 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs) setnumEQs(numEQs); } -sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, uint8_t velocity) +sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity) { AtomicGuard guard { givingOutEQs }; if (!canGiveOutEQs) diff --git a/src/sfizz/EQPool.h b/src/sfizz/EQPool.h index 879a2cdd..9890c10a 100644 --- a/src/sfizz/EQPool.h +++ b/src/sfizz/EQPool.h @@ -20,7 +20,7 @@ public: * @param numChannels the number of channels for the EQ * @param description the triggering velocity/value */ - void setup(const EQDescription& description, unsigned numChannels, uint8_t velocity); + void setup(const EQDescription& description, unsigned numChannels, float velocity); /** * @brief Process a block of stereo inputs * @@ -90,7 +90,7 @@ public: * @param velocity the triggering note velocity/value * @return EQHolderPtr release this when done with the filter; no deallocation will be done */ - EQHolderPtr getEQ(const EQDescription& description, unsigned numChannels, uint8_t velocity); + EQHolderPtr getEQ(const EQDescription& description, unsigned numChannels, float velocity); /** * @brief Get the number of active EQs * diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 3be79d7f..96f97308 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -16,8 +16,10 @@ void sfz::FilterHolder::reset() filter.clear(); } -void sfz::FilterHolder::setup(const FilterDescription& description, unsigned numChannels, int noteNumber, uint8_t velocity) +void sfz::FilterHolder::setup(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity) { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + this->description = &description; filter.setType(description.type); filter.setChannels(numChannels); @@ -30,7 +32,7 @@ void sfz::FilterHolder::setup(const FilterDescription& description, unsigned num } const auto keytrack = description.keytrack * (noteNumber - description.keycenter); baseCutoff *= centsFactor(keytrack); - const auto veltrack = static_cast(description.veltrack) * normalizeVelocity(velocity); + const auto veltrack = static_cast(description.veltrack) * velocity; baseCutoff *= centsFactor(veltrack); baseCutoff = Default::filterCutoffRange.clamp(baseCutoff); @@ -83,7 +85,7 @@ sfz::FilterPool::FilterPool(const MidiState& state, int numFilters) setNumFilters(numFilters); } -sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, uint8_t velocity) +sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity) { AtomicGuard guard { givingOutFilters }; if (!canGiveOutFilters) diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h index 792777c6..fb1a8aac 100644 --- a/src/sfizz/FilterPool.h +++ b/src/sfizz/FilterPool.h @@ -21,7 +21,7 @@ public: * @param noteNumber the triggering note number * @param velocity the triggering note velocity/value */ - void setup(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), uint8_t velocity = 0); + void setup(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), float velocity = 0); /** * @brief Process a block of stereo inputs * @@ -94,7 +94,7 @@ public: * @param velocity the triggering note velocity * @return FilterHolderPtr release this when done with the filter; no deallocation will be done */ - FilterHolderPtr getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), uint8_t velocity = 0); + FilterHolderPtr getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast(Default::filterKeycenter), float velocity = 0); /** * @brief Get the number of active filters * diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 0c812af4..6e8ccac3 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -13,10 +13,10 @@ sfz::MidiState::MidiState() reset(0); } -void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); - ASSERT(velocity >= 0 && velocity <= 127); + ASSERT(velocity >= 0 && velocity <= 1.0); if (noteNumber >= 0 && noteNumber < 128) { lastNoteVelocities[noteNumber] = velocity; @@ -26,10 +26,10 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) no } -void sfz::MidiState::noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::MidiState::noteOffEvent(int delay, int noteNumber, float velocity) noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); - ASSERT(velocity >= 0 && velocity <= 127); + ASSERT(velocity >= 0.0 && velocity <= 1.0); UNUSED(velocity); if (noteNumber >= 0 && noteNumber < 128) { if (activeNotes > 0) @@ -51,13 +51,14 @@ float sfz::MidiState::getNoteDuration(int noteNumber) const return 0.0f; } -uint8_t sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept +float sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept { ASSERT(noteNumber >= 0 && noteNumber <= 127); return lastNoteVelocities[noteNumber]; } + void sfz::MidiState::pitchBendEvent(int delay, int pitchBendValue) noexcept { ASSERT(pitchBendValue >= -8192 && pitchBendValue <= 8192); @@ -70,26 +71,20 @@ int sfz::MidiState::getPitchBend() const noexcept return pitchBend; } -void sfz::MidiState::ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept +void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept { - ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); - ASSERT(ccValue >= 0 && ccValue <= 127); + ASSERT(ccValue >= 0.0 && ccValue <= 1.0); cc[ccNumber] = ccValue; } -uint8_t sfz::MidiState::getCCValue(int ccNumber) const noexcept +float sfz::MidiState::getCCValue(int ccNumber) const noexcept { ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); return cc[ccNumber]; } -const sfz::SfzCCArray& sfz::MidiState::getCCArray() const noexcept -{ - return cc; -} - void sfz::MidiState::reset(int delay) noexcept { for (auto& velocity: lastNoteVelocities) @@ -104,7 +99,7 @@ void sfz::MidiState::reset(int delay) noexcept void sfz::MidiState::resetAllControllers(int delay) noexcept { - for (int idx = 0; idx < config::numCCs; idx++) + for (unsigned idx = 0; idx < config::numCCs; idx++) cc[idx] = 0; pitchBend = 0; diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index badb0e17..09c4ce78 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -7,7 +7,6 @@ #pragma once #include #include -#include "SfzHelpers.h" #include "CCMap.h" #include "Range.h" @@ -23,13 +22,14 @@ class MidiState { public: MidiState(); + /** * @brief Update the state after a note on event * * @param noteNumber * @param velocity */ - void noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept; + void noteOnEvent(int delay, int noteNumber, float velocity) noexcept; /** * @brief Update the state after a note off event @@ -37,7 +37,7 @@ public: * @param noteNumber * @param velocity */ - void noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept; + void noteOffEvent(int delay, int noteNumber, float velocity) noexcept; int getActiveNotes() const noexcept { return activeNotes; } @@ -53,9 +53,9 @@ public: * @brief Get the note on velocity for a given note * * @param noteNumber - * @return uint8_t + * @return float */ - uint8_t getNoteVelocity(int noteNumber) const noexcept; + float getNoteVelocity(int noteNumber) const noexcept; /** * @brief Register a pitch bend event @@ -77,22 +77,15 @@ public: * @param ccNumber * @param ccValue */ - void ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept; + void ccEvent(int delay, int ccNumber, float ccValue) noexcept; /** * @brief Get the CC value for CC number * * @param ccNumber - * @return uint8_t + * @return float */ - uint8_t getCCValue(int ccNumber) const noexcept; - - /** - * @brief Get the full CC status - * - * @return const SfzCCArray& - */ - const SfzCCArray& getCCArray() const noexcept; + float getCCValue(int ccNumber) const noexcept; /** * @brief Reset the midi state (does not impact the last note on time) @@ -120,7 +113,7 @@ public: T modulate(T value, const CCMap& modifiers, const Range& validRange, const modFunction& lambda = addToBase) const noexcept { for (auto& mod: modifiers) { - lambda(value, normalizeCC(getCCValue(mod.cc)) * mod.value); + lambda(value, getCCValue(mod.cc) * mod.value); } return validRange.clamp(value); } @@ -140,12 +133,12 @@ private: * depressed notes. * */ - MidiNoteArray lastNoteVelocities; + MidiNoteArray lastNoteVelocities; /** * @brief Current known values for the CCs. * */ - SfzCCArray cc; + std::array cc; /** * Pitch bend status */ diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 808f031e..7ab2dbfb 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -14,6 +14,7 @@ #include "MidiState.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_cat.h" +#include "absl/algorithm/container.h" #include template @@ -138,10 +139,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange); break; case hash("lovel"): - setRangeStartFromOpcode(opcode, velocityRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + velocityRange.setStart(normalizeVelocity(*value)); break; case hash("hivel"): - setRangeEndFromOpcode(opcode, velocityRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + velocityRange.setEnd(normalizeVelocity(*value)); break; // Region logic: MIDI conditions @@ -152,10 +155,16 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setRangeEndFromOpcode(opcode, bendRange, Default::bendRange); break; case hash("locc&"): - setRangeStartFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("hicc&"): - setRangeEndFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("sw_lokey"): setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange); @@ -247,11 +256,17 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("on_locc&"): // fallthrough case hash("start_locc&"): - setRangeStartFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("on_hicc&"): // fallthrough case hash("start_hicc&"): - setRangeEndFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; // Performance parameters: amplifier @@ -303,8 +318,11 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) case hash("amp_velcurve_&"): { auto value = readOpcode(opcode.value, Default::ampVelcurveRange); + if (opcode.parameters.back() > 127) + return false; + if (value) - velocityPoints.emplace_back(opcode.parameters.back(), *value); + velocityPoints.emplace_back(normalizeVelocity(opcode.parameters.back()), *value); } break; case hash("xfin_lokey"): @@ -320,16 +338,20 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange); break; case hash("xfin_lovel"): - setRangeStartFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelInRange.setStart(normalizeVelocity(*value)); break; case hash("xfin_hivel"): - setRangeEndFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelInRange.setEnd(normalizeVelocity(*value)); break; case hash("xfout_lovel"): - setRangeStartFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelOutRange.setStart(normalizeVelocity(*value)); break; case hash("xfout_hivel"): - setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange); + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeVelOutRange.setEnd(normalizeVelocity(*value)); break; case hash("xf_keycurve"): switch (hash(opcode.value)) { @@ -356,16 +378,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) } break; case hash("xfin_locc&"): - setRangeStartFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfin_hicc&"): - setRangeEndFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xfout_locc&"): - setRangeStartFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfout_hicc&"): - setRangeEndFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xf_cccurve"): switch (hash(opcode.value)) { @@ -771,8 +805,10 @@ bool sfz::Region::isSwitchedOn() const noexcept return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all(); } -bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept +bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue) noexcept { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + if (keyswitchRange.containsWithEnd(noteNumber)) { if (keyswitch) { if (*keyswitch == noteNumber) @@ -823,8 +859,10 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote); } -bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept +bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValue) noexcept { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + if (keyswitchRange.containsWithEnd(noteNumber)) { if (keyswitchDown && *keyswitchDown == noteNumber) keySwitched = false; @@ -847,8 +885,9 @@ bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randVa return keyOk && velOk && randOk && releaseTrigger; } -bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept +bool sfz::Region::registerCC(int ccNumber, float ccValue) noexcept { + ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) ccSwitched.set(ccNumber, true); else @@ -891,13 +930,15 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept bpmSwitched = false; } -float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept +float sfz::Region::getBasePitchVariation(int noteNumber, float velocity) const noexcept { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + std::uniform_int_distribution pitchDistribution { -pitchRandom, pitchRandom }; auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center pitchVariationInCents += tune; // sample tuning pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose - pitchVariationInCents += velocity / 127 * pitchVeltrack; // track velocity + pitchVariationInCents += static_cast(velocity * pitchVeltrack); // track velocity pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes return centsFactor(pitchVariationInCents); } @@ -961,8 +1002,13 @@ float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurv { if (value < crossfadeRange.getStart()) return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + else if (value < crossfadeRange.getEnd()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / std::max(static_cast(crossfadeRange.length()), 1.0f); + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; if (curve == SfzCrossfadeCurve::power) return sqrt(crossfadePosition); if (curve == SfzCrossfadeCurve::gain) @@ -977,8 +1023,13 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur { if (value > crossfadeRange.getEnd()) return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + else if (value > crossfadeRange.getStart()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / std::max(static_cast(crossfadeRange.length()), 1.0f); + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; if (curve == SfzCrossfadeCurve::power) return std::sqrt(1 - crossfadePosition); if (curve == SfzCrossfadeCurve::gain) @@ -988,8 +1039,10 @@ float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCur return 1.0f; } -float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept +float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept { + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + float baseGain { 1.0f }; // Amplitude key tracking @@ -1009,19 +1062,19 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept return baseGain; } -float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) const noexcept +float sfz::Region::getCrossfadeGain() const noexcept { float gain { 1.0f }; // Crossfades due to CC states for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = ccState[valuePair.cc]; + const auto ccValue = midiState.getCCValue(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = ccState[valuePair.cc]; + const auto ccValue = midiState.getCCValue(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } @@ -1029,27 +1082,27 @@ float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) const noexce return gain; } -float sfz::Region::velocityCurve(uint8_t velocity) const noexcept +float sfz::Region::velocityCurve(float velocity) const noexcept { - float gain { 1.0f }; + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + float gain { 1.0f }; if (velocityPoints.size() > 0) { // Custom velocity curve - auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](const std::pair& val) { return val.first >= velocity; }); + auto after = absl::c_find_if(velocityPoints, [velocity](const std::pair& val) { return val.first >= velocity; }); auto before = after == velocityPoints.begin() ? velocityPoints.begin() : after - 1; // Linear interpolation float relativePositionInSegment { - static_cast(velocity - before->first) / static_cast(after->first - before->first) + (velocity - before->first) / (after->first - before->first) }; float segmentEndpoints { after->second - before->second }; gain *= relativePositionInSegment * segmentEndpoints; } else { // Standard velocity curve - const float floatVelocity { static_cast(velocity) / 127.0f }; // FIXME: Maybe there's a prettier way to check the boundaries? const float gaindB = [&]() { if (ampVeltrack >= 0) - return floatVelocity == 0.0f ? -90.0f : 40 * std::log(floatVelocity) / std::log(10.0f); + return velocity == 0.0f ? -90.0f : 40 * std::log(velocity) / std::log(10.0f); else - return floatVelocity == 1.0f ? -90.0f : 40 * std::log(1 - floatVelocity) / std::log(10.0f); + return velocity == 1.0f ? -90.0f : 40 * std::log(1 - velocity) / std::log(10.0f); }(); gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd()); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 1fd0fa67..eb3adb13 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -85,7 +85,7 @@ struct Region { * @return true if the region should trigger on this event. * @return false */ - bool registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept; + bool registerNoteOn(int noteNumber, float velocity, float randValue) noexcept; /** * @brief Register a new note off event. The region may be switched on or off using keys so * this function updates the keyswitches state. @@ -97,7 +97,7 @@ struct Region { * @return true if the region should trigger on this event. * @return false */ - bool registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept; + bool registerNoteOff(int noteNumber, float velocity, float randValue) noexcept; /** * @brief Register a new CC event. The region may be switched on or off using CCs so * this function checks if it indeeds need to activate or not. @@ -107,7 +107,7 @@ struct Region { * @return true if the region should trigger on this event * @return false */ - bool registerCC(int ccNumber, uint8_t ccValue) noexcept; + bool registerCC(int ccNumber, float ccValue) noexcept; /** * @brief Register a new pitch wheel event. * @@ -135,7 +135,7 @@ struct Region { * @param velocity * @return float */ - float getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept; + float getBasePitchVariation(int noteNumber, float velocity) const noexcept; /** * @brief Get the note-related gain of the region depending on which note has been * pressed and at which velocity. @@ -144,7 +144,7 @@ struct Region { * @param velocity * @return float */ - float getNoteGain(int noteNumber, uint8_t velocity) const noexcept; + float getNoteGain(int noteNumber, float velocity) const noexcept; /** * @brief Get the additional crossfade gain of the region depending on the * CC values @@ -152,7 +152,7 @@ struct Region { * @param ccState * @return float */ - float getCrossfadeGain(const SfzCCArray& ccState) const noexcept; + float getCrossfadeGain() const noexcept; /** * @brief Get the base volume of the region depending on which note has been * pressed to trigger the region. @@ -178,7 +178,7 @@ struct Region { * * @return float */ - float velocityCurve(uint8_t velocity) const noexcept; + float velocityCurve(float velocity) const noexcept; /** * @brief Get the region offset in samples * @@ -243,11 +243,11 @@ struct Region { // Region logic: key mapping Range keyRange { Default::keyRange }; //lokey, hikey and key - Range velocityRange { Default::velocityRange }; // hivel and lovel + Range velocityRange { Default::velocityRange }; // hivel and lovel // Region logic: MIDI conditions Range bendRange { Default::bendRange }; // hibend and lobend - CCMap> ccConditions { Default::ccValueRange }; + CCMap> ccConditions { Default::ccValueRange }; Range keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey absl::optional keyswitch {}; // sw_last absl::optional keyswitchUp {}; // sw_up @@ -266,7 +266,7 @@ struct Region { // Region logic: triggers SfzTrigger trigger { Default::trigger }; // trigger - CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN + CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN // Performance parameters: amplifier float volume { Default::volume }; // volume @@ -282,17 +282,17 @@ struct Region { uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_keytrack - std::vector> velocityPoints; // amp_velcurve_N + std::vector> velocityPoints; // amp_velcurve_N float ampRandom { Default::ampRandom }; // amp_random Range crossfadeKeyInRange { Default::crossfadeKeyInRange }; Range crossfadeKeyOutRange { Default::crossfadeKeyOutRange }; - Range crossfadeVelInRange { Default::crossfadeVelInRange }; - Range crossfadeVelOutRange { Default::crossfadeVelOutRange }; + Range crossfadeVelInRange { Default::crossfadeVelInRange }; + Range crossfadeVelOutRange { Default::crossfadeVelOutRange }; SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve }; SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve }; SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve }; - CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN - CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN + CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN + CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN float rtDecay { Default::rtDecay }; // rt_decay // Filters and EQs diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index e8134809..2e217c3a 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -523,7 +523,7 @@ void multiplyAdd(absl::Span gain, absl::Span void multiplyAdd(const T gain, absl::Span input, absl::Span output) noexcept { - ASSERT(input.size() <= output.size()); + // ASSERT(input.size() <= output.size()); auto* in = input.begin(); auto* out = output.begin(); auto* sentinel = out + std::min(output.size(), input.size()); diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 173eec03..2e904a5e 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -18,7 +18,6 @@ namespace sfz { -using SfzCCArray = std::array; using CCNamePair = std::pair; template @@ -83,6 +82,16 @@ constexpr T denormalize7Bits(float value) return static_cast(value * 127.0f); } +constexpr uint8_t denormalizeCC(float value) +{ + return denormalize7Bits(value); +} + +constexpr uint8_t denormalizeVelocity(float value) +{ + return denormalize7Bits(value); +} + template constexpr float normalize7Bits(T value) { @@ -141,21 +150,17 @@ constexpr float normalizeBend(float bendValue) return min(max(bendValue, -8191.0f), 8191.0f) / 8191.0f; } -/** - * @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone. - * - * @param ccValues - * @param ccSwitch - * @param value - * @return float - */ -inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional>& ccSwitch, float value) noexcept +namespace literals { - if (ccSwitch) - return value + ccSwitch->value * normalizeCC(ccValues[ccSwitch->cc]); - else - return value; +inline float operator ""_norm(unsigned long long int value) +{ + if (value > 127) + value = 127; + + return normalize7Bits(value); } +} + /** * @brief Convert a note in string to its equivalent midi note number diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 9ef64406..a3efca3e 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -44,7 +44,7 @@ sfz::Synth::~Synth() std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto& voice: voices) + for (auto& voice : voices) voice->reset(); resources.filePool.emptyFileLoadingQueues(); @@ -129,11 +129,11 @@ void sfz::Synth::clear() std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto &voice: voices) + for (auto& voice : voices) voice->reset(); - for (auto& list: noteActivationLists) + for (auto& list : noteActivationLists) list.clear(); - for (auto& list: ccActivationLists) + for (auto& list : ccActivationLists) list.clear(); regions.clear(); effectBuses.clear(); @@ -181,8 +181,9 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Set_cc&"): // fallthrough case hash("set_cc&"): if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) { - const auto ccValue = readOpcode(member.value, Default::ccValueRange).value_or(0); - resources.midiState.ccEvent(0, member.parameters.back(), ccValue); + const auto ccValue = readOpcode(member.value, Default::midi7Range); + if (ccValue) + resources.midiState.ccEvent(0, member.parameters.back(), normalizeCC(*ccValue)); } break; case hash("Label_cc&"): // fallthrough @@ -275,17 +276,19 @@ void sfz::Synth::handleEffectOpcodes(const std::vector& members) void addEndpointsToVelocityCurve(sfz::Region& region) { if (region.velocityPoints.size() > 0) { - absl::c_sort(region.velocityPoints, [](const std::pair& lhs, const std::pair& rhs) { return lhs.first < rhs.first; }); + const auto velocityStart = sfz::Default::velocityRange.getStart(); + const auto velocityEnd = sfz::Default::velocityRange.getEnd(); + absl::c_sort(region.velocityPoints, [](const std::pair& lhs, const std::pair& rhs) { return lhs.first < rhs.first; }); if (region.ampVeltrack > 0) { - if (region.velocityPoints.back().first != sfz::Default::velocityRange.getEnd()) - region.velocityPoints.push_back(std::make_pair(127, 1.0f)); - if (region.velocityPoints.front().first != sfz::Default::velocityRange.getStart()) - region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(0, 0.0f)); + if (region.velocityPoints.front().first != velocityStart) + region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityStart, velocityStart)); + if (region.velocityPoints.back().first != velocityEnd) + region.velocityPoints.push_back(std::make_pair(velocityEnd, velocityEnd)); } else { - if (region.velocityPoints.front().first != sfz::Default::velocityRange.getEnd()) - region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(127, 0.0f)); - if (region.velocityPoints.back().first != sfz::Default::velocityRange.getStart()) - region.velocityPoints.push_back(std::make_pair(0, 1.0f)); + if (region.velocityPoints.front().first != velocityEnd) + region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityEnd, velocityStart)); + if (region.velocityPoints.back().first != velocityStart) + region.velocityPoints.push_back(std::make_pair(velocityStart, velocityEnd)); } } } @@ -341,8 +344,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) if (region->loopRange.getEnd() == Default::loopRange.getEnd()) region->loopRange.setEnd(region->sampleEnd); - if (fileInformation->loopBegin != Default::loopRange.getStart() && - fileInformation->loopEnd != Default::loopRange.getEnd()) { + if (fileInformation->loopBegin != Default::loopRange.getStart() && fileInformation->loopEnd != Default::loopRange.getEnd()) { if (region->loopRange.getStart() == Default::loopRange.getStart()) region->loopRange.setStart(fileInformation->loopBegin); @@ -374,38 +376,34 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) } for (auto note = 0; note < 128; note++) { - if (region->keyRange.containsWithEnd(note) || - (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note))) + if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note))) noteActivationLists[note].push_back(region); } - for (auto cc = 0; cc < config::numCCs; cc++) { + for (unsigned cc = 0; cc < config::numCCs; cc++) { if (region->ccTriggers.contains(cc) || region->ccConditions.contains(cc)) ccActivationLists[cc].push_back(region); } // Defaults - for (int ccIndex = 0; ccIndex < config::numCCs; ccIndex++) { - region->registerCC(ccIndex, resources.midiState.getCCValue(ccIndex)); + for (unsigned cc = 0; cc < config::numCCs; cc++) { + region->registerCC(cc, resources.midiState.getCCValue(cc)); } if (defaultSwitch) { - region->registerNoteOn(*defaultSwitch, 127, 1.0); - region->registerNoteOff(*defaultSwitch, 0, 1.0); + region->registerNoteOn(*defaultSwitch, 1.0f, 1.0f); + region->registerNoteOff(*defaultSwitch, 0.0f, 1.0f); } // Set the default frequencies on equalizers if needed if (region->equalizers.size() > 0 - && region->equalizers[0].frequency == Default::eqFrequencyUnset) - { + && 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::eqFrequencyUnset) { region->equalizers[1].frequency = Default::eqFrequency2; if (region->equalizers.size() > 2 - && region->equalizers[2].frequency == Default::eqFrequencyUnset) - { + && region->equalizers[2].frequency == Default::eqFrequencyUnset) { region->equalizers[2].frequency = Default::eqFrequency3; } } @@ -425,7 +423,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) regions.resize(remainingRegions); modificationTime = checkModificationTime(); - for (auto& voice: voices) { + for (auto& voice : voices) { voice->setMaxFiltersPerVoice(maxFilters); voice->setMaxEQsPerVoice(maxEQs); } @@ -468,7 +466,6 @@ int sfz::Synth::getNumActiveVoices() const noexcept void sfz::Synth::garbageCollect() noexcept { - } void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept @@ -487,7 +484,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept for (auto& voice : voices) voice->setSamplesPerBlock(samplesPerBlock); - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) bus->setSamplesPerBlock(samplesPerBlock); } @@ -507,7 +504,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept resources.filterPool.setSampleRate(sampleRate); resources.eqPool.setSampleRate(sampleRate); - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) bus->setSampleRate(sampleRate); } @@ -532,7 +529,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept { // Prepare the effect inputs. They are mixes of per-region outputs. ScopedTiming logger { callbackBreakdown.effects }; - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) bus->clearInputs(numFrames); } @@ -576,7 +573,7 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept // without any , the signal is just going to flow through it. ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration }; - for (auto& bus: effectBuses) { + for (auto& bus : effectBuses) { if (bus) { bus->process(numFrames); bus->mixOutputsTo(buffer, tempMixNode, numFrames); @@ -604,15 +601,15 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept { ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - + const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOnEvent(delay, noteNumber, velocity); + resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) return; - noteOnDispatch(delay, noteNumber, velocity); + noteOnDispatch(delay, noteNumber, normalizedVelocity); } void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept @@ -620,9 +617,9 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); UNUSED(velocity); - + const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOffEvent(delay, noteNumber, velocity); + resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -639,7 +636,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept noteOffDispatch(delay, noteNumber, replacedVelocity); } -void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Synth::noteOffDispatch(int delay, int noteNumber, float velocity) noexcept { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { @@ -653,7 +650,7 @@ void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) no } } -void sfz::Synth::noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { @@ -676,9 +673,10 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept { ASSERT(ccNumber < config::numCCs); ASSERT(ccNumber >= 0); + const auto normalizedCC = normalizeCC(ccValue); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.ccEvent(delay, ccNumber, ccValue); + resources.midiState.ccEvent(delay, ccNumber, normalizedCC); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -690,15 +688,15 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept } for (auto& voice : voices) - voice->registerCC(delay, ccNumber, ccValue); + voice->registerCC(delay, ccNumber, normalizedCC); for (auto& region : ccActivationLists[ccNumber]) { - if (region->registerCC(ccNumber, ccValue)) { + if (region->registerCC(ccNumber, normalizedCC)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; - voice->startVoice(region, delay, ccNumber, ccValue, Voice::TriggerType::CC); + voice->startVoice(region, delay, ccNumber, normalizedCC, Voice::TriggerType::CC); } } } @@ -711,11 +709,11 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; resources.midiState.pitchBendEvent(delay, pitch); - for (auto& region: regions) { + for (auto& region : regions) { region->registerPitchWheel(pitch); } - for (auto& voice: voices) { + for (auto& voice : voices) { voice->registerPitchWheel(delay, pitch); } } @@ -753,10 +751,9 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const if (model.empty()) model = config::midnamModel; - doc.append_child(pugi::node_doctype).set_value( - "MIDINameDocument PUBLIC" - " \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\"" - " \"http://www.midi.org/dtds/MIDINameDocument10.dtd\""); + doc.append_child(pugi::node_doctype).set_value("MIDINameDocument PUBLIC" + " \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\"" + " \"http://www.midi.org/dtds/MIDINameDocument10.dtd\""); pugi::xml_node root = doc.append_child("MIDINameDocument"); @@ -767,9 +764,11 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const pugi::xml_node device = root.append_child("MasterDeviceNames"); device.append_child("Manufacturer") - .append_child(pugi::node_pcdata).set_value(std::string(manufacturer).c_str()); + .append_child(pugi::node_pcdata) + .set_value(std::string(manufacturer).c_str()); device.append_child("Model") - .append_child(pugi::node_pcdata).set_value(std::string(model).c_str()); + .append_child(pugi::node_pcdata) + .set_value(std::string(model).c_str()); { pugi::xml_node devmode = device.append_child("CustomDeviceMode"); @@ -795,7 +794,8 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const } chns.append_child("UsesControlNameList") - .append_attribute("Name").set_value("Controls"); + .append_attribute("Name") + .set_value("Controls"); } { @@ -876,7 +876,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept void sfz::Synth::resetVoices(int numVoices) { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -885,7 +885,7 @@ void sfz::Synth::resetVoices(int numVoices) for (int i = 0; i < numVoices; ++i) voices.push_back(absl::make_unique(resources)); - for (auto& voice: voices) { + for (auto& voice : voices) { voice->setSampleRate(this->sampleRate); voice->setSamplesPerBlock(this->samplesPerBlock); } @@ -896,12 +896,12 @@ void sfz::Synth::resetVoices(int numVoices) void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto& voice: voices) + for (auto& voice : voices) voice->reset(); resources.filePool.emptyFileLoadingQueues(); @@ -916,7 +916,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -951,15 +951,15 @@ void sfz::Synth::resetAllControllers(int delay) noexcept return; resources.midiState.resetAllControllers(delay); - for (auto& voice: voices) { + for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); - for (int cc = 0; cc < config::numCCs; ++cc) - voice->registerCC(delay, cc, 0); + for (unsigned cc = 0; cc < config::numCCs; ++cc) + voice->registerCC(delay, cc, 0.0f); } - for (auto& region: regions) { - for (int cc = 0; cc < config::numCCs; ++cc) - region->registerCC(cc, 0); + for (auto& region : regions) { + for (unsigned cc = 0; cc < config::numCCs; ++cc) + region->registerCC(cc, 0.0f); } } @@ -996,13 +996,13 @@ void sfz::Synth::disableLogging() noexcept void sfz::Synth::allSoundOff() noexcept { - AtomicDisabler callbackDisabler{ canEnterCallback }; + AtomicDisabler callbackDisabler { canEnterCallback }; while (inCallback) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - for (auto &voice: voices) + for (auto& voice : voices) voice->reset(); - for (auto& effectBus: effectBuses) + for (auto& effectBus : effectBuses) effectBus->clear(); } diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index f42d67e3..b18a453b 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -463,8 +463,8 @@ private: fs::file_time_type checkModificationTime(); - void noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept; - void noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept; + void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept; + void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept; // Opcode memory; these are used to build regions, as a new region // will integrate opcodes from the group, master and global block diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index e86375f2..7bd044c5 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -24,8 +24,10 @@ sfz::Voice::Voice(sfz::Resources& resources) waveOscillator.init(sampleRate); } -void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept +void sfz::Voice::startVoice(Region* region, int delay, int number, float value, sfz::Voice::TriggerType triggerType) noexcept { + ASSERT(value >= 0.0f && value <= 1.0f); + this->triggerType = triggerType; triggerNumber = number; triggerValue = value; @@ -71,13 +73,12 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value } speedRatio = static_cast(currentPromise->sampleRate / this->sampleRate); } - pitchRatio = region->getBasePitchVariation(number, value); baseVolumedB = region->getBaseVolumedB(number); auto volumedB = baseVolumedB; if (region->volumeCC) - volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->cc)) * region->volumeCC->value; + volumedB += resources.midiState.getCCValue(region->volumeCC->cc) * region->volumeCC->value; volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB))); baseGain = region->getBaseGain(); @@ -86,28 +87,28 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value float gain { baseGain }; if (region->amplitudeCC) - gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value); + gain += resources.midiState.getCCValue(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value); amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain)); - float crossfadeGain { region->getCrossfadeGain(resources.midiState.getCCArray()) }; + float crossfadeGain { region->getCrossfadeGain() }; crossfadeEnvelope.reset(Default::normalizedRange.clamp(crossfadeGain)); basePan = normalizePercents(region->pan); auto pan = basePan; if (region->panCC) - pan += normalizeCC(resources.midiState.getCCValue(region->panCC->cc)) * normalizePercents(region->panCC->value); + pan += resources.midiState.getCCValue(region->panCC->cc) * normalizePercents(region->panCC->value); panEnvelope.reset(Default::symmetricNormalizedRange.clamp(pan)); basePosition = normalizePercents(region->position); auto position = basePosition; if (region->positionCC) - position += normalizeCC(resources.midiState.getCCValue(region->positionCC->cc)) * normalizePercents(region->positionCC->value); + position += resources.midiState.getCCValue(region->positionCC->cc) * normalizePercents(region->positionCC->value); positionEnvelope.reset(Default::symmetricNormalizedRange.clamp(position)); baseWidth = normalizePercents(region->width); auto width = baseWidth; if (region->widthCC) - width += normalizeCC(resources.midiState.getCCValue(region->widthCC->cc)) * normalizePercents(region->widthCC->value); + width += resources.midiState.getCCValue(region->widthCC->cc) * normalizePercents(region->widthCC->value); widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width)); pitchBendEnvelope.setFunction([region](float pitchValue){ @@ -159,9 +160,11 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept } } -void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept { + ASSERT(velocity >= 0.0 && velocity <= 1.0); UNUSED(velocity); + if (region == nullptr) return; @@ -179,8 +182,9 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) no } } -void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept +void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept { + ASSERT(ccValue >= 0.0 && ccValue <= 1.0); if (region == nullptr) return; @@ -200,32 +204,32 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept delay = max(delay, minEnvelopeDelay); if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) { - const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->value) }; + const float newGain { baseGain + ccValue * normalizePercents(region->amplitudeCC->value) }; amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain)); } if (region->volumeCC && ccNumber == region->volumeCC->cc) { - const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->value }; + const float newVolumedB { baseVolumedB + ccValue * region->volumeCC->value }; volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB))); } if (region->panCC && ccNumber == region->panCC->cc) { - const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->value) }; + const float newPan { basePan + ccValue * normalizePercents(region->panCC->value) }; panEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPan)); } if (region->positionCC && ccNumber == region->positionCC->cc) { - const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->value) }; + const float newPosition { basePosition + ccValue * normalizePercents(region->positionCC->value) }; positionEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPosition)); } if (region->widthCC && ccNumber == region->widthCC->cc) { - const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->value) }; + const float newWidth { baseWidth + ccValue * normalizePercents(region->widthCC->value) }; widthEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newWidth)); } if (region->crossfadeCCInRange.contains(ccNumber) || region->crossfadeCCOutRange.contains(ccNumber)) { - const float crossfadeGain = region->getCrossfadeGain(resources.midiState.getCCArray()); + const float crossfadeGain = region->getCrossfadeGain(); crossfadeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(crossfadeGain)); } } @@ -552,7 +556,7 @@ int sfz::Voice::getTriggerNumber() const noexcept return triggerNumber; } -uint8_t sfz::Voice::getTriggerValue() const noexcept +float sfz::Voice::getTriggerValue() const noexcept { return triggerValue; } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 291b1a38..1772bf45 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -81,7 +81,7 @@ public: * @param value * @param triggerType */ - void startVoice(Region* region, int delay, int number, uint8_t value, TriggerType triggerType) noexcept; + void startVoice(Region* region, int delay, int number, float value, TriggerType triggerType) noexcept; /** * @brief Register a note-off event; this may trigger a release. @@ -90,7 +90,7 @@ public: * @param noteNumber * @param velocity */ - void registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept; + void registerNoteOff(int delay, int noteNumber, float velocity) noexcept; /** * @brief Register a CC event; this may trigger a release. If the voice is playing and its * region has CC modifiers, it will use this value to compute the CC envelope to apply to the @@ -100,7 +100,7 @@ public: * @param ccNumber * @param ccValue */ - void registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept; + void registerCC(int delay, int ccNumber, float ccValue) noexcept; /** * @brief Register a pitch wheel event; for now this does nothing * @@ -163,9 +163,9 @@ public: /** * @brief Get the value that triggered the voice (note velocity or cc value) * - * @return uint8_t + * @return float */ - uint8_t getTriggerValue() const noexcept; + float getTriggerValue() const noexcept; /** * @brief Get the type of trigger * @@ -261,7 +261,7 @@ private: TriggerType triggerType; int triggerNumber; - uint8_t triggerValue; + float triggerValue; absl::optional triggerDelay; float speedRatio { 1.0 }; diff --git a/tests/ADSREnvelopeT.cpp b/tests/ADSREnvelopeT.cpp index f11945b4..22783f3d 100644 --- a/tests/ADSREnvelopeT.cpp +++ b/tests/ADSREnvelopeT.cpp @@ -49,14 +49,14 @@ TEST_CASE("[ADSREnvelope] Attack") sfz::Region region { state }; region.amplitudeEG.attack = 0.02f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array output; std::array expected { 0.5f, 1.0f, 1.0f, 1.0f, 1.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -69,14 +69,14 @@ TEST_CASE("[ADSREnvelope] Attack again") sfz::Region region { state }; region.amplitudeEG.attack = 0.03f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array output; std::array expected { 0.33333f, 0.66667f, 1.0f, 1.0f, 1.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -90,7 +90,7 @@ TEST_CASE("[ADSREnvelope] Release") region.amplitudeEG.attack = 0.02f; region.amplitudeEG.release = 0.04f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(2); std::array output; std::array expected { 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f }; @@ -98,7 +98,7 @@ TEST_CASE("[ADSREnvelope] Release") out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(2); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); @@ -114,14 +114,14 @@ TEST_CASE("[ADSREnvelope] Delay") region.amplitudeEG.release = 0.04f; region.amplitudeEG.delay = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); @@ -138,13 +138,13 @@ TEST_CASE("[ADSREnvelope] Lower sustain") region.amplitudeEG.delay = 0.02f; region.amplitudeEG.sustain = 50.0f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -161,13 +161,13 @@ TEST_CASE("[ADSREnvelope] Decay") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.707107f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5 }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -185,13 +185,13 @@ TEST_CASE("[ADSREnvelope] Hold") region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.707107f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); REQUIRE(approxEqual(output, expected)); @@ -208,7 +208,7 @@ TEST_CASE("[ADSREnvelope] Hold with release") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(8); std::array output; std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.707107f, 0.5f, 0.05f, 0.005f, 0.0005f, 0.00005f, 0.0f, 0.0f }; @@ -216,7 +216,7 @@ TEST_CASE("[ADSREnvelope] Hold with release") out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(8); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); @@ -234,14 +234,14 @@ TEST_CASE("[ADSREnvelope] Hold with release 2") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); std::array output; std::array expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f, 0.0f, 0.0 }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(output, expected)); - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); absl::c_fill(output, -1.0f); envelope.getBlock(absl::MakeSpan(output)); diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index 70ce49ee..305a60fe 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -6,114 +6,116 @@ #include "sfizz/EGDescription.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[EGDescription] Attack range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.attack = 1; eg.vel2attack = -1.27f; eg.ccAttack = { 63, 1.27f }; - REQUIRE( eg.getAttack(ccArray, 0) == 1.0f ); - REQUIRE( eg.getAttack(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getAttack(ccArray, 127) == 1.0f ); - REQUIRE( eg.getAttack(ccArray, 0) == 2.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 }; - REQUIRE( eg.getAttack(ccArray, 0) == 100.0f ); + REQUIRE( eg.getAttack(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Delay range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.delay = 1; eg.vel2delay = -1.27f; eg.ccDelay = { 63, 1.27f }; - REQUIRE( eg.getDelay(ccArray, 0) == 1.0f ); - REQUIRE( eg.getDelay(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getDelay(ccArray, 127) == 1.0f ); - REQUIRE( eg.getDelay(ccArray, 0) == 2.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 }; - REQUIRE( eg.getDelay(ccArray, 0) == 100.0f ); + REQUIRE( eg.getDelay(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Decay range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.decay = 1.0f; eg.vel2decay = -1.27f; eg.ccDecay = { 63, 1.27f }; - REQUIRE( eg.getDecay(ccArray, 0) == 1.0f ); - REQUIRE( eg.getDecay(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getDecay(ccArray, 127) == 1.0f ); - REQUIRE( eg.getDecay(ccArray, 0) == 2.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 }; - REQUIRE( eg.getDecay(ccArray, 0) == 100.0f ); + REQUIRE( eg.getDecay(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Release range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.release = 1; eg.vel2release = -1.27f; eg.ccRelease = { 63, 1.27f }; - REQUIRE( eg.getRelease(ccArray, 0) == 1.0f ); - REQUIRE( eg.getRelease(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getRelease(ccArray, 127) == 1.0f ); - REQUIRE( eg.getRelease(ccArray, 0) == 2.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 }; - REQUIRE( eg.getRelease(ccArray, 0) == 100.0f ); + REQUIRE( eg.getRelease(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Hold range") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.hold = 1; eg.vel2hold = -1.27f; eg.ccHold = { 63, 1.27f }; - REQUIRE( eg.getHold(ccArray, 0) == 1.0f ); - REQUIRE( eg.getHold(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getHold(ccArray, 127) == 1.0f ); - REQUIRE( eg.getHold(ccArray, 0) == 2.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 }; - REQUIRE( eg.getHold(ccArray, 0) == 100.0f ); + REQUIRE( eg.getHold(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Sustain level") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.sustain = 50; eg.vel2sustain = -100; eg.ccSustain = { 63, 100.0f }; - REQUIRE( eg.getSustain(ccArray, 0) == 50.0f ); - REQUIRE( eg.getSustain(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getSustain(ccArray, 127) == 50.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 }; - REQUIRE( eg.getSustain(ccArray, 0) == 100.0f ); + REQUIRE( eg.getSustain(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Start level") { sfz::EGDescription eg; - sfz::SfzCCArray ccArray { 0 }; + sfz::MidiState state; eg.start = 0; eg.ccStart = { 63, 127.0f }; - REQUIRE( eg.getStart(ccArray, 0) == 0.0f ); - REQUIRE( eg.getStart(ccArray, 127) == 0.0f ); - ccArray[63] = 127; - REQUIRE( eg.getStart(ccArray, 0) == 100.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 }; - REQUIRE( eg.getStart(ccArray, 0) == 0.0f ); + REQUIRE( eg.getStart(state, 0_norm) == 0.0f ); } diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 6284ec6c..52d71e0d 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -5,9 +5,11 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Synth.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" #include "ghc/fs_std.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[Files] Single region (regions_one.sfz)") { @@ -137,11 +139,11 @@ TEST_CASE("[Files] Group from AVL") REQUIRE(synth.getRegionView(i)->volume == 6.0f); REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(36, 36)); } - REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range(1, 26)); - REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range(27, 52)); - REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range(53, 77)); - REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range(78, 102)); - REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range(103, 127)); + REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range(1_norm, 26_norm)); + REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range(27_norm, 52_norm)); + REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range(53_norm, 77_norm)); + REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range(78_norm, 102_norm)); + REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range(103_norm, 127_norm)); } TEST_CASE("[Files] Full hierarchy") @@ -232,9 +234,9 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getNumRegions() == 4); for (int i = 0; i < synth.getNumRegions(); ++i) { REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(12, 22)); - REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range(97, 127)); + REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range(97_norm, 127_norm)); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); - REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0, 13)); + REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0_norm, 13_norm)); } REQUIRE(synth.getRegionView(0)->randRange == sfz::Range(0, 0.25)); REQUIRE(synth.getRegionView(1)->randRange == sfz::Range(0.25, 0.5)); @@ -353,8 +355,8 @@ TEST_CASE("[Files] Set CC applies properly") { sfz::Synth synth; synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); - REQUIRE(synth.getMidiState().getCCValue(142) == 63); - REQUIRE(synth.getMidiState().getCCValue(61) == 122); + REQUIRE(synth.getMidiState().getCCValue(142) == 63_norm); + REQUIRE(synth.getMidiState().getCCValue(61) == 122_norm); } TEST_CASE("[Files] Note and octave offsets") diff --git a/tests/MidiStateT.cpp b/tests/MidiStateT.cpp index d06f2215..b00bbe64 100644 --- a/tests/MidiStateT.cpp +++ b/tests/MidiStateT.cpp @@ -11,28 +11,27 @@ */ #include "sfizz/MidiState.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" #include "absl/strings/string_view.h" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[MidiState] Initial values") { sfz::MidiState state; - for (auto& cc: state.getCCArray()) - REQUIRE( cc == 0 ); + for (unsigned cc = 0; cc < sfz::config::numCCs; cc++) + REQUIRE( state.getCCValue(cc) == 0_norm ); REQUIRE( state.getPitchBend() == 0 ); } TEST_CASE("[MidiState] Set and get CCs") { sfz::MidiState state; - const auto& cc = state.getCCArray(); - state.ccEvent(0, 24, 23); - state.ccEvent(0, 123, 124); - REQUIRE(state.getCCValue(24) == 23); - REQUIRE(cc[24] == 23); - REQUIRE(state.getCCValue(123) == 124); - REQUIRE(cc[123] == 124); + state.ccEvent(0, 24, 23_norm); + state.ccEvent(0, 123, 124_norm); + REQUIRE(state.getCCValue(24) == 23_norm); + REQUIRE(state.getCCValue(123) == 124_norm); } TEST_CASE("[MidiState] Set and get pitch bends") @@ -48,26 +47,25 @@ TEST_CASE("[MidiState] Reset") { sfz::MidiState state; state.pitchBendEvent(0, 894); - state.noteOnEvent(0, 64, 24); - state.ccEvent(0, 123, 124); + state.noteOnEvent(0, 64, 24_norm); + state.ccEvent(0, 123, 124_norm); state.reset(0); REQUIRE(state.getPitchBend() == 0); - REQUIRE(state.getNoteVelocity(64) == 0); - REQUIRE(state.getCCValue(123) == 0); + REQUIRE(state.getNoteVelocity(64) == 0_norm); + REQUIRE(state.getCCValue(123) == 0_norm); } TEST_CASE("[MidiState] Set and get note velocities") { sfz::MidiState state; - state.noteOnEvent(0, 64, 24); - REQUIRE(+state.getNoteVelocity(64) == 24); - state.noteOnEvent(0, 64, 123); - REQUIRE(+state.getNoteVelocity(64) == 123); + state.noteOnEvent(0, 64, 24_norm); + REQUIRE(+state.getNoteVelocity(64) == 24_norm); + state.noteOnEvent(0, 64, 123_norm); + REQUIRE(+state.getNoteVelocity(64) == 123_norm); } TEST_CASE("[MidiState] Extended CCs") { sfz::MidiState state; - REQUIRE(state.getCCArray().size() >= 142); - state.ccEvent(0, 142, 64); // should not trap + state.ccEvent(0, 142, 64_norm); // should not trap } diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 95821441..c2aae1d1 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Region.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("Region activation", "Region tests") { @@ -16,7 +18,7 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sample", "*sine" }); SECTION("Basic state") { - region.registerCC(4, 0); + region.registerCC(4, 0_norm); REQUIRE(region.isSwitchedOn()); } @@ -24,19 +26,19 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "locc4", "56" }); region.parseOpcode({ "hicc4", "59" }); - region.registerCC(4, 0); + region.registerCC(4, 0_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(4, 57); + region.registerCC(4, 57_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 56); + region.registerCC(4, 56_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 59); + region.registerCC(4, 59_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 43); + region.registerCC(4, 43_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(4, 65); + region.registerCC(4, 65_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(6, 57); + region.registerCC(6, 57_norm); REQUIRE(!region.isSwitchedOn()); } @@ -46,26 +48,26 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "hicc4", "59" }); region.parseOpcode({ "locc54", "18" }); region.parseOpcode({ "hicc54", "27" }); - region.registerCC(4, 0); - region.registerCC(54, 0); + region.registerCC(4, 0_norm); + region.registerCC(54, 0_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(4, 57); + region.registerCC(4, 57_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(54, 19); + region.registerCC(54, 19_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(54, 18); + region.registerCC(54, 18_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(54, 27); + region.registerCC(54, 27_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 56); + region.registerCC(4, 56_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 59); + region.registerCC(4, 59_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(54, 2); + region.registerCC(54, 2_norm); REQUIRE(!region.isSwitchedOn()); - region.registerCC(54, 26); + region.registerCC(54, 26_norm); REQUIRE(region.isSwitchedOn()); - region.registerCC(4, 65); + region.registerCC(4, 65_norm); REQUIRE(!region.isSwitchedOn()); } @@ -116,13 +118,13 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "sw_last", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 64, 0.5f); + region.registerNoteOff(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_last with non-default keyswitch range") @@ -131,20 +133,20 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_last", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_down with non-default keyswitch range") @@ -153,20 +155,20 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_down", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64, 0.5f); + region.registerNoteOn(60, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0, 0.5f); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOff(60, 0_norm, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); } SECTION("Keyswitches: sw_up with non-default keyswitch range") @@ -175,16 +177,16 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_up", "40" }); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } @@ -192,20 +194,20 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "sw_previous", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64, 0.5f); + region.registerNoteOn(41, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0, 0.5f); + region.registerNoteOff(41, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); } @@ -215,17 +217,17 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "1" }); region.parseOpcode({ "key", "40" }); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); } SECTION("Sequences: length 2, position 2") @@ -234,17 +236,17 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "key", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } SECTION("Sequences: length 3, position 2") @@ -253,21 +255,21 @@ TEST_CASE("Region activation", "Region tests") region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "key", "40" }); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64, 0.5f); + region.registerNoteOn(40, 64_norm, 0.5f); REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0, 0.5f); + region.registerNoteOff(40, 0_norm, 0.5f); REQUIRE(region.isSwitchedOn()); } } diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index f65ad406..c5338b49 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -6,8 +6,10 @@ #include "sfizz/MidiState.h" #include "sfizz/Region.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("[Region] Parsing opcodes") { @@ -204,19 +206,19 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("lovel, hivel") { - REQUIRE(region.velocityRange == sfz::Range(0, 127)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "lovel", "37" }); - REQUIRE(region.velocityRange == sfz::Range(37, 127)); + REQUIRE(region.velocityRange == sfz::Range(37_norm, 127_norm)); region.parseOpcode({ "lovel", "128" }); - REQUIRE(region.velocityRange == sfz::Range(127, 127)); + REQUIRE(region.velocityRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "lovel", "-3" }); - REQUIRE(region.velocityRange == sfz::Range(0, 127)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "hivel", "65" }); - REQUIRE(region.velocityRange == sfz::Range(0, 65)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 65_norm)); region.parseOpcode({ "hivel", "-1" }); - REQUIRE(region.velocityRange == sfz::Range(0, 0)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "hivel", "128" }); - REQUIRE(region.velocityRange == sfz::Range(0, 127)); + REQUIRE(region.velocityRange == sfz::Range(0_norm, 127_norm)); } SECTION("lobend, hibend") @@ -238,16 +240,16 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("locc, hicc") { - REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0, 127)); - REQUIRE(region.ccConditions[127] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "locc6", "4" }); - REQUIRE(region.ccConditions[6] == sfz::Range(4, 127)); + REQUIRE(region.ccConditions[6] == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "locc12", "-128" }); - REQUIRE(region.ccConditions[12] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions[12] == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "hicc65", "39" }); - REQUIRE(region.ccConditions[65] == sfz::Range(0, 39)); + REQUIRE(region.ccConditions[65] == sfz::Range(0_norm, 39_norm)); region.parseOpcode({ "hicc127", "135" }); - REQUIRE(region.ccConditions[127] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); } SECTION("sw_lokey, sw_hikey") @@ -425,10 +427,10 @@ TEST_CASE("[Region] Parsing opcodes") } region.parseOpcode({ "on_locc45", "15" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[45] == sfz::Range(15, 127)); + REQUIRE(region.ccTriggers[45] == sfz::Range(15_norm, 127_norm)); region.parseOpcode({ "on_hicc4", "47" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[4] == sfz::Range(0, 47)); + REQUIRE(region.ccTriggers[4] == sfz::Range(0_norm, 47_norm)); } SECTION("volume") @@ -563,9 +565,9 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("amp_velcurve") { region.parseOpcode({ "amp_velcurve_6", "0.4" }); - REQUIRE(region.velocityPoints.back() == std::make_pair(6, 0.4f)); + REQUIRE(region.velocityPoints.back() == std::make_pair(6_norm, 0.4f)); region.parseOpcode({ "amp_velcurve_127", "-1.0" }); - REQUIRE(region.velocityPoints.back() == std::make_pair(127, 0.0f)); + REQUIRE(region.velocityPoints.back() == std::make_pair(127_norm, 0.0f)); } SECTION("xfin_lokey, xfin_hikey") @@ -589,21 +591,21 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("xfin_lovel, xfin_hivel") { - REQUIRE(region.crossfadeVelInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_lovel", "4" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(4, 4)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_lovel", "128" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_lovel", "59" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hivel", "59" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59, 59)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hivel", "128" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hivel", "0" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hivel", "-1" }); - REQUIRE(region.crossfadeVelInRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelInRange == sfz::Range(0_norm, 0_norm)); } SECTION("xfout_lokey, xfout_hikey") @@ -627,59 +629,59 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("xfout_lovel, xfout_hivel") { - REQUIRE(region.crossfadeVelOutRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "4" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(4, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "128" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(127, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_lovel", "59" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hivel", "59" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59, 59)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hivel", "128" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(59, 127)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hivel", "0" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hivel", "-1" }); - REQUIRE(region.crossfadeVelOutRange == sfz::Range(0, 0)); + REQUIRE(region.crossfadeVelOutRange == sfz::Range(0_norm, 0_norm)); } SECTION("xfin_locc, xfin_hicc") { REQUIRE(!region.crossfadeCCInRange.contains(4)); region.parseOpcode({ "xfin_locc4", "4" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4, 4)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_locc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_locc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 59)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hicc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "0" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hicc4", "-1" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); } SECTION("xfout_locc, xfout_hicc") { REQUIRE(!region.crossfadeCCOutRange.contains(4)); region.parseOpcode({ "xfout_locc4", "4" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(4, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 59)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hicc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "0" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hicc4", "-1" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); } SECTION("xf_keycurve") diff --git a/tests/RegionTriggersT.cpp b/tests/RegionTriggersT.cpp index fe7f4569..75f5d9b5 100644 --- a/tests/RegionTriggersT.cpp +++ b/tests/RegionTriggersT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Region.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; TEST_CASE("Basic triggers", "Region triggers") { @@ -17,44 +19,44 @@ TEST_CASE("Basic triggers", "Region triggers") SECTION("key") { region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } SECTION("lokey and hikey") { region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "42" }); - REQUIRE(!region.registerNoteOn(39, 64, 0.5f)); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(region.registerNoteOn(42, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(43, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(42, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(42, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(!region.registerNoteOn(39, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(43, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(42, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(42, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } SECTION("key and release trigger") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "trigger", "release" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(41, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } SECTION("key and release_key trigger") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "trigger", "release_key" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(region.registerNoteOff(40, 64, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - REQUIRE(!region.registerNoteOff(41, 64, 0.5f)); - REQUIRE(!region.registerCC(63, 64)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOff(40, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + REQUIRE(!region.registerNoteOff(41, 64_norm, 0.5f)); + REQUIRE(!region.registerCC(63, 64_norm)); } // TODO: first and legato triggers SECTION("lovel and hivel") @@ -62,11 +64,11 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lovel", "60" }); region.parseOpcode({ "hivel", "70" }); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - REQUIRE(region.registerNoteOn(40, 60, 0.5f)); - REQUIRE(region.registerNoteOn(40, 70, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 71, 0.5f)); - REQUIRE(!region.registerNoteOn(41, 59, 0.5f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 60_norm, 0.5f)); + REQUIRE(region.registerNoteOn(40, 70_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 71_norm, 0.5f)); + REQUIRE(!region.registerNoteOn(41, 59_norm, 0.5f)); } SECTION("lorand and hirand") @@ -74,54 +76,54 @@ TEST_CASE("Basic triggers", "Region triggers") region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lorand", "0.35" }); region.parseOpcode({ "hirand", "0.40" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.34f)); - REQUIRE(region.registerNoteOn(40, 64, 0.35f)); - REQUIRE(region.registerNoteOn(40, 64, 0.36f)); - REQUIRE(region.registerNoteOn(40, 64, 0.37f)); - REQUIRE(region.registerNoteOn(40, 64, 0.38f)); - REQUIRE(region.registerNoteOn(40, 64, 0.39f)); - REQUIRE(!region.registerNoteOn(40, 64, 0.40f)); - REQUIRE(!region.registerNoteOn(40, 64, 0.41f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.34f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.35f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.36f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.37f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.38f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.39f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.40f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.41f)); } SECTION("lorand and hirand on 1.0f") { region.parseOpcode({ "key", "40" }); region.parseOpcode({ "lorand", "0.35" }); - REQUIRE(!region.registerNoteOn(40, 64, 0.34f)); - REQUIRE(region.registerNoteOn(40, 64, 0.35f)); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.34f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.35f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); } SECTION("Disable key trigger") { region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(!region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "hikey", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "key", "-1" }); - REQUIRE(!region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(!region.registerNoteOn(40, 64_norm, 1.0f)); region.parseOpcode({ "key", "40" }); - REQUIRE(region.registerNoteOn(40, 64, 1.0f)); + REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f)); } SECTION("on_loccN, on_hiccN") { region.parseOpcode({ "on_locc47", "64" }); region.parseOpcode({ "on_hicc47", "68" }); - REQUIRE(!region.registerCC(47, 63)); - REQUIRE(!region.registerCC(47, 64)); - REQUIRE(!region.registerCC(47, 65)); + REQUIRE(!region.registerCC(47, 63_norm)); + REQUIRE(!region.registerCC(47, 64_norm)); + REQUIRE(!region.registerCC(47, 65_norm)); region.parseOpcode({ "hikey", "-1" }); - REQUIRE(region.registerCC(47, 64)); - REQUIRE(region.registerCC(47, 65)); - REQUIRE(region.registerCC(47, 66)); - REQUIRE(region.registerCC(47, 67)); - REQUIRE(region.registerCC(47, 68)); - REQUIRE(!region.registerCC(47, 69)); - REQUIRE(!region.registerCC(40, 64)); + REQUIRE(region.registerCC(47, 64_norm)); + REQUIRE(region.registerCC(47, 65_norm)); + REQUIRE(region.registerCC(47, 66_norm)); + REQUIRE(region.registerCC(47, 67_norm)); + REQUIRE(region.registerCC(47, 68_norm)); + REQUIRE(!region.registerCC(47, 69_norm)); + REQUIRE(!region.registerCC(40, 64_norm)); } } @@ -135,16 +137,16 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "first" }); - midiState.noteOnEvent(0, 40, 64); - REQUIRE(region.registerNoteOn(40, 64, 0.5f)); - midiState.noteOnEvent(0, 41, 64); - REQUIRE(!region.registerNoteOn(41, 64, 0.5f)); - midiState.noteOffEvent(0, 40, 0); - region.registerNoteOff(40, 0, 0.5f); - midiState.noteOffEvent(0, 41, 0); - region.registerNoteOff(41, 0, 0.5f); - midiState.noteOnEvent(0, 42, 64); - REQUIRE(region.registerNoteOn(42, 64, 0.5f)); + midiState.noteOnEvent(0, 40, 64_norm); + REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f)); + midiState.noteOnEvent(0, 41, 64_norm); + REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f)); + midiState.noteOffEvent(0, 40, 0_norm); + region.registerNoteOff(40, 0_norm, 0.5f); + midiState.noteOffEvent(0, 41, 0_norm); + region.registerNoteOff(41, 0_norm, 0.5f); + midiState.noteOnEvent(0, 42, 64_norm); + REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f)); } SECTION("Second note playing") @@ -152,15 +154,15 @@ TEST_CASE("Legato triggers", "Region triggers") region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "trigger", "legato" }); - midiState.noteOnEvent(0, 40, 64); - REQUIRE(!region.registerNoteOn(40, 64, 0.5f)); - midiState.noteOnEvent(0, 41, 64); - REQUIRE(region.registerNoteOn(41, 64, 0.5f)); - midiState.noteOffEvent(0, 40, 64); - region.registerNoteOff(40, 0, 0.5f); - midiState.noteOffEvent(0, 41, 64); - region.registerNoteOff(41, 0, 0.5f); - midiState.noteOnEvent(0, 42, 64); - REQUIRE(!region.registerNoteOn(42, 64, 0.5f)); + midiState.noteOnEvent(0, 40, 64_norm); + REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f)); + midiState.noteOnEvent(0, 41, 64_norm); + REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f)); + midiState.noteOffEvent(0, 40, 64_norm); + region.registerNoteOff(40, 0_norm, 0.5f); + midiState.noteOffEvent(0, 41, 64_norm); + region.registerNoteOff(41, 0_norm, 0.5f); + midiState.noteOnEvent(0, 42, 64_norm); + REQUIRE(!region.registerNoteOn(42, 64_norm, 0.5f)); } } diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index eae54b0b..31566e76 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -12,6 +12,7 @@ #include #include using namespace Catch::literals; +using namespace sfz::literals; constexpr int numRandomTests { 64 }; TEST_CASE("[Region] Crossfade in on key") { @@ -20,9 +21,9 @@ TEST_CASE("[Region] Crossfade in on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE( region.getNoteGain(2, 127) == 0.70711_a ); - REQUIRE( region.getNoteGain(1, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(3, 127) == 1.0_a ); + REQUIRE( region.getNoteGain(2, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(3, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on key - 2") @@ -32,12 +33,12 @@ TEST_CASE("[Region] Crossfade in on key - 2") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE( region.getNoteGain(1, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(3, 127) == 0.70711_a ); - REQUIRE( region.getNoteGain(4, 127) == 0.86603_a ); - REQUIRE( region.getNoteGain(5, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(6, 127) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(3, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(4, 127_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(6, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on key - gain") @@ -48,11 +49,11 @@ TEST_CASE("[Region] Crossfade in on key - gain") region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE( region.getNoteGain(1, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 127) == 0.25_a ); - REQUIRE( region.getNoteGain(3, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(4, 127) == 0.75_a ); - REQUIRE( region.getNoteGain(5, 127) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 127_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(3, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(4, 127_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on key") @@ -62,13 +63,13 @@ TEST_CASE("[Region] Crossfade out on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE( region.getNoteGain(50, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(51, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(52, 127) == 0.86603_a ); - REQUIRE( region.getNoteGain(53, 127) == 0.70711_a ); - REQUIRE( region.getNoteGain(54, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(55, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(56, 127) == 0.0_a ); + REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(51, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(52, 127_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(53, 127_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(54, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on key - gain") @@ -79,13 +80,13 @@ TEST_CASE("[Region] Crossfade out on key - gain") region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE( region.getNoteGain(50, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(51, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(52, 127) == 0.75_a ); - REQUIRE( region.getNoteGain(53, 127) == 0.5_a ); - REQUIRE( region.getNoteGain(54, 127) == 0.25_a ); - REQUIRE( region.getNoteGain(55, 127) == 0.0_a ); - REQUIRE( region.getNoteGain(56, 127) == 0.0_a ); + REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(51, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(52, 127_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(53, 127_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(54, 127_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade in on velocity") @@ -96,13 +97,13 @@ TEST_CASE("[Region] Crossfade in on velocity") region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(1, 19) == 0.0_a ); - REQUIRE( region.getNoteGain(1, 20) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 21) == 0.5_a ); - REQUIRE( region.getNoteGain(3, 22) == 0.70711_a ); - REQUIRE( region.getNoteGain(4, 23) == 0.86603_a ); - REQUIRE( region.getNoteGain(5, 24) == 1.0_a ); - REQUIRE( region.getNoteGain(6, 25) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(1, 20_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 21_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(3, 22_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(4, 23_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(6, 25_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade in on vel - gain") @@ -114,13 +115,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain") region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(1, 19) == 0.0_a ); - REQUIRE( region.getNoteGain(1, 20) == 0.0_a ); - REQUIRE( region.getNoteGain(2, 21) == 0.25_a ); - REQUIRE( region.getNoteGain(3, 22) == 0.5_a ); - REQUIRE( region.getNoteGain(4, 23) == 0.75_a ); - REQUIRE( region.getNoteGain(5, 24) == 1.0_a ); - REQUIRE( region.getNoteGain(5, 25) == 1.0_a ); + REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(1, 20_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(2, 21_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(3, 22_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(4, 23_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(5, 25_norm) == 1.0_a ); } TEST_CASE("[Region] Crossfade out on vel") @@ -131,13 +132,13 @@ TEST_CASE("[Region] Crossfade out on vel") region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(5, 50) == 1.0_a ); - REQUIRE( region.getNoteGain(5, 51) == 1.0_a ); - REQUIRE( region.getNoteGain(5, 52) == 0.86603_a ); - REQUIRE( region.getNoteGain(5, 53) == 0.70711_a ); - REQUIRE( region.getNoteGain(5, 54) == 0.5_a ); - REQUIRE( region.getNoteGain(5, 55) == 0.0_a ); - REQUIRE( region.getNoteGain(5, 56) == 0.0_a ); + REQUIRE( region.getNoteGain(5, 50_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(5, 51_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(5, 52_norm) == 0.86603_a ); + REQUIRE( region.getNoteGain(5, 53_norm) == 0.70711_a ); + REQUIRE( region.getNoteGain(5, 54_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(5, 55_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(5, 56_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade out on vel - gain") @@ -149,13 +150,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain") region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(56, 50) == 1.0_a ); - REQUIRE( region.getNoteGain(56, 51) == 1.0_a ); - REQUIRE( region.getNoteGain(56, 52) == 0.75_a ); - REQUIRE( region.getNoteGain(56, 53) == 0.5_a ); - REQUIRE( region.getNoteGain(56, 54) == 0.25_a ); - REQUIRE( region.getNoteGain(56, 55) == 0.0_a ); - REQUIRE( region.getNoteGain(56, 56) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 50_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(56, 51_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(56, 52_norm) == 0.75_a ); + REQUIRE( region.getNoteGain(56, 53_norm) == 0.5_a ); + REQUIRE( region.getNoteGain(56, 54_norm) == 0.25_a ); + REQUIRE( region.getNoteGain(56, 55_norm) == 0.0_a ); + REQUIRE( region.getNoteGain(56, 56_norm) == 0.0_a ); } TEST_CASE("[Region] Crossfade in on CC") @@ -166,13 +167,13 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -184,13 +185,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); } TEST_CASE("[Region] Crossfade out on CC") { @@ -200,13 +201,13 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -218,13 +219,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); - midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a ); - midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a ); - midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a ); - midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a ); - midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); - midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a ); + midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); + midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); + midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); + midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); + midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); + midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") @@ -233,8 +234,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE( region.getNoteGain(64, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(64, 0) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a ); } @@ -244,8 +245,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE( region.getNoteGain(64, 127) == 1.0_a ); - REQUIRE( region.getNoteGain(64, 0) == Approx(0.0).margin(0.0001) ); + REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001) ); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") @@ -254,8 +255,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") sfz::Region region { midiState }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) ); - REQUIRE( region.getNoteGain(64, 0) == 1.0_a ); + REQUIRE( region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001) ); + REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a ); } TEST_CASE("[Region] rt_decay") @@ -265,15 +266,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "rt_decay", "10" }); - midiState.noteOnEvent(0, 64, 64); + midiState.noteOnEvent(0, 64, 64_norm); 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" }); - midiState.noteOnEvent(0, 64, 64); + midiState.noteOnEvent(0, 64, 64_norm); 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" }); - midiState.noteOnEvent(0, 64, 64); + midiState.noteOnEvent(0, 64, 64_norm); std::this_thread::sleep_for(std::chrono::milliseconds(100)); REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) ); } diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 70806b5e..4411d12b 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -5,8 +5,10 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Synth.h" +#include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; +using namespace sfz::literals; constexpr int blockSize { 256 }; @@ -139,9 +141,9 @@ TEST_CASE("[Synth] Reset all controllers") { sfz::Synth synth; synth.cc(0, 12, 64); - REQUIRE( synth.getMidiState().getCCValue(12) == 64 ); + REQUIRE( synth.getMidiState().getCCValue(12) == 64_norm ); synth.cc(0, 121, 64); - REQUIRE( synth.getMidiState().getCCValue(12) == 0 ); + REQUIRE( synth.getMidiState().getCCValue(12) == 0_norm ); } TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice")