commit ba399a1a3567741290985dfc6fc9d1ad33c5c5af parent 35119c8c0b5cf5fabf0f7a4574727c3592ca0948 Author: dsp56300 <dsp56300@users.noreply.github.com> Date: Thu, 27 Jan 2022 23:16:57 +0100 Merge branch 'dsp56300_ui-trancy' into dsp56300_ui # Conflicts: # source/dsp56300 Diffstat:
57 files changed, 3836 insertions(+), 9 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -1,3 +1,8 @@ temp/ vtune/ *.aps +/.vs +/out/build/x64-Debug +/temp2/cmake_win64 +/source/jucePlugin_24.01.2022.zip +/build_win64_VS2019.bat diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -28,6 +28,7 @@ if(${PROJECT_NAME}_BUILD_JUCEPLUGIN) set(JUCE_ENABLE_MODULE_SOURCE_GROUPS ON CACHE BOOL "" FORCE) add_subdirectory(source/JUCE) add_subdirectory(source/jucePlugin) + if(MSVC OR APPLE) if(JUCE_GLOBAL_VST2_SDK_PATH) install(TARGETS jucePlugin_VST DESTINATION . COMPONENT VST2) @@ -42,7 +43,22 @@ if(${PROJECT_NAME}_BUILD_JUCEPLUGIN) endif() install(TARGETS jucePlugin_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT VST3) endif() -endif() + endif() + + if(MSVC OR APPLE) + if(JUCE_GLOBAL_VST2_SDK_PATH) + install(TARGETS jucePlugin_Dark_VST DESTINATION . COMPONENT VST2) + endif() + install(TARGETS jucePlugin_Dark_VST3 DESTINATION . COMPONENT VST3) + if(APPLE) + install(TARGETS jucePlugin_Dark_AU DESTINATION . COMPONENT AU) + endif() + elseif(UNIX) + if(JUCE_GLOBAL_VST2_SDK_PATH) + install(TARGETS jucePlugin_Dark_VST LIBRARY DESTINATION /usr/local/lib/lxvst/ COMPONENT VST2) + endif() + install(TARGETS jucePlugin_Dark_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT VST3) + endif() # ----------------- Test Console diff --git a/build_win64.bat b/build_win64.bat @@ -12,4 +12,4 @@ IF %ERRORLEVEL% NEQ 0 ( ) cpack -G ZIP popd -move /y %outdir%*.zip deploy\ +move /y %outdir%*.zip deploy\ +\ No newline at end of file diff --git a/source/jucePlugin/CMakeLists.txt b/source/jucePlugin/CMakeLists.txt @@ -1,3 +1,4 @@ + cmake_minimum_required(VERSION 3.15) project(jucePlugin VERSION ${CMAKE_PROJECT_VERSION}) @@ -26,7 +27,6 @@ juce_add_plugin(jucePlugin FORMATS AU VST3 ${VST} Standalone # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 PRODUCT_NAME "DSP56300Emu" # The name of the final executable, which can differ from the target name ) - target_sources(jucePlugin PRIVATE PluginEditor.cpp @@ -99,9 +99,10 @@ PUBLIC JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call JUCE_VST3_CAN_REPLACE_VST2=0 + JUCE_WIN_PER_MONITOR_DPI_AWARE=0 ) -target_link_libraries(jucePlugin +target_link_libraries(jucePlugin PRIVATE jucePlugin_BinaryData juce::juce_audio_utils @@ -116,3 +117,124 @@ PUBLIC if(UNIX AND NOT APPLE) target_link_libraries(jucePlugin PUBLIC -static-libgcc -static-libstdc++) endif() + + + +#SKIN2--------------------------------------------------------------------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 3.15) +project(jucePlugin_Dark VERSION ${CMAKE_PROJECT_VERSION}) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h) + +if(JUCE_GLOBAL_VST2_SDK_PATH) + set(VST "VST") +else() + set(VST "") +endif() + +juce_add_plugin(jucePlugin_Dark + # VERSION ... # Set this if the plugin version is different to the project version + # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone + # ICON_SMALL ... + COMPANY_NAME "The Usual Suspects" # Specify the name of the plugin's author + IS_SYNTH TRUE # Is this a synth or an effect? + NEEDS_MIDI_INPUT TRUE # Does the plugin need midi input? + NEEDS_MIDI_OUTPUT TRUE # Does the plugin need midi output? + IS_MIDI_EFFECT FALSE # Is this plugin a MIDI effect? + EDITOR_WANTS_KEYBOARD_FOCUS TRUE # Does the editor need keyboard focus? + COPY_PLUGIN_AFTER_BUILD FALSE # Should the plugin be installed to a default location after building? + PLUGIN_MANUFACTURER_CODE TusP # A four-character manufacturer id with at least one upper-case character + PLUGIN_CODE TusV # A unique four-character plugin id with exactly one upper-case character + # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case + FORMATS AU VST3 ${VST} Standalone # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 + PRODUCT_NAME "DSP56300Emu_Dark" # The name of the final executable, which can differ from the target name +) +target_sources(jucePlugin_Dark +PRIVATE + PluginEditorSkin2.cpp + PluginEditorSkin2.h + PluginProcessor.cpp + PluginProcessor.h + ui2/Virus_Buttons.cpp + ui2/Virus_LookAndFeel.cpp + ui2/VirusEditor.cpp + ui2/Virus_Panel4_ArpEditor.cpp + ui2/Virus_Panel3_FxEditor.cpp + ui2/Virus_Panel2_LfoEditor.cpp + ui2/Virus_Panel1_OscEditor.cpp + ui2/Virus_Panel5_PatchBrowser.cpp + ui2/Virus_Buttons.h + ui2/Virus_LookAndFeel.h + ui2/VirusEditor.h + ui2/Virus_Panel4_ArpEditor.h + ui2/Virus_Panel3_FxEditor.h + ui2/Virus_Panel2_LfoEditor.h + ui2/Virus_Panel1_OscEditor.h + ui2/Virus_Panel5_PatchBrowser.h + ui2/Ui_Utils.h + VirusController.cpp + VirusController.h + VirusParameter.cpp + VirusParameter.h + VirusParameterBinding.cpp + VirusParameterBinding.h + version.h +) + +# https://forum.juce.com/t/help-needed-using-binarydata-with-cmake-juce-6/40486 +# "This might be because the BinaryData files are generated during the build, so the IDE may not be able to find them until the build has been run once (and even then, some IDEs might need a bit of a nudge to re-index the binary directory…)" +juce_add_binary_data(jucePlugin_BinaryData_Dark + SOURCES + "assets2/main_background.png" + "assets2/panels/panel_1.png" + "assets2/panels/panel_2.png" + "assets2/panels/panel_3.png" + "assets2/panels/panel_4.png" + "assets2/panels/panel_5.png" + "assets2/buttons/btn_main_1.png" + "assets2/buttons/btn_main_2.png" + "assets2/buttons/btn_main_3.png" + "assets2/buttons/btn_main_4.png" + "assets2/buttons/btn_main_5.png" + "assets2/buttons/btn_1.png" + "assets2/buttons/btn_2.png" + "assets2/buttons/btn_3.png" + "assets2/buttons/btn_4.png" + "assets2/buttons/btn_left.png" + "assets2/buttons/btn_right.png" + "assets2/buttons/btn_down.png" + "assets2/buttons/btn_menu.png" + "assets2/buttons/btn_load_bank.png" + "assets2/buttons/btn_save_preset.png" + "assets2/combobox/cmb_1.png" + "assets2/combobox/cmb_2.png" + "assets2/knobs/knob_1_128.png" + "assets2/knobs/knob_2_128.png" + "assets2/font/Digital" +) + +target_compile_definitions(jucePlugin_Dark +PUBLIC + # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them. + JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call + JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call + JUCE_VST3_CAN_REPLACE_VST2=0 + JUCE_WIN_PER_MONITOR_DPI_AWARE=0 +) + +target_link_libraries(jucePlugin_Dark +PRIVATE + jucePlugin_BinaryData_Dark + juce::juce_audio_utils + juce::juce_cryptography +PUBLIC + virusLib + #juce::juce_recommended_config_flags + #juce::juce_recommended_lto_flags + #juce::juce_recommended_warning_flags +) + +if(UNIX AND NOT APPLE) + target_link_libraries(jucePlugin_Dark PUBLIC -static-libgcc -static-libstdc++) +endif() +\ No newline at end of file diff --git a/source/jucePlugin/PluginEditorSkin2.cpp b/source/jucePlugin/PluginEditorSkin2.cpp @@ -0,0 +1,43 @@ +#include "PluginProcessor.h" +#include "PluginEditorSkin2.h" +#include "VirusController.h" +#include "version.h" +// AH TODO: need compiler switch +#include "ui2/VirusEditor.h" + +//============================================================================== +AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor(AudioPluginAudioProcessor &p) : + AudioProcessorEditor(&p), processorRef(p), m_parameterBinding(p), m_virusEditor(new VirusEditor(m_parameterBinding, processorRef)) +{ + const auto config = processorRef.getController().getConfig(); + + setResizable(true,false); + setScaleFactor(config->getDoubleValue("skin_scale_factor", 0.75f)); + setSize(m_virusEditor->iSkinSizeWidth, m_virusEditor->iSkinSizeHeight); + m_virusEditor->m_AudioPlugInEditor = (AudioPluginAudioProcessorEditor*)this; + + addAndMakeVisible(m_virusEditor); +} + +AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor() +{ + delete m_virusEditor; +} + +//============================================================================== +void AudioPluginAudioProcessorEditor::paint (juce::Graphics& g) +{ +} + +void AudioPluginAudioProcessorEditor::timerCallback() +{ + // ugly (polling!) way for refreshing presets names as this is temporary ui +} + +void AudioPluginAudioProcessorEditor::resized() +{ + // This is generally where you'll want to lay out the positions of any + // subcomponents in your editor.. + auto area = getLocalBounds(); + area.removeFromTop(35); +} diff --git a/source/jucePlugin/PluginEditorSkin2.h b/source/jucePlugin/PluginEditorSkin2.h @@ -0,0 +1,34 @@ +#pragma once + +#include "VirusParameterBinding.h" +#include "ui2/VirusEditor.h" +#include "PluginProcessor.h" +#include <juce_audio_devices/juce_audio_devices.h> + +//============================================================================== +class AudioPluginAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::Timer +{ +public: + explicit AudioPluginAudioProcessorEditor (AudioPluginAudioProcessor&); + ~AudioPluginAudioProcessorEditor() override; + + //============================================================================== + //void handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message) override; + void paint (juce::Graphics&) override; + void resized() override; + +private: + void timerCallback() override; + + // This reference is provided as a quick way for your editor to + // access the processor object that created it. + AudioPluginAudioProcessor& processorRef; + VirusParameterBinding m_parameterBinding; + + // New "real" editor + VirusEditor *m_virusEditor; + juce::ComboBox m_scale; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessorEditor) + +}; diff --git a/source/jucePlugin/VirusController.cpp b/source/jucePlugin/VirusController.cpp @@ -27,12 +27,17 @@ namespace Virus const uint8_t prg = isMultiMode() ? 0x0 : 0x40; sendSysEx(constructMessage({MessageType::REQUEST_SINGLE, 0x0, prg})); sendSysEx(constructMessage({MessageType::REQUEST_MULTI, 0x0, prg})); + + if (onMsgDone) + { + onMsgDone(); + } }; sendSysEx(constructMessage({MessageType::REQUEST_TOTAL})); sendSysEx(constructMessage({MessageType::REQUEST_ARRANGEMENT})); for(uint8_t i=3; i<=8; ++i) - sendSysEx(constructMessage({MessageType::REQUEST_BANK_SINGLE, i})); + sendSysEx(constructMessage({MessageType::REQUEST_BANK_SINGLE, i})); startTimer(5); } @@ -1297,6 +1302,143 @@ namespace Virus } } + juce::String filterKeytrackBase(float v, Parameter::Description) + { + const auto ridx = juce::roundToInt(v); + switch (ridx) + { + case 0: return "C-1"; + case 1: return "C#-1"; + case 2: return "D-1"; + case 3: return "D#-1"; + case 4: return "E-1"; + case 5: return "F-1"; + case 6: return "F#-1"; + case 7: return "G-1"; + case 8: return "G#-1"; + case 9: return "A-1"; + case 10: return "A#-1"; + case 11: return "B-1"; + case 12: return "C0"; + case 13: return "C#0"; + case 14: return "D0"; + case 15: return "D#0"; + case 16: return "E0"; + case 17: return "F0"; + case 18: return "F#0"; + case 19: return "G0"; + case 20: return "G#0"; + case 21: return "A0"; + case 22: return "A#0"; + case 23: return "B0"; + case 24: return "C1"; + case 25: return "C#1"; + case 26: return "D1"; + case 27: return "D#1"; + case 28: return "E1"; + case 29: return "F1"; + case 30: return "F#1"; + case 31: return "G1"; + case 32: return "G#1"; + case 33: return "A1"; + case 34: return "A#1"; + case 35: return "B1"; + case 36: return "C2"; + case 37: return "C#2"; + case 38: return "D2"; + case 39: return "D#2"; + case 40: return "E2"; + case 41: return "F2"; + case 42: return "F#2"; + case 43: return "G2"; + case 44: return "G#2"; + case 45: return "A2"; + case 46: return "A#2"; + case 47: return "B2"; + case 48: return "C3"; + case 49: return "C#3"; + case 50: return "D3"; + case 51: return "D#3"; + case 52: return "E3"; + case 53: return "F3"; + case 54: return "F#3"; + case 55: return "G3"; + case 56: return "G#3"; + case 57: return "A3"; + case 58: return "A#3"; + case 59: return "B3"; + case 60: return "C4"; + case 61: return "C#4"; + case 62: return "D4"; + case 63: return "D#4"; + case 64: return "E4"; + case 65: return "F4"; + case 66: return "F#4"; + case 67: return "G4"; + case 68: return "G#4"; + case 69: return "A4"; + case 70: return "A#4"; + case 71: return "B4"; + case 72: return "C5"; + case 73: return "C#5"; + case 74: return "D5"; + case 75: return "D#5"; + case 76: return "E5"; + case 77: return "F5"; + case 78: return "F#5"; + case 79: return "G5"; + case 80: return "G#5"; + case 81: return "A5"; + case 82: return "A#5"; + case 83: return "B5"; + case 84: return "C6"; + case 85: return "C#6"; + case 86: return "D6"; + case 87: return "D#6"; + case 88: return "E6"; + case 89: return "F6"; + case 90: return "F#6"; + case 91: return "G6"; + case 92: return "G#6"; + case 93: return "A6"; + case 94: return "A#6"; + case 95: return "B6"; + case 96: return "C7"; + case 97: return "C#7"; + case 98: return "D7"; + case 99: return "D#7"; + case 100: return "E7"; + case 101: return "F7"; + case 102: return "F#7"; + case 103: return "G7"; + case 104: return "G#7"; + case 105: return "A7"; + case 106: return "A#7"; + case 107: return "B7"; + case 108: return "C8"; + case 109: return "C#8"; + case 110: return "D8"; + case 111: return "D#8"; + case 112: return "E8"; + case 113: return "F8"; + case 114: return "F#8"; + case 115: return "G8"; + case 116: return "G#8"; + case 117: return "A8"; + case 118: return "A#8"; + case 119: return "B8"; + case 120: return "C9"; + case 121: return "C#9"; + case 122: return "D9"; + case 123: return"D#9"; + case 124: return "E9"; + case 125: return "F9"; + case 126: return "F#9"; + case 127: return "G9"; + default: return juce::String(v); + } + } + juce::String numToOscFmMode(float idx, Parameter::Description) { const auto ridx = juce::roundToInt(idx); @@ -1512,7 +1654,7 @@ namespace Virus {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 38, "Ringmodulator Volume", {0,127}, {},{}, true, false, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A|Parameter::Class::VIRUS_C, 39, "Noise Color", {0,127}, paramTo7bitSigned, textTo7bitSigned, true, false, false, true}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 40, "Cutoff", {0,127}, {},{}, true, false, false}, - {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 41, "Cutoff2", {0,127}, {},{}, true, false, false}, + {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 41, "Cutoff2", {0,127}, {},{}, true, false, false, true}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 42, "Filter1 Resonance", {0,127}, {},{}, true, false, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 43, "Filter2 Resonance", {0,127}, {},{}, true, false, false}, {Parameter::Page::A, Parameter::Class::SOUNDBANK_A, 44, "Filter1 Env Amt", {0,127}, {},{}, true, false, false}, @@ -1614,7 +1756,7 @@ namespace Virus {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 30, "Filter1 Env Polarity", {0,1}, numToNegPos, {}, true, false, true}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 31, "Filter1 Env Polarity", {0,1}, numToNegPos, {}, true, false, true}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 32, "Filter2 Cutoff Link", {0,1}, {},{}, true, false, true}, - {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 33, "Filter Keytrack Base", {0,127}, {},{}, true, false, false}, + {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 33, "Filter Keytrack Base", {0,127}, filterKeytrackBase,{}, true, true, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B|Parameter::Class::VIRUS_C, 34, "Osc FM Mode", {0,12}, numToOscFmMode,{}, true, true, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 35, "Osc Init Phase", {0,127}, {},{}, true, false, false}, {Parameter::Page::B, Parameter::Class::SOUNDBANK_B, 36, "Punch Intensity", {0,127}, {},{}, true, false, false}, @@ -1698,7 +1840,7 @@ namespace Virus {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 36, "Part High Key", {0,127}, {},{}, false, true, false}, {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 37, "Part Transpose", {0,127}, paramTo7bitSigned, textTo7bitSigned, false, false, false, true}, {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 38, "Part Detune", {0,127}, paramTo7bitSigned, textTo7bitSigned, false, false, false, true}, - {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 39, "Part Volume", {0,127}, paramTo7bitSigned,textTo7bitSigned, true, false, false, true}, + {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 39, "Part Volume", {0,127}, paramTo7bitSigned,textTo7bitSigned, true, false, false, false}, {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 40, "Part Midi Volume Init", {0,127}, {},{}, false, true, false}, {Parameter::Page::C, Parameter::Class::MULTI_PARAM, 41, "Part Output Select", {0,14}, numToOutputSelect,{}, false, true, false}, {Parameter::Page::C, Parameter::Class::GLOBAL, 45, "Second Output Select", {0,15}, {},{}, false, true, false}, @@ -1898,7 +2040,9 @@ namespace Virus parseControllerDump(msg); } else - parseMessage(msg.sysex); + { + parseMessage(msg.sysex); + } } m_virusOut.clear(); } diff --git a/source/jucePlugin/VirusController.h b/source/jucePlugin/VirusController.h @@ -429,6 +429,8 @@ namespace Virus void onStateLoaded(); juce::PropertiesFile* getConfig() { return m_config; } std::function<void()> onProgramChange = {}; + std::function<void()> onMsgDone = {}; + private: void timerCallback() override; static constexpr size_t kDataSizeInBytes = 256; // same for multi and single diff --git a/source/jucePlugin/assets2/buttons/btn_1.png b/source/jucePlugin/assets2/buttons/btn_1.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_2.png b/source/jucePlugin/assets2/buttons/btn_2.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_3.png b/source/jucePlugin/assets2/buttons/btn_3.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_4.png b/source/jucePlugin/assets2/buttons/btn_4.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_down.png b/source/jucePlugin/assets2/buttons/btn_down.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_left.png b/source/jucePlugin/assets2/buttons/btn_left.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_load_bank.png b/source/jucePlugin/assets2/buttons/btn_load_bank.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_main_1.png b/source/jucePlugin/assets2/buttons/btn_main_1.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_main_2.png b/source/jucePlugin/assets2/buttons/btn_main_2.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_main_3.png b/source/jucePlugin/assets2/buttons/btn_main_3.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_main_4.png b/source/jucePlugin/assets2/buttons/btn_main_4.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_main_5.png b/source/jucePlugin/assets2/buttons/btn_main_5.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_menu.png b/source/jucePlugin/assets2/buttons/btn_menu.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_right.png b/source/jucePlugin/assets2/buttons/btn_right.png Binary files differ. diff --git a/source/jucePlugin/assets2/buttons/btn_save_preset.png b/source/jucePlugin/assets2/buttons/btn_save_preset.png Binary files differ. diff --git a/source/jucePlugin/assets2/combobox/cmb_1.png b/source/jucePlugin/assets2/combobox/cmb_1.png Binary files differ. diff --git a/source/jucePlugin/assets2/combobox/cmb_2.png b/source/jucePlugin/assets2/combobox/cmb_2.png Binary files differ. diff --git a/source/jucePlugin/assets2/font/Digital b/source/jucePlugin/assets2/font/Digital Binary files differ. diff --git a/source/jucePlugin/assets2/knobs/knob_1_128.png b/source/jucePlugin/assets2/knobs/knob_1_128.png Binary files differ. diff --git a/source/jucePlugin/assets2/knobs/knob_2_128.png b/source/jucePlugin/assets2/knobs/knob_2_128.png Binary files differ. diff --git a/source/jucePlugin/assets2/main_background.png b/source/jucePlugin/assets2/main_background.png Binary files differ. diff --git a/source/jucePlugin/assets2/main_background_marker_points.png b/source/jucePlugin/assets2/main_background_marker_points.png Binary files differ. diff --git a/source/jucePlugin/assets2/panel_1_marker_points.png b/source/jucePlugin/assets2/panel_1_marker_points.png Binary files differ. diff --git a/source/jucePlugin/assets2/panel_2_marker_points.png b/source/jucePlugin/assets2/panel_2_marker_points.png Binary files differ. diff --git a/source/jucePlugin/assets2/panel_3_marker_points.png b/source/jucePlugin/assets2/panel_3_marker_points.png Binary files differ. diff --git a/source/jucePlugin/assets2/panel_4_marker_points.png b/source/jucePlugin/assets2/panel_4_marker_points.png Binary files differ. diff --git a/source/jucePlugin/assets2/panel_5_marker_points.png b/source/jucePlugin/assets2/panel_5_marker_points.png Binary files differ. diff --git a/source/jucePlugin/assets2/panels/panel_1.png b/source/jucePlugin/assets2/panels/panel_1.png Binary files differ. diff --git a/source/jucePlugin/assets2/panels/panel_2.png b/source/jucePlugin/assets2/panels/panel_2.png Binary files differ. diff --git a/source/jucePlugin/assets2/panels/panel_3.png b/source/jucePlugin/assets2/panels/panel_3.png Binary files differ. diff --git a/source/jucePlugin/assets2/panels/panel_4.png b/source/jucePlugin/assets2/panels/panel_4.png Binary files differ. diff --git a/source/jucePlugin/assets2/panels/panel_5.png b/source/jucePlugin/assets2/panels/panel_5.png Binary files differ. diff --git a/source/jucePlugin/ui2/Ui_Utils.h b/source/jucePlugin/ui2/Ui_Utils.h @@ -0,0 +1,67 @@ +#include "Virus_LookAndFeel.h" +#include "../VirusController.h" +#include "../../virusLib/microcontrollerTypes.h" + +constexpr auto knobSize = Virus::LookAndFeel::kKnobSize; +constexpr auto knobSizeSmall = Virus::LookAndFeelSmallButton::kKnobSize; + +class Controller; +using namespace virusLib; + +static void setupBackground(juce::Component &parent, std::unique_ptr<juce::Drawable> &bg, const void *data, + const size_t numBytes) +{ + bg = juce::Drawable::createFromImageData(data, numBytes); + parent.addAndMakeVisible(bg.get()); + bg->setBounds(bg->getDrawableBounds().toNearestIntEdges()); +} + +static void setupRotary(juce::Component &parent, juce::Slider &slider) +{ + slider.setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag); + slider.setTextBoxStyle(juce::Slider::NoTextBox, false, 0, 0); + parent.addAndMakeVisible(slider); +} + + +static juce::String getCurrentPartBankStr(virusLib::BankNumber currentBank) +{ + switch (currentBank) + { + case virusLib::BankNumber::A: return "A"; + case virusLib::BankNumber::B: return "B"; + case virusLib::BankNumber::C: return "C"; + case virusLib::BankNumber::D: return "D"; + case virusLib::BankNumber::E: return "E"; + case virusLib::BankNumber::F: return "F"; + case virusLib::BankNumber::G: return "G"; + case virusLib::BankNumber::H: return "H"; + case virusLib::BankNumber::EditBuffer: return "EB"; + } + + return "ERR"; +} + +static VirusModel guessVersion(uint8_t *data) +{ + if (data[51] > 3) + { + // check extra filter modes + return VirusModel::C; + } + if(data[179] == 0x40 && data[180] == 0x40) // soft knobs don't exist on B so they have fixed value + { + return VirusModel::B; + } + /*if (data[232] != 0x03 || data[235] != 0x6c || data[238] != 0x01) { // extra mod slots + return VirusModel::C; + }*/ + /*if(data[173] != 0x00 || data[174] != 0x00) // EQ + return VirusModel::C;*/ + /*if (data[220] != 0x40 || data[221] != 0x54 || data[222] != 0x20 || data[223] != 0x40 || data[224] != 0x40) { + // eq controls + return VirusModel::C; + }*/ + //return VirusModel::C; + +} +\ No newline at end of file diff --git a/source/jucePlugin/ui2/VirusEditor.cpp b/source/jucePlugin/ui2/VirusEditor.cpp @@ -0,0 +1,491 @@ +#include "VirusEditor.h" +#include "BinaryData.h" +#include "../version.h" +#include "Virus_Panel4_ArpEditor.h" +#include "Virus_Panel3_FxEditor.h" +#include "Virus_Panel2_LfoEditor.h" +#include "Virus_Panel1_OscEditor.h" +#include "Virus_Panel5_PatchBrowser.h" +#include "../VirusParameterBinding.h" +#include "../VirusController.h" +#include "Ui_Utils.h" + +using namespace juce; + +enum Tabs +{ + ArpSettings, + Effects, + LfoMatrix, + OscFilter, + Patches +}; +static uint8_t currentTab = Tabs::OscFilter; + +VirusEditor::VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef) : + m_parameterBinding(_parameterBinding), processorRef(_processorRef), m_controller(_processorRef.getController()) ,m_controlLabel("ctrlLabel", ""), m_properties(_processorRef.getController().getConfig()) +{ + setLookAndFeel(&m_lookAndFeel); + + m_background = Drawable::createFromImageData(BinaryData::main_background_png, BinaryData::main_background_pngSize); + m_background->setBufferedToImage (true); + + addAndMakeVisible (*m_background); + addAndMakeVisible (m_mainButtons); + + m_arpEditor = std::make_unique<ArpEditor>(_parameterBinding, processorRef); + m_fxEditor = std::make_unique<FxEditor>(_parameterBinding, processorRef); + m_lfoEditor = std::make_unique<LfoEditor>(_parameterBinding); + m_oscEditor = std::make_unique<OscEditor>(_parameterBinding); + m_patchBrowser = std::make_unique<PatchBrowser>(_parameterBinding, processorRef); + + applyToSections([this](Component *s) { addChildComponent(s); }); + + //Init Keyboard + setWantsKeyboardFocus(true); + addKeyListener(this); + + // show/hide section from buttons.. + m_mainButtons.updateSection = [this]() { + if (m_mainButtons.m_arpSettings.getToggleState()) { + currentTab = Tabs::ArpSettings; + } + else if (m_mainButtons.m_effects.getToggleState()) { + currentTab = Tabs::Effects; + } + else if (m_mainButtons.m_lfoMatrix.getToggleState()) { + currentTab = Tabs::LfoMatrix; + } + else if (m_mainButtons.m_oscFilter.getToggleState()) { + currentTab = Tabs::OscFilter; + } + else if (m_mainButtons.m_patches.getToggleState()) { + currentTab = Tabs::Patches; + } + m_arpEditor->setVisible(m_mainButtons.m_arpSettings.getToggleState()); + m_fxEditor->setVisible(m_mainButtons.m_effects.getToggleState()); + m_lfoEditor->setVisible(m_mainButtons.m_lfoMatrix.getToggleState()); + m_oscEditor->setVisible(m_mainButtons.m_oscFilter.getToggleState()); + m_patchBrowser->setVisible(m_mainButtons.m_patches.getToggleState()); + }; + + uint8_t index = 0; + applyToSections([this, index](Component *s) mutable + { + if (currentTab == index) + { + s->setVisible(true); + } + index++; + }); + + index = 0; + m_mainButtons.applyToMainButtons([this, &index](DrawableButton *s) mutable + { + if (currentTab == index) + { + s->setToggleState(true, juce::dontSendNotification); + } + index++; + }); + + //Draw Main Menu + ShowMainMenue(); + + //MainDisplay (Patchname) + m_patchName.setBounds(1473, 35, 480, 58); + m_patchName.setJustificationType(Justification::left); + m_patchName.setFont(juce::Font("Register", "Normal", 30.f)); + m_patchName.setEditable(false, true, true); + addAndMakeVisible(m_patchName); + + //MainDisplay + m_controlLabel.setBounds(1473, 35, 650, 58); + m_controlLabel.setFont(juce::Font("Register", "Normal", 30.f)); + addAndMakeVisible(m_controlLabel); + + //ToolTip + //m_ToolTip.setLookAndFeel(&m_landfToolTip); + m_ToolTip.setBounds(200, 50, 200, 200); + m_ToolTip.setFont(juce::Font("Register", "Normal", 15.f)); + m_ToolTip.setColour(juce::Label::ColourIds::backgroundColourId, juce::Colours::black); + m_ToolTip.setColour(juce::Label::ColourIds::textColourId, juce::Colours::white); + m_ToolTip.setJustificationType(Justification::centred); + m_ToolTip.setAlpha(0.90); + addAndMakeVisible(m_ToolTip); + m_ToolTip.setVisible(false); + + //MainDisplay Value + m_controlLabelValue.setBounds(1900, 35, 197, 58); + m_controlLabelValue.setJustificationType(Justification::centredRight); + //m_controlLabelValue.setColour(juce::Label::textColourId, juce::Colours::red); + m_controlLabelValue.setFont(juce::Font("Register", "Normal", 30.f)); + addAndMakeVisible(m_controlLabelValue); + + //PresetsSwitch + addAndMakeVisible(m_PresetLeft); + addAndMakeVisible(m_PresetRight); + m_PresetLeft.setBounds(2166 - m_PresetLeft.kWidth / 2, 62 - m_PresetLeft.kHeight / 2, m_PresetLeft.kWidth, m_PresetLeft.kHeight); + m_PresetRight.setBounds(2199 - m_PresetRight.kWidth / 2, 62 - m_PresetRight.kHeight / 2, m_PresetRight.kWidth, m_PresetRight.kHeight); + + m_PresetLeft.onClick = [this]() { + + postCommandMessage(VirusEditor::Commands::PrevPatch); + postCommandMessage(VirusEditor::Commands::UpdateParts); + }; + m_PresetRight.onClick = [this]() + { + postCommandMessage(VirusEditor::Commands::NextPatch); + postCommandMessage(VirusEditor::Commands::UpdateParts); + }; + + //Show Version + m_version.setText(std::string(g_pluginVersionString), NotificationType::dontSendNotification); + m_version.setBounds(250, 1123, 50, 17); + m_version.setColour(juce::Label::textColourId, juce::Colours::silver); + m_version.setFont(juce::Font("Arial", "Bold", 20.f)); + m_version.setJustificationType(Justification::left); + m_version.setJustificationType(Justification::centred); + addAndMakeVisible(m_version); + + //Show Synth Model + m_SynthModel.setText(m_controller.getVirusModel() == virusLib::VirusModel::B ? "B" : "C", NotificationType::dontSendNotification); + m_SynthModel.setBounds(430, 1123, 50, 17); + m_SynthModel.setFont(juce::Font("Arial", "Bold", 20.f)); + m_SynthModel.setJustificationType(Justification::left); + m_SynthModel.setColour(juce::Label::textColourId, juce::Colours::silver); + m_SynthModel.setJustificationType(Justification::centred); + addAndMakeVisible(m_SynthModel); + + //Show RomName + m_RomName.setText(_processorRef.getRomName()+".bin", NotificationType::dontSendNotification); + m_RomName.setBounds(642, 1123, 150, 17); + m_RomName.setColour(juce::Label::textColourId, juce::Colours::silver); + m_RomName.setFont(juce::Font("Arial", "Bold", 20.f)); + m_RomName.setJustificationType(Justification::left); + m_RomName.setJustificationType(Justification::centred); + addAndMakeVisible(m_RomName); + + //Hyperlink + m_hyperLink.setBounds(900, 1115, 400, 35); + m_hyperLink.setColour(juce::Label::textColourId, juce::Colours::silver); + m_hyperLink.setFont(juce::Font("Arial", "Bold", 20.f), true, dontSendNotification); + m_hyperLink.setJustificationType(Justification::left); + m_hyperLink.setJustificationType(Justification::centred); + addAndMakeVisible(m_hyperLink); + + + m_controller.onProgramChange = [this]() + { + updateParts(); + m_arpEditor->refreshParts(); + }; + m_controller.onMsgDone = [this]() + { + m_controller.onMsgDone = nullptr; + postCommandMessage(VirusEditor::Commands::InitPatches); + postCommandMessage(VirusEditor::Commands::SelectFirstPatch); + postCommandMessage(VirusEditor::Commands::UpdateParts); + }; + + m_controller.getBankCount(); + addMouseListener(this, true); + setSize (kPanelWidth, kPanelHeight); +} + +VirusEditor::~VirusEditor() +{ + m_controller.onProgramChange = nullptr; + m_mainMenu.onClick = nullptr; + selectorMenu.setLookAndFeel(nullptr); + SubSkinSizeSelector.setLookAndFeel(nullptr); + m_mainMenu.setLookAndFeel (nullptr); + selector.setLookAndFeel (nullptr); + setLookAndFeel(nullptr); +} + +bool VirusEditor::keyPressed(const KeyPress &k, Component *c) +{ + if( k.getKeyCode() == 65573) + { + postCommandMessage(VirusEditor::Commands::PrevPatch); + } + if( k.getKeyCode() == 65575) + { + postCommandMessage(VirusEditor::Commands::NextPatch); + } + //43 + + //45 - + return true; +} + +void VirusEditor::updateParts() +{ + const auto multiMode = m_controller.isMultiMode(); + for (auto pt = 0; pt < 16; pt++) + { + bool singlePartOrInMulti = pt == 0 || multiMode; + if (pt == m_controller.getCurrentPart()) + { + if (m_patchBrowser->GetIsFileMode()) + { + m_patchName.setText("["+juce::String(m_controller.getCurrentPart()+1) + +"][FILE] " + + juce::String(m_patchBrowser->GetTablePatchList()->getSelectedRow(0)+1)+": " + m_patchBrowser->GetLastPatchSelected(), dontSendNotification); + } + else + { + const auto patchName = m_controller.getCurrentPartPresetName(pt); + if(m_patchName.getText() != patchName) + { + String sZero; + m_patchName.setText("["+juce::String(m_controller.getCurrentPart()+1) + +"][" + m_patchBrowser->GetSelectBankNum() + "] " + + juce::String(processorRef.getController().getCurrentPartProgram(m_controller.getCurrentPart())+1)+": " + patchName, dontSendNotification); + } + } + } + } +} + + +void VirusEditor::ShowMainMenue() +{ + m_mainMenu.setLookAndFeel (&m_landfButtons); + + m_mainMenu.onClick = [this]() + { + selectorMenu.setLookAndFeel(&m_landfButtons); + selectorMenu.clear(); + SubSkinSizeSelector.setLookAndFeel(&m_landfButtons); + SubSkinSizeSelector.clear(); + + for (float d = 375; d < 1250; d = d + 125) + { + SubSkinSizeSelector.addItem(std::to_string(200 * int(d) / 1000) + "%", [this, d] + { + double dScaleFactor = float(d / 1000.0); + m_AudioPlugInEditor->setScaleFactor(dScaleFactor); + (*this).setSize(iSkinSizeWidth, iSkinSizeHeight); + m_properties->setValue("skin_scale_factor", juce::String(dScaleFactor)); + m_properties->save(); + }); + } + + selectorMenu.addSubMenu("Skin size", SubSkinSizeSelector, true); + selectorMenu.addItem("About", [this]() { AboutWindow(); }); + selectorMenu.showMenu(juce::PopupMenu::Options()); + }; + + //draw Main Menu Button + m_mainMenu.setBounds(2301 - m_mainMenu.kWidth / 2, 62 - m_mainMenu.kHeight / 2, m_mainMenu.kWidth, m_mainMenu.kHeight); + addAndMakeVisible(m_mainMenu); +} + + +void VirusEditor::applyToSections(std::function<void(Component *)> action) +{ + for (auto *section : {static_cast<Component *>(m_arpEditor.get()), static_cast<Component *>(m_fxEditor.get()), + static_cast<Component *>(m_lfoEditor.get()), static_cast<Component *>(m_oscEditor.get()), + static_cast<Component *>(m_patchBrowser.get())}) + { + action(section); + } +} + +void VirusEditor::MainButtons::applyToMainButtons(std::function<void(DrawableButton *)> action) +{ + for (auto *section : {static_cast<juce::DrawableButton *>(&m_arpSettings), static_cast<DrawableButton *>(&m_effects), + static_cast<DrawableButton *>(&m_lfoMatrix), static_cast<DrawableButton *>(&m_oscFilter), + static_cast<DrawableButton *>(&m_patches)}) + { + action(section); + } +} + + +void VirusEditor::updateControlLabel(Component* eventComponent) +{ + auto props = eventComponent->getProperties(); + if(props.contains("type") && props["type"] == "slider") { + + auto comp = dynamic_cast<juce::Slider*>(eventComponent); + if(comp && comp->isEnabled()) + { + int iValue = (props.contains("bipolar") && props["bipolar"])?juce::roundToInt(comp->getValue())-64:juce::roundToInt(comp->getValue()); + + //ToolTip handle + m_ToolTip.setVisible(true); + m_ToolTip.setText(std::to_string(iValue), juce::dontSendNotification); + //todo --> ugly --> Need to be fixed + m_ToolTip.setBounds(comp->getX()+comp->getWidth()*2-43, comp->getY()+comp->getHeight()*2+83, 70,30); + m_ToolTip.toFront(true); + + //Main display handle + m_controlLabel.setVisible(true); + m_controlLabelValue.setVisible(true); + m_patchName.setVisible(false); + + if(m_paramDisplayLocal) + { + m_controlLabel.setColour(juce::Label::ColourIds::backgroundColourId, juce::Colours::black); + m_controlLabel.setColour(juce::Label::ColourIds::outlineColourId, juce::Colours::white); + } + + m_controlLabel.setText(props["name"].toString(), juce::dontSendNotification); + m_controlLabelValue.setText(juce::String(iValue), juce::dontSendNotification); + } + } + +} +void VirusEditor::mouseDrag(const juce::MouseEvent & event) +{ + updateControlLabel(event.eventComponent); +} + +void VirusEditor::mouseEnter(const juce::MouseEvent& event) +{ + if (event.mouseWasDraggedSinceMouseDown()) { + return; + } + updateControlLabel(event.eventComponent); +} + +void VirusEditor::mouseExit(const juce::MouseEvent& event) +{ + if (event.mouseWasDraggedSinceMouseDown()) { + return; + } + m_controlLabel.setText("", juce::dontSendNotification); + m_controlLabel.setVisible(false); + m_controlLabelValue.setVisible(false); + m_patchName.setVisible(true); + m_ToolTip.setVisible(false); +} + +void VirusEditor::mouseDown(const juce::MouseEvent &event) +{ +} + +void VirusEditor::mouseUp(const juce::MouseEvent & event) +{ + m_controlLabel.setText("", juce::dontSendNotification); + m_controlLabel.setVisible(false); + m_controlLabelValue.setVisible(false); + m_patchName.setVisible(true); + m_ToolTip.setVisible(false); +} +void VirusEditor::mouseWheelMove(const juce::MouseEvent& event, const juce::MouseWheelDetails& wheel) +{ + updateControlLabel(event.eventComponent); +} + + +void VirusEditor::resized() +{ + m_background->setBounds (getLocalBounds()); + + //Position of first main button (osc/filt) + m_mainButtons.setBounds(121, 36, m_mainButtons.getWidth(), m_mainButtons.getHeight()); + + // Panel Positions + applyToSections([this](Component *s) { s->setTopLeftPosition(101, 120); }); +} + +void VirusEditor::handleCommandMessage(int commandId) +{ + switch (commandId) { + case Commands::Rebind: recreateControls(); + case Commands::UpdateParts: + { + updateParts(); + m_arpEditor->refreshParts(); + }; break; + case Commands::InitPatches: + { + m_patchBrowser->IntiPatches(); + }; break; + case Commands::PrevPatch: + { + if (m_patchBrowser->GetTablePatchList()->getSelectedRow(0)>0) + { + m_patchBrowser->GetTablePatchList()->selectRow(m_patchBrowser->GetTablePatchList()->getSelectedRow(0)-1,false,false); + } + };break; + case Commands::NextPatch: + { + if (m_patchBrowser->GetTablePatchList()->getSelectedRow(0)<m_patchBrowser->GetTablePatchList()->getNumRows()-1) + { + m_patchBrowser->GetTablePatchList()->selectRow(m_patchBrowser->GetTablePatchList()->getSelectedRow(0)+1,false,false); + } + };break; + case Commands::SelectFirstPatch: + { + m_patchBrowser->GetTablePatchList()->selectRow(0,false,false); + };break; + + default: return; + } +} + +void VirusEditor::recreateControls() +{ + removeChildComponent(m_oscEditor.get()); + removeChildComponent(m_lfoEditor.get()); + removeChildComponent(m_fxEditor.get()); + removeChildComponent(m_arpEditor.get()); + // removeChildComponent(m_patchBrowser.get()); + + m_oscEditor = std::make_unique<OscEditor>(m_parameterBinding); + addChildComponent(m_oscEditor.get()); + + m_lfoEditor = std::make_unique<LfoEditor>(m_parameterBinding); + addChildComponent(m_lfoEditor.get()); + + m_fxEditor = std::make_unique<FxEditor>(m_parameterBinding, processorRef); + addChildComponent(m_fxEditor.get()); + + m_arpEditor = std::make_unique<ArpEditor>(m_parameterBinding, processorRef); + addChildComponent(m_arpEditor.get()); + + //m_patchBrowser = std::make_unique<PatchBrowser>(m_parameterBinding, processorRef); + //addChildComponent(m_patchBrowser.get()); + + m_mainButtons.updateSection(); + resized(); +} + +VirusEditor::MainButtons::MainButtons() +: m_oscFilter ("OSC|FILTER", DrawableButton::ImageRaw) +, m_lfoMatrix ("LFO|MATRIX", DrawableButton::ImageRaw) +, m_effects ("EFFECTS", DrawableButton::ImageRaw) +, m_arpSettings ("ARP|SETTINGS", DrawableButton::ImageRaw) +, m_patches("PATCHES", DrawableButton::ImageRaw) +{ + constexpr auto numOfMainButtons = 5; + setupButton(0, Drawable::createFromImageData(BinaryData::btn_main_1_png, BinaryData::btn_main_1_pngSize), m_oscFilter); + setupButton(1, Drawable::createFromImageData(BinaryData::btn_main_2_png, BinaryData::btn_main_2_pngSize), m_lfoMatrix); + setupButton(2, Drawable::createFromImageData(BinaryData::btn_main_3_png, BinaryData::btn_main_3_pngSize), m_effects); + setupButton(3, Drawable::createFromImageData(BinaryData::btn_main_4_png, BinaryData::btn_main_4_pngSize), m_arpSettings); + setupButton(4, Drawable::createFromImageData(BinaryData::btn_main_5_png, BinaryData::btn_main_5_pngSize), m_patches); + setSize ((kButtonWidth + kMargin) * numOfMainButtons, kButtonHeight); +} + + +void VirusEditor::MainButtons::valueChanged(juce::Value &) { updateSection(); } + +void VirusEditor::MainButtons::setupButton (int i, std::unique_ptr<Drawable>&& btnImage, juce::DrawableButton& btn) +{ + auto onImage = btnImage->createCopy(); + onImage->setOriginWithOriginalSize({0, -kButtonHeight}); + btn.setClickingTogglesState (true); + btn.setRadioGroupId (kGroupId); + btn.setImages (btnImage.get(), nullptr, nullptr, nullptr, onImage.get()); + btn.setBounds ((i > 1 ? -1 : 0) + i * (kButtonWidth + kMargin)-5, 0, kButtonWidth, kButtonHeight); + btn.getToggleStateValue().addListener(this); + addAndMakeVisible (btn); +} + + +void VirusEditor::AboutWindow() {} + diff --git a/source/jucePlugin/ui2/VirusEditor.h b/source/jucePlugin/ui2/VirusEditor.h @@ -0,0 +1,118 @@ +#pragma once + +#include <juce_gui_extra/juce_gui_extra.h> +#include <juce_audio_devices/juce_audio_devices.h> +#include "Virus_Buttons.h" +#include "Virus_LookAndFeel.h" +#include "../VirusController.h" +#include "Virus_LookAndFeel.h" + +class VirusParameterBinding; +class OscEditor; +class LfoEditor; +class FxEditor; +class ArpEditor; +class PatchBrowser; +class AudioProcessorEditor; + +class VirusEditor : public juce::Component, private KeyListener +{ +public: + VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef); + ~VirusEditor() override; + void resized() override; + void recreateControls(); + void updatePartsPresetNames(); + void ShowMenuePatchList(); + void ShowMainMenue(); + void updateParts(); + bool keyPressed(const KeyPress &k, Component *c) override; + + enum Commands { + None, + Rebind = 0x100, + UpdateParts = 0x101, + InitPatches = 0x102, + NextPatch = 0x103, + PrevPatch = 0x104, + SelectFirstPatch = 0x105 + }; + + const int iSkinSizeWidth = 2501, iSkinSizeHeight = 1152; + juce::AudioProcessorEditor *m_AudioPlugInEditor; + +private: + void handleCommandMessage(int commandId) override; + + juce::Label m_version; + juce::Label m_SynthModel; + HyperlinkButton m_hyperLink { "donate: paypal.me/dsp56300", { "http://www.paypal.me/dsp56300" } }; + juce::Label m_RomName; + juce::Label m_patchName; + juce::Label m_controlLabel; + juce::Label m_controlLabelValue; + juce::Label m_ToolTip; + + Buttons::ButtonMenu m_mainMenu; + juce::PopupMenu m_mainMenuSubSkinsize; + juce::PopupMenu selector; + juce::PopupMenu selectorMenu; + juce::PopupMenu SubSkinSizeSelector; + + struct MainButtons : juce::Component, juce::Value::Listener + { + MainButtons(); + void setupButton (int i, std::unique_ptr<juce::Drawable>&& btn, juce::DrawableButton&); + void valueChanged(juce::Value &) override; + + std::function<void()> updateSection; + juce::DrawableButton m_oscFilter, m_lfoMatrix, m_effects, m_arpSettings, m_patches; + static constexpr auto kMargin = 0; + static constexpr auto kButtonWidth = 268; + static constexpr auto kButtonHeight = 106/2; + static constexpr auto kGroupId = 0x3FBBA; + void applyToMainButtons(std::function<void(juce::DrawableButton *)>); + } m_mainButtons; + + Buttons::PresetButtonLeft m_PresetLeft; + Buttons::PresetButtonRight m_PresetRight; + //Buttons::PresetButtonDown m_PresetPatchList; + + struct PartButtons : juce::Component { + PartButtons(); + }; + void applyToSections(std::function<void(juce::Component *)>); + void AboutWindow(); + void InitPropertiesFile(); + + //PropertiesFile + juce::PropertiesFile *m_properties; + + VirusParameterBinding& m_parameterBinding; + AudioPluginAudioProcessor &processorRef; + + + Virus::Controller& m_controller; + std::unique_ptr<OscEditor> m_oscEditor; + std::unique_ptr<LfoEditor> m_lfoEditor; + std::unique_ptr<FxEditor> m_fxEditor; + std::unique_ptr<ArpEditor> m_arpEditor; + std::unique_ptr<PatchBrowser> m_patchBrowser; + std::unique_ptr<juce::Drawable> m_background; + + Virus::LookAndFeel m_lookAndFeel; + Virus::LookAndFeelButtons m_landfButtons; + Virus::CustomLAF m_landfToolTip; + + juce::String m_previousPath; + bool m_paramDisplayLocal = false; + + void updateControlLabel(Component *eventComponent); + void mouseEnter (const juce::MouseEvent &event) override; + void mouseExit (const juce::MouseEvent &event) override; + void mouseDrag (const juce::MouseEvent &event) override; + void mouseDown (const juce::MouseEvent &event) override; + void mouseUp (const juce::MouseEvent &event) override; + void mouseWheelMove (const juce::MouseEvent &event, const juce::MouseWheelDetails &wheel) override; + +}; diff --git a/source/jucePlugin/ui2/Virus_Buttons.cpp b/source/jucePlugin/ui2/Virus_Buttons.cpp @@ -0,0 +1,117 @@ +#include "Virus_Buttons.h" +#include "BinaryData.h" +#include "Virus_LookAndFeel.h" + +using namespace juce; + +namespace Buttons +{ + + Buttons::Button1::Button1() : DrawableButton("Button1", DrawableButton::ImageRaw) + { + auto off = Drawable::createFromImageData(BinaryData::btn_1_png, BinaryData::btn_1_pngSize); + auto on = off->createCopy(); + on->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setClickingTogglesState(true); + on->setOriginWithOriginalSize({0, -kHeight}); + setImages(off.get(), nullptr, on.get(), nullptr, on.get()); + } + + Buttons::Button2::Button2() : DrawableButton("Button2", DrawableButton::ImageRaw) + { + auto off = Drawable::createFromImageData(BinaryData::btn_2_png, BinaryData::btn_2_pngSize); + auto on = off->createCopy(); + on->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setClickingTogglesState(true); + on->setOriginWithOriginalSize({0, -kHeight}); + setImages(off.get(), nullptr, on.get(), nullptr, on.get()); + } + + Buttons::Button3::Button3() : DrawableButton("Button3", DrawableButton::ImageRaw) + { + auto off = Drawable::createFromImageData(BinaryData::btn_3_png, BinaryData::btn_3_pngSize); + auto on = off->createCopy(); + on->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setClickingTogglesState(true); + on->setOriginWithOriginalSize({0, -kHeight}); + setImages(off.get(), nullptr, on.get(), nullptr, on.get()); + } + + Buttons::Button4::Button4() : DrawableButton("Button4", DrawableButton::ImageRaw) + { + auto off = Drawable::createFromImageData(BinaryData::btn_4_png, BinaryData::btn_4_pngSize); + auto on = off->createCopy(); + on->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setClickingTogglesState(true); + on->setOriginWithOriginalSize({0, -kHeight}); + setImages(off.get(), nullptr, on.get(), nullptr, on.get()); + } + + Buttons::ButtonMenu::ButtonMenu() : DrawableButton("ButtonMenu", DrawableButton::ImageRaw) + { + auto normal = Drawable::createFromImageData(BinaryData::btn_menu_png, BinaryData::btn_menu_pngSize); + auto pressed = normal->createCopy(); + pressed->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setImages(normal.get(), nullptr, pressed.get(), nullptr, pressed.get(), nullptr, normal.get()); + } + + Buttons::PresetButtonLeft::PresetButtonLeft() : DrawableButton("PresetButtonLeft", DrawableButton::ImageRaw) + { + auto normal = Drawable::createFromImageData(BinaryData::btn_left_png, BinaryData::btn_left_pngSize); + auto pressed = normal->createCopy(); + pressed->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setImages(normal.get(), nullptr, pressed.get(), nullptr, pressed.get(), nullptr, normal.get()); + } + + Buttons::PresetButtonRight::PresetButtonRight() : DrawableButton("PresetButtonLeft", DrawableButton::ImageRaw) + { + auto normal = Drawable::createFromImageData(BinaryData::btn_right_png, BinaryData::btn_right_pngSize); + auto pressed = normal->createCopy(); + pressed->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setImages(normal.get(), nullptr, pressed.get(), nullptr, pressed.get(), nullptr, normal.get()); + } + + Buttons::PresetButtonDown::PresetButtonDown() : DrawableButton("PresetButtonDown", DrawableButton::ImageRaw) + { + auto normal = Drawable::createFromImageData(BinaryData::btn_down_png, BinaryData::btn_down_pngSize); + auto pressed = normal->createCopy(); + pressed->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setImages(normal.get(), nullptr, pressed.get(), nullptr, pressed.get(), nullptr, normal.get()); + } + + Buttons::OptionButtonLoadBank::OptionButtonLoadBank() : DrawableButton("LoadBank", DrawableButton::ImageRaw) + { + auto normal = Drawable::createFromImageData(BinaryData::btn_load_bank_png, BinaryData::btn_load_bank_pngSize); + auto pressed = normal->createCopy(); + pressed->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setImages(normal.get(), nullptr, pressed.get(), nullptr, pressed.get(), nullptr, normal.get()); + } + + Buttons::OptionButtonSavePreset::OptionButtonSavePreset() : DrawableButton("LoadBank", DrawableButton::ImageRaw) + { + auto normal = Drawable::createFromImageData(BinaryData::btn_save_preset_png, BinaryData::btn_save_preset_pngSize); + auto pressed = normal->createCopy(); + pressed->setOriginWithOriginalSize({0, 2}); + setColour(DrawableButton::ColourIds::backgroundColourId, Colours::transparentBlack); + setColour(DrawableButton::ColourIds::backgroundOnColourId, Colours::transparentBlack); + setImages(normal.get(), nullptr, pressed.get(), nullptr, pressed.get(), nullptr, normal.get()); + } +}; // namespace Buttons diff --git a/source/jucePlugin/ui2/Virus_Buttons.h b/source/jucePlugin/ui2/Virus_Buttons.h @@ -0,0 +1,87 @@ +#pragma once + +#include <juce_gui_extra/juce_gui_extra.h> + +namespace Buttons +{ + class Button1 : public juce::DrawableButton + { + public: + Button1(); + static constexpr auto kWidth = 36; + static constexpr auto kHeight = 136/2; + }; + + class Button2 : public juce::DrawableButton + { + public: + Button2(); + static constexpr auto kWidth = 69; + static constexpr auto kHeight = 134/2; + }; + + class Button3 : public juce::DrawableButton + { + public: + Button3(); + static constexpr auto kWidth = 69; + static constexpr auto kHeight = 134/2; + }; + + class Button4 : public juce::DrawableButton + { + public: + Button4(); + static constexpr auto kWidth = 68; + static constexpr auto kHeight = 72/2; + }; + + class ButtonMenu: public juce::DrawableButton + { + public: + static constexpr auto kWidth = 172; + static constexpr auto kHeight = 51; + ButtonMenu(); + }; + + class PresetButtonLeft: public juce::DrawableButton + { + public: + static constexpr auto kWidth = 33; + static constexpr auto kHeight = 51; + PresetButtonLeft(); + }; + + class PresetButtonRight: public juce::DrawableButton + { + public: + static constexpr auto kWidth = 33; + static constexpr auto kHeight = 51; + PresetButtonRight(); + }; + + class PresetButtonDown: public juce::DrawableButton + { + public: + static constexpr auto kWidth = 33; + static constexpr auto kHeight = 51; + PresetButtonDown(); + }; + + class OptionButtonLoadBank: public juce::DrawableButton + { + public: + static constexpr auto kWidth = 172; + static constexpr auto kHeight = 50; + OptionButtonLoadBank(); + }; + + class OptionButtonSavePreset: public juce::DrawableButton + { + public: + static constexpr auto kWidth = 172; + static constexpr auto kHeight = 50; + OptionButtonSavePreset(); + }; + +} // namespace Buttons diff --git a/source/jucePlugin/ui2/Virus_LookAndFeel.cpp b/source/jucePlugin/ui2/Virus_LookAndFeel.cpp @@ -0,0 +1,82 @@ +#include "Virus_LookAndFeel.h" +#include "BinaryData.h" + +using namespace juce; + +namespace Virus +{ + const juce::Font getCustomFont() + { + static auto typefaceDigiFont = juce::Typeface::createSystemTypefaceFor(BinaryData::Digital, BinaryData::DigitalSize); + return juce::Font (typefaceDigiFont); + } + + //LookAndFeel + LookAndFeelSmallButton::LookAndFeelSmallButton() + { + // setup knobs... + m_genKnob = Drawable::createFromImageData(BinaryData::knob_2_128_png, BinaryData::knob_2_128_pngSize); + + m_knobImageSteps.start = 1; + m_knobImageSteps.end = 127; + } + + void LookAndFeelSmallButton::drawRotarySlider(Graphics &g, int x, int y, int width, int height, float sliderPosProportional, + float rotaryStartAngle, float rotaryEndAngle, Slider &s) + { + Drawable *knob = m_genKnob.get(); + + int step; + (s.isEnabled())?step=roundToInt(m_knobImageSteps.convertFrom0to1(sliderPosProportional)):step=0; + + knob->setOriginWithOriginalSize({0.0f, -float(kKnobSize) * step}); + + // this won't support scaling! + knob->drawAt(g, x, y, 1.0f); + } + + LookAndFeel::LookAndFeel() + { + // setup knobs... + m_genKnob = Drawable::createFromImageData(BinaryData::knob_1_128_png, BinaryData::knob_1_128_pngSize); + + m_knobImageSteps.start = 1; + m_knobImageSteps.end = 127; + } + + void LookAndFeel::drawRotarySlider(Graphics &g, int x, int y, int width, int height, float sliderPosProportional, + float rotaryStartAngle, float rotaryEndAngle, Slider &s) + { + Drawable *knob = m_genKnob.get(); + + int step; + (s.isEnabled())?step=roundToInt(m_knobImageSteps.convertFrom0to1(sliderPosProportional)):step=0; + + knob->setOriginWithOriginalSize({0.0f, -float(kKnobSize) * step}); + + // this won't support scaling! + knob->drawAt(g, x, y, 1.0f); + } + + void LookAndFeel::drawComboBox (juce::Graphics& gGrafik, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox& box) + { + //gGrafik.setOpacity(0); + //box.setColour(juce::ComboBox::textColourId, juce::Colours::red); + }; + + //LookAndFeelPatchBrowser + LookAndFeelPatchBrowser::LookAndFeelPatchBrowser() + { + } + + //LookAndFeelButtons + LookAndFeelButtons::LookAndFeelButtons() + { + } + + void LookAndFeelButtons::drawComboBox (juce::Graphics& gGrafik, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox& box) + { + gGrafik.setOpacity(0); + }; + +} // namespace Virus diff --git a/source/jucePlugin/ui2/Virus_LookAndFeel.h b/source/jucePlugin/ui2/Virus_LookAndFeel.h @@ -0,0 +1,155 @@ +#pragma once + +#include <juce_gui_extra/juce_gui_extra.h> +#include "BinaryData.h" + +constexpr auto comboBoxHeight = 38; +constexpr auto comboBoxWidth = 126; + +constexpr auto comboBox2Height = 52; +constexpr auto comboBox2Width = 74; + +constexpr auto comboBox3Height = 52; +constexpr auto comboBox3Width = 186; + +constexpr auto kPanelWidth = 2501; +constexpr auto kPanelHeight = 1152; + +constexpr auto comboBoxXMargin = 5; + +using namespace juce; + +namespace Virus +{ + const juce::Font getCustomFont(); + + class CustomLAF : public juce::LookAndFeel_V4 + { + public: + void drawLabel(Graphics& g, Label& l) override + { + // g.fillAll (label.findColour (Label::backgroundColourId)); + g.setColour (l.findColour (Label::backgroundColourId)); + g.fillRoundedRectangle (l.getLocalBounds().toFloat(), 5); + + // g.drawRect (label.getLocalBounds()); + g.drawRoundedRectangle (l.getLocalBounds().toFloat(), 10, 1); + } + juce::Font getLabelFont(Label& label) override + { + Font fFont(getCustomFont().getTypeface()); + fFont.setHeight(20.f); + return fFont; + } + }; + + class LookAndFeelSmallButton : public juce::LookAndFeel_V4/*, juce::Slider*/ + { + public: + LookAndFeelSmallButton(); + + static constexpr auto kKnobSize = 55; + static const char *KnobStyleProp; + + void drawRotarySlider(juce::Graphics &, int x, int y, int width, int height, float sliderPosProportional, + float rotaryStartAngle, float rotaryEndAngle, juce::Slider &sSlider) override; + + + private: + std::unique_ptr<juce::Drawable> m_genKnob; + juce::NormalisableRange<float> m_knobImageSteps; + }; + + + class LookAndFeel : public juce::LookAndFeel_V4/*, juce::Slider*/ + { + public: + LookAndFeel(); + + static constexpr auto kKnobSize = 75; + static const char *KnobStyleProp; + + void drawRotarySlider(juce::Graphics &, int x, int y, int width, int height, float sliderPosProportional, + float rotaryStartAngle, float rotaryEndAngle, juce::Slider &sSlider) override; + + + Typeface::Ptr getTypefaceForFont (const juce::Font& f) override + { + return getCustomFont().getTypeface(); + } + + void drawComboBox (juce::Graphics& gGrafik, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox& box) override; + + juce::Font getComboBoxFont (juce::ComboBox& ) override + { + Font fFont(getCustomFont().getTypeface()); + fFont.setHeight(20.f); + return fFont; + } + + juce::Font getTextButtonFont(TextButton& button, int buttonHeight) override + { + Font fFont(getCustomFont().getTypeface()); + fFont.setHeight(20.f); + return fFont; + } + + juce::Font getPopupMenuFont() override + { + Font fFont(getCustomFont().getTypeface()); + fFont.setHeight(20.f); + return fFont; + } + + private: + std::unique_ptr<juce::Drawable> m_genKnob; + juce::NormalisableRange<float> m_knobImageSteps; + }; + + class LookAndFeelPatchBrowser : public juce::LookAndFeel_V4 + { + public: + LookAndFeelPatchBrowser(); + + juce::Font getComboBoxFont (juce::ComboBox& /*box*/) override + { + return getCommonMenuFont(); + } + + juce::Font getPopupMenuFont() override + { + return getCommonMenuFont(); + } + + private: + juce::Font getCommonMenuFont() + { + return juce::Font("Arial", "Normal", 13.f); + } + }; + + class LookAndFeelButtons : public juce::LookAndFeel_V4 + { + public: + LookAndFeelButtons(); + + juce::Font getComboBoxFont (juce::ComboBox& /*box*/) override + { + return getCommonMenuFont(); + } + + juce::Font getPopupMenuFont() override + { + return getCommonMenuFont(); + } + + void drawComboBox (juce::Graphics& gGrafik, int width, int height, bool isButtonDown, int buttonX, int buttonY, int buttonW, int buttonH, juce::ComboBox& box) override; + + private: + juce::Font getCommonMenuFont() + { + return juce::Font("Arial", "Normal", 23.f); + } + }; + +} // namespace Virus diff --git a/source/jucePlugin/ui2/Virus_Panel1_OscEditor.cpp b/source/jucePlugin/ui2/Virus_Panel1_OscEditor.cpp @@ -0,0 +1,272 @@ +#include "Virus_Panel1_OscEditor.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "../VirusParameterBinding.h" + +using namespace juce; + +OscEditor::OscEditor(VirusParameterBinding& _parameterBinding) +{ + setupBackground(*this, m_background, BinaryData::panel_1_png, BinaryData::panel_1_pngSize); + setBounds(m_background->getDrawableBounds().toNearestIntEdges()); + + //setLookAndFeel(&m_lookAndFeel); + + for (auto *s : {&m_osc1Shape, &m_osc1PulseWidth, &m_osc1Semitone, &m_osc1KeyFollow}) + { + setupRotary(*this, *s); + } + + //OSC 1 + m_osc1Shape.setBounds(287 - knobSize / 2, 103 - knobSize / 2, knobSize, knobSize); + m_osc1PulseWidth.setBounds(433 - knobSize / 2, 103 - knobSize / 2, knobSize, knobSize); + m_osc1Semitone.setBounds(577 - knobSize / 2, 103 - knobSize / 2, knobSize, knobSize); + m_osc1KeyFollow.setBounds(722 - knobSize / 2, 103 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_osc1WaveSelect); + m_osc1WaveSelect.setBounds(114+comboBoxXMargin - comboBoxWidth / 2, 103 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + + _parameterBinding.bind(m_osc1Semitone, Virus::Param_Osc1Semitone); + _parameterBinding.bind(m_osc1Shape, Virus::Param_Osc1Shape); + _parameterBinding.bind(m_osc1PulseWidth, Virus::Param_Osc1PW ); + _parameterBinding.bind(m_osc1KeyFollow, Virus::Param_Osc1Keyfollow ); + _parameterBinding.bind(m_osc1WaveSelect, Virus::Param_Osc1Wave); + + //OSC 2 + for (auto *s : {&m_osc2Shape, &m_osc2PulseWidth, &m_osc2Semitone, &m_osc2KeyFollow}) + { + setupRotary(*this, *s); + } + + m_osc2Shape.setBounds(287 - knobSize / 2, 311 - knobSize / 2, knobSize, knobSize); + m_osc2PulseWidth.setBounds(433 - knobSize / 2, 311 - knobSize / 2, knobSize, knobSize); + m_osc2Semitone.setBounds(577 - knobSize / 2, 311 - knobSize / 2, knobSize, knobSize); + m_osc2KeyFollow.setBounds(722 - knobSize / 2, 311 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_osc2WaveSelect); + m_osc2WaveSelect.setBounds(114+comboBoxXMargin - comboBoxWidth / 2, 311 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + + for (auto *s : {&m_osc2FmAmount, &m_osc2Detune, &m_osc2EnvFm, &m_osc2envOSC, &m_osc2PhaseInit}) + { + setupRotary(*this, *s); + } + + m_osc2FmAmount.setBounds(287 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_osc2Detune.setBounds(433 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_osc2EnvFm.setBounds(577 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_osc2envOSC.setBounds(722 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_osc2PhaseInit.setBounds(871 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_osc2FmMode); + m_osc2FmMode.setBounds(114+comboBoxXMargin - comboBoxWidth / 2, 485 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + + addAndMakeVisible(m_syncOsc1Osc2); + m_syncOsc1Osc2.setBounds(871 - m_syncOsc1Osc2.kWidth/ 2, 308 - m_syncOsc1Osc2.kHeight / 2, m_syncOsc1Osc2.kWidth, m_syncOsc1Osc2.kHeight); + + _parameterBinding.bind(m_osc2Semitone, Virus::Param_Osc2Semitone); + _parameterBinding.bind(m_osc2Shape, Virus::Param_Osc2Shape); + _parameterBinding.bind(m_osc2PulseWidth, Virus::Param_Osc2PW); + _parameterBinding.bind(m_osc2KeyFollow, Virus::Param_Osc2Keyfollow); + _parameterBinding.bind(m_osc2WaveSelect, Virus::Param_Osc2Wave); + + _parameterBinding.bind(m_osc2FmAmount, Virus::Param_Osc2FMAmount); + _parameterBinding.bind(m_osc2Detune, Virus::Param_Osc2Detune); + _parameterBinding.bind(m_osc2EnvFm, Virus::Param_FMFiltEnvAmt); + _parameterBinding.bind(m_osc2envOSC, Virus::Param_Osc2FltEnvAmt); + _parameterBinding.bind(m_osc2PhaseInit, Virus::Param_OscInitPhase); + _parameterBinding.bind(m_osc2FmMode, Virus::Param_OscFMMode); + _parameterBinding.bind(m_syncOsc1Osc2, Virus::Param_Osc2Sync); + + //OSC 3 + for (auto *s : {&m_osc3Semitone, &m_osc3Detune, &m_osc3Level}) + { + setupRotary(*this, *s); + } + + m_osc3Semitone.setBounds(287 - knobSize / 2, 694 - knobSize / 2, knobSize, knobSize); + m_osc3Detune.setBounds(434 - knobSize / 2, 694 - knobSize / 2, knobSize, knobSize); + m_osc3Mode.setBounds(114+comboBoxXMargin - comboBoxWidth / 2, 694 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_osc3Mode); + + _parameterBinding.bind(m_osc3Semitone, Virus::Param_Osc3Semitone); + _parameterBinding.bind(m_osc3Detune, Virus::Param_Osc3Detune); + _parameterBinding.bind(m_osc3Mode, Virus::Param_Osc3Mode); + + // OSC SUB + setupRotary(*this, m_noiseColor); + m_noiseColor.setBounds(841 - knobSize / 2, 695 - knobSize / 2, knobSize, knobSize); + m_subWaveform.setBounds(656 - m_subWaveform.kWidth / 2, 692 - m_subWaveform.kHeight / 2, m_subWaveform.kWidth, m_subWaveform.kHeight); + addAndMakeVisible(m_subWaveform); + _parameterBinding.bind(m_noiseColor, Virus::Param_NoiseColor); + _parameterBinding.bind(m_subWaveform, Virus::Param_SubOscShape); + + //UNISON + for (auto *s : {&m_detune, &m_panSpread, &m_lfoPhase}) + { + setupRotary(*this, *s); + } + + m_detune.setBounds(287 - knobSize / 2, 903 - knobSize / 2, knobSize, knobSize); + m_panSpread.setBounds(434 - knobSize / 2, 903 - knobSize / 2, knobSize, knobSize); + m_lfoPhase.setBounds(577 - knobSize / 2, 903 - knobSize / 2, knobSize, knobSize); + addAndMakeVisible(m_unisonVoices); + m_unisonVoices.setBounds(114+comboBoxXMargin - comboBoxWidth / 2, 903 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + + _parameterBinding.bind(m_detune, Virus::Param_UnisonDetune); + _parameterBinding.bind(m_panSpread, Virus::Param_UnisonPanSpread); + _parameterBinding.bind(m_lfoPhase, Virus::Param_UnisonLfoPhase); + _parameterBinding.bind(m_unisonVoices, Virus::Param_UnisonMode); + + // Punch + setupRotary(*this, m_Punch); + m_Punch.setBounds(841 - knobSize / 2, 903 - knobSize / 2, knobSize, knobSize); + _parameterBinding.bind(m_Punch, Virus::Param_PunchIntensity); + + // Mixer + for (auto *s : {&m_oscBalance, &m_oscLevel, &m_osc3Level, &m_oscSublevel, &m_noiseLevel, &m_ringModLevel}) + { + setupRotary(*this, *s); + } + + m_oscBalance.setBounds(1260 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_oscLevel.setBounds(1260 - knobSize / 2, 263 - knobSize / 2, knobSize, knobSize); + m_osc3Level.setBounds(1260 - knobSize / 2, 422 - knobSize / 2, knobSize, knobSize); + m_oscSublevel.setBounds(1260 - knobSize / 2, 579 - knobSize / 2, knobSize, knobSize); + m_noiseLevel.setBounds(1260 - knobSize / 2, 744 - knobSize / 2, knobSize, knobSize); + m_ringModLevel.setBounds(1260 - knobSize / 2, 903 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_oscBalance, Virus::Param_OscBalance); + _parameterBinding.bind(m_oscLevel, Virus::Param_OscMainVolume); + _parameterBinding.bind(m_osc3Level, Virus::Param_Osc3Volume); + _parameterBinding.bind(m_oscSublevel, Virus::Param_SubOscVolume); + _parameterBinding.bind(m_noiseLevel, Virus::Param_NoiseVolume); + _parameterBinding.bind(m_ringModLevel, Virus::Param_RingModMVolume); + + //Velo mod + for (auto *s : {&m_osc1ShapeVelocity, &m_pulseWidthVelocity, &m_ampVelocity, &m_panoramaVelocity, &m_osc2ShapeVelocity, &m_fmAmountVelocity}) + { + setupRotary(*this, *s); + } + + m_osc1ShapeVelocity.setBounds(1067 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_osc2ShapeVelocity.setBounds(1067 - knobSize / 2, 264 - knobSize / 2, knobSize, knobSize); + m_pulseWidthVelocity.setBounds(1067 - knobSize / 2, 421 - knobSize / 2, knobSize, knobSize); + m_fmAmountVelocity.setBounds(1067 - knobSize / 2, 579 - knobSize / 2, knobSize, knobSize); + m_ampVelocity.setBounds(1067 - knobSize / 2, 743 - knobSize / 2, knobSize, knobSize); + m_panoramaVelocity.setBounds(1067 - knobSize / 2, 903 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_osc1ShapeVelocity, Virus::Param_Osc1ShapeVelocity); + _parameterBinding.bind(m_pulseWidthVelocity, Virus::Param_PulseWidthVelocity); + _parameterBinding.bind(m_ampVelocity, Virus::Param_AmpVelocity); + _parameterBinding.bind(m_panoramaVelocity, Virus::Param_PanoramaVelocity); + _parameterBinding.bind(m_osc2ShapeVelocity, Virus::Param_Osc2ShapeVelocity); + _parameterBinding.bind(m_fmAmountVelocity, Virus::Param_FmAmountVelocity); + + //Filters + for (auto *s : {&m_cutoff1, &m_res1, &m_envAmount1, &m_keyTrack1, &m_resVel1, &m_envVel1, &m_cutoff2, &m_res2, &m_envAmount2, &m_keyTrack2, &m_resVel2, &m_envVel2, &m_filterBalance}) + { + setupRotary(*this, *s); + } + + m_cutoff1.setBounds(1474 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_res1.setBounds(1618 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_envAmount1.setBounds(1762 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_keyTrack1.setBounds(1909 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_resVel1.setBounds(2053 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_envVel1.setBounds(2193 - knobSize / 2, 102 - knobSize / 2, knobSize, knobSize); + m_cutoff2.setBounds(1474 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_res2.setBounds(1618 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_envAmount2.setBounds(1762 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_keyTrack2.setBounds(1909 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_resVel2.setBounds(2053 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_envVel2.setBounds(2193 - knobSize / 2, 485 - knobSize / 2, knobSize, knobSize); + m_filterBalance.setBounds(1522 - knobSize / 2, 302 - knobSize / 2, knobSize, knobSize); + + m_filterMode1.setBounds(1710+comboBoxXMargin - comboBox3Width / 2, 239 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + m_filterMode2.setBounds(1710+comboBoxXMargin - comboBox3Width / 2, 353 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + m_filterRouting.setBounds(2017+comboBoxXMargin - comboBox3Width / 2, 242 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + m_saturationCurve.setBounds(2017+comboBoxXMargin - comboBox3Width / 2, 353 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + m_keyFollowBase.setBounds(1876+comboBoxXMargin - comboBox2Width / 2, 303 - comboBox2Height / 2, comboBox2Width, comboBox2Height); + + addAndMakeVisible(m_filterMode1); + addAndMakeVisible(m_filterMode2); + addAndMakeVisible(m_filterRouting); + addAndMakeVisible(m_saturationCurve); + addAndMakeVisible(m_keyFollowBase); + + m_envPol1.setBounds(2166 - m_envPol1.kWidth / 2, 241 - m_envPol1.kHeight / 2, m_envPol1.kWidth, m_envPol1.kHeight); + m_envPol2.setBounds(2166 - m_envPol2.kWidth / 2, 351 - m_envPol2.kHeight / 2, m_envPol2.kWidth, m_envPol2.kHeight); + m_link1.setBounds(1401 - m_link1.kWidth / 2, 302 - m_link1.kHeight / 2, m_link1.kWidth, m_link1.kHeight); + m_link2.setBounds(2266 - m_link2.kWidth / 2, 302 - m_link2.kHeight / 2, m_link2.kWidth, m_link2.kHeight); + + addAndMakeVisible(m_envPol1); + addAndMakeVisible(m_envPol2); + addAndMakeVisible(m_link1); + addAndMakeVisible(m_link2); + + _parameterBinding.bind(m_cutoff1, Virus::Param_FilterCutA); + _parameterBinding.bind(m_res1, Virus::Param_FilterResA); + _parameterBinding.bind(m_envAmount1, Virus::Param_FilterEnvAmtA); + _parameterBinding.bind(m_keyTrack1, Virus::Param_FilterKeyFollowA); + _parameterBinding.bind(m_resVel1, Virus::Param_Resonance1Velocity); + _parameterBinding.bind(m_envVel1, Virus::Param_Filter1EnvAmtVelocity); + _parameterBinding.bind(m_cutoff2, Virus::Param_FilterCutB); + _parameterBinding.bind(m_res2, Virus::Param_FilterResB); + _parameterBinding.bind(m_envAmount2, Virus::Param_FilterEnvAmtB); + _parameterBinding.bind(m_keyTrack2, Virus::Param_FilterKeyFollowB); + _parameterBinding.bind(m_resVel2, Virus::Param_Resonance2Velocity); + _parameterBinding.bind(m_envVel2, Virus::Param_Filter2EnvAmtVelocity); + _parameterBinding.bind(m_filterBalance, Virus::Param_FilterBalance); + + _parameterBinding.bind(m_filterMode1, Virus::Param_FilterModeA); + _parameterBinding.bind(m_filterMode2, Virus::Param_FilterModeB); + _parameterBinding.bind(m_filterRouting, Virus::Param_FilterRouting); + _parameterBinding.bind(m_saturationCurve, Virus::Param_SaturationCurve); + _parameterBinding.bind(m_keyFollowBase, Virus::Param_FilterKeytrackBase); + + _parameterBinding.bind(m_envPol1, Virus::Param_Filter1EnvPolarity); + _parameterBinding.bind(m_envPol2, Virus::Param_Filter2EnvPolarity); + _parameterBinding.bind(m_link1, Virus::Param_Filter2CutoffLink); + //todo no parameter for link2! + //_parameterBinding.bind(m_link2, Virus::Param_Filter ); + + //Env filter + for (auto *s : {&m_fltAttack, &m_fltDecay, &m_fltSustain, &m_fltTime, &m_fltRelease}) + { + setupRotary(*this, *s); + } + + m_fltAttack.setBounds(1474 - knobSize / 2, 695 - knobSize / 2, knobSize, knobSize); + m_fltDecay.setBounds(1648 - knobSize / 2, 695 - knobSize / 2, knobSize, knobSize); + m_fltSustain.setBounds(1830 - knobSize / 2, 695 - knobSize / 2, knobSize, knobSize); + m_fltTime.setBounds(2016 - knobSize / 2, 695 - knobSize / 2, knobSize, knobSize); + m_fltRelease.setBounds(2193 - knobSize / 2, 695 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_fltAttack, Virus::Param_FilterEnvAttack); + _parameterBinding.bind(m_fltDecay, Virus::Param_FilterEnvDecay); + _parameterBinding.bind(m_fltSustain, Virus::Param_FilterEnvSustain); + _parameterBinding.bind(m_fltTime, Virus::Param_FilterEnvSustainTime); + _parameterBinding.bind(m_fltRelease, Virus::Param_FilterEnvRelease); + + //Env Amp + for (auto *s : {&m_ampAttack, &m_ampDecay, &m_ampSustain, &m_ampTime, &m_ampRelease}) + { + setupRotary(*this, *s); + } + + m_ampAttack.setBounds(1474 - knobSize / 2, 904 - knobSize / 2, knobSize, knobSize); + m_ampDecay.setBounds(1648 - knobSize / 2, 904 - knobSize / 2, knobSize, knobSize); + m_ampSustain.setBounds(1830 - knobSize / 2, 904 - knobSize / 2, knobSize, knobSize); + m_ampTime.setBounds(2016 - knobSize / 2, 904 - knobSize / 2, knobSize, knobSize); + m_ampRelease.setBounds(2193 - knobSize / 2, 904 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_ampAttack, Virus::Param_AmpEnvAttack); + _parameterBinding.bind(m_ampDecay, Virus::Param_AmpEnvDecay); + _parameterBinding.bind(m_ampSustain, Virus::Param_AmpEnvSustain); + _parameterBinding.bind(m_ampTime, Virus::Param_AmpEnvSustainTime); + _parameterBinding.bind(m_ampRelease, Virus::Param_AmpEnvRelease); +} + +void OscEditor::resized() +{ +} +\ No newline at end of file diff --git a/source/jucePlugin/ui2/Virus_Panel1_OscEditor.h b/source/jucePlugin/ui2/Virus_Panel1_OscEditor.h @@ -0,0 +1,66 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" + +class VirusParameterBinding; + +class OscEditor : public juce::Component +{ +public: + OscEditor(VirusParameterBinding& _parameterBinding); + void resized() override; + +private: + + // OSC1 + juce::Slider m_osc1Shape, m_osc1PulseWidth, m_osc1Semitone, m_osc1KeyFollow; + juce::ComboBox m_osc1WaveSelect; + + // OSC2 + juce::Slider m_osc2Shape,m_osc2PulseWidth,m_osc2Semitone,m_osc2KeyFollow; + juce::ComboBox m_osc2WaveSelect; + + juce::Slider m_osc2FmAmount, m_osc2Detune, m_osc2EnvFm, m_osc2envOSC, m_osc2PhaseInit; + juce::ComboBox m_osc2FmMode; + + Buttons::Button3 m_syncOsc1Osc2; + + // OSC3 + juce::Slider m_osc3Semitone,m_osc3Detune; + juce::ComboBox m_osc3Mode; + + // OSC Sub + Buttons::Button2 m_subWaveform; + juce::Slider m_noiseColor; + + // Unison + juce::Slider m_detune, m_panSpread,m_lfoPhase; + juce::ComboBox m_unisonVoices; + + // Punch + juce::Slider m_Punch; + + // Mixer : juce::Component + juce::Slider m_oscBalance,m_oscLevel,m_osc3Level, m_oscSublevel, m_noiseLevel,m_ringModLevel; + + // Vel + juce::Slider m_osc1ShapeVelocity,m_pulseWidthVelocity,m_ampVelocity, m_panoramaVelocity,m_osc2ShapeVelocity,m_fmAmountVelocity; + + // Filters + juce::Slider m_cutoff1,m_res1, m_envAmount1,m_keyTrack1,m_resVel1,m_envVel1; + juce::Slider m_cutoff2,m_res2, m_envAmount2, m_keyTrack2,m_resVel2,m_envVel2; + juce::Slider m_filterBalance; + + Buttons::Button2 m_envPol1, m_envPol2; + Buttons::Button1 m_link1, m_link2; + juce::ComboBox m_filterMode1, m_filterMode2, m_filterRouting, m_saturationCurve, m_keyFollowBase; + + // Filter Envelope + juce::Slider m_fltAttack,m_fltDecay, m_fltSustain,m_fltTime,m_fltRelease; + + // Amp Envelope + juce::Slider m_ampAttack,m_ampDecay,m_ampSustain,m_ampTime,m_ampRelease; + + std::unique_ptr<juce::Drawable> m_background; +}; diff --git a/source/jucePlugin/ui2/Virus_Panel2_LfoEditor.cpp b/source/jucePlugin/ui2/Virus_Panel2_LfoEditor.cpp @@ -0,0 +1,245 @@ +#include "Virus_Panel2_LfoEditor.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "../VirusParameterBinding.h" + +using namespace juce; + + +LfoEditor::LfoEditor(VirusParameterBinding& _parameterBinding) +{ + setupBackground(*this, m_background, BinaryData::panel_2_png, BinaryData::panel_2_pngSize); + setBounds(m_background->getDrawableBounds().toNearestIntEdges()); + + //LFO 1 + for (auto *s : {&m_lfo1Rate, &m_lfo1Symmetry, &m_lfo1Osc1Pitch, &m_lfo1Osc2Pitch, &m_lfo1Pw12, &m_lfo1Reso12, + &m_lfo1KeyFollow, &m_lfo1Keytrigger, &m_lfo1AssignAmount, &m_lfo1FilterGain}) + { + setupRotary(*this, *s); + } + + m_lfo1Rate.setBounds(102 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_lfo1Symmetry.setBounds(417 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_lfo1Osc1Pitch.setBounds(102 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo1Osc2Pitch.setBounds(262 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo1Pw12.setBounds(417 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo1Reso12.setBounds(102 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + m_lfo1KeyFollow.setBounds(258 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + m_lfo1Keytrigger.setBounds(417 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + m_lfo1AssignAmount.setBounds(102 - knobSize / 2, 705 - knobSize / 2, knobSize, knobSize); + m_lfo1FilterGain.setBounds(417 - knobSize / 2, 886 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_lfo1Clock); + m_lfo1Clock.setBounds(258+comboBoxXMargin - comboBoxWidth / 2, 84 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfo1Shape); + m_lfo1Shape.setBounds(258+comboBoxXMargin - comboBoxWidth / 2, 169 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfo1AssignDest); + m_lfo1AssignDest.setBounds(283+comboBoxXMargin - comboBox3Width / 2, 700 - comboBoxHeight / 2, comboBox3Width, comboBoxHeight); + + addAndMakeVisible(m_lfo1Link); + m_lfo1Link.setBounds(177 - m_lfo1Link.kWidth / 2, 416 - m_lfo1Link.kHeight / 2, m_lfo1Link.kWidth, m_lfo1Link.kHeight); + addAndMakeVisible(m_lfo1LfoMode); + m_lfo1LfoMode.setBounds(102 - m_lfo1LfoMode.kWidth / 2, 879 - m_lfo1LfoMode.kHeight / 2, m_lfo1LfoMode.kWidth, m_lfo1LfoMode.kHeight); + addAndMakeVisible(m_lfo1EnvMode); + m_lfo1EnvMode.setBounds(442 - m_lfo1EnvMode.kWidth / 2, 701 - m_lfo1EnvMode.kHeight / 2, m_lfo1EnvMode.kWidth, m_lfo1EnvMode.kHeight); + + _parameterBinding.bind(m_lfo1Rate, Virus::Param_Lfo1Rate); + _parameterBinding.bind(m_lfo1Symmetry, Virus::Param_Lfo1Symmetry); + _parameterBinding.bind(m_lfo1Osc1Pitch, Virus::Param_Osc1Lfo1Amount); + _parameterBinding.bind(m_lfo1Osc2Pitch, Virus::Param_Osc2Lfo1Amount); + _parameterBinding.bind(m_lfo1Pw12, Virus::Param_PWLfo1Amount); + _parameterBinding.bind(m_lfo1Reso12, Virus::Param_ResoLfo1Amount); + _parameterBinding.bind(m_lfo1KeyFollow, Virus::Param_Lfo1Keyfollow); + _parameterBinding.bind(m_lfo1Keytrigger, Virus::Param_Lfo1KeyTrigger); + _parameterBinding.bind(m_lfo1AssignAmount, Virus::Param_Lfo1AssignAmount); + _parameterBinding.bind(m_lfo1FilterGain, Virus::Param_FltGainLfo1Amount); + + _parameterBinding.bind(m_lfo1Clock, Virus::Param_Lfo1Clock); + _parameterBinding.bind(m_lfo1Shape, Virus::Param_Lfo1Shape); + _parameterBinding.bind(m_lfo1AssignDest, Virus::Param_Lfo1AssignDest); + + //todo no link! _parameterBinding.bind(m_lfo1Link, Virus::lfo); + _parameterBinding.bind(m_lfo1LfoMode, Virus::Param_Lfo1Mode); + _parameterBinding.bind(m_lfo1EnvMode, Virus::Param_Lfo1EnvMode); + + // LFO 2 + for (auto *s : {&m_lfo2Rate, &m_lfo2Symmetry, &m_lfo2Filter1Cutoff, &m_lfo2Filter2Cutoff, &m_lfo2Shape12, + &m_lfo2Panorama, &m_lfo2KeyFollow, &m_lfo2Keytrigger, &m_lfo2AssignAmount, &m_lfo2AmtFM}) + { + setupRotary(*this, *s); + } + + m_lfo2Rate.setBounds(102 + 532 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_lfo2Symmetry.setBounds(417 + 532 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_lfo2Filter1Cutoff.setBounds(102 + 532 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo2Filter2Cutoff.setBounds(262 + 532 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo2Shape12.setBounds(417 + 532 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo2Panorama.setBounds(102 + 532 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + m_lfo2KeyFollow.setBounds(258 + 532 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + m_lfo2Keytrigger.setBounds(417 + 532 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + m_lfo2AssignAmount.setBounds(102 + 532 - knobSize / 2, 705 - knobSize / 2, knobSize, knobSize); + m_lfo2AmtFM.setBounds(417 + 532 - knobSize / 2, 886 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_lfo2Clock); + m_lfo2Clock.setBounds(258 + 532 + comboBoxXMargin - comboBoxWidth / 2, 84 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfo2Shape); + m_lfo2Shape.setBounds(258 + 532 +comboBoxXMargin - comboBoxWidth / 2, 169 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfo2AssignDest); + m_lfo2AssignDest.setBounds(283 + 532 +comboBoxXMargin - comboBox3Width / 2, 700 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_lfo2Link); + m_lfo2Link.setBounds(177 + 532 - m_lfo2Link.kWidth / 2, 416 - m_lfo2Link.kHeight / 2, m_lfo2Link.kWidth, m_lfo2Link.kHeight); + addAndMakeVisible(m_lfo2LfoMode); + m_lfo2LfoMode.setBounds(102 + 532 - m_lfo2LfoMode.kWidth / 2, 879 - m_lfo2LfoMode.kHeight / 2, m_lfo2LfoMode.kWidth, m_lfo2LfoMode.kHeight); + addAndMakeVisible(m_lfo2EnvMode); + m_lfo2EnvMode.setBounds(442 + 532 - m_lfo2EnvMode.kWidth / 2, 701 - m_lfo2EnvMode.kHeight / 2, m_lfo2EnvMode.kWidth, m_lfo2EnvMode.kHeight); + + _parameterBinding.bind(m_lfo2Rate, Virus::Param_Lfo2Rate); + _parameterBinding.bind(m_lfo2Symmetry, Virus::Param_Lfo2Symmetry); + _parameterBinding.bind(m_lfo2Filter1Cutoff, Virus::Param_Cutoff1Lfo2Amount); + _parameterBinding.bind(m_lfo2Filter2Cutoff, Virus::Param_Cutoff2Lfo2Amount); + _parameterBinding.bind(m_lfo2Shape12, Virus::Param_OscShapeLfo2Amount); + _parameterBinding.bind(m_lfo2Panorama, Virus::Param_PanoramaLfo2Amount); + _parameterBinding.bind(m_lfo2KeyFollow, Virus::Param_Lfo2Keyfollow); + _parameterBinding.bind(m_lfo2Keytrigger, Virus::Param_Lfo2Keytrigger); + _parameterBinding.bind(m_lfo2AssignAmount, Virus::Param_Lfo2AssignAmount); + _parameterBinding.bind(m_lfo2AmtFM, Virus::Param_FmAmountLfo2Amount); + + _parameterBinding.bind(m_lfo2Clock, Virus::Param_Lfo2Clock); + _parameterBinding.bind(m_lfo2Shape, Virus::Param_Lfo2Shape); + _parameterBinding.bind(m_lfo2AssignDest, Virus::Param_Lfo2AssignDest); + + // todo no link! _parameterBinding.bind(m_lfo2Link, Virus::lfo); + _parameterBinding.bind(m_lfo2LfoMode, Virus::Param_Lfo2Mode); + _parameterBinding.bind(m_lfo2EnvMode, Virus::Param_Lfo2EnvMode); + + + // LFO 3 + for (auto *s : {&m_lfo3Rate, &m_lfo3FadeIn, &m_lfo3KeyFollow, &m_lfo3AssignAmount,}) + { + setupRotary(*this, *s); + } + + m_lfo3Rate.setBounds(1166 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_lfo3FadeIn.setBounds(1166 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo3KeyFollow.setBounds(1322 - knobSize / 2, 313 - knobSize / 2, knobSize, knobSize); + m_lfo3AssignAmount.setBounds(1166 - knobSize / 2, 517 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_lfo3Clock); + m_lfo3Clock.setBounds(1322+comboBoxXMargin - comboBoxWidth / 2, 84 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfo3Shape); + m_lfo3Shape.setBounds(1322+comboBoxXMargin - comboBoxWidth / 2, 169 - comboBoxHeight / 2, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_lfo3AssignDest); + m_lfo3AssignDest.setBounds(1211+comboBoxXMargin - comboBox3Width / 2, 675 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_lfo3LfoMode); + m_lfo3LfoMode.setBounds(1305 - m_lfo3LfoMode.kWidth / 2, 509 - m_lfo3LfoMode.kHeight / 2, m_lfo3LfoMode.kWidth, m_lfo3LfoMode.kHeight); + + _parameterBinding.bind(m_lfo3Rate, Virus::Param_Lfo3Rate); + _parameterBinding.bind(m_lfo3FadeIn, Virus::Param_Lfo3FadeInTime); + _parameterBinding.bind(m_lfo3KeyFollow, Virus::Param_Lfo3Keyfollow); + _parameterBinding.bind(m_lfo3AssignAmount, Virus::Param_OscLfo3Amount); + + _parameterBinding.bind(m_lfo3Clock, Virus::Param_Lfo3Clock); + _parameterBinding.bind(m_lfo3Shape, Virus::Param_Lfo3Shape); + _parameterBinding.bind(m_lfo3AssignDest, Virus::Param_Lfo3Destination); + + // todo no link! _parameterBinding.bind(m_lfo3Link, Virus::lfo); + _parameterBinding.bind(m_lfo3LfoMode, Virus::Param_Lfo3Mode); + + //Matrix Slo1 + for (auto *s : {&m_MatSlot1Amount1,&m_MatSlot1Amount2,&m_MatSlot1Amount3,&m_MatSlot1Amount4, + }) + { + setupRotary(*this, *s); + } + + m_MatSlot1Amount1.setBounds(1792 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_MatSlot1Amount2.setBounds(2197 - knobSize / 2, 125 - knobSize / 2, knobSize, knobSize); + m_MatSlot1Amount3.setBounds(1792 - knobSize / 2, 355 - knobSize / 2, knobSize, knobSize); + m_MatSlot1Amount4.setBounds(2197 - knobSize / 2, 355 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_MatSlot1Source1); + m_MatSlot1Source1.setBounds(1597+comboBoxXMargin - comboBox3Width / 2, 82 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1Source2); + m_MatSlot1Source2.setBounds(2002+comboBoxXMargin - comboBox3Width / 2, 82 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1Source3); + m_MatSlot1Source3.setBounds(1597+comboBoxXMargin - comboBox3Width / 2, 313 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1Source4); + m_MatSlot1Source4.setBounds(2002+comboBoxXMargin - comboBox3Width / 2, 313 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1AssignDest1); + m_MatSlot1AssignDest1.setBounds(1597+comboBoxXMargin - comboBox3Width / 2, 171 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1AssignDest2); + m_MatSlot1AssignDest2.setBounds(2002+comboBoxXMargin - comboBox3Width / 2, 171 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1AssignDest3); + m_MatSlot1AssignDest3.setBounds(1597+comboBoxXMargin- comboBox3Width / 2, 400 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot1AssignDest4); + m_MatSlot1AssignDest4.setBounds(2002+comboBoxXMargin - comboBox3Width / 2, 400 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_MatSlot1Source1, Virus::Param_Assign1Source); + _parameterBinding.bind(m_MatSlot1Source2, Virus::Param_Assign4Source); + _parameterBinding.bind(m_MatSlot1Source3, Virus::Param_Assign5Source); + _parameterBinding.bind(m_MatSlot1Source4, Virus::Param_Assign6Source); + _parameterBinding.bind(m_MatSlot1Amount1, Virus::Param_Assign1Amount); + _parameterBinding.bind(m_MatSlot1Amount2, Virus::Param_Assign4Amount); + _parameterBinding.bind(m_MatSlot1Amount3, Virus::Param_Assign5Amount); + _parameterBinding.bind(m_MatSlot1Amount4, Virus::Param_Assign6Amount); + _parameterBinding.bind(m_MatSlot1AssignDest1, Virus::Param_Assign1Destination); + _parameterBinding.bind(m_MatSlot1AssignDest2, Virus::Param_Assign4Destination); + _parameterBinding.bind(m_MatSlot1AssignDest3, Virus::Param_Assign5Destination); + _parameterBinding.bind(m_MatSlot1AssignDest4, Virus::Param_Assign6Destination); + + // Matrix Slot 2 + for (auto *s : { &m_MatSlot2Amount1, &m_MatSlot2Amount2}) + { + setupRotary(*this, *s); + } + + m_MatSlot2Amount1.setBounds(1792 - knobSize / 2, 624 - knobSize / 2, knobSize, knobSize); + m_MatSlot2Amount2.setBounds(2197 - knobSize / 2, 624 - knobSize / 2, knobSize, knobSize); + addAndMakeVisible(m_MatSlot2Source12); + m_MatSlot2Source12.setBounds(1597+comboBoxXMargin - comboBox3Width / 2, 576 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot2AssignDest1); + m_MatSlot2AssignDest1.setBounds(1597+comboBoxXMargin - comboBox3Width / 2, 666 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_MatSlot2AssignDest2); + m_MatSlot2AssignDest2.setBounds(2002+comboBoxXMargin - comboBox3Width / 2, 624 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_MatSlot2Source12, Virus::Param_Assign2Source); + _parameterBinding.bind(m_MatSlot2Amount1, Virus::Param_Assign2Amount1); + _parameterBinding.bind(m_MatSlot2Amount2, Virus::Param_Assign2Amount2); + _parameterBinding.bind(m_MatSlot2AssignDest1, Virus::Param_Assign2Destination1); + _parameterBinding.bind(m_MatSlot2AssignDest2, Virus::Param_Assign2Destination2); + + + // Matrix Slot 3 + for (auto *s : {&m_MatSlot3Amount1,&m_MatSlot3Amount2,&m_MatSlot3Amount3}) + { + setupRotary(*this, *s); + } + + m_MatSlot3Amount1.setBounds(1404 - knobSize / 2, 886 - knobSize / 2, knobSize, knobSize); + m_MatSlot3Amount2.setBounds(1792 - knobSize / 2, 886 - knobSize / 2, knobSize, knobSize); + m_MatSlot3Amount3.setBounds(2197 - knobSize / 2, 886 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_MatSlot3Source123); + m_MatSlot3Source123.setBounds(1210+comboBoxXMargin - comboBox3Width / 2, 838 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_MatSlot3AssignDest1); + m_MatSlot3AssignDest1.setBounds(1210+comboBoxXMargin - comboBox3Width / 2, 928 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_MatSlot3AssignDest2); + m_MatSlot3AssignDest2.setBounds(1596+comboBoxXMargin - comboBox3Width / 2, 888 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_MatSlot3AssignDest3); + m_MatSlot3AssignDest3.setBounds(2002+comboBoxXMargin - comboBox3Width / 2, 888 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_MatSlot3Source123, Virus::Param_Assign3Source); + + _parameterBinding.bind(m_MatSlot3Amount1, Virus::Param_Assign3Amount1); + _parameterBinding.bind(m_MatSlot3Amount2, Virus::Param_Assign3Amount2); + _parameterBinding.bind(m_MatSlot3Amount3, Virus::Param_Assign2Amount3); //todo: need fix typ --> Param_Assign3Amount3 + + _parameterBinding.bind(m_MatSlot3AssignDest1, Virus::Param_Assign3Destination1); + _parameterBinding.bind(m_MatSlot3AssignDest2, Virus::Param_Assign3Destination2); + _parameterBinding.bind(m_MatSlot3AssignDest3, Virus::Param_Assign2Destination3); //todo: need fix typ --> Param_Assign3Destination3 +} diff --git a/source/jucePlugin/ui2/Virus_Panel2_LfoEditor.h b/source/jucePlugin/ui2/Virus_Panel2_LfoEditor.h @@ -0,0 +1,52 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" + + +class VirusParameterBinding; + +class LfoEditor : public juce::Component +{ +public: + LfoEditor(VirusParameterBinding& _parameterBinding); + +private: + //LFO 1 + juce::Slider m_lfo1Rate, m_lfo1Symmetry, m_lfo1Osc1Pitch, m_lfo1Osc2Pitch, m_lfo1Pw12, m_lfo1Reso12, + m_lfo1KeyFollow, m_lfo1Keytrigger, m_lfo1AssignAmount, m_lfo1FilterGain; + juce::ComboBox m_lfo1Clock, m_lfo1Shape, m_lfo1AssignDest; + Buttons::Button4 m_lfo1Link; + Buttons::Button2 m_lfo1LfoMode; + Buttons::Button3 m_lfo1EnvMode; + + //LFO 2 + juce::Slider m_lfo2Rate, m_lfo2Symmetry, m_lfo2Filter1Cutoff, m_lfo2Filter2Cutoff, m_lfo2Shape12, m_lfo2Panorama, + m_lfo2KeyFollow, m_lfo2Keytrigger, m_lfo2AssignAmount, m_lfo2AmtFM; + juce::ComboBox m_lfo2Clock, m_lfo2Shape, m_lfo2AssignDest; + Buttons::Button4 m_lfo2Link; + Buttons::Button2 m_lfo2LfoMode; + Buttons::Button3 m_lfo2EnvMode; + + //LFO 3 + juce::Slider m_lfo3Rate, m_lfo3FadeIn, m_lfo3KeyFollow, m_lfo3AssignAmount; + juce::ComboBox m_lfo3Clock, m_lfo3Shape, m_lfo3AssignDest; + Buttons::Button2 m_lfo3LfoMode; + + //Matrix Slo1 + juce::Slider m_MatSlot1Amount1, m_MatSlot1Amount2, m_MatSlot1Amount3, m_MatSlot1Amount4; + juce::ComboBox m_MatSlot1Source1, m_MatSlot1Source2, m_MatSlot1Source3, m_MatSlot1Source4; + juce::ComboBox m_MatSlot1AssignDest1, m_MatSlot1AssignDest2, m_MatSlot1AssignDest3, m_MatSlot1AssignDest4; + + // Matrix Slo2 + juce::Slider m_MatSlot2Amount1, m_MatSlot2Amount2; + juce::ComboBox m_MatSlot2Source12; + juce::ComboBox m_MatSlot2AssignDest1, m_MatSlot2AssignDest2; + + // Matrix Slo3 + juce::Slider m_MatSlot3Amount1, m_MatSlot3Amount2, m_MatSlot3Amount3; + juce::ComboBox m_MatSlot3Source123; + juce::ComboBox m_MatSlot3AssignDest1, m_MatSlot3AssignDest2, m_MatSlot3AssignDest3; + + std::unique_ptr<juce::Drawable> m_background; +}; diff --git a/source/jucePlugin/ui2/Virus_Panel3_FxEditor.cpp b/source/jucePlugin/ui2/Virus_Panel3_FxEditor.cpp @@ -0,0 +1,351 @@ +#include "Virus_Panel3_FxEditor.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "../VirusParameterBinding.h" + +using namespace juce; + + +FxEditor::FxEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef): m_controller(_processorRef.getController()) +{ + setupBackground(*this, m_background, BinaryData::panel_3_png, BinaryData::panel_3_pngSize); + setBounds(m_background->getDrawableBounds().toNearestIntEdges()); + + // envFollow + for (auto *s : {&m_envFollowLevel, &m_envFollowAttack, &m_eEnvFollowRelease}) + { + setupRotary(*this, *s); + } + + m_envFollowLevel.setBounds(219 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_envFollowAttack.setBounds(365 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_eEnvFollowRelease.setBounds(508 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_envFollow); + m_envFollow.setBounds(86 - m_envFollow.kWidth / 2, 113 - m_envFollow.kHeight / 2, m_envFollow.kWidth, m_envFollow.kHeight); + + _parameterBinding.bind(m_envFollow, Virus::Param_InputFollowMode); + _parameterBinding.bind(m_envFollowAttack, Virus::Param_FilterEnvAttack); + _parameterBinding.bind(m_eEnvFollowRelease, Virus::Param_FilterEnvDecay); + _parameterBinding.bind(m_envFollowLevel, Virus::Param_FilterEnvSustain); + + // Distortion + for (auto *s : {&m_distortionIntensity}) + { + setupRotary(*this, *s); + } + m_distortionIntensity.setBounds(905 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_distortionCurve); + m_distortionCurve.setBounds(741+comboBoxXMargin - comboBox3Width / 2, 113 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_distortionIntensity, Virus::Param_DistortionIntensity); + _parameterBinding.bind(m_distortionCurve, Virus::Param_DistortionCurve); + + // Chorus + for (auto *s : {&m_chorusRate, &m_chorusDepth, &m_chorusFeedback, &m_chorusMix, &m_chorusDelay}) + { + setupRotary(*this, *s); + } + m_chorusRate.setBounds(1299 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_chorusDepth.setBounds(1440 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_chorusFeedback.setBounds(1584 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_chorusMix.setBounds(1722 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_chorusDelay.setBounds(1861 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_choruslfoShape); + m_choruslfoShape.setBounds(1129+comboBoxXMargin - comboBox3Width / 2, 113 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_chorusRate, Virus::Param_ChorusRate); + _parameterBinding.bind(m_chorusDepth, Virus::Param_ChorusDepth); + _parameterBinding.bind(m_chorusFeedback, Virus::Param_ChorusFeedback); + _parameterBinding.bind(m_chorusMix, Virus::Param_ChorusMix); + _parameterBinding.bind(m_chorusDelay, Virus::Param_ChorusDelay); + _parameterBinding.bind(m_choruslfoShape, Virus::Param_ChorusLfoShape); + + // AnalogBoost + for (auto *s : {&m_analogBoostIntensity, &m_AnalogBoostTune}) + { + setupRotary(*this, *s); + } + + m_analogBoostIntensity.setBounds(2060 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_AnalogBoostTune.setBounds(2203 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_analogBoostIntensity, Virus::Param_BassIntensity); + _parameterBinding.bind(m_AnalogBoostTune, Virus::Param_BassTune); + + // Phaser + for (auto *s : {&m_phaserMix, &m_phaserSpread, &m_phaserRate, &m_phaserDepth, &m_phaserFreq, &m_phaserFeedback}) + { + setupRotary(*this, *s); + } + m_phaserMix.setBounds(283 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_phaserSpread.setBounds(420 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_phaserRate.setBounds(554 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_phaserDepth.setBounds(691 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_phaserFreq.setBounds(825 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_phaserFeedback.setBounds(961 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_stages); + m_stages.setBounds(114+comboBoxXMargin - comboBox3Width / 2, 339 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_phaserMix, Virus::Param_PhaserMix); + _parameterBinding.bind(m_phaserSpread, Virus::Param_PhaserSpread); + _parameterBinding.bind(m_phaserRate, Virus::Param_PhaserRate); + _parameterBinding.bind(m_phaserDepth, Virus::Param_PhaserDepth); + _parameterBinding.bind(m_phaserFreq, Virus::Param_PhaserFreq); + _parameterBinding.bind(m_phaserFeedback, Virus::Param_PhaserFeedback); + _parameterBinding.bind(m_stages, Virus::Param_PhaserMode); + + // Equalizer + m_equalizerLowGgain, m_equalizerLowFreq, m_equalizerMidGain, m_equalizerMidFreq, m_equalizerMidQ, m_equalizerHighGgain, m_equalizerHighFreq; + + for (auto *s : {&m_equalizerLowGgain, &m_equalizerLowFreq, &m_equalizerMidGain, &m_equalizerMidFreq, &m_equalizerMidQ, + &m_equalizerHighGgain, &m_equalizerHighFreq}) + { + setupRotary(*this, *s); + } + m_equalizerLowGgain.setBounds(1164 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_equalizerLowFreq.setBounds(1304 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_equalizerMidGain.setBounds(1448 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_equalizerMidFreq.setBounds(1588 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_equalizerMidQ.setBounds(1737 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_equalizerHighGgain.setBounds(1882 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + m_equalizerHighFreq.setBounds(2021 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_equalizerLowGgain, Virus::Param_LowEqGain); + _parameterBinding.bind(m_equalizerLowFreq, Virus::Param_LowEqFreq); + _parameterBinding.bind(m_equalizerMidGain, Virus::Param_MidEqGain); + _parameterBinding.bind(m_equalizerMidFreq, Virus::Param_MidEqFreq); + _parameterBinding.bind(m_equalizerMidQ, Virus::Param_MidEqQFactor); + _parameterBinding.bind(m_equalizerHighGgain, Virus::Param_HighEqGain); + _parameterBinding.bind(m_equalizerHighFreq, Virus::Param_HighEqFreq); + + // RingModInput + for (auto *s : {&m_ringModMix}) + { + setupRotary(*this, *s); + } + m_ringModMix.setBounds(2212 - knobSize / 2, 339 - knobSize / 2, knobSize, knobSize); + + _parameterBinding.bind(m_ringModMix, Virus::Param_InputRingMod); + + // Delay/Reverb + //Show hide Reverb + for (auto *s : {&m_delayReverbSend}) + { + setupRotary(*this, *s); + } + m_delayReverbSend.setBounds(113 - knobSize / 2, 607 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_delayReverbMode); + m_delayReverbMode.setBounds(113+comboBoxXMargin - comboBox3Width / 2, 763 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_delayReverbSend, Virus::Param_EffectSend); + _parameterBinding.bind(m_delayReverbMode, Virus::Param_DelayReverbMode); + + auto p = m_controller.getParameter(Virus::Param_DelayReverbMode, 0); + if (p) { + const auto val = (int)p->getValueObject().getValueSource().getValue(); + DelayReverb(); + + p->onValueChanged = nullptr; + p->onValueChanged = [this, p]() { + DelayReverb(); + }; + } + + //Reverb selected + // Reverb + for (auto *s : {&m_reverbDecayTime, &m_reverbDaming, &m_reverbColoration, &m_reverbPredelay, &m_reverbFeedback}) + { + setupRotary(*this, *s); + } + + m_reverbDecayTime.setBounds(297 - knobSize / 2, 854 - knobSize / 2, knobSize, knobSize); + m_reverbDaming.setBounds(632 - knobSize / 2, 854 - knobSize / 2, knobSize, knobSize); + m_reverbColoration.setBounds(771 - knobSize / 2, 854 - knobSize / 2, knobSize, knobSize); + m_reverbPredelay.setBounds(909 - knobSize / 2, 854 - knobSize / 2, knobSize, knobSize); + m_reverbFeedback.setBounds(1052 - knobSize / 2, 854 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_reverbType); + m_reverbType.setBounds(467+comboBoxXMargin - comboBox3Width / 2, 854 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_reverbDecayTime, Virus::Param_DelayRateReverbDecayTime); + _parameterBinding.bind(m_reverbDaming, Virus::Param_DelayLfoShape); + _parameterBinding.bind(m_reverbColoration, Virus::Param_DelayColor); + _parameterBinding.bind(m_reverbPredelay, Virus::Param_DelayTime); + _parameterBinding.bind(m_reverbFeedback, Virus::Param_DelayFeedback); + _parameterBinding.bind(m_reverbType, Virus::Param_DelayDepthReverbRoomSize); + + // todo Need to check these parameters bindings for delay and reverb + // Delay + for (auto *s : {&m_delayTime, &m_delayRate, &m_delayFeedback, &m_delayColoration, &m_delayDepth}) + { + setupRotary(*this, *s); + } + m_delayTime.setBounds(297 - knobSize / 2, 607 - knobSize / 2, knobSize, knobSize); + m_delayRate.setBounds(632 - knobSize / 2, 607 - knobSize / 2, knobSize, knobSize); + m_delayFeedback.setBounds(771 - knobSize / 2, 607 - knobSize / 2, knobSize, knobSize); + m_delayColoration.setBounds(909 - knobSize / 2, 607 - knobSize / 2, knobSize, knobSize); + m_delayDepth.setBounds(1052 - knobSize / 2, 607 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_delayClock); + m_delayClock.setBounds(467+comboBoxXMargin - comboBox3Width / 2, 555 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_delayShape); + m_delayShape.setBounds(467+comboBoxXMargin - comboBox3Width / 2, 653 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_delayTime, Virus::Param_DelayTime); + _parameterBinding.bind(m_delayRate, Virus::Param_DelayRateReverbDecayTime); + _parameterBinding.bind(m_delayFeedback, Virus::Param_DelayFeedback); + _parameterBinding.bind(m_delayColoration, Virus::Param_DelayColor); + _parameterBinding.bind(m_delayDepth, Virus::Param_DelayDepthReverbRoomSize); + _parameterBinding.bind(m_delayClock, Virus::Param_DelayClock); + _parameterBinding.bind(m_delayShape, Virus::Param_DelayLfoShape); + + // Vocoder + for (auto *s : {&m_vocoderCenterFreq, &m_vocoderModOffset, &m_vocoderModQ, &m_vocoderModSpread, &m_vocoderCarrQ, + &m_vocoderCarrSpread, &m_vocoderSpectralBal, &m_vocoderBands, &m_vocoderAttack, &m_vocoderRelease, + &m_vocoderSourceBal}) + { + setupRotary(*this, *s); + } + + m_vocoderCenterFreq.setBounds(1487 - knobSize / 2, 625 - knobSize / 2, knobSize, knobSize); + m_vocoderModOffset.setBounds(1625 - knobSize / 2, 625 - knobSize / 2, knobSize, knobSize); + m_vocoderModQ.setBounds(1768 - knobSize / 2, 625 - knobSize / 2, knobSize, knobSize); + m_vocoderModSpread.setBounds(1912 - knobSize / 2, 625 - knobSize / 2, knobSize, knobSize); + m_vocoderCarrQ.setBounds(2055 - knobSize / 2, 625 - knobSize / 2, knobSize, knobSize); + m_vocoderCarrSpread.setBounds(2198 - knobSize / 2, 625 - knobSize / 2, knobSize, knobSize); + + m_vocoderSpectralBal.setBounds(1487 - knobSize / 2, 836 - knobSize / 2, knobSize, knobSize); + m_vocoderBands.setBounds(1625 - knobSize / 2, 836 - knobSize / 2, knobSize, knobSize); + m_vocoderAttack.setBounds(1768 - knobSize / 2, 836 - knobSize / 2, knobSize, knobSize); + m_vocoderRelease.setBounds(1912 - knobSize / 2, 836 - knobSize / 2, knobSize, knobSize); + m_vocoderSourceBal.setBounds(2055 - knobSize / 2, 836 - knobSize / 2, knobSize, knobSize); + + addAndMakeVisible(m_vocoderMode); + m_vocoderMode.setBounds(1273+comboBoxXMargin - comboBox3Width / 2, 672 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_vocoderModInput); + m_vocoderModInput.setBounds(1273+comboBoxXMargin - comboBox3Width / 2, 787 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + m_vocoderLink.setBounds(1987 - m_vocoderLink.kWidth / 2, 526 - m_vocoderLink.kHeight / 2, m_vocoderLink.kWidth, m_vocoderLink.kHeight); + addAndMakeVisible(m_vocoderLink); + + //todo Need to check these parameters bindings + _parameterBinding.bind(m_vocoderCenterFreq, Virus::Param_FilterCutA); + _parameterBinding.bind(m_vocoderModOffset, Virus::Param_FilterCutB); + _parameterBinding.bind(m_vocoderModQ, Virus::Param_FilterResA); + _parameterBinding.bind(m_vocoderModSpread, Virus::Param_FilterKeyFollowA); + _parameterBinding.bind(m_vocoderCarrQ, Virus::Param_FilterResB); + _parameterBinding.bind(m_vocoderCarrSpread, Virus::Param_FilterKeyFollowB); + _parameterBinding.bind(m_vocoderSpectralBal, Virus::Param_FilterEnvSustainTime); + + _parameterBinding.bind(m_vocoderBands, Virus::Param_FilterEnvRelease); + _parameterBinding.bind(m_vocoderAttack, Virus::Param_FilterEnvAttack); + _parameterBinding.bind(m_vocoderRelease, Virus::Param_FilterEnvDecay); + _parameterBinding.bind(m_vocoderSourceBal, Virus::Param_FilterBalance); + + _parameterBinding.bind(m_vocoderMode, Virus::Param_VocoderMode); + _parameterBinding.bind(m_vocoderModInput, Virus::Param_InputSelect); + _parameterBinding.bind(m_vocoderLink, Virus::Param_Filter2CutoffLink); + + auto p1 = m_controller.getParameter(Virus::Param_VocoderMode, 0); + if (p1) { + const auto val = (int)p1->getValueObject().getValueSource().getValue(); + Vocoder(); + + p1->onValueChanged = nullptr; + p1->onValueChanged = [this, p1]() { + Vocoder(); + }; + } + +} + +void FxEditor::Vocoder() +{ + //Vocoder + //m_vocoderMode + auto p = m_controller.getParameter(Virus::Param_VocoderMode, 0); + const auto value = (int)p->getValueObject().getValueSource().getValue(); + m_vocoderMode.setSelectedId(value + 1, juce::dontSendNotification); + + int iSelectedIndex = m_vocoderMode.getSelectedItemIndex(); + bool bVocoder = (iSelectedIndex > 0); + float fAlpha = (bVocoder)?1.0f:0.3f; + + m_vocoderCenterFreq.setEnabled(bVocoder); + m_vocoderCenterFreq.setAlpha(fAlpha); + m_vocoderModOffset.setEnabled(bVocoder); + m_vocoderModOffset.setAlpha(fAlpha); + m_vocoderModQ.setEnabled(bVocoder); + m_vocoderModQ.setAlpha(fAlpha); + m_vocoderModSpread.setEnabled(bVocoder); + m_vocoderModSpread.setAlpha(fAlpha); + m_vocoderCarrQ.setEnabled(bVocoder); + m_vocoderCarrQ.setAlpha(fAlpha); + m_vocoderCarrSpread.setEnabled(bVocoder); + m_vocoderCarrSpread.setAlpha(fAlpha); + m_vocoderSpectralBal.setEnabled(bVocoder); + m_vocoderSpectralBal.setAlpha(fAlpha); + m_vocoderBands.setEnabled(bVocoder); + m_vocoderBands.setAlpha(fAlpha); + m_vocoderAttack.setEnabled(bVocoder); + m_vocoderAttack.setAlpha(fAlpha); + m_vocoderRelease.setEnabled(bVocoder); + m_vocoderRelease.setAlpha(fAlpha); + m_vocoderSourceBal.setEnabled(bVocoder); + m_vocoderSourceBal.setAlpha(fAlpha); + //m_vocoderMode.setVisible(bVocoder); + m_vocoderModInput.setEnabled(bVocoder); + m_vocoderModInput.setAlpha(fAlpha); + m_vocoderLink.setEnabled(bVocoder); + m_vocoderLink.setAlpha(fAlpha); +} + +void FxEditor::DelayReverb() +{ + //rebind(); + auto p = m_controller.getParameter(Virus::Param_DelayReverbMode, 0); + const auto value = (int)p->getValueObject().getValueSource().getValue(); + m_delayReverbMode.setSelectedId(value + 1, juce::dontSendNotification); + + //Delay/Reverb + int iSelectedIndex = m_delayReverbMode.getSelectedItemIndex(); + bool bReverb = (iSelectedIndex >= 2 && iSelectedIndex <= 4); + float fReverbAlpha = (bReverb)?1.0f:0.3f; + bool bDelay = (iSelectedIndex ==1 || iSelectedIndex >= 5); + float fDelayAlpha = (bDelay)?1.0f:0.3f; + + m_reverbDecayTime.setEnabled(bReverb); + m_reverbDecayTime.setAlpha(fReverbAlpha); + m_reverbDaming.setEnabled(bReverb); + m_reverbDaming.setAlpha(fReverbAlpha); + m_reverbColoration.setEnabled(bReverb); + m_reverbColoration.setAlpha(fReverbAlpha); + m_reverbPredelay.setEnabled(bReverb); + m_reverbPredelay.setAlpha(fReverbAlpha); + m_reverbFeedback.setEnabled(bReverb); + m_reverbFeedback.setAlpha(fReverbAlpha); + m_reverbType.setEnabled(bReverb); + m_reverbType.setAlpha(fReverbAlpha); + + m_delayTime.setEnabled(bDelay); + m_delayTime.setAlpha(fDelayAlpha); + m_delayRate.setEnabled(bDelay); + m_delayRate.setAlpha(fDelayAlpha); + m_delayFeedback.setEnabled(bDelay); + m_delayFeedback.setAlpha(fDelayAlpha); + m_delayColoration.setEnabled(bDelay); + m_delayColoration.setAlpha(fDelayAlpha); + m_delayDepth.setEnabled(bDelay); + m_delayDepth.setAlpha(fDelayAlpha); + m_delayClock.setEnabled(bDelay); + m_delayClock.setAlpha(fDelayAlpha); + m_delayShape.setEnabled(bDelay); + m_delayShape.setAlpha(fDelayAlpha); +} +\ No newline at end of file diff --git a/source/jucePlugin/ui2/Virus_Panel3_FxEditor.h b/source/jucePlugin/ui2/Virus_Panel3_FxEditor.h @@ -0,0 +1,67 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" + + +class VirusParameterBinding; + +class FxEditor : public juce::Component +{ +public: + FxEditor(VirusParameterBinding& _parameterBinding, AudioPluginAudioProcessor &_processorRef); + +private: + void DelayReverb(); + void Vocoder(); + Virus::Controller &m_controller; + + // Env Follower + juce::Slider m_envFollowLevel, m_envFollowAttack, m_eEnvFollowRelease; + Buttons::Button3 m_envFollow; + + // Distortion + juce::Slider m_distortionIntensity; + juce::ComboBox m_distortionCurve; + + // Chorus + juce::Slider m_chorusRate, m_chorusDepth, m_chorusFeedback, m_chorusMix, m_chorusDelay; + juce::ComboBox m_choruslfoShape; + + // AnalogBoost + juce::Slider m_analogBoostIntensity, m_AnalogBoostTune; + + // Phaser + juce::Slider m_phaserMix, m_phaserSpread, m_phaserRate, m_phaserDepth, m_phaserFreq, m_phaserFeedback; + juce::ComboBox m_stages; + + // Equalizer + juce::Slider m_equalizerLowGgain, m_equalizerLowFreq, m_equalizerMidGain, m_equalizerMidFreq, m_equalizerMidQ, + m_equalizerHighGgain, m_equalizerHighFreq; + + // Ring Mod + juce::Slider m_ringModMix; + + //Delay/Reverb + juce::Slider m_delayReverbSend; + juce::ComboBox m_delayReverbMode; + + //Delay + juce::Slider m_delayTime, m_delayRate, m_delayFeedback, m_delayColoration, m_delayDepth; + juce::ComboBox m_delayClock; + juce::ComboBox m_delayShape; + + //Reverb + juce::Slider m_reverbDecayTime, m_reverbDaming, m_reverbColoration, m_reverbPredelay, m_reverbFeedback; + juce::ComboBox m_reverbType; + + //Vocoder + juce::Slider m_vocoderCenterFreq, m_vocoderModOffset, m_vocoderModQ, m_vocoderModSpread, m_vocoderCarrQ, + m_vocoderCarrSpread, m_vocoderSpectralBal, m_vocoderBands, m_vocoderAttack, m_vocoderRelease, m_vocoderSourceBal; + + Buttons::Button4 m_vocoderLink; + juce::ComboBox m_vocoderMode; + juce::ComboBox m_vocoderModInput; + + std::unique_ptr<juce::Drawable> m_background; +}; diff --git a/source/jucePlugin/ui2/Virus_Panel4_ArpEditor.cpp b/source/jucePlugin/ui2/Virus_Panel4_ArpEditor.cpp @@ -0,0 +1,377 @@ +#include "Virus_Panel4_ArpEditor.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "../VirusParameterBinding.h" +#include "VirusEditor.h" + +using namespace juce; +using namespace virusLib; + +static uint8_t g_playMode = 0; + +ArpEditor::ArpEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef): + processorRef(_processorRef), m_properties(_processorRef.getController().getConfig()), m_controller(_processorRef.getController()), m_parameterBinding(_parameterBinding) +{ + setupBackground(*this, m_background, BinaryData::panel_4_png, BinaryData::panel_4_pngSize); + setBounds(m_background->getDrawableBounds().toNearestIntEdges()); + + int xPosMargin = 10; + + //Inputs + addAndMakeVisible(m_inputMode); + m_inputMode.setBounds(1488 + xPosMargin - comboBox3Width/2, 821 - comboBox3Height/2, comboBox3Width, comboBox3Height); + addAndMakeVisible(m_inputSelect); + m_inputSelect.setBounds(1488 + xPosMargin - comboBox3Width/2, 921 - comboBox3Height/2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_inputMode, Virus::Param_InputMode); + _parameterBinding.bind(m_inputSelect, Virus::Param_InputSelect); + + //Arpeggiator + constexpr auto y = 18; + for (auto *s : {&m_globalTempo, &m_noteLength, &m_noteSwing}) + setupRotary(*this, *s); + + m_globalTempo.setBounds(1970 - knobSize / 2, 613 - knobSize / 2, knobSize, knobSize); + m_noteLength.setBounds(1424 - knobSize / 2, 613 - knobSize / 2, knobSize, knobSize); + m_noteSwing.setBounds(1574 - knobSize / 2, 613 - knobSize / 2, knobSize, knobSize); + + for (auto *c : {&m_mode, &m_pattern, &m_octaveRange, &m_resolution}) + addAndMakeVisible(c); + + m_mode.setBounds(1228 + xPosMargin - comboBox3Width/2, 558 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_pattern.setBounds(1228 + xPosMargin - comboBox3Width/2, 656 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_resolution.setBounds(1773 + xPosMargin - comboBox3Width/2, 656 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_octaveRange.setBounds(1773 + xPosMargin - comboBox3Width/2, 558 - comboBox3Height/2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_arpHold); + + m_arpHold.setBounds(2129 - m_arpHold.kWidth/ 2, 613 - m_arpHold.kHeight / 2, m_arpHold.kWidth, m_arpHold.kHeight); + m_globalTempo.setEnabled(false); + m_globalTempo.setAlpha(0.3f); + + _parameterBinding.bind(m_globalTempo, Virus::Param_ClockTempo, 0); + _parameterBinding.bind(m_noteLength, Virus::Param_ArpNoteLength); + _parameterBinding.bind(m_noteSwing, Virus::Param_ArpSwing); + _parameterBinding.bind(m_mode, Virus::Param_ArpMode); + _parameterBinding.bind(m_pattern, Virus::Param_ArpPatternSelect); + _parameterBinding.bind(m_octaveRange, Virus::Param_ArpOctaveRange); + _parameterBinding.bind(m_resolution, Virus::Param_ArpClock); + _parameterBinding.bind(m_arpHold, Virus::Param_ArpHoldEnable); + + //SoftKnobs + m_softknobFunc1.setBounds(1756 + xPosMargin - comboBox3Width/2, 822 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_softknobFunc2.setBounds(1756 + xPosMargin - comboBox3Width/2, 921 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_softknobName1.setBounds(1983 + xPosMargin - comboBox3Width/2, 822 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_softknobName2.setBounds(1983 + xPosMargin - comboBox3Width/2, 921 - comboBox3Height/2, comboBox3Width, comboBox3Height); + + for (auto *c : {&m_softknobFunc1, &m_softknobFunc2, &m_softknobName1, &m_softknobName2}) + addAndMakeVisible(c); + + _parameterBinding.bind(m_softknobName1, Virus::Param_SoftKnob1ShortName); + _parameterBinding.bind(m_softknobName2, Virus::Param_SoftKnob2ShortName); + _parameterBinding.bind(m_softknobFunc1, Virus::Param_SoftKnob1Single); + _parameterBinding.bind(m_softknobFunc2, Virus::Param_SoftKnob2Single); + + //PatchSettings + for (auto *s : {&m_patchVolume, &m_panning, &m_outputBalance, &m_transpose}) + setupRotary(*this, *s); + + for (auto *c : {&m_patchVolume, &m_panning, &m_outputBalance, &m_transpose}) + addAndMakeVisible(c); + + m_patchVolume.setBounds(1428 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_panning.setBounds(1572 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_outputBalance.setBounds(1715 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + m_transpose.setBounds(1862 - knobSize / 2, 113 - knobSize / 2, knobSize, knobSize); + + for (auto *c : {&m_keyMode, &m_bendUp, &m_bendDown, &m_bendScale, &m_smoothMode, &m_cat1, &m_cat2}) + addAndMakeVisible(c); + + m_keyMode.setBounds(1232 + xPosMargin - comboBox3Width/2, 113 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_smoothMode.setBounds(1232 + xPosMargin - comboBox3Width/2, 259 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_bendScale.setBounds(1436 + xPosMargin - comboBox3Width/2, 259 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_bendUp.setBounds(1641 + xPosMargin - comboBox3Width/2, 259 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_bendDown.setBounds(1848 + xPosMargin - comboBox3Width/2, 259 - comboBox3Height/2, comboBox3Width, comboBox3Height); + + m_cat1.setBounds(1232 + xPosMargin - comboBox3Width/2, 382 - comboBox3Height/2, comboBox3Width, comboBox3Height); + m_cat2.setBounds(1436 + xPosMargin - comboBox3Width/2, 382 - comboBox3Height/2, comboBox3Width, comboBox3Height); + + _parameterBinding.bind(m_patchVolume, Virus::Param_PatchVolume); + _parameterBinding.bind(m_panning, Virus::Param_Panorama); + //_parameterBinding.bind(m_outputBalance, Virus::Param_SecondOutputBalance); + _parameterBinding.bind(m_transpose, Virus::Param_Transpose); + _parameterBinding.bind(m_keyMode, Virus::Param_KeyMode); + _parameterBinding.bind(m_bendUp, Virus::Param_BenderRangeUp); + _parameterBinding.bind(m_bendDown, Virus::Param_BenderRangeDown); + _parameterBinding.bind(m_bendScale, Virus::Param_BenderScale); + _parameterBinding.bind(m_smoothMode, Virus::Param_ControlSmoothMode); + _parameterBinding.bind(m_cat1, Virus::Param_Category1); + _parameterBinding.bind(m_cat2, Virus::Param_Category2); + _parameterBinding.bind(m_secondaryOutput, Virus::Param_PartOutputSelect); + + //Channels + int iMarginYChannels = 118; + int iMarginXChannels = 0; + int iIndex = 0; + + for (auto pt = 0; pt < 16; pt++) + { + if (pt==8) + { + iIndex=0; + iMarginXChannels=549; + } + + //Buttons + m_partSelect[pt].setBounds(95 - m_partSelect[pt].kWidth/2 + iMarginXChannels, 96 - m_partSelect[pt].kHeight/2 + iIndex*(iMarginYChannels), m_partSelect[pt].kWidth, m_partSelect[pt].kHeight); + m_partSelect[pt].setRadioGroupId(kPartGroupId); + m_partSelect[pt].setClickingTogglesState(true); + m_partSelect[pt].onClick = [this, pt]() {this->changePart(pt);}; + addAndMakeVisible(m_partSelect[pt]); + + m_presetNames[pt].setBounds(245 - 225/2 + iMarginXChannels, 97 - 70/2 + iIndex * (iMarginYChannels), 225, 70); + m_presetNames[pt].setText(m_controller.getCurrentPartPresetName(pt), juce::dontSendNotification); + m_presetNames[pt].setFont(juce::Font("Register", "Normal", 23.f)); + m_presetNames[pt].setJustificationType(Justification::left); + + addAndMakeVisible(m_presetNames[pt]); + + //Knobs + for (auto *s : {&m_partVolumes[pt], &m_partPans[pt]}) + { + setupRotary(*this, *s); + } + + m_partVolumes[pt].setLookAndFeel(&m_lookAndFeelSmallButton); + m_partVolumes[pt].setBounds(407 - knobSizeSmall / 2 + iMarginXChannels, 98 - knobSizeSmall / 2 + iIndex * (iMarginYChannels), knobSizeSmall, knobSizeSmall); + _parameterBinding.bind(m_partVolumes[pt], Virus::Param_PartVolume, pt); + addAndMakeVisible(m_partVolumes[pt]); + + m_partPans[pt].setLookAndFeel(&m_lookAndFeelSmallButton); + m_partPans[pt].setBounds(495 - knobSizeSmall / 2 + iMarginXChannels, 98 - knobSizeSmall / 2 + iIndex * (iMarginYChannels), knobSizeSmall, knobSizeSmall); + _parameterBinding.bind(m_partPans[pt], Virus::Param_Panorama, pt); + addAndMakeVisible(m_partPans[pt]); + + iIndex++; + } + + m_btWorkingMode.setBounds(1203 - m_btWorkingMode.kWidth / 2, 868 - m_btWorkingMode.kHeight / 2, m_btWorkingMode.kWidth, m_btWorkingMode.kHeight); + addAndMakeVisible(m_btWorkingMode); + m_btWorkingMode.onClick = [this]() + { + if (m_btWorkingMode.getToggleState()==1) + { + setPlayMode(virusLib::PlayMode::PlayModeSingle); + } + else + { + setPlayMode(virusLib::PlayMode::PlayModeMulti); + } + updatePlayModeButtons(); + }; + + m_partSelect[m_controller.getCurrentPart()].setToggleState(true, NotificationType::dontSendNotification); + refreshParts(); + + MidiInit(); +} + +ArpEditor::~ArpEditor() +{ + for (auto pt = 0; pt < 16; pt++) + { + m_partVolumes[pt].setLookAndFeel(nullptr); + m_partPans[pt].setLookAndFeel(nullptr); + } +} + +void ArpEditor::MidiInit() +{ + //MIDI settings + juce::String midiIn = m_properties->getValue("midi_input", ""); + juce::String midiOut = m_properties->getValue("midi_output", ""); + + if (midiIn != "") + { + processorRef.setMidiInput(midiIn); + } + if (midiOut != "") + { + processorRef.setMidiOutput(midiOut); + } + + m_cmbMidiInput.setBounds(2138 - 250 / 2, 81 - 47 / 2, 250, 47); + m_cmbMidiOutput.setBounds(2138 - 250 / 2, 178 - 47 / 2, 250, 47); + + addAndMakeVisible(m_cmbMidiInput); + addAndMakeVisible(m_cmbMidiOutput); + + m_cmbMidiInput.setTextWhenNoChoicesAvailable("No MIDI Inputs Enabled"); + auto midiInputs = juce::MidiInput::getAvailableDevices(); + juce::StringArray midiInputNames; + midiInputNames.add(" - Midi In - "); + auto inIndex = 0; + + for (int i = 0; i < midiInputs.size(); i++) + { + const auto input = midiInputs[i]; + if (processorRef.getMidiInput() != nullptr && input.identifier == processorRef.getMidiInput()->getIdentifier()) + { + inIndex = i + 1; + } + midiInputNames.add(input.name); + } + + m_cmbMidiInput.addItemList(midiInputNames, 1); + m_cmbMidiInput.setSelectedItemIndex(inIndex, juce::dontSendNotification); + m_cmbMidiOutput.setTextWhenNoChoicesAvailable("No MIDI Outputs Enabled"); + auto midiOutputs = juce::MidiOutput::getAvailableDevices(); + juce::StringArray midiOutputNames; + midiOutputNames.add(" - Midi Out - "); + auto outIndex = 0; + + for (int i = 0; i < midiOutputs.size(); i++) + { + const auto output = midiOutputs[i]; + if (processorRef.getMidiOutput() != nullptr && + output.identifier == processorRef.getMidiOutput()->getIdentifier()) + { + outIndex = i + 1; + } + midiOutputNames.add(output.name); + } + + m_cmbMidiOutput.addItemList(midiOutputNames, 1); + m_cmbMidiOutput.setSelectedItemIndex(outIndex, juce::dontSendNotification); + m_cmbMidiInput.onChange = [this]() { updateMidiInput(m_cmbMidiInput.getSelectedItemIndex()); }; + m_cmbMidiOutput.onChange = [this]() { updateMidiOutput(m_cmbMidiOutput.getSelectedItemIndex()); }; +} + + +void ArpEditor::refreshParts() +{ + const auto multiMode = m_controller.isMultiMode(); + for (auto pt = 0; pt < 16; pt++) + { + bool singlePartOrInMulti = pt == 0 || multiMode; + float fAlpha=(multiMode)?1.0:0.3; + + if (pt>0) + { + m_partVolumes[pt].setEnabled(singlePartOrInMulti); + m_partVolumes[pt].setAlpha(fAlpha); + m_partPans[pt].setEnabled(singlePartOrInMulti); + m_partPans[pt].setAlpha(fAlpha); + m_partSelect[pt].setEnabled(singlePartOrInMulti); + m_partSelect[pt].setAlpha(fAlpha); + m_presetNames[pt].setEnabled(singlePartOrInMulti); + m_presetNames[pt].setAlpha(fAlpha); + } + if (singlePartOrInMulti) + m_presetNames[pt].setText(m_controller.getCurrentPartPresetName(pt), juce::dontSendNotification); + else + m_presetNames[pt].setText("", juce::dontSendNotification); + } + + updatePlayModeButtons(); +} + +void ArpEditor::changePart(uint8_t _part) +{ + for (auto &p : m_partSelect) + { + p.setToggleState(false, juce::dontSendNotification); + } + m_partSelect[_part].setToggleState(true, juce::dontSendNotification); + m_parameterBinding.setPart(_part); + getParentComponent()->postCommandMessage(VirusEditor::Commands::UpdateParts); + getParentComponent()->postCommandMessage(VirusEditor::Commands::Rebind); +} + +void ArpEditor::setPlayMode(uint8_t _mode) +{ + m_controller.getParameter(Virus::Param_PlayMode)->setValue(_mode); + if (_mode == virusLib::PlayModeSingle && m_controller.getCurrentPart() != 0) + { + changePart(0); + } + getParentComponent()->postCommandMessage(VirusEditor::Commands::Rebind); +} + + +void ArpEditor::updatePlayModeButtons() +{ + const auto modeParam = m_controller.getParameter(Virus::Param_PlayMode, 0); + if (modeParam == nullptr) { + return; + } + const auto _mode = (int)modeParam->getValue(); + if (_mode == virusLib::PlayModeSingle) + { + m_btWorkingMode.setToggleState(true, juce::dontSendNotification); + } + else if (_mode == virusLib::PlayModeMulti || _mode == virusLib::PlayModeMultiSingle) + { + m_btWorkingMode.setToggleState(false, juce::dontSendNotification); + } +} + +void ArpEditor::updateMidiInput(int index) +{ + auto list = juce::MidiInput::getAvailableDevices(); + + if (index == 0) + { + m_properties->setValue("midi_input", ""); + m_properties->save(); + m_lastInputIndex = index; + m_cmbMidiInput.setSelectedItemIndex(index, juce::dontSendNotification); + return; + } + index--; + auto newInput = list[index]; + + if (!deviceManager.isMidiInputDeviceEnabled(newInput.identifier)) + deviceManager.setMidiInputDeviceEnabled(newInput.identifier, true); + + if (!processorRef.setMidiInput(newInput.identifier)) + { + m_cmbMidiInput.setSelectedItemIndex(0, juce::dontSendNotification); + m_lastInputIndex = 0; + return; + } + + m_properties->setValue("midi_input", newInput.identifier); + m_properties->save(); + + m_cmbMidiInput.setSelectedItemIndex(index + 1, juce::dontSendNotification); + m_lastInputIndex = index; +} + +void ArpEditor::updateMidiOutput(int index) +{ + auto list = juce::MidiOutput::getAvailableDevices(); + + if (index == 0) + { + m_properties->setValue("midi_output", ""); + m_properties->save(); + m_cmbMidiOutput.setSelectedItemIndex(index, juce::dontSendNotification); + m_lastOutputIndex = index; + processorRef.setMidiOutput(""); + return; + } + index--; + auto newOutput = list[index]; + if (!processorRef.setMidiOutput(newOutput.identifier)) + { + m_cmbMidiOutput.setSelectedItemIndex(0, juce::dontSendNotification); + m_lastOutputIndex = 0; + return; + } + m_properties->setValue("midi_output", newOutput.identifier); + m_properties->save(); + + m_cmbMidiOutput.setSelectedItemIndex(index + 1, juce::dontSendNotification); + m_lastOutputIndex = index; +} +\ No newline at end of file diff --git a/source/jucePlugin/ui2/Virus_Panel4_ArpEditor.h b/source/jucePlugin/ui2/Virus_Panel4_ArpEditor.h @@ -0,0 +1,68 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" +#include <juce_gui_extra/juce_gui_extra.h> +#include "../VirusController.h" +#include "Virus_LookAndFeel.h" + +class VirusParameterBinding; + +class ArpEditor : public juce::Component +{ +public: + ArpEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef); + ~ArpEditor() override; + + static constexpr auto kPartGroupId = 0x3FBBC; + void refreshParts(); +private: + void updateMidiInput(int index); + void updateMidiOutput(int index); + void MidiInit(); + Virus::LookAndFeelSmallButton m_lookAndFeelSmallButton; + + //WorkingMode + juce::ComboBox m_WorkingMode; + + //Channels + void changePart(uint8_t _part); + void setPlayMode(uint8_t _mode); + void updatePlayModeButtons(); + Virus::Controller &m_controller; + VirusParameterBinding &m_parameterBinding; + + Buttons::Button3 m_partSelect[16]; + juce::Label m_presetNames[16]; + juce::Slider m_partVolumes[16]; + juce::Slider m_partPans[16]; + + Buttons::Button2 m_btWorkingMode; + + //MIDI Settings + juce::AudioDeviceManager deviceManager; + juce::ComboBox m_cmbMidiInput; + juce::ComboBox m_cmbMidiOutput; + int m_lastInputIndex = 0; + int m_lastOutputIndex = 0; + juce::PropertiesFile *m_properties; + AudioPluginAudioProcessor &processorRef; + + //Inputs + juce::ComboBox m_inputMode, m_inputSelect; + + //Arpeggiator + juce::Slider m_globalTempo, m_noteLength, m_noteSwing; + juce::ComboBox m_mode, m_pattern, m_octaveRange, m_resolution; + Buttons::Button3 m_arpHold; + + //SoftKnobs + juce::ComboBox m_softknobFunc1, m_softknobFunc2, m_softknobName1, m_softknobName2; + + //PatchSettings + juce::Slider m_patchVolume, m_panning, m_outputBalance, m_transpose; + juce::ComboBox m_keyMode, m_secondaryOutput; + juce::ComboBox m_bendUp, m_bendDown, m_bendScale, m_smoothMode, m_cat1, m_cat2; + + std::unique_ptr<juce::Drawable> m_background; +}; diff --git a/source/jucePlugin/ui2/Virus_Panel5_PatchBrowser.cpp b/source/jucePlugin/ui2/Virus_Panel5_PatchBrowser.cpp @@ -0,0 +1,684 @@ +#include "../VirusParameterBinding.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "Virus_Panel5_PatchBrowser.h" +#include <juce_gui_extra/juce_gui_extra.h> +#include <juce_cryptography/juce_cryptography.h> +#include "VirusEditor.h" + +using namespace juce; +using namespace virusLib; + +float fBrowserScaleFactor = 2.0f; + +const juce::Array<juce::String> categories = {"", "Lead", "Bass", "Pad", "Decay", "Pluck", + "Acid", "Classic", "Arpeggiator", "Effects", "Drums", "Percussion", + "Input", "Vocoder", "Favourite 1", "Favourite 2", "Favourite 3"}; + +PatchBrowser::PatchBrowser(VirusParameterBinding & _parameterBinding, AudioPluginAudioProcessor &_processorRef) : + m_parameterBinding(_parameterBinding), m_properties(_processorRef.getController().getConfig()), + m_controller(_processorRef.getController()), + m_patchList("Patch browser"), + m_fileFilter("*.syx;*.mid;*.midi", "*", "virus patch dumps"), + m_bankList(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile), &m_fileFilter, NULL), + m_search("Search Box") +{ + setupBackground(*this, m_background, BinaryData::panel_5_png, BinaryData::panel_5_pngSize); + + m_bankList.setLookAndFeel(&m_landf); + + //PatchBrowser + m_bankList.setBounds(0, 185/fBrowserScaleFactor , 1030/fBrowserScaleFactor , 810/fBrowserScaleFactor ); + auto bankDir = m_properties->getValue("virus_bank_dir", ""); + if (bankDir != "" && juce::File(bankDir).isDirectory()) + { + m_bankList.setRoot(bankDir); + } + + setBounds(0, 0, kPanelWidth, kPanelHeight); + + //PatchList + m_patchList.setBounds(1049/fBrowserScaleFactor , 50/fBrowserScaleFactor , 1010/fBrowserScaleFactor , 870/fBrowserScaleFactor ); + + m_patchList.getHeader().addColumn("#", ColumnsPatch::INDEX, 32); + m_patchList.getHeader().addColumn("Name", ColumnsPatch::NAME, 130); + m_patchList.getHeader().addColumn("Category1", ColumnsPatch::CAT1, 84); + m_patchList.getHeader().addColumn("Category2", ColumnsPatch::CAT2, 84); + m_patchList.getHeader().addColumn("Arp", ColumnsPatch::ARP, 32); + m_patchList.getHeader().addColumn("Uni", ColumnsPatch::UNI, 32); + m_patchList.getHeader().addColumn("ST+-", ColumnsPatch::ST, 32); + m_patchList.getHeader().addColumn("Ver", ColumnsPatch::VER, 32); + + addAndMakeVisible(m_patchList); + + m_bankList.setTransform(AffineTransform::scale(fBrowserScaleFactor)); + m_patchList.setTransform(AffineTransform::scale(fBrowserScaleFactor)); + + m_bankList.addListener(this); + m_patchList.setModel(this); + + //Search + m_search.setTransform(AffineTransform::scale(fBrowserScaleFactor)); + m_search.setSize(m_patchList.getWidth(), 20); + m_search.setColour(TextEditor::textColourId, juce::Colours::white); + + m_search.setTopLeftPosition(m_patchList.getBounds().getBottomLeft().translated(0, 8)); + + m_search.onTextChange = [this] { + m_filteredPatches.clear(); + for(auto patch : m_patches) { + const auto searchValue = m_search.getText(); + if (searchValue.isEmpty()) { + m_filteredPatches.add(patch); + } + else if(patch.name.containsIgnoreCase(searchValue)) { + if (patch.name!="") + m_filteredPatches.add(patch); + } + } + m_patchList.updateContent(); + m_patchList.deselectAllRows(); + m_patchList.repaint(); + }; + m_search.setTextToShowWhenEmpty("search...", juce::Colours::grey); + addAndMakeVisible(m_search); + + //Show Options Buttons/Cmb + addAndMakeVisible(m_SavePreset); + addAndMakeVisible(m_ROMBankSelect); + addAndMakeVisible(m_bankList); + + m_ROMBankSelect.setBounds(510 - 961 / 2, 78 - 51 / 2, 961, 51); + for (int i=1; i<=m_controller.getBankCount();i++) + { + m_ROMBankSelect.addItem("BANK: " + getCurrentPartBankStr((virusLib::BankNumber)i),i+1); + } + + m_ROMBankSelect.onChange = [this]() + { + m_LastBankRomNoUsed = m_ROMBankSelect.getSelectedItemIndex()+1; + LoadBankNr(m_LastBankRomNoUsed); + m_bIsFileMode = false; + m_search.setText("", true); + getParentComponent()->postCommandMessage(VirusEditor::Commands::SelectFirstPatch); + getParentComponent()->postCommandMessage(VirusEditor::Commands::UpdateParts); + SaveSettings(); + }; + + m_SavePreset.setBounds(2197 - m_SavePreset.kWidth / 2, 72 - m_SavePreset.kHeight / 2, m_SavePreset.kWidth, m_SavePreset.kHeight); + m_SavePreset.onClick = [this]() { savePreset(); }; +} + +PatchBrowser::~PatchBrowser() +{ + setLookAndFeel(nullptr); + m_bankList.setLookAndFeel(nullptr); +} + +void PatchBrowser::IntiPatches() +{ + //Read Last Patch used from config file + m_bIsFileMode = m_properties->getBoolValue("is_file_used", false); + m_LastBankRomNoUsed = m_properties->getIntValue("last_bank_rom_no_used", 1); + m_LastFileUsed = m_properties->getValue("last_file_used", ""); + + if(!m_bIsFileMode) + { + m_controller.setCurrentPartPreset(0,(virusLib::BankNumber)(m_LastBankRomNoUsed),0); + m_ROMBankSelect.setSelectedItemIndex(m_LastBankRomNoUsed-1, dontSendNotification); + LoadBankNr(m_LastBankRomNoUsed); + m_search.setText("", true); + + } + else + { + m_bankList.setFileName(m_LastFileUsed); + m_bankList.selectionChanged(); + const juce::File &file(m_LastFileUsed); + LoadPatchesFromFile(file); + } +} + +void PatchBrowser::SaveSettings() +{ + m_properties->setValue("last_file_used", m_LastFileUsed); + m_properties->setValue("is_file_used", m_bIsFileMode); + m_properties->setValue("last_bank_rom_no_used", m_LastBankRomNoUsed); + m_properties->save(); +} + + +juce::String PatchBrowser::GetLastPatchSelected() +{ + return m_LastPatchSelected; +} + +juce::TableListBox* PatchBrowser::GetTablePatchList() +{ + return &m_patchList; +} + +juce::String PatchBrowser::GetSelectBankNum() +{ + return getCurrentPartBankStr((virusLib::BankNumber)m_LastBankRomNoUsed); +} + + + + +bool PatchBrowser::GetIsFileMode() +{ + return m_bIsFileMode; +} + +void PatchBrowser::selectionChanged() {} + +void PatchBrowser::LoadBankNr(int iBankNo) +{ + juce::StringArray patches = m_controller.getSinglePresetNames((virusLib::BankNumber)(iBankNo)); + m_patches.clear(); + + for (int i=0 ; i<128 ; i++) + { + Patch patch; + patch.progNumber = i; + //data.copyTo(patch.data, 267*index + 9, 256); + patch.name = patches.strings[i]; + patch.category1 = 0; + patch.category2 = 0; + patch.unison = 0; + patch.transpose = 0; + patch.model = VirusModel::A; + m_patches.add(patch); + } + + m_filteredPatches.clear(); + for(auto patch : m_patches) { + const auto searchValue = m_search.getText(); + if (searchValue.isEmpty()) { + m_filteredPatches.add(patch); + } + else if(patch.name.containsIgnoreCase(searchValue)) { + m_filteredPatches.add(patch); + } + } + m_patchList.updateContent(); + m_patchList.deselectAllRows(); + m_patchList.repaint(); +} + + +int PatchBrowser::loadBankFile(const juce::File& file, const int _startIndex = 0, const bool dedupe = false) { + auto ext = file.getFileExtension().toLowerCase(); + auto path = file.getParentDirectory().getFullPathName(); + int loadedCount = 0; + int index = _startIndex; + if (ext == ".syx") + { + juce::MemoryBlock data; + if (!file.loadFileAsData(data)) { + return 0; + } + for (auto it = data.begin(); it != data.end(); it += 267) + { + if ((uint8_t)*it == (uint8_t)0xf0 + && (uint8_t)*(it+1) == (uint8_t)0x00 + && (uint8_t)*(it+2) == (uint8_t)0x20 + && (uint8_t)*(it+3) == (uint8_t)0x33 + && (uint8_t)*(it+4) == (uint8_t)0x01 + && (uint8_t)*(it+6) == (uint8_t)virusLib::DUMP_SINGLE) + { + Patch patch; + patch.progNumber = index; + data.copyTo(patch.data, 267*index + 9, 256); + patch.name = parseAsciiText(patch.data, 128 + 112); + patch.category1 = patch.data[251]; + patch.category2 = patch.data[252]; + patch.unison = patch.data[97]; + patch.transpose = patch.data[93]; + if ((uint8_t)*(it + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 266) != (uint8_t)0xf8) { + patch.model = VirusModel::TI; + } + else { + patch.model = guessVersion(patch.data); + } + auto md5 = juce::MD5(it+9 + 17, 256-17-3).toHexString(); + if(!dedupe || !m_checksums.contains(md5)) { + m_checksums.set(md5, true); + m_patches.add(patch); + index++; + } + } + } + } + else if (ext == ".mid" || ext == ".midi") + { + juce::MemoryBlock data; + if (!file.loadFileAsData(data)) + { + return 0; + } + + const uint8_t *ptr = (uint8_t *)data.getData(); + const auto end = ptr + data.getSize(); + + for (auto it = ptr; it < end; it += 1) + { + if ((uint8_t)*it == (uint8_t)0xf0 && (it + 267) < end) // we don't check for sysex eof so we can load TI banks + { + if ((uint8_t) *(it+1) == (uint8_t)0x00 + && (uint8_t)*(it+2) == 0x20 + && (uint8_t)*(it+3) == 0x33 + && (uint8_t)*(it+4) == 0x01 + && (uint8_t)*(it+6) == virusLib::DUMP_SINGLE) + { + auto syx = Virus::SysEx(it, it + 267); + syx[7] = 0x01; // force to bank a + syx[266] = 0xf7; + + Patch patch; + patch.progNumber = index; + std::copy(syx.begin() + 9, syx.end() - 2, patch.data); + patch.name = parseAsciiText(patch.data, 128 + 112); + patch.category1 = patch.data[251]; + patch.category2 = patch.data[252]; + patch.unison = patch.data[97]; + patch.transpose = patch.data[93]; + if ((uint8_t)*(it + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 266) != (uint8_t)0xf8) { + patch.model = VirusModel::TI; + } + else { + patch.model = guessVersion(patch.data); + } + auto md5 = juce::MD5(it+9 + 17, 256-17-3).toHexString(); + if(!dedupe || !m_checksums.contains(md5)) { + m_checksums.set(md5, true); + m_patches.add(patch); + index++; + } + + it += 266; + } + else if((uint8_t)*(it+3) == 0x00 // some midi files have two bytes after the 0xf0 + && (uint8_t)*(it+4) == 0x20 + && (uint8_t)*(it+5) == 0x33 + && (uint8_t)*(it+6) == 0x01 + && (uint8_t)*(it+8) == virusLib::DUMP_SINGLE) + { + auto syx = Virus::SysEx(); + syx.push_back(0xf0); + for (auto i = it + 3; i < it + 3 + 266; i++) + { + syx.push_back((uint8_t)*i); + } + syx[7] = 0x01; // force to bank a + syx[266] = 0xf7; + + Patch patch; + std::memcpy(patch.data, syx.data()+9, 256); + patch.progNumber = index; + patch.name = parseAsciiText(patch.data, 128 + 112); + patch.category1 = patch.data[251]; + patch.category2 = patch.data[252]; + patch.unison = patch.data[97]; + patch.transpose = patch.data[93]; + if ((uint8_t)*(it + 2 + 266) != (uint8_t)0xf7 && (uint8_t)*(it + 2 + 266) != (uint8_t)0xf8) { + patch.model = VirusModel::TI; + } + else { + patch.model = guessVersion(patch.data); + } + auto md5 = juce::MD5(it+2+9 + 17, 256-17-3).toHexString(); + if(!dedupe || !m_checksums.contains(md5)) { + m_checksums.set(md5, true); + m_patches.add(patch); + index++; + } + loadedCount++; + + it += 266; + } + } + } + } + return index; +} + +void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e) +{ + auto ext = file.getFileExtension().toLowerCase(); + auto path = file.getParentDirectory().getFullPathName(); + + //Show popup only on directory + if (file.isDirectory() && e.mods.isRightButtonDown()) { + auto p = juce::PopupMenu(); + p.addItem("Add directory contents to patch list", [this, file]() { + m_patches.clear(); + m_checksums.clear(); + int lastIndex = 0; + for (auto f : juce::RangedDirectoryIterator(file, false, "*.syx;*.mid;*.midi", juce::File::findFiles)) { + lastIndex = loadBankFile(f.getFile(), lastIndex, true); + } + m_filteredPatches.clear(); + for(auto patch : m_patches) { + const auto searchValue = m_search.getText(); + if (searchValue.isEmpty()) { + m_filteredPatches.add(patch); + } + else if(patch.name.containsIgnoreCase(searchValue)) { + m_filteredPatches.add(patch); + } + } + m_patchList.updateContent(); + m_patchList.deselectAllRows(); + m_patchList.repaint(); + }); + p.showMenu(juce::PopupMenu::Options()); + + return; + } + + m_properties->setValue("virus_bank_dir", path); + + if(file.existsAsFile() && ext == ".syx" || ext == ".midi" || ext == ".mid") { + LoadPatchesFromFile(file); + m_LastFileUsed = file.getFullPathName(); + } + m_bIsFileMode = true; + m_search.setText("", true); + m_ROMBankSelect.setText("",true); + SaveSettings(); + getParentComponent()->postCommandMessage(VirusEditor::Commands::SelectFirstPatch); + getParentComponent()->postCommandMessage(VirusEditor::Commands::UpdateParts); +} + +void PatchBrowser::LoadPatchesFromFile(const juce::File &file) +{ + m_patches.clear(); + loadBankFile(file); + m_filteredPatches.clear(); + for(auto patch : m_patches) { + const auto searchValue = m_search.getText(); + if (searchValue.isEmpty()) { + m_filteredPatches.add(patch); + } + else if(patch.name.containsIgnoreCase(searchValue)) { + m_filteredPatches.add(patch); + } + } + m_patchList.updateContent(); + m_patchList.deselectAllRows(); + m_patchList.repaint(); +} + + +void PatchBrowser::fileDoubleClicked(const juce::File &file) {} + +void PatchBrowser::browserRootChanged(const File &newRoot) {} + +int PatchBrowser::getNumRows() +{ + return m_filteredPatches.size(); +} + +void PatchBrowser::paintRowBackground(Graphics &g, int rowNumber, int width, int height, bool rowIsSelected) +{ + auto alternateColour = getLookAndFeel() + .findColour(juce::ListBox::backgroundColourId) + .interpolatedWith(getLookAndFeel().findColour(juce::ListBox::textColourId), 0.03f); + if (rowIsSelected) + g.fillAll(juce::Colours::lightblue); + else if (rowNumber % 2) + g.fillAll(alternateColour); +} + +void PatchBrowser::paintCell(Graphics &g, int rowNumber, int columnId, int width, int height, bool rowIsSelected) +{ + //Banks from file + g.setColour(rowIsSelected ? juce::Colours::darkblue + : getLookAndFeel().findColour(juce::ListBox::textColourId)); // [5] + + auto rowElement = m_filteredPatches[rowNumber]; + //auto text = rowElement.name; + juce::String text = ""; + + if (m_bIsFileMode) + { + if (columnId == ColumnsPatch::INDEX) + text = juce::String(rowElement.progNumber+1); + else if (columnId == ColumnsPatch::NAME) + text = rowElement.name; + else if (columnId == ColumnsPatch::CAT1) + text = categories[rowElement.category1]; + else if (columnId == ColumnsPatch::CAT2) + text = categories[rowElement.category2]; + else if (columnId == ColumnsPatch::ARP) + text = rowElement.data[129] != 0 ? "Y" : " "; + else if(columnId == ColumnsPatch::UNI) + text = rowElement.unison == 0 ? " " : juce::String(rowElement.unison+1); + else if(columnId == ColumnsPatch::ST) + text = rowElement.transpose != 64 ? juce::String(rowElement.transpose - 64) : " "; + else if (columnId == ColumnsPatch::VER) { + if(rowElement.model < ModelList.size()) + text = ModelList[rowElement.model]; + } + } + else + { + if (columnId == ColumnsPatch::INDEX) + text = juce::String(rowElement.progNumber+1); + else if (columnId == ColumnsPatch::NAME) + text = rowElement.name; + } + + g.drawText(text, 2, 0, width - 4, height, juce::Justification::centredLeft, true); // [6] + g.setColour(getLookAndFeel().findColour(juce::ListBox::backgroundColourId)); + g.fillRect(width - 1, 0, 1, height); // [7] + +} + + +void PatchBrowser::selectedRowsChanged(int lastRowSelected) { + auto idx = m_patchList.getSelectedRow(); + if (idx == -1) { + return; + } + + juce::Component *c; + c = m_patchList.getCellComponent(1,idx); + + if (m_bIsFileMode) + { + uint8_t syxHeader[9] = {0xF0, 0x00, 0x20, 0x33, 0x01, 0x00, 0x10, 0x00, 0x00}; + syxHeader[8] = m_controller.isMultiMode() ? m_controller.getCurrentPart() : virusLib::ProgramType::SINGLE; // set edit buffer + const uint8_t syxEof = 0xF7; + uint8_t cs = syxHeader[5] + syxHeader[6] + syxHeader[7] + syxHeader[8]; + uint8_t data[256]; + for (int i = 0; i < 256; i++) + { + data[i] = m_filteredPatches[idx].data[i]; + cs += data[i]; + } + cs = cs & 0x7f; + Virus::SysEx syx; + for (auto i : syxHeader) + { + syx.push_back(i); + } + for (auto i : data) + { + syx.push_back(i); + } + syx.push_back(cs); + syx.push_back(syxEof); + m_controller.sendSysEx(syx); // send to edit buffer + m_controller.parseMessage(syx); // update ui + + m_LastPatchSelected = parseAsciiText(data, 128 + 112); + getParentComponent()->postCommandMessage(VirusEditor::Commands::UpdateParts); + } + else + { + m_controller.setCurrentPartPreset(m_controller.getCurrentPart(), (virusLib::BankNumber) m_LastBankRomNoUsed, m_filteredPatches[idx].progNumber); + getParentComponent()->postCommandMessage(VirusEditor::Commands::UpdateParts); + } +} + +void PatchBrowser::cellDoubleClicked(int rowNumber, int columnId, const juce::MouseEvent &) +{ + /* if(m_modeIndex==1) + { + selectedRowsChanged(0); + } + else if (m_modeIndex==0) + { + m_controller.setCurrentPartPreset(m_controller.getCurrentPart(),m_controller.getCurrentPartBank(m_controller.getCurrentPart()),lastRowSelected); + }*/ +} + + + +void PatchBrowser::sortOrderChanged(int newSortColumnId, bool isForwards) +{ + if (newSortColumnId != 0) + { + PatchBrowser::PatchBrowserSorter sorter (newSortColumnId, isForwards); + m_patches.sort(sorter); + m_patchList.updateContent(); + } +} + + +void PatchBrowser::loadBankFileToRom(const juce::File &file) +{ + bool sentData = false; + auto sExt = file.getFileExtension().toLowerCase(); + m_previousPath = file.getParentDirectory().getFullPathName();; + juce::MemoryBlock data; + + if (sExt == ".syx") + { + if (!file.loadFileAsData(data)) + { + return; + } + + for (auto it = data.begin(); it != data.end(); it += 267) + { + if ((it + 267) <= data.end()) + { + m_controller.sendSysEx(Virus::SysEx(it, it + 267)); + sentData = true; + } + } + } + else if (sExt == ".mid" || sExt == ".midi") + { + if (!file.loadFileAsData(data)) + { + return; + } + + const uint8_t *ptr = (uint8_t *)data.getData(); + const auto end = ptr + data.getSize(); + + for (auto it = ptr; it < end; it += 1) + { + if ((uint8_t)*it == (uint8_t)0xf0 && (it + 267) < end) + { + if ((uint8_t)*(it+1) == 0x00 + && (uint8_t)*(it+2) == 0x20 + && (uint8_t)*(it+3) == 0x33 + && (uint8_t)*(it+4) == 0x01 + && (uint8_t)*(it+6) == virusLib::DUMP_SINGLE) + { + auto syx = Virus::SysEx(it, it + 267); + syx[7] = 0x01; // force to bank a + syx[266] = 0xf7; + + m_controller.sendSysEx(syx); + + it += 266; + } + else if((uint8_t)*(it+3) == 0x00 + && (uint8_t)*(it+4) == 0x20 + && (uint8_t)*(it+5) == 0x33 + && (uint8_t)*(it+6) == 0x01 + && (uint8_t)*(it+8) == virusLib::DUMP_SINGLE)// some midi files have two bytes after the 0xf0 + { + auto syx = Virus::SysEx(); + syx.push_back(0xf0); + for (auto i = it + 3; i < it + 3 + 266; i++) + { + syx.push_back((uint8_t)*i); + } + syx[7] = 0x01; // force to bank a + syx[266] = 0xf7; + m_controller.sendSysEx(syx); + it += 266; + } + + sentData = true; + } + } + } + + if (sentData) + { + m_controller.onStateLoaded(); + } +} + + +void PatchBrowser::loadFile() +{ + juce::MemoryBlock data; + juce::FileChooser chooser( + "Choose syx/midi banks to import", + m_previousPath.isEmpty() + ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() + : m_previousPath, + "*.syx,*.mid,*.midi", true); + + if (!chooser.browseForFileToOpen()) + return; + + loadBankFileToRom(chooser.getResult()); +} + +void PatchBrowser::savePreset() { + juce::FileChooser chooser( + "Save preset as syx", + m_previousPath.isEmpty() + ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() + : m_previousPath, + "*.syx", true); + + if (!chooser.browseForFileToSave(true)) + return; + + bool sentData = false; + const auto result = chooser.getResult(); + m_previousPath = result.getParentDirectory().getFullPathName(); + const auto ext = result.getFileExtension().toLowerCase(); + + const uint8_t syxHeader[9] = {0xF0, 0x00, 0x20, 0x33, 0x01, 0x00, 0x10, 0x01, 0x00}; + const uint8_t syxEof[1] = {0xF7}; + uint8_t cs = syxHeader[5] + syxHeader[6] + syxHeader[7] + syxHeader[8]; + uint8_t data[256]; + for (int i = 0; i < 256; i++) + { + auto param = m_controller.getParamValue(m_controller.getCurrentPart(), i < 128 ? 0 : 1, i % 128); + + data[i] = param ? (int)param->getValue() : 0; + cs += data[i]; + } + cs = cs & 0x7f; + + result.deleteFile(); + result.create(); + result.appendData(syxHeader, 9); + result.appendData(data, 256); + result.appendData(&cs, 1); + result.appendData(syxEof, 1); +} +\ No newline at end of file diff --git a/source/jucePlugin/ui2/Virus_Panel5_PatchBrowser.h b/source/jucePlugin/ui2/Virus_Panel5_PatchBrowser.h @@ -0,0 +1,155 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" +#include <juce_gui_extra/juce_gui_extra.h> +#include "../VirusController.h" + +const juce::Array<juce::String> ModelList = {"A","B","C","TI"}; + +struct Patch +{ + int progNumber; + juce::String name; + uint8_t category1; + uint8_t category2; + uint8_t data[256]; + virusLib::VirusModel model; + uint8_t unison; + uint8_t transpose; +}; + + +class PatchBrowser : public juce::Component, juce::FileBrowserListener, juce::TableListBoxModel +{ + +public: + PatchBrowser(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef); + ~PatchBrowser(); + void loadFile(); + void loadBankFileToRom(const juce::File &file); + void savePreset(); + bool GetIsFileMode(); + juce::String GetSelectBankNum(); + juce::String GetLastPatchSelected(); + juce::TableListBox* GetTablePatchList(); + + void IntiPatches(); +private: + + Virus::LookAndFeelPatchBrowser m_landf; + + VirusParameterBinding &m_parameterBinding; + Virus::Controller& m_controller; + + template <typename T> juce::String parseAsciiText(const T &msg, const int start) const + { + char text[Virus::Controller::kNameLength + 1]; + text[Virus::Controller::kNameLength] = 0; // termination + for (auto pos = 0; pos < Virus::Controller::kNameLength; ++pos) + text[pos] = msg[start + pos]; + return juce::String(text); + } + + juce::WildcardFileFilter m_fileFilter; + juce::FileBrowserComponent m_bankList; + + juce::TextEditor m_search; + juce::Array<Patch> m_patches; + juce::Array<Patch> m_filteredPatches; + juce::PropertiesFile *m_properties; + juce::HashMap<juce::String, bool> m_checksums; + int loadBankFile(const juce::File &file, const int _startIndex, const bool dedupe); + // Inherited via FileBrowserListener + Buttons::OptionButtonSavePreset m_SavePreset; + + void LoadBankNr(int iBankNo); + void SaveSettings(); + void LoadPatchesFromFile(const juce::File &file); + + juce::ComboBox m_ROMBankSelect; + juce::String m_previousPath; + juce::String m_LastFileUsed; + juce::TableListBox m_patchList; + juce::String m_LastPatchSelected; + + int m_LastBankRomNoUsed; + + bool m_bIsFileMode; + + // Inherited via FileBrowserListener + void selectionChanged() override; + void fileClicked(const juce::File &file, const juce::MouseEvent &e) override; + void fileDoubleClicked(const juce::File &file) override; + void browserRootChanged(const juce::File &newRoot) override; + + // Inherited via TableListBoxModel + virtual int getNumRows() override; + virtual void paintRowBackground(juce::Graphics &, int rowNumber, int width, int height, + bool rowIsSelected) override; + virtual void paintCell(juce::Graphics &, int rowNumber, int columnId, int width, int height, + bool rowIsSelected) override; + + virtual void selectedRowsChanged(int lastRowSelected) override; + virtual void cellDoubleClicked (int rowNumber, int columnId, const juce::MouseEvent &) override; + void sortOrderChanged(int newSortColumnId, bool isForwards) override; + class PatchBrowserSorter; + + enum ColumnsPatch { + INDEX = 1, + NAME = 2, + CAT1 = 3, + CAT2 = 4, + ARP = 5, + UNI = 6, + ST = 7, + VER = 8, + }; + + std::unique_ptr<juce::Drawable> m_background; +}; + + +class PatchBrowser::PatchBrowserSorter +{ +public: + PatchBrowserSorter (int attributeToSortBy, bool forwards) + : attributeToSort (attributeToSortBy), + direction (forwards ? 1 : -1) + {} + + int compareElements (Patch first, Patch second) const + { + if(attributeToSort == ColumnsPatch::INDEX) { + return direction * (first.progNumber - second.progNumber); + } + else if (attributeToSort == ColumnsPatch::NAME) { + return direction * first.name.compareIgnoreCase(second.name); + } + else if (attributeToSort == ColumnsPatch::CAT1) { + return direction * (first.category1 - second.category1); + } + else if (attributeToSort == ColumnsPatch::CAT2) { + return direction * (first.category2 - second.category2); + } + else if (attributeToSort == ColumnsPatch::ARP) { + return direction * (first.data[129]- second.data[129]); + } + else if (attributeToSort == ColumnsPatch::UNI) { + return direction * (first.unison - second.unison); + } + else if (attributeToSort == ColumnsPatch::VER) { + return direction * (first.model - second.model); + } + else if (attributeToSort == ColumnsPatch::ST) { + return direction * (first.transpose - second.transpose); + } + return direction * (first.progNumber - second.progNumber); + } + +private: + int attributeToSort; + int direction; +}; + +