diff --git a/docs/community_features.md b/docs/community_features.md index 9892d55a12..d4c9d932f4 100644 --- a/docs/community_features.md +++ b/docs/community_features.md @@ -38,6 +38,12 @@ Here is a list of general improvements that have been made, ordered from newest #### 3.7 - Mod Wheel - ([#512]) Incoming mod wheel on non-MPE synths now maps to y axis +#### 3.8 - Visual Feedback on Value Changes with Mod Encoders and Increased Resolution for Value's in Menu's +- ([#636]) Changing parameter values with Mod (Gold) Encoders now displays a pop-up with the current value of the Parameter. The Menu's for Parameters and Patch Cables have also been adjusted to show the same value range as displayed with the Mod Encoders. + - This allows for better fine-tuning of values. + - The value range displayed is 0-50 for non-MIDI parameters and 0-127 for MIDI parameters. + - Note: In the Menu, if you wish to scroll through the parameter value range faster at an accelerated rate of +/- 5, hold Shift while turning the Select Encoder. + ## 4. New Features Added Here is a list of features that have been added to the firmware as a list, grouped by category: @@ -58,7 +64,6 @@ Here is a list of features that have been added to the firmware as a list, group - Default (DEFA) - the default Deluge clip type. - Fill (FILL) - Fill clip. It appears orange/cyan on the status pads, and when triggered it will schedule itself to start at such a time that it _finishes_ at the start of the next loop. If the fill clip is longer than the remaining time, it is triggered immediately at a point midway through. The loop length is set by the longest playing clip, or by the total length of a section times the repeat count set for that section. **Limitation**: a fill clip is still subject to the one clip per instrument behavior of the Deluge. Fill clips can steal an output from another fill, but they cannot steal from a non-fill. This can lead to some fills never starting since a default type clip has the needed instrument. This can be worked around by cloning the instrument to an independent copy. - #### 4.1.4 - Catch Notes - ([#221]) The normal behavior of the Deluge is to try to keep up with 'in progress' notes when instant switching between clips by playing them late. However this leads to glitches with drum clips and other percussive sounds. Changing this setting to OFF will prevent this behavior and *not* try to keep up with those notes, leading to smoother instant switching between clips. @@ -197,6 +202,7 @@ Synchronization modes accessible through the "LFO SYNC" shortcut. - Follow-up PR's: - ([#347]) Added new automatable parameters - ([#360]) Fixed interpolation bugs, added fine tuning for long presses, and added pad selection mode + - ([#636]) Updated Parameter Values displayed in Automation View to match Parameter Value Ranges displayed in the Menu's. E.g. instead of 0 - 128, it now displays 0 - 50 (except for Pan which now displays -25 to +25 and MIDI instrument clips which now display 0 - 127). #### 4.3.6 - Set Probability By Row @@ -390,4 +396,5 @@ This list includes all preprocessor switches that can alter firmware behaviour a [#368]: https://github.com/SynthstromAudible/DelugeFirmware/pull/368 [#395]: https://github.com/SynthstromAudible/DelugeFirmware/pull/395 [#512]: https://github.com/SynthstromAudible/DelugeFirmware/pull/512 +[#636]: https://github.com/SynthstromAudible/DelugeFirmware/pull/636 [Automation View Documentation]: https://github.com/SynthstromAudible/DelugeFirmware/blob/release/1.0/docs/features/automation_view.md diff --git a/docs/features/automation_view.md b/docs/features/automation_view.md index 1b7e3ae989..daa0ccf4d5 100644 --- a/docs/features/automation_view.md +++ b/docs/features/automation_view.md @@ -146,7 +146,9 @@ The Automation Editor **will:** - enable you to quickly change parameters in focus for editing by turning select or using shift + shortcut pad - enable you to view the current parameter value setting for the parameters that are currently automatable. - illuminate each pad row according to the current value within the range of 0-128. E.g. bottom pad = 0-16, then 17-32, 33-48, 49-64, 65-80, 81-96, 97-112, 113-128) +> **Update** The values displayed in automation view have been updated to display the same value range displayed in the menu's for consistency across the Deluge UI. So instead of displaying 0 - 128, it now displays 0 - 50. Calculations in automation view are still being done based on the 0 - 128 range, but the display converts it to the 0 - 50 range. - edit new or existing parameter automations on a per step basis, at any zoom level across the entire timeline. Each row in a step column corresponds to a range of values in the parameter value range (0-128) (see above). If you press the bottom row, the value will be set to 0. if you press the top row, the value will be set to 128. Pressing the rows in between increments/decrements the value by 18 (e.g. 0, 18, 36, 54, 72, 90, 108, 128). +> **Update** The values displayed in automation view have been updated to display the same value range displayed in the menu's for consistency across the Deluge UI. So instead of displaying 0 - 128, it now displays 0 - 50. Calculations in automation view are still being done based on the 0 - 128 range, but the display converts it to the 0 - 50 range. ![image](https://github.com/seangoodvibes/DelugeFirmware/assets/138174805/8cc7befa-9071-4bd3-ac3c-15049f69b250) diff --git a/src/definitions_cxx.hpp b/src/definitions_cxx.hpp index f80aeaf1b5..ec5fe76ffd 100644 --- a/src/definitions_cxx.hpp +++ b/src/definitions_cxx.hpp @@ -356,7 +356,23 @@ constexpr int32_t kNumPatchSources = static_cast(kLastPatchSource); constexpr PatchSource kFirstLocalSource = PatchSource::ENVELOPE_0; //constexpr PatchSource kFirstUnchangeableSource = PatchSource::VELOCITY; -//Automation Instrument Clip View constants +//Menu Min Max Values + +//regular menu range e.g. 0 - 50 +constexpr int32_t kMaxMenuValue = 50; +constexpr int32_t kMinMenuValue = 0; +constexpr int32_t kMidMenuValue = kMinMenuValue + ((kMaxMenuValue - kMinMenuValue) / 2); + +//pan menu range e.g. -25 to +25 +constexpr int32_t kMaxMenuPanValue = kMaxMenuValue / 2; +constexpr int32_t kMinMenuPanValue = -1 * kMaxMenuPanValue; + +//patch cable menu range e.g. -5000 to 5000 +constexpr int32_t kMaxMenuPatchCableValue = kMaxMenuValue * 100; +constexpr int32_t kMinMenuPatchCableValue = -1 * kMaxMenuPatchCableValue; +// + +//Automation View constants constexpr int32_t kNoSelection = 255; constexpr int32_t kNumNonKitAffectEntireParamsForAutomation = 55; constexpr int32_t kNumKitAffectEntireParamsForAutomation = 24; diff --git a/src/deluge/gui/menu_item/arpeggiator/midi_cv/gate.h b/src/deluge/gui/menu_item/arpeggiator/midi_cv/gate.h index cc676bb6f2..645fd12efa 100644 --- a/src/deluge/gui/menu_item/arpeggiator/midi_cv/gate.h +++ b/src/deluge/gui/menu_item/arpeggiator/midi_cv/gate.h @@ -27,13 +27,13 @@ class Gate final : public Integer { void readCurrentValue() override { auto* current_clip = static_cast(currentSong->currentClip); int64_t arp_gate = (int64_t)current_clip->arpeggiatorGate + 2147483648; - this->setValue((arp_gate * 50 + 2147483648) >> 32); + this->setValue((arp_gate * kMaxMenuValue + 2147483648) >> 32); } void writeCurrentValue() override { (static_cast(currentSong->currentClip))->arpeggiatorGate = (uint32_t)this->getValue() * 85899345 - 2147483648; } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } bool isRelevant(Sound* sound, int32_t whichThing) override { return soundEditor.editingCVOrMIDIClip(); } }; } // namespace deluge::gui::menu_item::arpeggiator::midi_cv diff --git a/src/deluge/gui/menu_item/arpeggiator/midi_cv/rate.h b/src/deluge/gui/menu_item/arpeggiator/midi_cv/rate.h index cc77b61ae3..a5915d049f 100644 --- a/src/deluge/gui/menu_item/arpeggiator/midi_cv/rate.h +++ b/src/deluge/gui/menu_item/arpeggiator/midi_cv/rate.h @@ -26,12 +26,13 @@ class Rate final : public Integer { using Integer::Integer; void readCurrentValue() override { this->setValue( - (((int64_t)(static_cast(currentSong->currentClip))->arpeggiatorRate + 2147483648) * 50 + (((int64_t)(static_cast(currentSong->currentClip))->arpeggiatorRate + 2147483648) + * kMaxMenuValue + 2147483648) >> 32); } void writeCurrentValue() override { - if (this->getValue() == 25) { + if (this->getValue() == kMidMenuValue) { (static_cast(currentSong->currentClip))->arpeggiatorRate = 0; } else { @@ -39,7 +40,7 @@ class Rate final : public Integer { (uint32_t)this->getValue() * 85899345 - 2147483648; } } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } bool isRelevant(Sound* sound, int32_t whichThing) override { return soundEditor.editingCVOrMIDIClip(); } }; } // namespace deluge::gui::menu_item::arpeggiator::midi_cv diff --git a/src/deluge/gui/menu_item/audio_clip/attack.h b/src/deluge/gui/menu_item/audio_clip/attack.h index ed2e793a43..bb356778de 100644 --- a/src/deluge/gui/menu_item/audio_clip/attack.h +++ b/src/deluge/gui/menu_item/audio_clip/attack.h @@ -27,13 +27,14 @@ class Attack final : public Integer { void readCurrentValue() override { this->setValue( - (((int64_t)(static_cast(currentSong->currentClip))->attack + 2147483648) * 50 + 2147483648) + (((int64_t)(static_cast(currentSong->currentClip))->attack + 2147483648) * kMaxMenuValue + + 2147483648) >> 32); } void writeCurrentValue() override { (static_cast(currentSong->currentClip))->attack = (uint32_t)this->getValue() * 85899345 - 2147483648; } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } }; } // namespace deluge::gui::menu_item::audio_clip diff --git a/src/deluge/gui/menu_item/audio_clip/hpf_freq.h b/src/deluge/gui/menu_item/audio_clip/hpf_freq.h index 47b3100a04..b276dd8807 100644 --- a/src/deluge/gui/menu_item/audio_clip/hpf_freq.h +++ b/src/deluge/gui/menu_item/audio_clip/hpf_freq.h @@ -25,7 +25,7 @@ class HPFFreq final : public UnpatchedParam { // 7SEG ONLY void drawValue() override { - if (this->getValue() == 0) { + if (this->getValue() == kMinMenuValue) { display->setText(l10n::get(l10n::String::STRING_FOR_DISABLED)); } else { diff --git a/src/deluge/gui/menu_item/audio_clip/lpf_freq.h b/src/deluge/gui/menu_item/audio_clip/lpf_freq.h index 33e7e68ab0..4085246105 100644 --- a/src/deluge/gui/menu_item/audio_clip/lpf_freq.h +++ b/src/deluge/gui/menu_item/audio_clip/lpf_freq.h @@ -25,7 +25,7 @@ class LPFFreq final : public UnpatchedParam { // 7Seg ONLY void drawValue() override { - if (this->getValue() == 50) { + if (this->getValue() == kMaxMenuValue) { display->setText(l10n::get(l10n::String::STRING_FOR_DISABLED)); } else { diff --git a/src/deluge/gui/menu_item/filter/hpf_freq.h b/src/deluge/gui/menu_item/filter/hpf_freq.h index 91979e8118..2241d64903 100644 --- a/src/deluge/gui/menu_item/filter/hpf_freq.h +++ b/src/deluge/gui/menu_item/filter/hpf_freq.h @@ -27,7 +27,7 @@ class HPFFreq final : public patched_param::IntegerNonFM { // 7Seg ONLY void drawValue() override { - if (this->getValue() == 0 + if (this->getValue() == kMinMenuValue && !soundEditor.currentParamManager->getPatchCableSet()->doesParamHaveSomethingPatchedToIt( ::Param::Local::HPF_FREQ)) { display->setText(l10n::get(l10n::String::STRING_FOR_DISABLED)); diff --git a/src/deluge/gui/menu_item/filter/lpf_freq.h b/src/deluge/gui/menu_item/filter/lpf_freq.h index 50b6121ac4..bb7125f3a5 100644 --- a/src/deluge/gui/menu_item/filter/lpf_freq.h +++ b/src/deluge/gui/menu_item/filter/lpf_freq.h @@ -26,7 +26,7 @@ class LPFFreq final : public patched_param::IntegerNonFM { // 7Seg ONLY void drawValue() override { - if (this->getValue() == 50 + if (this->getValue() == kMaxMenuValue && !soundEditor.currentParamManager->getPatchCableSet()->doesParamHaveSomethingPatchedToIt( ::Param::Local::LPF_FREQ)) { display->setText(l10n::get(l10n::String::STRING_FOR_DISABLED)); diff --git a/src/deluge/gui/menu_item/midi/default_velocity_to_level.h b/src/deluge/gui/menu_item/midi/default_velocity_to_level.h index 7475715426..36702de590 100644 --- a/src/deluge/gui/menu_item/midi/default_velocity_to_level.h +++ b/src/deluge/gui/menu_item/midi/default_velocity_to_level.h @@ -24,12 +24,13 @@ namespace deluge::gui::menu_item::midi { class DefaultVelocityToLevel final : public IntegerWithOff { public: using IntegerWithOff::IntegerWithOff; - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } void readCurrentValue() override { - this->setValue(((int64_t)soundEditor.currentMIDIDevice->defaultVelocityToLevel * 50 + 536870912) >> 30); + this->setValue(((int64_t)soundEditor.currentMIDIDevice->defaultVelocityToLevel * kMaxMenuValue + 536870912) + >> 30); } void writeCurrentValue() override { - soundEditor.currentMIDIDevice->defaultVelocityToLevel = this->getValue() * 21474836; + soundEditor.currentMIDIDevice->defaultVelocityToLevel = this->getValue() * (2147483648 / (kMaxMenuValue * 2)); currentSong->grabVelocityToLevelFromMIDIDeviceAndSetupPatchingForEverything(soundEditor.currentMIDIDevice); MIDIDeviceManager::anyChangesToSave = true; } diff --git a/src/deluge/gui/menu_item/osc/pulse_width.h b/src/deluge/gui/menu_item/osc/pulse_width.h index 8aa639a18d..8242e56363 100644 --- a/src/deluge/gui/menu_item/osc/pulse_width.h +++ b/src/deluge/gui/menu_item/osc/pulse_width.h @@ -29,11 +29,22 @@ class PulseWidth final : public menu_item::source::PatchedParam, public Formatte [[nodiscard]] std::string_view getTitle() const override { return FormattedTitle::title(); } - int32_t getFinalValue() override { return (uint32_t)this->getValue() * (85899345 >> 1); } + int32_t getFinalValue() override { + if (this->getValue() == kMaxMenuValue) { + return 2147483647; + } + else if (this->getValue() == kMinMenuValue) { + return 0; + } + else { + return (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) >> 1; + } + } void readCurrentValue() override { this->setValue( - ((int64_t)soundEditor.currentParamManager->getPatchedParamSet()->getValue(getP()) * 100 + 2147483648) + ((int64_t)soundEditor.currentParamManager->getPatchedParamSet()->getValue(getP()) * (kMaxMenuValue * 2) + + 2147483648) >> 32); } diff --git a/src/deluge/gui/menu_item/param.h b/src/deluge/gui/menu_item/param.h index 3ea4080580..0e0211a043 100644 --- a/src/deluge/gui/menu_item/param.h +++ b/src/deluge/gui/menu_item/param.h @@ -28,8 +28,8 @@ namespace deluge::gui::menu_item { class Param { public: Param(int32_t newP = 0) : p(newP) {} - [[nodiscard]] virtual int32_t getMaxValue() const { return 50; } - [[nodiscard]] virtual int32_t getMinValue() const { return 0; } + [[nodiscard]] virtual int32_t getMaxValue() const { return kMaxMenuValue; } + [[nodiscard]] virtual int32_t getMinValue() const { return kMinMenuValue; } virtual uint8_t getP() { return p; }; MenuItem* selectButtonPress(); virtual ModelStackWithAutoParam* getModelStack(void* memory) = 0; diff --git a/src/deluge/gui/menu_item/patch_cable_strength.cpp b/src/deluge/gui/menu_item/patch_cable_strength.cpp index 3c7059e588..5498af8467 100644 --- a/src/deluge/gui/menu_item/patch_cable_strength.cpp +++ b/src/deluge/gui/menu_item/patch_cable_strength.cpp @@ -112,7 +112,7 @@ void PatchCableStrength::renderOLED() { char buffer[12]; if (preferBarDrawing) { - int32_t rounded = (this->getValue() + 50 * (this->getValue() > 0 ? 1 : -1)) / 100; + int32_t rounded = this->getValue() / 100; intToString(rounded, buffer, 1); deluge::hid::display::OLED::drawStringAlignRight( buffer, extraY + OLED_MAIN_TOPMOST_PIXEL + 4 + destinationDescriptor.isJustAParam(), @@ -152,7 +152,7 @@ void PatchCableStrength::readCurrentValue() { int32_t paramValue = patchCableSet->patchCables[c].param.getCurrentValue(); // the internal values are stored in the range -(2^30) to 2^30. // rescale them to the range -5000 to 5000 and round to nearest. - this->setValue(((int64_t)paramValue * 5000 + (1 << 29)) >> 30); + this->setValue(((int64_t)paramValue * kMaxMenuPatchCableValue + (1 << 29)) >> 30); } } @@ -175,8 +175,9 @@ void PatchCableStrength::writeCurrentValue() { return; } - // rescale from 5000 to 2**30. The magic constant is ((2^30)/5000), shifted 32 bits for precision ((1<<(30+32))/5000) - int32_t finalValue = ((int64_t)922337203685477 * this->getValue()) >> 32; + //rescale from 5000 to 2^30. The magic constant is ((2^30)/5000), shifted 32 bits for precision ((1<<(30+32))/5000) + int64_t magicConstant = (922337203685477 * 5000) / kMaxMenuPatchCableValue; + int32_t finalValue = (magicConstant * this->getValue()) >> 32; modelStackWithParam->autoParam->setCurrentValueInResponseToUserInput(finalValue, modelStackWithParam); } diff --git a/src/deluge/gui/menu_item/patch_cable_strength.h b/src/deluge/gui/menu_item/patch_cable_strength.h index ba0892d83f..c36b37f7c3 100644 --- a/src/deluge/gui/menu_item/patch_cable_strength.h +++ b/src/deluge/gui/menu_item/patch_cable_strength.h @@ -27,8 +27,8 @@ class PatchCableStrength : public Decimal, public MenuItemWithCCLearning { void beginSession(MenuItem* navigatedBackwardFrom) final; void readCurrentValue() final; void writeCurrentValue() override; - [[nodiscard]] int32_t getMinValue() const final { return -5000; } - [[nodiscard]] int32_t getMaxValue() const final { return 5000; } + [[nodiscard]] int32_t getMinValue() const final { return kMinMenuPatchCableValue; } + [[nodiscard]] int32_t getMaxValue() const final { return kMaxMenuPatchCableValue; } [[nodiscard]] int32_t getNumDecimalPlaces() const final { return 2; } virtual int32_t getDefaultEditPos() { return 2; } MenuPermission checkPermissionToBeginSession(Sound* sound, int32_t whichThing, MultiRange** currentRange) override; diff --git a/src/deluge/gui/menu_item/patch_cables.cpp b/src/deluge/gui/menu_item/patch_cables.cpp index acc269baf5..c5bf5f4aed 100644 --- a/src/deluge/gui/menu_item/patch_cables.cpp +++ b/src/deluge/gui/menu_item/patch_cables.cpp @@ -79,7 +79,7 @@ void PatchCables::renderOptions() { } int32_t param_value = cable->param.getCurrentValue(); - int32_t level = ((int64_t)param_value * 5000 + (1 << 29)) >> 30; + int32_t level = ((int64_t)param_value * kMaxMenuPatchCableValue + (1 << 29)) >> 30; floatToString((float)level / 100, buf + off + 5, 2, 2); //fmt::vformat_to_n(buf + off + 5, 5, "{:4}", fmt::make_format_args(); diff --git a/src/deluge/gui/menu_item/patched_param/integer.cpp b/src/deluge/gui/menu_item/patched_param/integer.cpp index 155a2d048b..19785c6cc3 100644 --- a/src/deluge/gui/menu_item/patched_param/integer.cpp +++ b/src/deluge/gui/menu_item/patched_param/integer.cpp @@ -21,9 +21,10 @@ namespace deluge::gui::menu_item::patched_param { void Integer::readCurrentValue() { - this->setValue((((int64_t)soundEditor.currentParamManager->getPatchedParamSet()->getValue(getP()) + 2147483648) * 50 - + 2147483648) - >> 32); + this->setValue( + (((int64_t)soundEditor.currentParamManager->getPatchedParamSet()->getValue(getP()) + 2147483648) * kMaxMenuValue + + 2147483648) + >> 32); } void Integer::writeCurrentValue() { @@ -35,10 +36,15 @@ void Integer::writeCurrentValue() { } int32_t Integer::getFinalValue() { - if (this->getValue() == 25) { - return 0; + if (this->getValue() == kMaxMenuValue) { + return 2147483647; + } + else if (this->getValue() == kMinMenuValue) { + return -2147483648; + } + else { + return (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) - 2147483648; } - return (uint32_t)this->getValue() * 85899345 - 2147483648; } } // namespace deluge::gui::menu_item::patched_param diff --git a/src/deluge/gui/menu_item/patched_param/pan.cpp b/src/deluge/gui/menu_item/patched_param/pan.cpp index 93bc87be8f..178e7d59d6 100644 --- a/src/deluge/gui/menu_item/patched_param/pan.cpp +++ b/src/deluge/gui/menu_item/patched_param/pan.cpp @@ -47,17 +47,21 @@ void Pan::drawValue() { } int32_t Pan::getFinalValue() { - if (this->getValue() == 32) { + if (this->getValue() == kMaxMenuPanValue) { return 2147483647; } - if (this->getValue() == -32) { + else if (this->getValue() == kMinMenuPanValue) { return -2147483648; } - return ((int32_t)this->getValue() * 33554432 * 2); + else { + return ((int32_t)this->getValue() * (2147483648 / (kMaxMenuPanValue * 2)) * 2); + } } void Pan::readCurrentValue() { - this->setValue(((int64_t)soundEditor.currentParamManager->getPatchedParamSet()->getValue(getP()) * 64 + 2147483648) - >> 32); + this->setValue( + ((int64_t)soundEditor.currentParamManager->getPatchedParamSet()->getValue(getP()) * (kMaxMenuPanValue * 2) + + 2147483648) + >> 32); } } // namespace deluge::gui::menu_item::patched_param diff --git a/src/deluge/gui/menu_item/patched_param/pan.h b/src/deluge/gui/menu_item/patched_param/pan.h index 1d44d61a14..c6d61afe5d 100644 --- a/src/deluge/gui/menu_item/patched_param/pan.h +++ b/src/deluge/gui/menu_item/patched_param/pan.h @@ -24,8 +24,8 @@ class Pan : public Integer { void drawValue() override; protected: - [[nodiscard]] int32_t getMaxValue() const override { return 32; } - [[nodiscard]] int32_t getMinValue() const override { return -32; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuPanValue; } + [[nodiscard]] int32_t getMinValue() const override { return kMinMenuPanValue; } int32_t getFinalValue() override; void readCurrentValue() override; }; diff --git a/src/deluge/gui/menu_item/reverb/compressor/shape.h b/src/deluge/gui/menu_item/reverb/compressor/shape.h index 29a1579e32..8e11736f6e 100644 --- a/src/deluge/gui/menu_item/reverb/compressor/shape.h +++ b/src/deluge/gui/menu_item/reverb/compressor/shape.h @@ -26,13 +26,13 @@ class Shape final : public Integer { public: using Integer::Integer; void readCurrentValue() override { - this->setValue((((int64_t)AudioEngine::reverbCompressorShape + 2147483648) * 50 + 2147483648) >> 32); + this->setValue((((int64_t)AudioEngine::reverbCompressorShape + 2147483648) * kMaxMenuValue + 2147483648) >> 32); } void writeCurrentValue() override { AudioEngine::reverbCompressorShape = (uint32_t)this->getValue() * 85899345 - 2147483648; AudioEngine::mustUpdateReverbParamsBeforeNextRender = true; } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } bool isRelevant(Sound* sound, int32_t whichThing) override { return (AudioEngine::reverbCompressorVolume >= 0); } }; } // namespace deluge::gui::menu_item::reverb::compressor diff --git a/src/deluge/gui/menu_item/reverb/compressor/volume.h b/src/deluge/gui/menu_item/reverb/compressor/volume.h index 7af983e7cc..813fdfd615 100644 --- a/src/deluge/gui/menu_item/reverb/compressor/volume.h +++ b/src/deluge/gui/menu_item/reverb/compressor/volume.h @@ -30,7 +30,7 @@ class Volume final : public Integer { AudioEngine::reverbCompressorVolume = this->getValue() * 21474836; AudioEngine::mustUpdateReverbParamsBeforeNextRender = true; } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } [[nodiscard]] int32_t getMinValue() const override { return -1; } void drawValue() override { diff --git a/src/deluge/gui/menu_item/reverb/dampening.h b/src/deluge/gui/menu_item/reverb/dampening.h index 4a1874f3a9..35ae79d784 100644 --- a/src/deluge/gui/menu_item/reverb/dampening.h +++ b/src/deluge/gui/menu_item/reverb/dampening.h @@ -25,8 +25,8 @@ namespace deluge::gui::menu_item::reverb { class Dampening final : public Integer { public: using Integer::Integer; - void readCurrentValue() override { this->setValue(std::round(AudioEngine::reverb.getdamp() * 50)); } - void writeCurrentValue() override { AudioEngine::reverb.setdamp((float)this->getValue() / 50); } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + void readCurrentValue() override { this->setValue(std::round(AudioEngine::reverb.getdamp() * kMaxMenuValue)); } + void writeCurrentValue() override { AudioEngine::reverb.setdamp((float)this->getValue() / kMaxMenuValue); } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } }; } // namespace deluge::gui::menu_item::reverb diff --git a/src/deluge/gui/menu_item/reverb/pan.h b/src/deluge/gui/menu_item/reverb/pan.h index abe4c3cb4c..3c52c02b1f 100644 --- a/src/deluge/gui/menu_item/reverb/pan.h +++ b/src/deluge/gui/menu_item/reverb/pan.h @@ -43,8 +43,10 @@ class Pan final : public Integer { void writeCurrentValue() override { AudioEngine::reverbPan = ((int32_t)this->getValue() * 33554432); } - void readCurrentValue() override { this->setValue(((int64_t)AudioEngine::reverbPan * 128 + 2147483648) >> 32); } - [[nodiscard]] int32_t getMaxValue() const override { return 32; } - [[nodiscard]] int32_t getMinValue() const override { return -32; } + void readCurrentValue() override { + this->setValue(((int64_t)AudioEngine::reverbPan * (kMaxMenuPanValue * 4) + 2147483648) >> 32); + } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuPanValue; } + [[nodiscard]] int32_t getMinValue() const override { return kMinMenuPanValue; } }; } // namespace deluge::gui::menu_item::reverb diff --git a/src/deluge/gui/menu_item/reverb/room_size.h b/src/deluge/gui/menu_item/reverb/room_size.h index ed42469f58..0a31e8524f 100644 --- a/src/deluge/gui/menu_item/reverb/room_size.h +++ b/src/deluge/gui/menu_item/reverb/room_size.h @@ -25,8 +25,8 @@ namespace deluge::gui::menu_item::reverb { class RoomSize final : public Integer { public: using Integer::Integer; - void readCurrentValue() override { this->setValue(std::round(AudioEngine::reverb.getroomsize() * 50)); } - void writeCurrentValue() override { AudioEngine::reverb.setroomsize((float)this->getValue() / 50); } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + void readCurrentValue() override { this->setValue(std::round(AudioEngine::reverb.getroomsize() * kMaxMenuValue)); } + void writeCurrentValue() override { AudioEngine::reverb.setroomsize((float)this->getValue() / kMaxMenuValue); } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } }; } // namespace deluge::gui::menu_item::reverb diff --git a/src/deluge/gui/menu_item/reverb/width.h b/src/deluge/gui/menu_item/reverb/width.h index 127d022792..ac56279de6 100644 --- a/src/deluge/gui/menu_item/reverb/width.h +++ b/src/deluge/gui/menu_item/reverb/width.h @@ -25,8 +25,8 @@ namespace deluge::gui::menu_item::reverb { class Width final : public Integer { public: using Integer::Integer; - void readCurrentValue() override { this->setValue(std::round(AudioEngine::reverb.getwidth() * 50)); } - void writeCurrentValue() override { AudioEngine::reverb.setwidth((float)this->getValue() / 50); } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + void readCurrentValue() override { this->setValue(std::round(AudioEngine::reverb.getwidth() * kMaxMenuValue)); } + void writeCurrentValue() override { AudioEngine::reverb.setwidth((float)this->getValue() / kMaxMenuValue); } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } }; } // namespace deluge::gui::menu_item::reverb diff --git a/src/deluge/gui/menu_item/sidechain/send.h b/src/deluge/gui/menu_item/sidechain/send.h index ef40660a07..b00f00ad42 100644 --- a/src/deluge/gui/menu_item/sidechain/send.h +++ b/src/deluge/gui/menu_item/sidechain/send.h @@ -24,17 +24,17 @@ class Send final : public Integer { public: using Integer::Integer; void readCurrentValue() override { - this->setValue(((uint64_t)soundEditor.currentSound->sideChainSendLevel * 50 + 1073741824) >> 31); + this->setValue(((uint64_t)soundEditor.currentSound->sideChainSendLevel * kMaxMenuValue + 1073741824) >> 31); } void writeCurrentValue() override { - if (this->getValue() == 50) { + if (this->getValue() == kMaxMenuValue) { soundEditor.currentSound->sideChainSendLevel = 2147483647; } else { soundEditor.currentSound->sideChainSendLevel = this->getValue() * 42949673; } } - [[nodiscard]] int32_t getMaxValue() const override { return 50; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuValue; } bool isRelevant(Sound* sound, int32_t whichThing) override { return (soundEditor.editingKit()); } }; } // namespace deluge::gui::menu_item::sidechain diff --git a/src/deluge/gui/menu_item/unpatched_param.cpp b/src/deluge/gui/menu_item/unpatched_param.cpp index e18cc71a35..f87d584ae3 100644 --- a/src/deluge/gui/menu_item/unpatched_param.cpp +++ b/src/deluge/gui/menu_item/unpatched_param.cpp @@ -31,10 +31,10 @@ extern "C" { namespace deluge::gui::menu_item { void UnpatchedParam::readCurrentValue() { - this->setValue( - (((int64_t)soundEditor.currentParamManager->getUnpatchedParamSet()->getValue(getP()) + 2147483648) * 50 - + 2147483648) - >> 32); + this->setValue((((int64_t)soundEditor.currentParamManager->getUnpatchedParamSet()->getValue(getP()) + 2147483648) + * kMaxMenuValue + + 2147483648) + >> 32); } ModelStackWithAutoParam* UnpatchedParam::getModelStack(void* memory) { @@ -52,11 +52,14 @@ void UnpatchedParam::writeCurrentValue() { } int32_t UnpatchedParam::getFinalValue() { - if (this->getValue() == 25) { - return 0; + if (this->getValue() == kMaxMenuValue) { + return 2147483647; + } + else if (this->getValue() == kMinMenuValue) { + return -2147483648; } else { - return (uint32_t)this->getValue() * 85899345 - 2147483648; + return (uint32_t)this->getValue() * (2147483648 / kMidMenuValue) - 2147483648; } } diff --git a/src/deluge/gui/menu_item/unpatched_param/pan.cpp b/src/deluge/gui/menu_item/unpatched_param/pan.cpp index 8c29436630..dd19bff0d5 100644 --- a/src/deluge/gui/menu_item/unpatched_param/pan.cpp +++ b/src/deluge/gui/menu_item/unpatched_param/pan.cpp @@ -42,20 +42,22 @@ void Pan::drawValue() { // TODO: should really combine this with the "patched } int32_t Pan::getFinalValue() { - if (this->getValue() == 32) { + if (this->getValue() == kMaxMenuPanValue) { return 2147483647; } - else if (this->getValue() == -32) { + else if (this->getValue() == kMinMenuPanValue) { return -2147483648; } else { - return ((int32_t)this->getValue() * 33554432 * 2); + return ((int32_t)this->getValue() * (2147483648 / (kMaxMenuPanValue * 2)) * 2); } } void Pan::readCurrentValue() { this->setValue( - ((int64_t)soundEditor.currentParamManager->getUnpatchedParamSet()->getValue(getP()) * 64 + 2147483648) >> 32); + ((int64_t)soundEditor.currentParamManager->getUnpatchedParamSet()->getValue(getP()) * (kMaxMenuPanValue * 2) + + 2147483648) + >> 32); } } // namespace deluge::gui::menu_item::unpatched_param diff --git a/src/deluge/gui/menu_item/unpatched_param/pan.h b/src/deluge/gui/menu_item/unpatched_param/pan.h index 9ec2351bb9..a191227021 100644 --- a/src/deluge/gui/menu_item/unpatched_param/pan.h +++ b/src/deluge/gui/menu_item/unpatched_param/pan.h @@ -26,8 +26,8 @@ class Pan final : public UnpatchedParam { virtual void drawValue(); protected: - [[nodiscard]] int32_t getMaxValue() const override { return 32; } - [[nodiscard]] int32_t getMinValue() const override { return -32; } + [[nodiscard]] int32_t getMaxValue() const override { return kMaxMenuPanValue; } + [[nodiscard]] int32_t getMinValue() const override { return kMinMenuPanValue; } int32_t getFinalValue() override; void readCurrentValue() override; }; diff --git a/src/deluge/gui/ui/sound_editor.cpp b/src/deluge/gui/ui/sound_editor.cpp index ac31d6213f..b9eb48c407 100644 --- a/src/deluge/gui/ui/sound_editor.cpp +++ b/src/deluge/gui/ui/sound_editor.cpp @@ -650,6 +650,10 @@ ActionResult SoundEditor::horizontalEncoderAction(int32_t offset) { void SoundEditor::selectEncoderAction(int8_t offset) { + if (Buttons::isShiftButtonPressed()) { + offset = offset * 5; + } + if (currentUIMode != UI_MODE_NONE && currentUIMode != UI_MODE_AUDITIONING && currentUIMode != UI_MODE_HOLDING_AFFECT_ENTIRE_IN_SOUND_EDITOR) { return; diff --git a/src/deluge/gui/views/automation_instrument_clip_view.cpp b/src/deluge/gui/views/automation_instrument_clip_view.cpp index e1b48be93d..62d60b2700 100644 --- a/src/deluge/gui/views/automation_instrument_clip_view.cpp +++ b/src/deluge/gui/views/automation_instrument_clip_view.cpp @@ -810,21 +810,27 @@ DisplayParameterValue DisplayParameterName */ void AutomationInstrumentClipView::renderDisplay(int32_t knobPosLeft, int32_t knobPosRight, bool modEncoderAction) { + InstrumentClip* clip = getCurrentClip(); + Instrument* instrument = (Instrument*)clip->output; + + //if you're not in a MIDI instrument clip, convert the knobPos to the same range as the menu (0-50) + if (instrument->type != InstrumentType::MIDI_OUT) { + knobPosLeft = calculateKnobPosForDisplay(clip, knobPosLeft); + knobPosRight = calculateKnobPosForDisplay(clip, knobPosRight); + } + //OLED Display if (display->haveOLED()) { - renderDisplayOLED(knobPosLeft, knobPosRight); + renderDisplayOLED(clip, instrument, knobPosLeft, knobPosRight); } //7SEG Display else { - renderDisplay7SEG(knobPosLeft, modEncoderAction); + renderDisplay7SEG(clip, instrument, knobPosLeft, modEncoderAction); } } -void AutomationInstrumentClipView::renderDisplayOLED(int32_t knobPosLeft, int32_t knobPosRight) { - - InstrumentClip* clip = getCurrentClip(); - Instrument* instrument = (Instrument*)clip->output; - +void AutomationInstrumentClipView::renderDisplayOLED(InstrumentClip* clip, Instrument* instrument, int32_t knobPosLeft, + int32_t knobPosRight) { deluge::hid::display::OLED::clearMainImage(); if (isOnAutomationOverview() || (instrument->type == InstrumentType::CV)) { @@ -852,7 +858,7 @@ void AutomationInstrumentClipView::renderDisplayOLED(int32_t knobPosLeft, int32_ else if (instrument->type != InstrumentType::CV) { //display parameter name char parameterName[30]; - getParameterName(parameterName); + getParameterName(clip, instrument, parameterName); #if OLED_MAIN_HEIGHT_PIXELS == 64 int32_t yPos = OLED_MAIN_TOPMOST_PIXEL + 12; @@ -917,11 +923,8 @@ void AutomationInstrumentClipView::renderDisplayOLED(int32_t knobPosLeft, int32_ deluge::hid::display::OLED::sendMainImage(); } -void AutomationInstrumentClipView::renderDisplay7SEG(int32_t knobPosLeft, bool modEncoderAction) { - - InstrumentClip* clip = getCurrentClip(); - Instrument* instrument = (Instrument*)clip->output; - +void AutomationInstrumentClipView::renderDisplay7SEG(InstrumentClip* clip, Instrument* instrument, int32_t knobPosLeft, + bool modEncoderAction) { //display OVERVIEW or CANT if (isOnAutomationOverview() || (instrument->type == InstrumentType::CV)) { if (instrument->type != InstrumentType::CV) { @@ -962,18 +965,14 @@ void AutomationInstrumentClipView::renderDisplay7SEG(int32_t knobPosLeft, bool m //display parameter name else { char parameterName[30]; - getParameterName(parameterName); + getParameterName(clip, instrument, parameterName); display->setScrollingText(parameterName); } } } //get's the name of the Parameter being edited so it can be displayed on the screen -void AutomationInstrumentClipView::getParameterName(char* parameterName) { - - InstrumentClip* clip = getCurrentClip(); - Instrument* instrument = (Instrument*)clip->output; - +void AutomationInstrumentClipView::getParameterName(InstrumentClip* clip, Instrument* instrument, char* parameterName) { if (instrument->type == InstrumentType::SYNTH || instrument->type == InstrumentType::KIT) { if (clip->lastSelectedParamKind == Param::Kind::PATCHED) { strncpy(parameterName, getPatchedParamDisplayName(clip->lastSelectedParamID), 29); @@ -1017,6 +1016,20 @@ void AutomationInstrumentClipView::getParameterName(char* parameterName) { } } +//for non-MIDI instruments, convert deluge internal knobPos range to same range as used by menu's. +int32_t AutomationInstrumentClipView::calculateKnobPosForDisplay(InstrumentClip* clip, int32_t knobPos) { + int32_t offset = 0; + + //if the parameter is pan, convert knobPos from 0 - 50 to -25 to +25 + if ((clip->lastSelectedParamID == Param::Local::PAN) + || (clip->lastSelectedParamID == Param::Unpatched::GlobalEffectable::PAN)) { + offset = kMaxMenuPanValue; + } + + //convert knobPos from 0 - 128 to 0 - 50 + return (((((knobPos << 20) / kMaxKnobPos) * kMaxMenuValue) >> 20) - offset); +} + //adjust the LED meters and update the display /*updated function for displaying automation when playback is enabled (called from ui_timer_manager). @@ -2416,6 +2429,15 @@ bool AutomationInstrumentClipView::modEncoderActionForSelectedPad(int32_t whichM int32_t newKnobPos = calculateKnobPosForModEncoderTurn(knobPos, offset); + //ignore modEncoderTurn for Midi CC if current or new knobPos exceeds 127 + //if current knobPos exceeds 127, e.g. it's 128, then it needs to drop to 126 before a value change gets recorded + //if newKnobPos exceeds 127, then it means current knobPos was 127 and it was increased to 128. In which case, ignore value change + if (clip->output->type == InstrumentType::MIDI_OUT) { + if ((knobPos == 64) || (newKnobPos == 64)) { + return true; + } + } + //use default interpolation settings initInterpolation(); @@ -2454,6 +2476,15 @@ void AutomationInstrumentClipView::modEncoderActionForUnselectedPad(int32_t whic int32_t newKnobPos = calculateKnobPosForModEncoderTurn(knobPos, offset); + //ignore modEncoderTurn for Midi CC if current or new knobPos exceeds 127 + //if current knobPos exceeds 127, e.g. it's 128, then it needs to drop to 126 before a value change gets recorded + //if newKnobPos exceeds 127, then it means current knobPos was 127 and it was increased to 128. In which case, ignore value change + if (clip->output->type == InstrumentType::MIDI_OUT) { + if ((knobPos == 64) || (newKnobPos == 64)) { + return; + } + } + int32_t newValue = modelStackWithParam->paramCollection->knobPosToParamValue(newKnobPos, modelStackWithParam); @@ -2686,7 +2717,13 @@ void AutomationInstrumentClipView::selectEncoderAction(int8_t offset) { InstrumentClip* clip = getCurrentClip(); Instrument* instrument = (Instrument*)clip->output; - if (instrument->type == InstrumentType::SYNTH || instrument->type == InstrumentType::KIT) { + //if you've selected a mod encoder (e.g. by pressing modEncoderButton) and you're in Automation Overview + //the currentUIMode will change to Selecting Midi CC. In this case, turning select encoder should allow + //you to change the midi CC assignment to that modEncoder + if (currentUIMode == UI_MODE_SELECTING_MIDI_CC) { + InstrumentClipMinder::selectEncoderAction(offset); + } + else if (instrument->type == InstrumentType::SYNTH || instrument->type == InstrumentType::KIT) { //if you're a kit with affect entire enabled if (instrument->type == InstrumentType::KIT && instrumentClipView.getAffectEntire()) { @@ -3301,7 +3338,7 @@ void AutomationInstrumentClipView::handleSinglePadPress(ModelStackWithTimelineCo //use default interpolation settings initInterpolation(); - int32_t newKnobPos = calculateKnobPosForSinglePadPress(yDisplay); + int32_t newKnobPos = calculateKnobPosForSinglePadPress(instrument, yDisplay); setParameterAutomationValue(modelStackWithParam, newKnobPos, squareStart, xDisplay, effectiveLength); } } @@ -3311,7 +3348,7 @@ void AutomationInstrumentClipView::handleSinglePadPress(ModelStackWithTimelineCo } //calculates what the new parameter value is when you press a single pad -int32_t AutomationInstrumentClipView::calculateKnobPosForSinglePadPress(int32_t yDisplay) { +int32_t AutomationInstrumentClipView::calculateKnobPosForSinglePadPress(Instrument* instrument, int32_t yDisplay) { int32_t newKnobPos = 0; @@ -3321,7 +3358,13 @@ int32_t AutomationInstrumentClipView::calculateKnobPosForSinglePadPress(int32_t } //if you are pressing the top pad, set the value to max (128) else { - newKnobPos = kMaxKnobPos; + //for Midi Clips, maxKnobPos = 127 + if (instrument->type == InstrumentType::MIDI_OUT) { + newKnobPos = kMaxKnobPos - 1; //128 - 1 = 127 + } + else { + newKnobPos = kMaxKnobPos; + } } //in the deluge knob positions are stored in the range of -64 to + 64, so need to adjust newKnobPos set above. @@ -3361,8 +3404,8 @@ void AutomationInstrumentClipView::handleMultiPadPress(ModelStackWithTimelineCou //otherwise if it's a regular long press, calculate values from the y position of the pads pressed else { - firstPadValue = calculateKnobPosForSinglePadPress(firstPadY) + kKnobPosOffset; - secondPadValue = calculateKnobPosForSinglePadPress(secondPadY) + kKnobPosOffset; + firstPadValue = calculateKnobPosForSinglePadPress(instrument, firstPadY) + kKnobPosOffset; + secondPadValue = calculateKnobPosForSinglePadPress(instrument, secondPadY) + kKnobPosOffset; } //converting variables to float for more accurate interpolation calculation diff --git a/src/deluge/gui/views/automation_instrument_clip_view.h b/src/deluge/gui/views/automation_instrument_clip_view.h index accee9439f..d6bad3fa9b 100644 --- a/src/deluge/gui/views/automation_instrument_clip_view.h +++ b/src/deluge/gui/views/automation_instrument_clip_view.h @@ -62,8 +62,6 @@ class AutomationInstrumentClipView final : public ClipView, public InstrumentCli uint8_t occupancyMask[][kDisplayWidth + kSideBarWidth]); void renderDisplay(int32_t knobPosLeft = kNoSelection, int32_t knobPosRight = kNoSelection, bool modEncoderAction = false); - void renderDisplayOLED(int32_t knobPosLeft = kNoSelection, int32_t knobPosRight = kNoSelection); - void renderDisplay7SEG(int32_t knobPosLeft = kNoSelection, bool modEncoderAction = false); void displayAutomation(bool padSelected = false, bool updateDisplay = true); void renderOLED(uint8_t image[][OLED_MAIN_WIDTH_PIXELS]) { InstrumentClipMinder::renderOLED(image); } @@ -129,6 +127,10 @@ class AutomationInstrumentClipView final : public ClipView, public InstrumentCli void renderRow(ModelStackWithTimelineCounter* modelStack, ModelStackWithAutoParam* modelStackWithParam, uint8_t* image, uint8_t occupancyMask[], int32_t yDisplay = 0, bool isAutomated = false); void renderLove(uint8_t* image, uint8_t occupancyMask[], int32_t yDisplay = 0); + void renderDisplayOLED(InstrumentClip* clip, Instrument* instrument, int32_t knobPosLeft = kNoSelection, + int32_t knobPosRight = kNoSelection); + void renderDisplay7SEG(InstrumentClip* clip, Instrument* instrument, int32_t knobPosLeft = kNoSelection, + bool modEncoderAction = false); //Enter/Exit Scale Mode void enterScaleMode(uint8_t yDisplay = 255); @@ -151,7 +153,7 @@ class AutomationInstrumentClipView final : public ClipView, public InstrumentCli int32_t getEffectiveLength(ModelStackWithTimelineCounter* modelStack); uint32_t getMiddlePosFromSquare(ModelStackWithTimelineCounter* modelStack, int32_t xDisplay); - void getParameterName(char* parameterName); + void getParameterName(InstrumentClip* clip, Instrument* instrument, char* parameterName); int32_t getParameterKnobPos(ModelStackWithAutoParam* modelStack, uint32_t pos); bool getNodeInterpolation(ModelStackWithAutoParam* modelStack, int32_t pos, bool reversed); @@ -164,7 +166,7 @@ class AutomationInstrumentClipView final : public ClipView, public InstrumentCli bool recordSinglePadPress(int32_t xDisplay, int32_t yDisplay); void handleSinglePadPress(ModelStackWithTimelineCounter* modelStack, InstrumentClip* clip, int32_t xDisplay, int32_t yDisplay, bool shortcutPress = false); - int32_t calculateKnobPosForSinglePadPress(int32_t yDisplay); + int32_t calculateKnobPosForSinglePadPress(Instrument* instrument, int32_t yDisplay); void handleMultiPadPress(ModelStackWithTimelineCounter* modelStack, InstrumentClip* clip, int32_t firstPadX, int32_t firstPadY, int32_t secondPadX, int32_t secondPadY, bool modEncoderAction = false); @@ -172,6 +174,7 @@ class AutomationInstrumentClipView final : public ClipView, public InstrumentCli int32_t xDisplay = kNoSelection, bool modEncoderAction = false); int32_t calculateKnobPosForModEncoderTurn(int32_t knobPos, int32_t offset); + int32_t calculateKnobPosForDisplay(InstrumentClip* clip, int32_t knobPos); void displayCVErrorMessage(); void resetShortcutBlinking(); diff --git a/src/deluge/gui/views/view.cpp b/src/deluge/gui/views/view.cpp index 1fd37a6885..955438be4f 100644 --- a/src/deluge/gui/views/view.cpp +++ b/src/deluge/gui/views/view.cpp @@ -859,26 +859,95 @@ void View::modEncoderAction(int32_t whichModEncoder, int32_t offset) { // Or, if normal case - an actual param else { + char modelStackTempMemory[MODEL_STACK_MAX_SIZE]; + copyModelStack(modelStackTempMemory, modelStackWithParam, sizeof(ModelStackWithThreeMainThings)); + ModelStackWithThreeMainThings* tempModelStack = (ModelStackWithThreeMainThings*)modelStackTempMemory; - char newModelStackMemory[MODEL_STACK_MAX_SIZE]; - - // Hack to make it so stutter can't be automated - if (modelStackWithParam->timelineCounterIsSet() - && !modelStackWithParam->paramCollection->doesParamIdAllowAutomation(modelStackWithParam)) { - copyModelStack(newModelStackMemory, modelStackWithParam, sizeof(ModelStackWithAutoParam)); - modelStackWithParam = (ModelStackWithAutoParam*)newModelStackMemory; - modelStackWithParam->setTimelineCounter(NULL); - } + InstrumentClip* clip = (InstrumentClip*)tempModelStack->getTimelineCounter(); int32_t value = modelStackWithParam->autoParam->getValuePossiblyAtPos(modPos, modelStackWithParam); int32_t knobPos = modelStackWithParam->paramCollection->paramValueToKnobPos(value, modelStackWithParam); int32_t lowerLimit = std::min(-64_i32, knobPos); int32_t newKnobPos = knobPos + offset; newKnobPos = std::clamp(newKnobPos, lowerLimit, 64_i32); + //ignore modEncoderTurn for Midi CC if current or new knobPos exceeds 127 + //if current knobPos exceeds 127, e.g. it's 128, then it needs to drop to 126 before a value change gets recorded + //if newKnobPos exceeds 127, then it means current knobPos was 127 and it was increased to 128. In which case, ignore value change + if ((getRootUI() == &instrumentClipView) || (getRootUI() == &automationInstrumentClipView)) { + if (clip->output->type == InstrumentType::MIDI_OUT) { + if ((knobPos == 64) || (newKnobPos == 64)) { + return; + } + } + } + + //don't display pop-up while in soundEditor as values are displayed on the menu screen + //unless you're turning a mod encoder for a different param than the menu displayed + if (((getCurrentUI() != &soundEditor) + || ((getCurrentUI() == &soundEditor) + && (soundEditor.getCurrentMenuItem()->getPatchedParamIndex() != modelStackWithParam->paramId))) + && !(modelStackWithParam->paramId == Param::Unpatched::STUTTER_RATE + && (runtimeFeatureSettings.get(RuntimeFeatureSettingType::QuantizedStutterRate) + == RuntimeFeatureStateToggle::On) + && !isUIModeActive(UI_MODE_STUTTERING))) { + + char buffer[5]; + int32_t valueForDisplay; + if (clip->output->type == InstrumentType::MIDI_OUT) { + valueForDisplay = newKnobPos = kKnobPosOffset; + } + else if ((modelStackWithParam->paramId == Param::Local::PAN) + || (modelStackWithParam->paramId == Param::Unpatched::GlobalEffectable::PAN)) { + valueForDisplay = + ((((newKnobPos << 20) / (kMaxKnobPos - kKnobPosOffset)) * kMaxMenuPanValue) >> 20); + } + else { + valueForDisplay = + (((((newKnobPos + kKnobPosOffset) << 20) / kMaxKnobPos) * kMaxMenuValue) >> 20); + } + intToString(valueForDisplay, buffer); + display->displayPopup(buffer); + } + + //if turning stutter mod encoder and stutter quantize is enabled + //display stutter quantization instead of knob position + if (modelStackWithParam->paramId == Param::Unpatched::STUTTER_RATE + && (runtimeFeatureSettings.get(RuntimeFeatureSettingType::QuantizedStutterRate) + == RuntimeFeatureStateToggle::On) + && !isUIModeActive(UI_MODE_STUTTERING)) { + char buffer[10]; + if (newKnobPos < -39) { // 4ths stutter: no leds turned on + strncpy(buffer, "4ths", 10); + } + else if (newKnobPos < -14) { // 8ths stutter: 1 led turned on + strncpy(buffer, "8ths", 10); + } + else if (newKnobPos < 14) { // 16ths stutter: 2 leds turned on + strncpy(buffer, "16ths", 10); + } + else if (newKnobPos < 39) { // 32nds stutter: 3 leds turned on + strncpy(buffer, "32nds", 10); + } + else { // 64ths stutter: all 4 leds turned on + strncpy(buffer, "64ths", 10); + } + display->displayPopup(buffer); + } + if (newKnobPos == knobPos) { return; } + char newModelStackMemory[MODEL_STACK_MAX_SIZE]; + + // Hack to make it so stutter can't be automated + if (modelStackWithParam->timelineCounterIsSet() + && !modelStackWithParam->paramCollection->doesParamIdAllowAutomation(modelStackWithParam)) { + copyModelStack(newModelStackMemory, modelStackWithParam, sizeof(ModelStackWithAutoParam)); + modelStackWithParam = (ModelStackWithAutoParam*)newModelStackMemory; + modelStackWithParam->setTimelineCounter(NULL); + } + int32_t newValue = modelStackWithParam->paramCollection->knobPosToParamValue(newKnobPos, modelStackWithParam); @@ -887,11 +956,6 @@ void View::modEncoderAction(int32_t whichModEncoder, int32_t offset) { modLength); if (activeModControllableModelStack.timelineCounterIsSet()) { - char modelStackTempMemory[MODEL_STACK_MAX_SIZE]; - copyModelStack(modelStackTempMemory, modelStackWithParam, sizeof(ModelStackWithThreeMainThings)); - ModelStackWithThreeMainThings* tempModelStack = - (ModelStackWithThreeMainThings*)modelStackTempMemory; - bool noteTailsAllowedAfter = modelStackWithParam->modControllable->allowNoteTails(tempModelStack->addSoundFlags()); diff --git a/src/deluge/modulation/midi/midi_param_collection.cpp b/src/deluge/modulation/midi/midi_param_collection.cpp index 5cbb89d6b8..d3686f6c1a 100644 --- a/src/deluge/modulation/midi/midi_param_collection.cpp +++ b/src/deluge/modulation/midi/midi_param_collection.cpp @@ -279,19 +279,6 @@ bool MIDIParamCollection::mayParamInterpolate(int32_t paramId) { } int32_t MIDIParamCollection::knobPosToParamValue(int32_t knobPos, ModelStackWithAutoParam* modelStack) { - - if (getCurrentUI() - != &automationInstrumentClipView) { //let the automation instrument clip view handle the drawing of midi cc value - char buffer[5]; - int32_t valueForDisplay = knobPos; - valueForDisplay += 64; - if (valueForDisplay == 128) { - valueForDisplay = 127; - } - intToString(valueForDisplay, buffer); - display->displayPopup(buffer, 3, true); - } - return ParamCollection::knobPosToParamValue(knobPos, modelStack); } diff --git a/src/deluge/modulation/params/param_set.cpp b/src/deluge/modulation/params/param_set.cpp index 0a1f41f464..6936da6249 100644 --- a/src/deluge/modulation/params/param_set.cpp +++ b/src/deluge/modulation/params/param_set.cpp @@ -553,21 +553,6 @@ void ExpressionParamSet::notifyParamModifiedInSomeWay(ModelStackWithAutoParam co // Displays text number. This will only actually end up getting used/seen on MIDI Clips, at channel/Clip level - not MPE/polyphonic. int32_t ExpressionParamSet::knobPosToParamValue(int32_t knobPos, ModelStackWithAutoParam* modelStack) { - - if (getCurrentUI() - != &automationInstrumentClipView) { //let the automation instrument clip view handle the drawing of midi cc value - char buffer[5]; - int32_t valueForDisplay = knobPos; - if (modelStack->paramId == 2) { // Just for aftertouch - valueForDisplay += 64; - if (valueForDisplay == 128) { - valueForDisplay = 127; - } - } - intToString(valueForDisplay, buffer); - display->displayPopup(buffer, 3, true); - } - // Everything but aftertouch gets handled by parent from here if (modelStack->paramId != 2) { return ParamSet::knobPosToParamValue(knobPos, modelStack);