diff --git a/src/sfizz/CCMap.h b/src/sfizz/CCMap.h index c025f3e3..a71ba2f4 100644 --- a/src/sfizz/CCMap.h +++ b/src/sfizz/CCMap.h @@ -6,6 +6,7 @@ #pragma once #include "LeakDetector.h" +#include "SfzHelpers.h" #include #include @@ -43,11 +44,11 @@ public: */ const ValueType& getWithDefault(int index) const noexcept { - auto it = absl::c_find_if(container, [&](auto&& pair){ return pair.first == index; }); - if (it == container.end()) { + auto it = absl::c_lower_bound(container, index, CompareCC{}); + if (it == container.end() || it->cc != index) { return defaultValue; } else { - return it->second; + return it->value; } } @@ -59,13 +60,12 @@ public: */ ValueType& operator[](const int& index) noexcept { - auto it = absl::c_find_if(container, [&](auto&& pair){ return pair.first == index; }); - if (it == container.end()) { - auto newElement = std::make_pair(index, defaultValue); - auto inserted = container.insert(absl::c_upper_bound(container, newElement, [](auto& lhs, auto& rhs) { return lhs.first < rhs.first; }), newElement); - return inserted->second; + auto it = absl::c_lower_bound(container, index, CompareCC{}); + if (it == container.end() || it->cc != index) { + auto inserted = container.insert(it, { index, defaultValue }); + return inserted->value; } else { - return it->second; + return it->value; } } @@ -85,15 +85,16 @@ public: */ bool contains(int index) const noexcept { - return absl::c_find_if(container, [&](auto&& pair){ return pair.first == index; }) != container.end(); + return absl::c_binary_search(container, index, CompareCC{}); } - typename std::vector>::const_iterator begin() const { return container.cbegin(); } - typename std::vector>::const_iterator end() const { return container.cend(); } + typename std::vector>::const_iterator begin() const { return container.cbegin(); } + typename std::vector>::const_iterator end() const { return container.cend(); } private: // typename std::vector>::iterator begin() { return container.begin(); } // typename std::vector>::iterator end() { return container.end(); } + const ValueType defaultValue; - std::vector> container; + std::vector> container; LEAK_DETECTOR(CCMap); }; } diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index cc655511..afaa32c8 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -64,13 +64,13 @@ struct EGDescription 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 diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index afba37c8..ad8238fd 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -120,7 +120,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.first)) * mod.second); + lambda(value, normalizeCC(getCCValue(mod.cc)) * mod.value); } return validRange.clamp(value); } diff --git a/src/sfizz/Opcode.h b/src/sfizz/Opcode.h index 227c3d28..2ac43209 100644 --- a/src/sfizz/Opcode.h +++ b/src/sfizz/Opcode.h @@ -184,11 +184,11 @@ inline void setRangeStartFromOpcode(const Opcode& opcode, Range& targ * @param validRange the range of admitted values used to clamp the opcode */ template -inline void setCCPairFromOpcode(const Opcode& opcode, absl::optional& target, const Range& validRange) +inline void setCCPairFromOpcode(const Opcode& opcode, absl::optional>& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (value && Default::ccNumberRange.containsWithEnd(opcode.parameters.back())) - target = std::make_pair(opcode.parameters.back(), *value); + target = { opcode.parameters.back(), *value }; else target = {}; } diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 9e2c162e..004b5fe9 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -992,14 +992,14 @@ float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) noexcept // Crossfades due to CC states for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = ccState[valuePair.first]; - const auto crossfadeRange = valuePair.second; + const auto ccValue = ccState[valuePair.cc]; + const auto crossfadeRange = valuePair.value; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = ccState[valuePair.first]; - const auto crossfadeRange = valuePair.second; + const auto ccValue = ccState[valuePair.cc]; + const auto crossfadeRange = valuePair.value; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index cf08657b..fa5ea786 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -264,11 +264,11 @@ struct Region { float pan { Default::pan }; // pan float width { Default::width }; // width float position { Default::position }; // position - absl::optional volumeCC; // volume_oncc - absl::optional amplitudeCC; // amplitude_oncc - absl::optional panCC; // pan_oncc - absl::optional widthCC; // width_oncc - absl::optional positionCC; // position_oncc + absl::optional> volumeCC; // volume_oncc + absl::optional> amplitudeCC; // amplitude_oncc + absl::optional> panCC; // pan_oncc + absl::optional> widthCC; // width_oncc + absl::optional> positionCC; // position_oncc uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_keytrack diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 1e590d1b..eb05e6bc 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -16,9 +16,32 @@ namespace sfz { using SfzCCArray = std::array; -using CCValuePair = std::pair ; using CCNamePair = std::pair; +template +struct CCValuePair { + int cc; + ValueType value; +}; + +template +struct CompareCC { + bool operator()(const CCValuePair& valuePair, const int& cc) + { + return (valuePair.cc < cc); + } + + bool operator()(const int& cc, const CCValuePair& valuePair) + { + return (cc < valuePair.cc); + } + + bool operator()(const CCValuePair& lhs, const CCValuePair& rhs) + { + return (lhs.cc < rhs.cc); + } +}; + /** * @brief Converts cents to a pitch ratio * @@ -94,10 +117,10 @@ constexpr float normalizeBend(float bendValue) * @param value * @return float */ -inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional& ccSwitch, float value) noexcept +inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional>& ccSwitch, float value) noexcept { if (ccSwitch) - return value + ccSwitch->second * normalizeCC(ccValues[ccSwitch->first]); + return value + ccSwitch->value * normalizeCC(ccValues[ccSwitch->cc]); else return value; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 64bac0f2..df3e3c10 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -47,7 +47,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value baseVolumedB = region->getBaseVolumedB(number); auto volumedB { baseVolumedB }; if (region->volumeCC) - volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->first)) * region->volumeCC->second; + volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->cc)) * region->volumeCC->value; volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB))); baseGain = region->getBaseGain(); @@ -56,7 +56,7 @@ 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->first)) * normalizePercents(region->amplitudeCC->second); + gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value); amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain)); float crossfadeGain { region->getCrossfadeGain(resources.midiState.getCCArray()) }; @@ -65,19 +65,19 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value basePan = normalizePercents(region->pan); auto pan { basePan }; if (region->panCC) - pan += normalizeCC(resources.midiState.getCCValue(region->panCC->first)) * normalizePercents(region->panCC->second); + pan += normalizeCC(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->first)) * normalizePercents(region->positionCC->second); + position += normalizeCC(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->first)) * normalizePercents(region->widthCC->second); + width += normalizeCC(resources.midiState.getCCValue(region->widthCC->cc)) * normalizePercents(region->widthCC->value); widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width)); pitchBendEnvelope.setFunction([region](float pitchValue){ @@ -168,28 +168,28 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept // TODO: this feels like a hack, revisit this along with the smoothed envelopes... delay = max(delay, minEnvelopeDelay); - if (region->amplitudeCC && ccNumber == region->amplitudeCC->first) { - const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->second) }; + if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) { + const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->value) }; amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain)); } - if (region->volumeCC && ccNumber == region->volumeCC->first) { - const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->second }; + if (region->volumeCC && ccNumber == region->volumeCC->cc) { + const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->value }; volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB))); } - if (region->panCC && ccNumber == region->panCC->first) { - const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->second) }; + if (region->panCC && ccNumber == region->panCC->cc) { + const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->value) }; panEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPan)); } - if (region->positionCC && ccNumber == region->positionCC->first) { - const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->second) }; + if (region->positionCC && ccNumber == region->positionCC->cc) { + const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->value) }; positionEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPosition)); } - if (region->widthCC && ccNumber == region->widthCC->first) { - const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->second) }; + if (region->widthCC && ccNumber == region->widthCC->cc) { + const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->value) }; widthEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newWidth)); }