diff --git a/.vscode/launch.json b/.vscode/launch.json index d0f9cad265..43c44ff4c5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -54,8 +54,11 @@ "MIMode": "gdb", "miDebuggerPath": "arm-none-eabi-gdb", "windows": { - "miDebuggerPath": "${workspaceRoot}/toolchain/win32-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb.exe" + "miDebuggerPath": "${workspaceRoot}/toolchain/win32-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb.exe", + "debugServerPath": "C:/Program Files/SEGGER/JLink/JLinkGDBServerCL.exe", }, + "debugServerArgs": "-select USB -device R7S721020 -endian little -if SWD -speed auto -noir -LocalhostOnly -nologtofile -port 3333 -SWOPort 2332 -TelnetPort 2333", + "serverStarted": "Connected to target", "linux": { "miDebuggerPath": "${workspaceRoot}/toolchain/linux-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb" }, @@ -75,8 +78,11 @@ "MIMode": "gdb", "miDebuggerPath": "arm-none-eabi-gdb", "windows": { - "miDebuggerPath": "${workspaceRoot}/toolchain/win32-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb.exe" + "miDebuggerPath": "${workspaceRoot}/toolchain/win32-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb.exe", + "debugServerPath": "C:/Program Files/SEGGER/JLink/JLinkGDBServerCL.exe", }, + "debugServerArgs": "-select USB -device R7S721020 -endian little -if SWD -speed auto -noir -LocalhostOnly -nologtofile -port 3333 -SWOPort 2332 -TelnetPort 2333", + "serverStarted": "Connected to target", "linux": { "miDebuggerPath": "${workspaceRoot}/toolchain/linux-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb" }, @@ -115,8 +121,11 @@ "MIMode": "gdb", "miDebuggerPath": "arm-none-eabi-gdb", "windows": { - "miDebuggerPath": "${workspaceRoot}/toolchain/win32-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb.exe" + "miDebuggerPath": "${workspaceRoot}/toolchain/win32-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb.exe", + "debugServerPath": "C:/Program Files/SEGGER/JLink/JLinkGDBServerCL.exe", }, + "debugServerArgs": "-select USB -device R7S721020 -endian little -if SWD -speed auto -noir -LocalhostOnly -nologtofile -port 3333 -SWOPort 2332 -TelnetPort 2333", + "serverStarted": "Connected to target", "linux": { "miDebuggerPath": "${workspaceRoot}/toolchain/linux-x86_64/arm-none-eabi-gcc/bin/arm-none-eabi-gdb" }, diff --git a/CMakeLists.txt b/CMakeLists.txt index c6ad0b4c6e..7cef3a8a2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,8 +125,8 @@ add_compile_options( -fdiagnostics-parseable-fixits -fsigned-char - # Debug symbols - $<$:-ggdb3> # Include + # Debug symbols, also for release so we get a full ELF + $<$:-ggdb3> # Include # Optimization level $<$:-Og> @@ -215,7 +215,8 @@ target_link_options(deluge PUBLIC LINKER:--gc-sections LINKER:--entry=start # Set the entrypoint to 'start' (see RZA1/compiler/asm/start.S) - $<$:LINKER:--strip-all> # Strip + # Enabled to generate a full ELF, objcopy will do the stripping + # $<$:LINKER:--strip-all> # Strip -nostartfiles # Don't emit startfiles diff --git a/docs/community_features.md b/docs/community_features.md index 9892d55a12..ac666aa00f 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. @@ -74,7 +79,12 @@ Here is a list of features that have been added to the firmware as a list, group - Compared to rows layout overdub recording and copying clips to arranger is currently not supported - Every track (column) has a random generated color that can be changed in edit mode (see below) - Launched clips are full color, unlaunched dimmed and during soloing all non soloed clips are greyed out - - A new menu to select the default Layout has been added in Shift+Selection Encoder -> Defaults -> UI -> Song -> Layout + - New default settings that can be reached with Shift+Selection Encoder -> Defaults -> UI -> Song + - Layout: Select the default layout for all new songs + - Grid + - Default active mode: "Selection" allows changing the mode as described below, all other settings will always make mode snap back to the configured one (default Selection) + - Select in green mode: Enabling this will make allow holding clips in green (launch) mode to change their parameters like in blue mode, tradeoff is arming is executed on finger up (default on) + - Empty pad unarm: Enabling will make pressing empty pads in a track unarm all playing tracks in that track (default off) - There are different interaction modes that change how the grid behaves - The mode can be changed by clicking on one of the colored pads in the Audition/Section column on the right - To permanently switch the mode click on a pad and release, to temporarily switch hold the mode pad and use the grid, the mode will snap back to the current permanent one @@ -197,6 +207,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 +401,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/linker_script_rz_a1l.ld b/linker_script_rz_a1l.ld index b74d1285dd..8ab0d876e5 100644 --- a/linker_script_rz_a1l.ld +++ b/linker_script_rz_a1l.ld @@ -150,6 +150,8 @@ SECTIONS /* ALIGN(0x20) is only necessary for v0 bootloaders */ .reset : ALIGN(0x20) /* 0x10 isn't enough. Not sure why any of this is necessary though. */ { + PROVIDE(program_code_start = .); + execute = .; *start.S.obj (.text) *start.S.obj (.rodata) @@ -180,6 +182,8 @@ SECTIONS _etext = .; address_end_reset = .; + + PROVIDE(program_code_end = .); } > RAM012L .rodata : diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f89734f403..0fa27c4a70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_path(SET SHARED_INCLUDE ${CMAKE_CURRENT_LIST_DIR}) -target_sources(deluge PUBLIC main.c resetprg.c c_lib_alternatives.S) +target_sources(deluge PUBLIC main.c resetprg.c fault_handler.c c_lib_alternatives.S) add_subdirectory(deluge) add_subdirectory(RZA1) diff --git a/src/RZA1/compiler/asm/reset_handler.S b/src/RZA1/compiler/asm/reset_handler.S index 1c8e0c7abc..35a9a75e1a 100644 --- a/src/RZA1/compiler/asm/reset_handler.S +++ b/src/RZA1/compiler/asm/reset_handler.S @@ -234,18 +234,17 @@ Finished: /* Other Handler */ /* ========================================================================= */ undefined_handler: - B undefined_handler - svc_handler: - B svc_handler - -prefetch_handler: - B prefetch_handler - abort_handler: - B abort_handler - +prefetch_handler: reserved_handler: - B reserved_handler + CPSID i /* Disable interrupt handling to preserve stack */ + MOV r0, LR /* Store SYS LR value as first function parameter */ + MOV r1, SP /* Store SYS SP value as second function parameter */ + CPS #USR_MODE /* Switch into user mode to get those register values */ + MOV r2, LR /* Store USR LR value as third function parameter */ + MOV r3, SP /* Store USR SP value as fourth function parameter */ + CPS #SYS_MODE /* Go back to SYS mode */ + LDR r12,=handle_cpu_fault /* Load address of handle_cpu_fault */ + BX r12 /* Jump to handle_cpu_fault without link */ .end - diff --git a/src/definitions_cxx.hpp b/src/definitions_cxx.hpp index f80aeaf1b5..f1046d65de 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; @@ -721,6 +737,13 @@ enum class ModFXParam { FEEDBACK, OFFSET, }; + +enum class CompParam { + RATIO, + ATTACK, + RELEASE, +}; + constexpr auto kNumModFXParams = util::to_underlying(ModFXParam::OFFSET) + 1; enum class PatchCableAcceptance { @@ -1090,3 +1113,10 @@ enum SessionLayoutType : uint8_t { SessionLayoutTypeGrid, SessionLayoutTypeMaxElement // Keep as boundary }; + +enum GridDefaultActiveMode : uint8_t { + GridDefaultActiveModeSelection, + GridDefaultActiveModeGreen, + GridDefaultActiveModeBlue, + GridDefaultActiveModeMaxElement // Keep as boundary +}; diff --git a/src/deluge/dsp/master_compressor/master_compressor.cpp b/src/deluge/dsp/master_compressor/master_compressor.cpp index 1e402b037f..dd482f8edf 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.cpp +++ b/src/deluge/dsp/master_compressor/master_compressor.cpp @@ -32,6 +32,7 @@ MasterCompressor::MasterCompressor() { shape = getParamFromUserValue(Param::Unpatched::COMPRESSOR_SHAPE, 1); //an appropriate range is 0-50*one q 15 threshold = ONE_Q31; + rawThreshold = 0; follower = true; //this is about a 1:1 ratio ratio = ONE_Q31 >> 1; @@ -64,6 +65,8 @@ void MasterCompressor::updateER() { } void MasterCompressor::render(StereoSample* buffer, uint16_t numSamples, q31_t volAdjustL, q31_t volAdjustR) { + ratio = (rawRatio >> 1) + (3 << 28); + threshold = ONE_Q31 - rawThreshold; updateER(); q31_t over = std::max(0, (meanVolume - threshdb) / 21) * ONE_Q31; @@ -144,7 +147,7 @@ float MasterCompressor::calc_rms(StereoSample* buffer, uint16_t numSamples) { void MasterCompressor::setup(int32_t a, int32_t r, int32_t t, int32_t rat) { attack = a; release = r; - threshold = t; - ratio = rat; + rawThreshold = t; + rawRatio = rat; updateER(); } diff --git a/src/deluge/dsp/master_compressor/master_compressor.h b/src/deluge/dsp/master_compressor/master_compressor.h index b1b107aff1..4045021e68 100644 --- a/src/deluge/dsp/master_compressor/master_compressor.h +++ b/src/deluge/dsp/master_compressor/master_compressor.h @@ -37,6 +37,8 @@ class MasterCompressor : public Compressor { float calc_rms(StereoSample* buffer, uint16_t numSamples); uint8_t gainReduction; bool dither; + q31_t rawRatio; + q31_t rawThreshold; q31_t threshold; q31_t shape; q31_t ratio; diff --git a/src/deluge/gui/l10n/english.cpp b/src/deluge/gui/l10n/english.cpp index c950bf7d86..52bf6cd04a 100644 --- a/src/deluge/gui/l10n/english.cpp +++ b/src/deluge/gui/l10n/english.cpp @@ -466,13 +466,17 @@ PLACE_SDRAM_DATA Language english{ {STRING_FOR_COMMANDS, "Commands"}, {STRING_FOR_OUTPUT, "Output"}, {STRING_FOR_DEFAULT_UI, "UI"}, + {STRING_FOR_DEFAULT_UI_GRID, "Grid"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE, "Default active mode"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_UNARM_EMPTY_PADS, "Empty pad unarm"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ALLOW_GREEN_SELECTION, "Select in green mode"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE_SELECTION, "Selection"}, {STRING_FOR_DEFAULT_UI_LAYOUT, "Layout"}, {STRING_FOR_DEFAULT_UI_KEYBOARD, "Keyboard"}, {STRING_FOR_DEFAULT_UI_KEYBOARD_LAYOUT_ISOMORPHIC, "Isomorphic"}, {STRING_FOR_DEFAULT_UI_KEYBOARD_LAYOUT_INKEY, "In-Key"}, {STRING_FOR_DEFAULT_UI_SONG, "Song"}, {STRING_FOR_DEFAULT_UI_SONG_LAYOUT_ROWS, "Rows"}, - {STRING_FOR_DEFAULT_UI_SONG_LAYOUT_GRID, "Grid"}, {STRING_FOR_INPUT, "Input"}, {STRING_FOR_TEMPO_MAGNITUDE_MATCHING, "Tempo magnitude matching"}, {STRING_FOR_TRIGGER_CLOCK, "Trigger clock"}, diff --git a/src/deluge/gui/l10n/seven_segment.cpp b/src/deluge/gui/l10n/seven_segment.cpp index efba1db139..dd93ebfb41 100644 --- a/src/deluge/gui/l10n/seven_segment.cpp +++ b/src/deluge/gui/l10n/seven_segment.cpp @@ -308,13 +308,17 @@ PLACE_SDRAM_DATA Language seven_segment{ {STRING_FOR_COMMANDS, "CMD"}, {STRING_FOR_OUTPUT, "OUT"}, {STRING_FOR_DEFAULT_UI, "UI"}, + {STRING_FOR_DEFAULT_UI_GRID, "GRID"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE, "DEFMODE"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_UNARM_EMPTY_PADS, "UNARM"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ALLOW_GREEN_SELECTION, "GREENSELECT"}, + {STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE_SELECTION, "SELE"}, {STRING_FOR_DEFAULT_UI_LAYOUT, "LAYT"}, {STRING_FOR_DEFAULT_UI_KEYBOARD, "KBD"}, {STRING_FOR_DEFAULT_UI_KEYBOARD_LAYOUT_ISOMORPHIC, "ISO"}, {STRING_FOR_DEFAULT_UI_KEYBOARD_LAYOUT_INKEY, "INKY"}, {STRING_FOR_DEFAULT_UI_SONG, "SONG"}, {STRING_FOR_DEFAULT_UI_SONG_LAYOUT_ROWS, "ROWS"}, - {STRING_FOR_DEFAULT_UI_SONG_LAYOUT_GRID, "GRID"}, {STRING_FOR_INPUT, "IN"}, {STRING_FOR_TEMPO_MAGNITUDE_MATCHING, "MAGN"}, {STRING_FOR_TRIGGER_CLOCK, "TCLOCK"}, diff --git a/src/deluge/gui/l10n/strings.h b/src/deluge/gui/l10n/strings.h index ba13d03c79..0c1e787f0d 100644 --- a/src/deluge/gui/l10n/strings.h +++ b/src/deluge/gui/l10n/strings.h @@ -373,13 +373,17 @@ enum class String : size_t { STRING_FOR_COMMANDS, STRING_FOR_OUTPUT, STRING_FOR_DEFAULT_UI, + STRING_FOR_DEFAULT_UI_GRID, + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE, + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_UNARM_EMPTY_PADS, + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ALLOW_GREEN_SELECTION, + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE_SELECTION, STRING_FOR_DEFAULT_UI_LAYOUT, STRING_FOR_DEFAULT_UI_KEYBOARD, STRING_FOR_DEFAULT_UI_KEYBOARD_LAYOUT_ISOMORPHIC, STRING_FOR_DEFAULT_UI_KEYBOARD_LAYOUT_INKEY, STRING_FOR_DEFAULT_UI_SONG, STRING_FOR_DEFAULT_UI_SONG_LAYOUT_ROWS, - STRING_FOR_DEFAULT_UI_SONG_LAYOUT_GRID, STRING_FOR_TEMPO_MAGNITUDE_MATCHING, STRING_FOR_TRIGGER_CLOCK, STRING_FOR_FM_MODULATOR_1, 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/defaults/grid_allow_green_selection.h b/src/deluge/gui/menu_item/defaults/grid_allow_green_selection.h new file mode 100644 index 0000000000..f1a4df5e7a --- /dev/null +++ b/src/deluge/gui/menu_item/defaults/grid_allow_green_selection.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014-2023 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . +*/ +#pragma once +#include "gui/menu_item/toggle.h" + +namespace deluge::gui::menu_item::defaults { +class DefaultGridAllowGreenSelection final : public Toggle { +public: + using Toggle::Toggle; + void readCurrentValue() override { this->setValue(FlashStorage::gridAllowGreenSelection); } + void writeCurrentValue() override { FlashStorage::gridAllowGreenSelection = this->getValue(); } +}; +} // namespace deluge::gui::menu_item::defaults diff --git a/src/deluge/gui/menu_item/defaults/grid_default_active_mode.h b/src/deluge/gui/menu_item/defaults/grid_default_active_mode.h new file mode 100644 index 0000000000..693800356e --- /dev/null +++ b/src/deluge/gui/menu_item/defaults/grid_default_active_mode.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014-2023 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . +*/ +#pragma once +#include "definitions_cxx.hpp" +#include "gui/l10n/l10n.h" +#include "gui/l10n/strings.h" +#include "gui/menu_item/selection.h" +#include "hid/display/display.h" +#include "storage/flash_storage.h" +#include "util/misc.h" + +namespace deluge::gui::menu_item::defaults { +class DefaultGridDefaultActiveMode final : public Selection { +public: + using Selection::Selection; + void readCurrentValue() override { this->setValue(FlashStorage::defaultGridActiveMode); } + void writeCurrentValue() override { FlashStorage::defaultGridActiveMode = this->getValue(); } + std::vector getOptions() override { + return { + l10n::getView(l10n::String::STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE_SELECTION), + l10n::getView(l10n::String::STRING_FOR_GREEN), + l10n::getView(l10n::String::STRING_FOR_BLUE), + }; + } +}; +} // namespace deluge::gui::menu_item::defaults diff --git a/src/deluge/gui/menu_item/defaults/grid_unarm_empty_pads.h b/src/deluge/gui/menu_item/defaults/grid_unarm_empty_pads.h new file mode 100644 index 0000000000..1c416c9600 --- /dev/null +++ b/src/deluge/gui/menu_item/defaults/grid_unarm_empty_pads.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014-2023 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . +*/ +#pragma once +#include "gui/menu_item/toggle.h" + +namespace deluge::gui::menu_item::defaults { +class DefaultGridUnarmEmptyPads final : public Toggle { +public: + using Toggle::Toggle; + void readCurrentValue() override { this->setValue(FlashStorage::gridUnarmEmptyPads); } + void writeCurrentValue() override { FlashStorage::gridUnarmEmptyPads = this->getValue(); } +}; +} // namespace deluge::gui::menu_item::defaults diff --git a/src/deluge/gui/menu_item/defaults/session_layout.h b/src/deluge/gui/menu_item/defaults/session_layout.h index be4a55fdb8..348a69f971 100644 --- a/src/deluge/gui/menu_item/defaults/session_layout.h +++ b/src/deluge/gui/menu_item/defaults/session_layout.h @@ -33,7 +33,7 @@ class SessionLayout final : public Selection { std::vector getOptions() override { return { l10n::getView(l10n::String::STRING_FOR_DEFAULT_UI_SONG_LAYOUT_ROWS), - l10n::getView(l10n::String::STRING_FOR_DEFAULT_UI_SONG_LAYOUT_GRID), + l10n::getView(l10n::String::STRING_FOR_DEFAULT_UI_GRID), }; } }; 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/menus.cpp b/src/deluge/gui/ui/menus.cpp index 2a9fab86f3..83c4dd3ed3 100644 --- a/src/deluge/gui/ui/menus.cpp +++ b/src/deluge/gui/ui/menus.cpp @@ -27,6 +27,9 @@ #include "gui/menu_item/cv/volts.h" #include "gui/menu_item/decimal.h" #include "gui/menu_item/defaults/bend_range.h" +#include "gui/menu_item/defaults/grid_allow_green_selection.h" +#include "gui/menu_item/defaults/grid_default_active_mode.h" +#include "gui/menu_item/defaults/grid_unarm_empty_pads.h" #include "gui/menu_item/defaults/keyboard_layout.h" #include "gui/menu_item/defaults/magnitude.h" #include "gui/menu_item/defaults/scale.h" @@ -821,10 +824,21 @@ Submenu defaultUIKeyboard{ {&defaultKeyboardLayoutMenu}, }; +defaults::DefaultGridDefaultActiveMode defaultGridDefaultActiveMode{STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE, + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ACTIVE_MODE}; +defaults::DefaultGridAllowGreenSelection defaultGridAllowGreenSelection{ + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ALLOW_GREEN_SELECTION, STRING_FOR_DEFAULT_UI_DEFAULT_GRID_ALLOW_GREEN_SELECTION}; +defaults::DefaultGridUnarmEmptyPads defaultGridUnarmEmptyPads{STRING_FOR_DEFAULT_UI_DEFAULT_GRID_UNARM_EMPTY_PADS, + STRING_FOR_DEFAULT_UI_DEFAULT_GRID_UNARM_EMPTY_PADS}; +Submenu defaultSessionGridMenu{ + STRING_FOR_DEFAULT_UI_GRID, + {&defaultGridDefaultActiveMode, &defaultGridAllowGreenSelection, &defaultGridUnarmEmptyPads}, +}; + defaults::SessionLayout defaultSessionLayoutMenu{STRING_FOR_DEFAULT_UI_LAYOUT, STRING_FOR_DEFAULT_UI_LAYOUT}; Submenu defaultUISession{ STRING_FOR_DEFAULT_UI_SONG, - {&defaultSessionLayoutMenu}, + {&defaultSessionLayoutMenu, &defaultSessionGridMenu}, }; Submenu defaultUI{ 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/ui_timer_manager.h b/src/deluge/gui/ui_timer_manager.h index c6792296ec..09d27a884e 100644 --- a/src/deluge/gui/ui_timer_manager.h +++ b/src/deluge/gui/ui_timer_manager.h @@ -38,7 +38,8 @@ #define TIMER_OLED_CONSOLE 16 #define TIMER_OLED_SCROLLING_AND_BLINKING 17 #define TIMER_SYSEX_DISPLAY 18 -#define NUM_TIMERS 19 +#define TIMER_METER_INDICATOR_BLINK 19 +#define NUM_TIMERS 20 struct Timer { bool active; 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/session_view.cpp b/src/deluge/gui/views/session_view.cpp index 544470777a..ecfe7149d7 100644 --- a/src/deluge/gui/views/session_view.cpp +++ b/src/deluge/gui/views/session_view.cpp @@ -1921,9 +1921,8 @@ void SessionView::graphicsRoutine() { counter = (counter + 1) % 5; if (counter == 0) { uint8_t gr = AudioEngine::mastercompressor.gainReduction; - //uint8_t mv = int(6 * AudioEngine::mastercompressor.meanVolume); - indicator_leds::setKnobIndicatorLevel(1, gr); //Gain Reduction LED - //indicator_leds::setKnobIndicatorLevel(0, mv); //Input level LED + + indicator_leds::setMeterLevel(1, gr); //Gain Reduction LED } } } @@ -2667,6 +2666,7 @@ Clip* SessionView::getClipForLayout() { } void SessionView::selectLayout(int8_t offset) { + gridSetDefaultMode(); gridResetPresses(); gridModeActive = gridModeSelected; @@ -3215,8 +3215,13 @@ ActionResult SessionView::gridHandlePads(int32_t x, int32_t y, int32_t on) { } } else { - if (!gridActiveModeUsed) { - gridModeSelected = gridModeActive; + if (FlashStorage::defaultGridActiveMode == GridDefaultActiveModeSelection) { + if (!gridActiveModeUsed) { + gridModeSelected = gridModeActive; + } + } + else { + gridSetDefaultMode(); } gridModeActive = gridModeSelected; @@ -3413,6 +3418,24 @@ ActionResult SessionView::gridHandlePadsLaunch(int32_t x, int32_t y, int32_t on, } if (clip == nullptr) { + if (on && currentUIMode == UI_MODE_NONE && FlashStorage::gridUnarmEmptyPads) { + auto maxTrack = gridTrackCount(); + Output* track = gridTrackFromX(x, maxTrack); + if (track != nullptr) { + for (int32_t idxClip = 0; idxClip < currentSong->sessionClips.getNumElements(); ++idxClip) { + Clip* sessionClip = currentSong->sessionClips.getClipAtIndex(idxClip); + if (sessionClip->output == track) { + if (sessionClip->activeIfNoSolo) { + gridToggleClipPlay(sessionClip, Buttons::isShiftButtonPressed()); + } + else { + sessionClip->armState = ArmState::OFF; + } + } + } + } + } + return ActionResult::DEALT_WITH; } @@ -3422,13 +3445,77 @@ ActionResult SessionView::gridHandlePadsLaunch(int32_t x, int32_t y, int32_t on, return ActionResult::ACTIONED_AND_CAUSED_CHANGE; } + if (FlashStorage::gridAllowGreenSelection) { + return gridHandlePadsLaunchWithSelection(x, y, on, clip); + } + else { + return gridHandlePadsLaunchImmediate(x, y, on, clip); + } +} + +ActionResult SessionView::gridHandlePadsLaunchImmediate(int32_t x, int32_t y, int32_t on, Clip* clip) { // From here all actions only happen on press if (!on) { return ActionResult::DEALT_WITH; } - // Normal arming, handle cases normally in View::clipStatusPadAction - if (!Buttons::isShiftButtonPressed()) { + gridHandlePadsLaunchToggleArming(clip, Buttons::isShiftButtonPressed()); + + return ActionResult::ACTIONED_AND_CAUSED_CHANGE; +} + +ActionResult SessionView::gridHandlePadsLaunchWithSelection(int32_t x, int32_t y, int32_t on, Clip* clip) { + if (on) { + // Immediate arming, immediate consumption + if (Buttons::isShiftButtonPressed()) { + gridHandlePadsLaunchToggleArming(clip, true); + return ActionResult::ACTIONED_AND_CAUSED_CHANGE; + } + + if (gridFirstPressedX == -1 && gridFirstPressedY == -1) { + gridFirstPressedX = x; + gridFirstPressedY = y; + + // Allow clip control (selection) + currentUIMode = UI_MODE_CLIP_PRESSED_IN_SONG_VIEW; + performActionOnPadRelease = true; + selectedClipTimePressed = AudioEngine::audioSampleTimer; + view.setActiveModControllableTimelineCounter(clip); + view.displayOutputName(clip->output, true, clip); + if (display->haveOLED()) { + deluge::hid::display::OLED::sendMainImage(); + } + } + // Special case, if there are already selected pads we allow immediate arming all others + else { + return gridHandlePadsLaunchImmediate(x, y, on, clip); + } + } + else { + if (gridFirstPressedX == x && gridFirstPressedY == y) { + if (isUIModeActive(UI_MODE_CLIP_PRESSED_IN_SONG_VIEW) && performActionOnPadRelease + && AudioEngine::audioSampleTimer - selectedClipTimePressed < kShortPressTime) { + + gridHandlePadsLaunchToggleArming(clip, false); + } + + clipPressEnded(); + } + } + + return ActionResult::ACTIONED_AND_CAUSED_CHANGE; +} + +void SessionView::gridHandlePadsLaunchToggleArming(Clip* clip, bool immediate) { + if (immediate) { + if (currentUIMode == UI_MODE_HOLDING_HORIZONTAL_ENCODER_BUTTON) { + session.soloClipAction(clip, kInternalButtonPressLatency); + } + else { + gridToggleClipPlay(clip, true); + } + } + else { if (currentUIMode == UI_MODE_VIEWING_RECORD_ARMING) { // Here I removed the overdubbing settings clip->armedForRecording = !clip->armedForRecording; @@ -3447,17 +3534,6 @@ ActionResult SessionView::gridHandlePadsLaunch(int32_t x, int32_t y, int32_t on, // Make sure we can mute additional pads after this and don't loose UI_MODE_HOLDING_HORIZONTAL_ENCODER_BUTTON } } - // Immediate arming, immediate consumption - else { - if (currentUIMode == UI_MODE_HOLDING_HORIZONTAL_ENCODER_BUTTON) { - session.soloClipAction(clip, kInternalButtonPressLatency); - } - else { - gridToggleClipPlay(clip, true); - } - } - - return ActionResult::ACTIONED_AND_CAUSED_CHANGE; } ActionResult SessionView::gridHandleScroll(int32_t offsetX, int32_t offsetY) { diff --git a/src/deluge/gui/views/session_view.h b/src/deluge/gui/views/session_view.h index 1f07a03a4f..a95f9842cb 100644 --- a/src/deluge/gui/views/session_view.h +++ b/src/deluge/gui/views/session_view.h @@ -20,6 +20,7 @@ #include "definitions_cxx.hpp" #include "gui/views/clip_navigation_timeline_view.h" #include "hid/button.h" +#include "storage/flash_storage.h" class Editor; class InstrumentClip; @@ -141,6 +142,9 @@ class SessionView final : public ClipNavigationTimelineView { ActionResult gridHandlePads(int32_t x, int32_t y, int32_t on); ActionResult gridHandlePadsEdit(int32_t x, int32_t y, int32_t on, Clip* clip); ActionResult gridHandlePadsLaunch(int32_t x, int32_t y, int32_t on, Clip* clip); + ActionResult gridHandlePadsLaunchImmediate(int32_t x, int32_t y, int32_t on, Clip* clip); + ActionResult gridHandlePadsLaunchWithSelection(int32_t x, int32_t y, int32_t on, Clip* clip); + void gridHandlePadsLaunchToggleArming(Clip* clip, bool immediate); ActionResult gridHandleScroll(int32_t offsetX, int32_t offsetY); @@ -191,6 +195,19 @@ class SessionView final : public ClipNavigationTimelineView { int32_t gridTrackIndexFromX(uint32_t x, uint32_t maxTrack); Output* gridTrackFromX(uint32_t x, uint32_t maxTrack); Clip* gridClipFromCoords(uint32_t x, uint32_t y); + + inline void gridSetDefaultMode() { + switch (FlashStorage::defaultGridActiveMode) { + case GridDefaultActiveModeGreen: { + gridModeSelected = SessionGridModeLaunch; + break; + } + case GridDefaultActiveModeBlue: { + gridModeSelected = SessionGridModeEdit; + break; + } + } + } }; extern SessionView sessionView; 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/hid/led/indicator_leds.cpp b/src/deluge/hid/led/indicator_leds.cpp index a295c64d3d..3546dabf43 100644 --- a/src/deluge/hid/led/indicator_leds.cpp +++ b/src/deluge/hid/led/indicator_leds.cpp @@ -39,6 +39,8 @@ uint8_t whichLevelIndicatorBlinking; bool levelIndicatorBlinkOn; uint8_t levelIndicatorBlinksLeft; +uint8_t whichKnobMetering; + void setLedState(LED led, bool newState, bool allowContinuedBlinking) { if (!allowContinuedBlinking) { @@ -164,8 +166,25 @@ void indicateAlertOnLed(LED led) { blinkLed(led, 3, 1); } +//this sets the level only if there hasn't been a value update in 500ms +void setMeterLevel(uint8_t whichKnob, uint8_t level) { + whichKnobMetering = whichKnob; + if (!uiTimerManager.isTimerSet(TIMER_METER_INDICATOR_BLINK)) { + actuallySetKnobIndicatorLevel(whichKnob, level); + } +} + // Level is out of 128 +//Set level and block metering for 500ms void setKnobIndicatorLevel(uint8_t whichKnob, uint8_t level) { + if (whichKnob == whichKnobMetering) { + uiTimerManager.setTimer(TIMER_METER_INDICATOR_BLINK, 500); + } + actuallySetKnobIndicatorLevel(whichKnob, level); +} + +//Just set level +void actuallySetKnobIndicatorLevel(uint8_t whichKnob, uint8_t level) { // If this indicator was blinking, stop it if (uiTimerManager.isTimerSet(TIMER_LEVEL_INDICATOR_BLINK) && whichLevelIndicatorBlinking == whichKnob) { uiTimerManager.unsetTimer(TIMER_LEVEL_INDICATOR_BLINK); diff --git a/src/deluge/hid/led/indicator_leds.h b/src/deluge/hid/led/indicator_leds.h index 5ccb4887b1..e4503b017f 100644 --- a/src/deluge/hid/led/indicator_leds.h +++ b/src/deluge/hid/led/indicator_leds.h @@ -82,7 +82,9 @@ void setLedState(LED led, bool newState, bool allowContinuedBlinking = false); void blinkLed(LED led, uint8_t numBlinks = 255, uint8_t blinkingType = 0, bool initialState = true); void ledBlinkTimeout(uint8_t blinkingType, bool forceRestart = false, bool resetToState = true); void indicateAlertOnLed(LED led); +void setMeterLevel(uint8_t whichKnob, uint8_t level); void setKnobIndicatorLevel(uint8_t whichKnob, uint8_t level); +void actuallySetKnobIndicatorLevel(uint8_t whichKnob, uint8_t level); void clearKnobIndicatorLevels(); void blinkKnobIndicator(int32_t whichKnob); void stopBlinkingKnobIndicator(int32_t whichKnob); diff --git a/src/deluge/model/global_effectable/global_effectable.cpp b/src/deluge/model/global_effectable/global_effectable.cpp index a772bfb01d..053cccc3c9 100644 --- a/src/deluge/model/global_effectable/global_effectable.cpp +++ b/src/deluge/model/global_effectable/global_effectable.cpp @@ -49,6 +49,7 @@ GlobalEffectable::GlobalEffectable() { memset(allpassMemory, 0, sizeof(allpassMemory)); memset(&phaserMemory, 0, sizeof(phaserMemory)); editingComp = false; + currentCompParam = CompParam::RATIO; } void GlobalEffectable::cloneFrom(ModControllableAudio* other) { @@ -267,7 +268,17 @@ bool GlobalEffectable::modEncoderButtonAction(uint8_t whichModEncoder, bool on, else if (modKnobMode == 4) { if (whichModEncoder == 0) { // Reverb if (on) { - view.cycleThroughReverbPresets(); + //if we're in full move/editingComp then we cycle through the comp params + //otherwise cycle reverb sizes + if (!editingComp) { + view.cycleThroughReverbPresets(); + } + else { + currentCompParam = + static_cast((util::to_underlying(currentCompParam) + 1) % maxCompParam); + const char* params[3] = {"ratio", "attack", "release"}; + display->popupTextTemporary(params[int(currentCompParam)]); + } } } else { @@ -282,50 +293,102 @@ bool GlobalEffectable::modEncoderButtonAction(uint8_t whichModEncoder, bool on, return false; // Some cases could lead here } -ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offset, int32_t whichModEncoder, - ModelStackWithAutoParam* modelStack) { + +int32_t GlobalEffectable::getKnobPosForNonExistentParam(int32_t whichModEncoder, ModelStackWithAutoParam* modelStack) { + int displayLevel = -64; if (*getModKnobMode() == 4) { + int current; + + //this is only reachable in comp editing mode, otherwise it's an existent param if (whichModEncoder == 1) { //sidechain (threshold) - int current = AudioEngine::mastercompressor.threshold >> 24; - current -= offset; - current = std::clamp(current, 1, 128); - indicator_leds::setKnobIndicatorLevel(1, std::max(0, 128 - current)); - AudioEngine::mastercompressor.threshold = lshiftAndSaturate<24>(current); - return ActionResult::DEALT_WITH; - } - else if (whichModEncoder == 0) { //ratio/reverb (we can only get here in comp editing mode) - int current = AudioEngine::mastercompressor.ratio >> 24; - current += offset; - //this range is ratio of 2 to infinity - current = std::clamp(current, 48, 112); - indicator_leds::setKnobIndicatorLevel(0, (current - 48) * 2); - AudioEngine::mastercompressor.ratio = lshiftAndSaturate<24>(current); - return ActionResult::DEALT_WITH; + current = AudioEngine::mastercompressor.rawThreshold >> 24; + displayLevel = current; + } + else if (whichModEncoder == 0) { + switch (currentCompParam) { + + case CompParam::RATIO: + current = AudioEngine::mastercompressor.rawRatio >> 24; + displayLevel = current; + break; + + case CompParam::ATTACK: + current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack >> 2, attackRateTable, 50); + displayLevel = (current * 128) / 50; + break; + + case CompParam::RELEASE: + current = getLookupIndexFromValue(AudioEngine::mastercompressor.release >> 1, releaseRateTable, 50); + displayLevel = (current * 128) / 50; + break; + } } } - else if (*getModKnobMode() == 2) { - if (whichModEncoder == 1) { //attack - int current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack >> 2, attackRateTable, 50); + return displayLevel - 64; +} + +ActionResult GlobalEffectable::modEncoderActionForNonExistentParam(int32_t offset, int32_t whichModEncoder, + ModelStackWithAutoParam* modelStack) { + if (*getModKnobMode() == 4) { + int current; + int displayLevel; + int ledLevel; + //this is only reachable in comp editing mode, otherwise it's an existent param + if (whichModEncoder == 1) { //sidechain (threshold) + current = (AudioEngine::mastercompressor.rawThreshold >> 24) - 64; current += offset; - current = std::clamp(current, 1, 50); - indicator_leds::setKnobIndicatorLevel(1, (current * 128) / 50); - AudioEngine::mastercompressor.attack = attackRateTable[current] << 2; - return ActionResult::DEALT_WITH; + current = std::clamp(current, -64, 64); + ledLevel = (64 + current); + displayLevel = ((ledLevel)*kMaxMenuValue) / 128; + AudioEngine::mastercompressor.rawThreshold = lshiftAndSaturate<24>(current + 64); + indicator_leds::setKnobIndicatorLevel(1, ledLevel); } - if (whichModEncoder == 0) { //release - int current = getLookupIndexFromValue(AudioEngine::mastercompressor.release >> 1, releaseRateTable, 50); - current += offset; - current = std::clamp(current, 2, 50); - indicator_leds::setKnobIndicatorLevel(0, (current * 128) / 50); - AudioEngine::mastercompressor.release = releaseRateTable[current] << 1; + else if (whichModEncoder == 0) { + switch (currentCompParam) { + + case CompParam::RATIO: + current = (AudioEngine::mastercompressor.rawRatio >> 24) - 64; + current += offset; + //this range is ratio of 2 to 20 + current = std::clamp(current, -64, 64); + ledLevel = (64 + current); + displayLevel = ((ledLevel)*kMaxMenuValue) / 128; + + AudioEngine::mastercompressor.rawRatio = lshiftAndSaturate<24>(current + 64); + break; + + case CompParam::ATTACK: + current = getLookupIndexFromValue(AudioEngine::mastercompressor.attack >> 2, attackRateTable, 50); + current += offset; + current = std::clamp(current, 1, 50); + displayLevel = current; + ledLevel = (displayLevel * 128) / 50; + + AudioEngine::mastercompressor.attack = attackRateTable[current] << 2; + break; - return ActionResult::DEALT_WITH; + case CompParam::RELEASE: + + current = getLookupIndexFromValue(AudioEngine::mastercompressor.release, releaseRateTable, 50); + current += offset; + current = std::clamp(current, 0, 50); + displayLevel = current; + ledLevel = (displayLevel * 128) / 50; + + AudioEngine::mastercompressor.release = releaseRateTable[current]; + + break; + } + indicator_leds::setKnobIndicatorLevel(0, ledLevel); } - } + char buffer[5]; + intToString(displayLevel, buffer); + display->displayPopup(buffer); + return ActionResult::DEALT_WITH; + } return ActionResult::NOT_DEALT_WITH; } - // Always check this doesn't return NULL! int32_t GlobalEffectable::getParameterFromKnob(int32_t whichModEncoder) { diff --git a/src/deluge/model/global_effectable/global_effectable.h b/src/deluge/model/global_effectable/global_effectable.h index e9f8815ef2..0a9743416d 100644 --- a/src/deluge/model/global_effectable/global_effectable.h +++ b/src/deluge/model/global_effectable/global_effectable.h @@ -55,14 +55,17 @@ class GlobalEffectable : public ModControllableAudio { void setupDelayWorkingState(DelayWorkingState* delayWorkingState, ParamManager* paramManager, bool shouldLimitDelayFeedback = false); bool isEditingComp() override { return editingComp; } + int32_t getKnobPosForNonExistentParam(int32_t whichModEncoder, ModelStackWithAutoParam* modelStack) override; ActionResult modEncoderActionForNonExistentParam(int32_t offset, int32_t whichModEncoder, ModelStackWithAutoParam* modelStack) override; dsp::filter::FilterSet filterSet; ModFXParam currentModFXParam; FilterType currentFilterType; bool editingComp; + CompParam currentCompParam; protected: + int maxCompParam = 0; virtual int32_t getParameterFromKnob(int32_t whichModEncoder); ModFXType getActiveModFXType(ParamManager* paramManager); diff --git a/src/deluge/model/global_effectable/global_effectable_for_song.cpp b/src/deluge/model/global_effectable/global_effectable_for_song.cpp index fbe3270749..a124c2effe 100644 --- a/src/deluge/model/global_effectable/global_effectable_for_song.cpp +++ b/src/deluge/model/global_effectable/global_effectable_for_song.cpp @@ -19,4 +19,6 @@ GlobalEffectableForSong::GlobalEffectableForSong() { modKnobMode = 1; + //attack and release can't go in the param manager so this keeps them from changing in clip comps + maxCompParam = 3; } diff --git a/src/deluge/model/song/song.cpp b/src/deluge/model/song/song.cpp index 9cbe8ff6ba..0220261a48 100644 --- a/src/deluge/model/song/song.cpp +++ b/src/deluge/model/song/song.cpp @@ -138,7 +138,7 @@ Song::Song() : backedUpParamManagers(sizeof(BackedUpParamManager)) { masterCompressorAttack = attackRateTable[2] << 2; masterCompressorRelease = releaseRateTable[5] << 2; - masterCompressorThresh = ONE_Q31; + masterCompressorThresh = 0; masterCompressorRatio = ONE_Q31 >> 1; AudioEngine::mastercompressor.gainReduction = 0.0; 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); diff --git a/src/deluge/storage/flash_storage.cpp b/src/deluge/storage/flash_storage.cpp index e2ca106a4d..c228247d72 100644 --- a/src/deluge/storage/flash_storage.cpp +++ b/src/deluge/storage/flash_storage.cpp @@ -113,6 +113,11 @@ namespace FlashStorage { 114: GlobalMIDICommand::FILL channel + 1 115: GlobalMIDICommand::FILL noteCode + 1 116: GlobalMIDICommand::FILL product / vendor ids +117: defaultSessionLayout +118: defaultKeyboardLayout +119: gridUnarmEmptyPads +120: gridAllowGreenSelection +121: defaultGridActiveMode */ uint8_t defaultScale; @@ -133,6 +138,10 @@ uint8_t defaultBendRange[2] = { SessionLayoutType defaultSessionLayout; KeyboardLayoutType defaultKeyboardLayout; +bool gridUnarmEmptyPads; +bool gridAllowGreenSelection; +GridDefaultActiveMode defaultGridActiveMode; + void resetSettings() { cvEngine.setCVVoltsPerOctave(0, 100); @@ -199,6 +208,10 @@ void resetSettings() { defaultSessionLayout = SessionLayoutType::SessionLayoutTypeRows; defaultKeyboardLayout = KeyboardLayoutType::KeyboardLayoutTypeIsomorphic; + + gridUnarmEmptyPads = false; + gridAllowGreenSelection = true; + defaultGridActiveMode = GridDefaultActiveModeSelection; } void readSettings() { @@ -390,6 +403,10 @@ void readSettings() { defaultSessionLayout = static_cast(buffer[117]); defaultKeyboardLayout = static_cast(buffer[118]); + + gridUnarmEmptyPads = buffer[119]; + gridAllowGreenSelection = buffer[120]; + defaultGridActiveMode = static_cast(buffer[121]); } void writeSettings() { @@ -495,6 +512,10 @@ void writeSettings() { buffer[117] = util::to_underlying(defaultSessionLayout); buffer[118] = util::to_underlying(defaultKeyboardLayout); + buffer[119] = gridUnarmEmptyPads; + buffer[120] = gridAllowGreenSelection; + buffer[121] = util::to_underlying(defaultGridActiveMode); + R_SFLASH_EraseSector(0x80000 - 0x1000, SPIBSC_CH, SPIBSC_CMNCR_BSZ_SINGLE, 1, SPIBSC_OUTPUT_ADDR_24); R_SFLASH_ByteProgram(0x80000 - 0x1000, buffer, 256, SPIBSC_CH, SPIBSC_CMNCR_BSZ_SINGLE, SPIBSC_1BIT, SPIBSC_OUTPUT_ADDR_24); diff --git a/src/deluge/storage/flash_storage.h b/src/deluge/storage/flash_storage.h index 0a77a590a5..e4459ec59d 100644 --- a/src/deluge/storage/flash_storage.h +++ b/src/deluge/storage/flash_storage.h @@ -40,6 +40,10 @@ extern uint8_t defaultBendRange[2]; extern SessionLayoutType defaultSessionLayout; extern KeyboardLayoutType defaultKeyboardLayout; +extern bool gridUnarmEmptyPads; +extern bool gridAllowGreenSelection; +extern GridDefaultActiveMode defaultGridActiveMode; + void readSettings(); void writeSettings(); void resetSettings(); diff --git a/src/fault_handler.c b/src/fault_handler.c new file mode 100644 index 0000000000..8cfa9c3c60 --- /dev/null +++ b/src/fault_handler.c @@ -0,0 +1,199 @@ +/******************************************************************************* + * DISCLAIMER + * This software is supplied by Renesas Electronics Corporation and is only + * intended for use with Renesas products. No other uses are authorized. This + * software is owned by Renesas Electronics Corporation and is protected under + * all applicable laws, including copyright laws. + * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING + * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT + * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. + * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS + * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE + * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR + * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE + * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * Renesas reserves the right, without notice, to make changes to this software + * and to discontinue the availability of this software. By using this software, + * you agree to the additional terms and conditions found by accessing the + * following link: + * http://www.renesas.com/disclaimer + * + * Copyright (C) 2014 Renesas Electronics Corporation. All rights reserved. + *******************************************************************************/ +/******************************************************************************* + * File Name : resetprg.c + * Device(s) : RZ/A1H (R7S721001) + * Tool-Chain : GNUARM-NONEv14.02-EABI + * H/W Platform : RSK+RZA1H CPU Board + * Description : Sample Program - C library entry point + * : Variants of this file must be created for each compiler + *******************************************************************************/ +/******************************************************************************* + * History : DD.MM.YYYY Version Description + * : 21.10.2014 1.00 + *******************************************************************************/ + +/* + * Copyright © 2021-2023 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "RZA1/uart/sio_char.h" +#include "definitions.h" +#include "drivers/uart/uart.h" + +extern uint32_t program_stack_start; +extern uint32_t program_stack_end; +extern uint32_t program_code_start; +extern uint32_t program_code_end; + +[[gnu::always_inline]] inline void sendToPIC(uint8_t msg) { + intptr_t writePos = uartItems[UART_ITEM_PIC].txBufferWritePos; + volatile char* uncached_tx_buf = (volatile char*)(picTxBuffer + UNCACHED_MIRROR_OFFSET); + uncached_tx_buf[writePos] = msg; + uartItems[UART_ITEM_PIC].txBufferWritePos += 1; + uartItems[UART_ITEM_PIC].txBufferWritePos &= (PIC_TX_BUFFER_SIZE - 1); +} + +[[gnu::always_inline]] inline void sendColor(uint8_t r, uint8_t g, uint8_t b) { + sendToPIC(r); + sendToPIC(g); + sendToPIC(b); +} + +[[gnu::always_inline]] inline void drawByte(uint8_t byte, uint8_t r, uint8_t g, uint8_t b) { + for (int32_t idxBit = 7; idxBit >= 0; --idxBit) { + if (((byte >> idxBit) & 0x01) == 0x01) { + sendColor(r, g, b); + } + else { + sendColor(0, 0, 0); + } + } +} + +// Requires 32 pads so two double cloumns +[[gnu::always_inline]] inline int32_t drawPointer(uint32_t idxColumnPairStart, uint32_t pointerValue, uint8_t r, + uint8_t g, uint8_t b) { + sendToPIC(1 + idxColumnPairStart); + ++idxColumnPairStart; + + drawByte(pointerValue >> 24, r, g, b); + drawByte(pointerValue >> 16, r, g, b); + + sendToPIC(1 + idxColumnPairStart); + ++idxColumnPairStart; + + drawByte(pointerValue >> 8, r, g, b); + drawByte(pointerValue, r, g, b); + + return idxColumnPairStart; +} + +[[gnu::always_inline]] inline bool isStackPointer(uint32_t value) { + if (value >= (uint32_t)&program_stack_start && value < (uint32_t)&program_stack_end) { + return true; + } + + return false; +} + +[[gnu::always_inline]] inline bool isCodePointer(uint32_t value) { + if (value >= (uint32_t)&program_code_start && value < (uint32_t)&program_code_end) { + return true; + } + + return false; +} + +void handle_cpu_fault(uint32_t addrSYSLR, uint32_t addrSYSSP, uint32_t addrUSRLR, uint32_t addrUSRSP) { + uint32_t currentColumnPairIndex = 0; + + // Print LR from USR mode if it is valid + if (isCodePointer(addrUSRLR)) { + currentColumnPairIndex = drawPointer(currentColumnPairIndex, addrUSRLR, 255, 0, 255); + } + + // Print LR from SYS mode if it is valid and different from USR mode + if (isCodePointer(addrSYSLR) && addrSYSLR != addrUSRLR) { + currentColumnPairIndex = drawPointer(currentColumnPairIndex, addrSYSLR, 0, 0, 255); + } + + // Check if any stack pointer is valid preferring USR pointer + uint32_t stackPointer = 0; + if (isStackPointer(addrUSRSP)) { + stackPointer = addrUSRSP; + } + else if (isStackPointer(addrSYSSP)) { + stackPointer = addrSYSSP; + } + + if (stackPointer != 0x00000000) { + uint8_t currentBlueValue = 0; + uint32_t lastCodePointer = 0; + stackPointer = stackPointer - (stackPointer % 4); // Align to 4 bytes + while (stackPointer >= (uint32_t)&program_stack_start) { + uint32_t stackValue = *((uint32_t*)stackPointer); + // Print any pointer that is pointing to code, different from the LRs and not the same as before + if (isCodePointer(stackValue) && stackValue != lastCodePointer && stackValue != addrUSRLR + && stackValue != addrSYSLR) { + currentColumnPairIndex = drawPointer(currentColumnPairIndex, stackValue, 0, 255, currentBlueValue); + + // Stop after filling all columns + if (currentColumnPairIndex >= 8) { + break; + } + + // Alternate colors + if (currentBlueValue == 0) { + currentBlueValue = 255; + } + else if (currentBlueValue == 255) { + currentBlueValue = 0; + } + + lastCodePointer = stackValue; + } + + stackPointer -= 4; + } + } + + // Clear all other pads + for (; currentColumnPairIndex < 8; ++currentColumnPairIndex) { + sendToPIC(1 + currentColumnPairIndex); + + for (uint32_t idxColumnPairBuffer = 0; idxColumnPairBuffer < 16; ++idxColumnPairBuffer) { + sendColor(0, 0, 0); + } + } + + // Send error pattern to sidebar + sendToPIC(1 + currentColumnPairIndex); + bool lightActive = true; + for (uint32_t idxColumnPairBuffer = 0; idxColumnPairBuffer < 16; ++idxColumnPairBuffer) { + sendColor(lightActive ? 255 : 0, 0, 0); + if (idxColumnPairBuffer != 7) { + lightActive = !lightActive; + } + } + + uartFlushIfNotSending(UART_ITEM_PIC); + + while (1) { + __asm__("nop"); + } +}