commit 3785beee333f83380ae67769a8b411cfae5edd61 parent d8af9ce9d85f9c35cbe3ae5564e691ca9a2c5b99 Author: trancy2k5 <63122430+trancy2k5@users.noreply.github.com> Date: Wed, 19 Jan 2022 00:05:04 +0100 - Added new GUI - Added new bitmaps with minor corrections - Redesign GUI - Added new Section OSC/Filter - Added new Section LFO/Matrix - Added new Section Effects - Added new Section ARP/General (some missing knobs and sections) - Added new Section Patch Library - Unused areas, if e.g. a delay is selected, the reverb area is greyed out and no longer made invisible - Main display now shows part number and bank number - New tooltip introduced - New concept introduced. Either loading from file (mid/syx) or from ROM. - Small knobs added (used with channels) - LookAndFeel revised - Added embedded font Diffstat:
55 files changed, 3606 insertions(+), 17 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -1,3 +1,6 @@ temp/ vtune/ *.aps +/.vs +/out/build/x64-Debug +/temp2/cmake_win64 diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -28,21 +28,37 @@ 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) + install(TARGETS jucePlugin1_VST DESTINATION . COMPONENT VST2) endif() - install(TARGETS jucePlugin_VST3 DESTINATION . COMPONENT VST3) + install(TARGETS jucePlugin1_VST3 DESTINATION . COMPONENT VST3) if(APPLE) - install(TARGETS jucePlugin_AU DESTINATION . COMPONENT AU) + install(TARGETS jucePlugin1_AU DESTINATION . COMPONENT AU) endif() elseif(UNIX) if(JUCE_GLOBAL_VST2_SDK_PATH) - install(TARGETS jucePlugin_VST LIBRARY DESTINATION /usr/local/lib/lxvst/ COMPONENT VST2) + install(TARGETS jucePlugin1_VST LIBRARY DESTINATION /usr/local/lib/lxvst/ COMPONENT VST2) endif() - install(TARGETS jucePlugin_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT VST3) + install(TARGETS jucePlugin1_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT VST3) + endif() + endif() + + if(MSVC OR APPLE) + if(JUCE_GLOBAL_VST2_SDK_PATH) + install(TARGETS jucePlugin2_VST DESTINATION . COMPONENT VST2) + endif() + install(TARGETS jucePlugin2_VST3 DESTINATION . COMPONENT VST3) + if(APPLE) + install(TARGETS jucePlugin2_AU DESTINATION . COMPONENT AU) + endif() + elseif(UNIX) + if(JUCE_GLOBAL_VST2_SDK_PATH) + install(TARGETS jucePlugin2_VST LIBRARY DESTINATION /usr/local/lib/lxvst/ COMPONENT VST2) + endif() + install(TARGETS jucePlugin2_VST3 LIBRARY DESTINATION /usr/local/lib/vst3/ COMPONENT VST3) endif() -endif() # ----------------- Test Console diff --git a/build_win64.bat b/build_win64.bat @@ -1,5 +1,5 @@ set outdir=temp\cmake_win64\ -cmake . -B %outdir% -G "Visual Studio 15 2017 Win64" +cmake . -B %outdir% -G "Visual Studio 16 2019" IF %ERRORLEVEL% NEQ 0 ( popd exit /B 2 diff --git a/source/jucePlugin/CMakeLists.txt b/source/jucePlugin/CMakeLists.txt @@ -1,5 +1,6 @@ + cmake_minimum_required(VERSION 3.15) -project(jucePlugin VERSION ${CMAKE_PROJECT_VERSION}) +project(jucePlugin1 VERSION ${CMAKE_PROJECT_VERSION}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h) @@ -9,7 +10,7 @@ else() set(VST "") endif() -juce_add_plugin(jucePlugin +juce_add_plugin(jucePlugin1 # 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 ... @@ -24,10 +25,9 @@ juce_add_plugin(jucePlugin 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" # The name of the final executable, which can differ from the target name + PRODUCT_NAME "DSP56300Emu_Skin1" # The name of the final executable, which can differ from the target name ) - -target_sources(jucePlugin +target_sources(jucePlugin1 PRIVATE PluginEditor.cpp PluginEditor.h @@ -63,7 +63,7 @@ PRIVATE # 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 +juce_add_binary_data(jucePlugin_BinaryData1 SOURCES "assets/bg_1377x800.png" "assets/panels/bg_arp_1018x620.png" @@ -93,17 +93,18 @@ juce_add_binary_data(jucePlugin_BinaryData "assets/knobs/multi_18x18_100.png" ) -target_compile_definitions(jucePlugin +target_compile_definitions(jucePlugin1 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 +target_link_libraries(jucePlugin1 PRIVATE - jucePlugin_BinaryData + jucePlugin_BinaryData1 juce::juce_audio_utils juce::juce_cryptography PUBLIC @@ -114,5 +115,126 @@ PUBLIC ) if(UNIX AND NOT APPLE) - target_link_libraries(jucePlugin PUBLIC -static-libgcc -static-libstdc++) + target_link_libraries(jucePlugin1 PUBLIC -static-libgcc -static-libstdc++) endif() + + + +#SKIN2--------------------------------------------------------------------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 3.15) +project(jucePlugin2 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(jucePlugin2 + # 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_Skin2" # The name of the final executable, which can differ from the target name +) +target_sources(jucePlugin2 +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_BinaryData2 + 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(jucePlugin2 +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(jucePlugin2 +PRIVATE + jucePlugin_BinaryData2 + 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(jucePlugin2 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,44 @@ +#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)) +{ + double dScaleFactor = 0.75; + const auto config = processorRef.getController().getConfig(); + + setResizable(true,false); + dScaleFactor = config->getDoubleValue("skin_scale_factor", 0.75f); + setScaleFactor(dScaleFactor); + setSize(m_virusEditor->iSkinSizeWidth, m_virusEditor->iSkinSizeHeight); + m_virusEditor->m_AudioPlugInEditor = (AudioPluginAudioProcessorEditor*)this; + + addAndMakeVisible(m_virusEditor); +} + +AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor() +{ +} + +//============================================================================== +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/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,42 @@ +#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; + +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"; +} +\ No newline at end of file diff --git a/source/jucePlugin/ui2/VirusEditor.cpp b/source/jucePlugin/ui2/VirusEditor.cpp @@ -0,0 +1,463 @@ +#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); + 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); }); + + // 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++; + }); + + m_mainMenu.setLookAndFeel (&m_landfButtons); + + m_mainMenu.onClick = [this]() + { + juce::PopupMenu selector; + juce::PopupMenu SubSkinSizeSelector; + + selector.setLookAndFeel(&m_landfButtons); + SubSkinSizeSelector.setLookAndFeel(&m_landfButtons); + + 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(); + }); + } + + selector.addSubMenu("Skin size", SubSkinSizeSelector, true); + selector.addItem("About", [this]() { AboutWindow(); }); + selector.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); + + //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.setColour(juce::Label::textColourId, juce::Colours::red); + m_patchName.setEditable(false, true, true); + m_patchName.onTextChange = [this]() + { + auto text = m_patchName.getText(); + if(text.trim().length() > 0) { + if (text == "/pv") { // stupid debug thing to remove later + m_paramDisplayLocal = !m_paramDisplayLocal; + return; + } + m_controller.setSinglePresetName(m_controller.getCurrentPart(), text); + } + }; + addAndMakeVisible(m_patchName); + + //MainDisplay + m_controlLabel.setBounds(1473, 35, 650, 58); + m_controlLabel.setColour(juce::Label::textColourId, juce::Colours::red); + 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.setColour(juce::Label::textColourId, juce::Colours::red); + 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); + + //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); + + //m_controller.setCurrentPartPreset(0,virusLib::BankNumber::A,0); + + //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]() { + processorRef.getController().setCurrentPartPreset(m_controller.getCurrentPart(), processorRef.getController().getCurrentPartBank(m_controller.getCurrentPart()), + std::max(0, processorRef.getController().getCurrentPartProgram(m_controller.getCurrentPart()) - 1)); + }; + m_PresetRight.onClick = [this]() + { + processorRef.getController().setCurrentPartPreset(m_controller.getCurrentPart(), processorRef.getController().getCurrentPartBank(m_controller.getCurrentPart()), + std::min(127, processorRef.getController().getCurrentPartProgram(m_controller.getCurrentPart()) + 1)); + }; + + addAndMakeVisible(m_PresetPatchList); + m_PresetPatchList.setBounds(2133 - m_PresetPatchList.kWidth / 2, 62 - m_PresetPatchList.kHeight / 2, m_PresetPatchList.kWidth, m_PresetPatchList.kHeight); + m_PresetPatchList.onClick = [this]() {ShowMenuePatchList(); }; + + //Show Version + m_version.setText(std::string(g_pluginVersionString), NotificationType::dontSendNotification); + m_version.setBounds(250, 1124, 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, 1124, 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, 1124, 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); + + m_controller.onProgramChange = [this]() + { + updateParts(); + }; + + m_controller.getBankCount(); + addMouseListener(this, true); + setSize (kPanelWidth, kPanelHeight); + recreateControls(); + updateParts(); +} + + +void VirusEditor::updateParts() +{ + // ugly (polling!) way for refreshing presets names as this is temporary ui + const auto multiMode = m_controller.isMultiMode(); + for (auto pt = 0; pt < 16; pt++) + { + bool singlePartOrInMulti = pt == 0 || multiMode; + if (pt == m_controller.getCurrentPart()) + { + int iZeroCount; + if (m_controller.getCurrentPartProgram(m_controller.getCurrentPart()) < 10) iZeroCount = 2; + else if (m_controller.getCurrentPartProgram(m_controller.getCurrentPart()) < 100) iZeroCount = 1; + else iZeroCount = 0; + + const auto patchName = m_controller.getCurrentPartPresetName(pt); + if(m_patchName.getText() != patchName) + { + String sZero = ""; + for (int i=0; i<iZeroCount; i++) + { + sZero=sZero+"0"; + } + m_patchName.setText("["+juce::String(m_controller.getCurrentPart()+1) + +"][" + getCurrentPartBankStr(m_controller.getCurrentPartBank(m_controller.getCurrentPart())) + "] " + + sZero + + juce::String(processorRef.getController().getCurrentPartProgram(m_controller.getCurrentPart()))+": " + patchName, NotificationType::dontSendNotification); + } + } + } +} + + + +void VirusEditor::ShowMenuePatchList() +{ + juce::PopupMenu selector; + auto pt = m_controller.getCurrentPart(); + + selector.setLookAndFeel (&m_landfButtons); + + for (uint8_t b = 0; b < m_controller.getBankCount(); ++b) + { + const auto bank = virusLib::fromArrayIndex(b); + auto presetNames = m_controller.getSinglePresetNames(bank); + juce::PopupMenu p; + for (uint8_t j = 0; j < 128; j++) + { + const auto presetName = presetNames[j]; + p.addItem(presetNames[j], [this, bank, j, pt, presetName] { + m_controller.setCurrentPartPreset(pt, bank, j); + //m_presetNames[pt].setButtonText(presetName); + }); + } + std::stringstream bankName; + bankName << "Bank " << static_cast<char>('A' + b); + selector.addSubMenu(std::string(bankName.str()), p); + } + selector.showMenu(juce::PopupMenu::Options()); +} + + +VirusEditor::~VirusEditor() +{ + setLookAndFeel(nullptr); + m_controller.onProgramChange = nullptr; +} + +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();}; + 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); + 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,108 @@ +#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 +{ +public: + VirusEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef); + ~VirusEditor() override; + void resized() override; + void recreateControls(); + void updatePartsPresetNames(); + void ShowMenuePatchList(); + void updateParts(); + + enum Commands { + None, + Rebind = 0x100, + UpdateParts = 0x101, + }; + + const int iSkinSizeWidth = 2501, iSkinSizeHeight = 1152; + juce::AudioProcessorEditor *m_AudioPlugInEditor; + +private: + void handleCommandMessage(int commandId) override; + + juce::Label m_version; + juce::Label m_SynthModel; + 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; + + 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,80 @@ +#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(); + //s.setPopupDisplayEnabled(true, true, nullptr, 2000); + + const auto step = roundToInt(m_knobImageSteps.convertFrom0to1(sliderPosProportional)); + 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(); + //s.setPopupDisplayEnabled(true, true, nullptr, 2000); + + const auto step = roundToInt(m_knobImageSteps.convertFrom0to1(sliderPosProportional)); + 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 = 60; +constexpr auto comboBox2Width = 49; + +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(1884+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,69 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" +#include "Virus_LookAndFeel.h" + +class VirusParameterBinding; + +class OscEditor : public juce::Component +{ +public: + OscEditor(VirusParameterBinding& _parameterBinding); + void resized() override; + //Virus::BoxLookAndFeel landf; + + +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,326 @@ +#include "Virus_Panel3_FxEditor.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "../VirusParameterBinding.h" + +using namespace juce; + + +FxEditor::FxEditor(VirusParameterBinding &_parameterBinding) +{ + 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); + + //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_reverbClock); + m_reverbClock.setBounds(467+comboBoxXMargin - comboBox3Width / 2, 803 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + + addAndMakeVisible(m_reverbType); + m_reverbType.setBounds(467+comboBoxXMargin - comboBox3Width / 2, 900 - 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_reverbClock, Virus::Param_DelayClock); + _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); + + startTimerHz(2); +} + +void FxEditor::timerCallback() +{ + //Delay/Reverb + int iSelectedIndex = m_delayReverbMode.getSelectedItemIndex(); + bool bReverb = (iSelectedIndex >= 2 && iSelectedIndex <= 4); + float fReverbAlpha = (iSelectedIndex >= 2 && iSelectedIndex <= 4)?1.0f:0.3f; + bool bDelay = (iSelectedIndex ==1 || iSelectedIndex > 5); + float fDelayAlpha = (iSelectedIndex ==1 || iSelectedIndex > 5)?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_reverbClock.setEnabled(bReverb); + m_reverbClock.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); + + //Vocoder + //m_vocoderMode + iSelectedIndex = m_vocoderMode.getSelectedItemIndex(); + bool bVocoder = (iSelectedIndex > 0); + float fAlpha = (iSelectedIndex > 0)?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); +} +\ 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,66 @@ +#pragma once + +#include "../PluginProcessor.h" +#include "Virus_Buttons.h" + + +class VirusParameterBinding; + +class FxEditor : public juce::Component, private juce::Timer +{ +public: + FxEditor(VirusParameterBinding& _parameterBinding); + +private: + void timerCallback(); + + // 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_reverbClock; + 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,487 @@ +#include "Virus_Panel4_ArpEditor.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "../VirusParameterBinding.h" +#include "VirusEditor.h" + +using namespace juce; +using namespace virusLib; + +Virus::LookAndFeelSmallButton m_lookAndFeelSmallButton; + +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()); + bRunning = false; + //setLookAndFeel(&m_lookAndFeel); + + //Mode + m_WorkingMode.addItem("Single",1); + m_WorkingMode.addItem("Multi & Single",2); + m_WorkingMode.addItem("Multi",3); + m_WorkingMode.setBounds(1234 + comboBoxXMargin - comboBox3Width / 2, 868 - comboBox3Height / 2, comboBox3Width, comboBox3Height); + m_WorkingMode.setSelectedItemIndex(0); + + m_WorkingMode.onChange = [this]() + { + if (bRunning ) + { + if (m_WorkingMode.getSelectedItemIndex()==0) + setPlayMode(virusLib::PlayMode::PlayModeSingle); + else if (m_WorkingMode.getSelectedItemIndex()==1) + setPlayMode(virusLib::PlayMode::PlayModeMultiSingle); + else + setPlayMode(virusLib::PlayMode::PlayModeMulti); + } + }; + + addAndMakeVisible(m_WorkingMode); + + + //MIDI settings + auto midiIn = m_properties->getValue("midi_input", ""); + auto midiOut = m_properties->getValue("midi_output", ""); + + if (midiIn != "") + { + processorRef.setMidiInput(midiIn); + } + if (midiOut != "") + { + processorRef.setMidiOutput(midiOut); + } + + m_cmbMidiInput.setBounds(1515 - 250 / 2, 827 - 47 / 2, 250, 47); + m_cmbMidiOutput.setBounds(1515 - 250 / 2, 926 - 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()); }; + + int iMarginYChannels = 118; + int iMarginXChannels = 0; + int iIndex = 0; + + //Channels + for (auto pt = 0; pt < 16; pt++) + { + /*m_partLabels[pt].setBounds(34, 161 + pt * (36), 24, 36); + m_partLabels[pt].setText(juce::String(pt + 1), juce::dontSendNotification); + m_partLabels[pt].setColour(0, juce::Colours::white); + m_partLabels[pt].setColour(1, juce::Colour(45, 24, 24)); + m_partLabels[pt].setJustificationType(Justification::centred); + addAndMakeVisible(m_partLabels[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(195 - 120/2 + iMarginXChannels, 104 - 50/2 + iIndex * (iMarginYChannels), 120, 38); + m_presetNames[pt].setText(m_controller.getCurrentPartPresetName(pt), juce::dontSendNotification); + m_presetNames[pt].setFont(juce::Font("Arial", "Normal", 20.f)); + m_presetNames[pt].setColour(juce::Label::ColourIds::textColourId,juce::Colours::red); + + addAndMakeVisible(m_PresetPatch[pt]); + m_PresetPatch[pt].setBounds(275 - m_PresetPatch[pt].kWidth / 2 + iMarginXChannels, 96 - m_PresetPatch[pt].kHeight / 2 + iIndex * (iMarginYChannels), m_PresetPatch[pt].kWidth, m_PresetPatch[pt].kHeight); + //m_PresetPatchList.onClick = [this]() {ShowMenuePatchList(); }; + + m_PresetPatch[pt].onClick = [this, pt]() { + juce::PopupMenu selector; + + for (uint8_t b = 0; b < m_controller.getBankCount(); ++b) + { + const auto bank = virusLib::fromArrayIndex(b); + auto presetNames = m_controller.getSinglePresetNames(bank); + juce::PopupMenu p; + for (uint8_t j = 0; j < 128; j++) + { + const auto presetName = presetNames[j]; + p.addItem(presetNames[j], [this, bank, j, pt, presetName] { + m_controller.setCurrentPartPreset(pt, bank, j); + m_PresetPatch[pt].setButtonText(presetName); + }); + } + std::stringstream bankName; + bankName << "Bank " << static_cast<char>('A' + b); + selector.addSubMenu(std::string(bankName.str()), p); + } + selector.showMenu(juce::PopupMenu::Options()); + }; + addAndMakeVisible(m_presetNames[pt]); + + + m_prevPatch[pt].setBounds(307 - m_prevPatch[pt].kWidth / 2 + iMarginXChannels, 96 - m_prevPatch[pt].kHeight / 2 + iIndex * (iMarginYChannels), m_prevPatch[pt].kWidth, m_prevPatch[pt].kHeight); + m_nextPatch[pt].setBounds(340 - m_nextPatch[pt].kWidth / 2 + iMarginXChannels, 96 - m_nextPatch[pt].kHeight / 2 + iIndex * (iMarginYChannels), m_nextPatch[pt].kWidth, m_nextPatch[pt].kHeight); + + m_prevPatch[pt].onClick = [this, pt]() + { + m_controller.setCurrentPartPreset( + pt, m_controller.getCurrentPartBank(pt), + std::max(0, m_controller.getCurrentPartProgram(pt) - 1)); + }; + m_nextPatch[pt].onClick = [this, pt]() + { + m_controller.setCurrentPartPreset( + pt, m_controller.getCurrentPartBank(pt), + std::min(127, m_controller.getCurrentPartProgram(pt) + 1)); + }; + + addAndMakeVisible(m_prevPatch[pt]); + addAndMakeVisible(m_nextPatch[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); + m_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); + m_parameterBinding.bind(m_partPans[pt], Virus::Param_Panorama, pt); + addAndMakeVisible(m_partPans[pt]); + + iIndex++; + } + m_partSelect[m_controller.getCurrentPart()].setToggleState(true, NotificationType::sendNotification); + + /*m_btSingleMode.setRadioGroupId(0x3cf); + m_btMultiMode.setRadioGroupId(0x3cf); + m_btMultiSingleMode.setRadioGroupId(0x3cf); + addAndMakeVisible(m_btSingleMode); + addAndMakeVisible(m_btMultiMode); + addAndMakeVisible(m_btMultiSingleMode); + /m_btSingleMode.setTopLeftPosition(102, 756); + m_btSingleMode.setSize(70, 30); + + m_btMultiMode.getToggleStateValue().referTo(*m_controller.getParamValue(Virus::Param_PlayMode)); + m_btSingleMode.setClickingTogglesState(true); + m_btMultiMode.setClickingTogglesState(true); + m_btMultiSingleMode.setClickingTogglesState(true); + + m_btSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white); + m_btSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey); + m_btMultiMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white); + m_btMultiMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey); + m_btMultiSingleMode.setColour(TextButton::ColourIds::textColourOnId, juce::Colours::white); + m_btMultiSingleMode.setColour(TextButton::ColourIds::textColourOffId, juce::Colours::grey); + + m_btSingleMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeSingle); }; + m_btMultiSingleMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeMultiSingle); }; + m_btMultiMode.onClick = [this]() { setPlayMode(virusLib::PlayMode::PlayModeMulti); }; + + m_btMultiSingleMode.setBounds(m_btSingleMode.getBounds().translated(m_btSingleMode.getWidth()+4, 0)); + m_btMultiMode.setBounds(m_btMultiSingleMode.getBounds().translated(m_btMultiSingleMode.getWidth()+4, 0)); + + const uint8_t playMode = g_playMode; + if (playMode == virusLib::PlayModeSingle) { + m_btSingleMode.setToggleState(true, juce::dontSendNotification); + } + else if (playMode == virusLib::PlayModeMultiSingle) { + m_btMultiSingleMode.setToggleState(true, juce::dontSendNotification); + } + else if (playMode == virusLib::PlayModeMulti) { + m_btMultiMode.setToggleState(true, juce::dontSendNotification); + }*/ + + + + startTimerHz(5); + +} + +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::Rebind); +} + +void ArpEditor::setPlayMode(uint8_t _mode) +{ + m_controller.getParameter(Virus::Param_PlayMode)->setValue(_mode); + g_playMode = _mode; + //getParentComponent()->postCommandMessage(VirusEditor::Commands::Rebind); +} + +void ArpEditor::timerCallback() +{ + 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_PresetPatch[pt].setEnabled(singlePartOrInMulti); + m_PresetPatch[pt].setAlpha(fAlpha); + m_partSelect[pt].setEnabled(singlePartOrInMulti); + m_partSelect[pt].setAlpha(fAlpha); + m_presetNames[pt].setEnabled(singlePartOrInMulti); + m_presetNames[pt].setAlpha(fAlpha); + m_prevPatch[pt].setEnabled(singlePartOrInMulti); + m_prevPatch[pt].setAlpha(fAlpha); + m_nextPatch[pt].setEnabled(singlePartOrInMulti); + m_nextPatch[pt].setAlpha(fAlpha); + } + if (singlePartOrInMulti) + m_presetNames[pt].setText(m_controller.getCurrentPartPresetName(pt), juce::dontSendNotification); + } + bRunning=true; +} + +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; +} + + +#if NULL +ArpEditor::VelocityAmount::VelocityAmount(VirusParameterBinding &_parameterBinding) +{ + constexpr auto y = 19; + for (auto *s : {&m_osc1Shape, &m_filter1Freq, &m_filter1Res, &m_pulseWidth, &m_volume, &m_panorama, &m_osc2Shape, + &m_filter2Freq, &m_filter2Res, &m_fmAmount}) + setupRotary(*this, *s); + m_osc1Shape.setBounds(31, y, knobSize, knobSize); + m_filter1Freq.setBounds(m_osc1Shape.getRight() - 7, y, knobSize, knobSize); + m_filter1Res.setBounds(m_filter1Freq.getRight() - 8, y, knobSize, knobSize); + m_pulseWidth.setBounds(m_filter1Res.getRight() - 7, y, knobSize, knobSize); + m_volume.setBounds(m_pulseWidth.getRight() - 3, y, knobSize, knobSize); + m_panorama.setBounds(m_volume.getRight() - 9, y, knobSize, knobSize); + + const auto y2 = m_osc1Shape.getBottom() + y; + m_osc2Shape.setBounds(31, y2, knobSize, knobSize); + m_filter2Freq.setBounds(m_osc1Shape.getRight() - 7, y2, knobSize, knobSize); + m_filter2Res.setBounds(m_filter1Freq.getRight() - 8, y2, knobSize, knobSize); + m_fmAmount.setBounds(m_filter1Res.getRight() - 7, y2, knobSize, knobSize); + + _parameterBinding.bind(m_osc1Shape, Virus::Param_Osc1ShapeVelocity); + _parameterBinding.bind(m_filter1Freq, Virus::Param_Filter1EnvAmtVelocity); + _parameterBinding.bind(m_filter1Res, Virus::Param_Resonance1Velocity); + _parameterBinding.bind(m_pulseWidth, Virus::Param_PulseWidthVelocity); + _parameterBinding.bind(m_volume, Virus::Param_AmpVelocity); + _parameterBinding.bind(m_panorama, Virus::Param_PanoramaVelocity); + _parameterBinding.bind(m_osc2Shape, Virus::Param_Osc2ShapeVelocity); + _parameterBinding.bind(m_filter2Freq, Virus::Param_Filter2EnvAmtVelocity); + _parameterBinding.bind(m_filter2Res, Virus::Param_Resonance2Velocity); + _parameterBinding.bind(m_fmAmount, Virus::Param_FmAmountVelocity); +} + +ArpEditor::Inputs::Inputs(VirusParameterBinding &_parameterBinding) +{ + addAndMakeVisible(m_inputMode); + m_inputMode.setBounds(43, 38, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_inputSelect); + m_inputSelect.setBounds(145, 38, comboBoxWidth, comboBoxHeight); + + _parameterBinding.bind(m_inputMode, Virus::Param_InputMode); + _parameterBinding.bind(m_inputSelect, Virus::Param_InputSelect); +} + +ArpEditor::Arpeggiator::Arpeggiator(VirusParameterBinding &_parameterBinding) +{ + constexpr auto y = 18; + for (auto *s : {&m_globalTempo, &m_noteLength, &m_noteSwing}) + setupRotary(*this, *s); + m_globalTempo.setBounds(341, y, knobSize, knobSize); + m_noteLength.setBounds(m_globalTempo.getRight() - 8, y, knobSize, knobSize); + m_noteSwing.setBounds(m_noteLength.getRight() - 7, y, knobSize, knobSize); + + for (auto *c : {&m_mode, &m_pattern, &m_octaveRange, &m_resolution}) + addAndMakeVisible(c); + + constexpr auto comboBoxWidth = 90; + constexpr auto comboBoxHeight = 15; + constexpr auto comboTopY = 35; + + m_mode.setBounds(35, 40, 100, 18); + m_pattern.setBounds(114, comboTopY, comboBoxWidth, comboBoxHeight); + m_resolution.setBounds(220, comboTopY, comboBoxWidth, comboBoxHeight); + m_octaveRange.setBounds(m_pattern.getBounds().translated(0, comboBoxHeight + 18)); + + addAndMakeVisible(m_arpHold); + m_arpHold.setBounds(222, m_octaveRange.getY()+2, 28, 11); + + _parameterBinding.bind(m_globalTempo, Virus::Param_ClockTempo); + _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); +} + +ArpEditor::SoftKnobs::SoftKnobs(VirusParameterBinding &_parameterBinding) +{ + auto distance = 105; + for (auto i = 0; i < 2; i++) + { + addAndMakeVisible(m_funcAs[i]); + m_funcAs[i].setBounds(i == 0 ? 18 : 338, 42, comboBoxWidth, comboBoxHeight); + addAndMakeVisible(m_name[i]); + m_name[i].setBounds(m_funcAs[i].getX() + distance, 42, comboBoxWidth, comboBoxHeight); + } +} + +ArpEditor::PatchSettings::PatchSettings(VirusParameterBinding &_parameterBinding) +{ + constexpr auto y = 18; + for (auto *s : {&m_patchVolume, &m_panning, &m_outputBalance, &m_transpose}) + setupRotary(*this, *s); + m_patchVolume.setBounds(101, y, knobSize, knobSize); + m_panning.setBounds(m_patchVolume.getRight() - 9, y, knobSize, knobSize); + const auto y2 = m_patchVolume.getBottom() + 9; + m_outputBalance.setBounds(m_patchVolume.getX(), y2, knobSize, knobSize); + m_transpose.setBounds(m_panning.getX(), y2, knobSize, knobSize); + + for (auto *cb : + {&m_keyMode, &m_secondaryOutput, &m_bendUp, &m_bendDown, &m_bendScale, &m_smoothMode, &m_cat1, &m_cat2}) + addAndMakeVisible(cb); + + + constexpr auto yDist = 50; + m_keyMode.setBounds(18, 42, comboBoxWidth, comboBoxHeight); + m_secondaryOutput.setBounds(18, 122, comboBoxWidth, comboBoxHeight); + constexpr auto x1 = 338; + constexpr auto x2 = 444; + m_bendUp.setBounds(x1, 42, comboBoxWidth, comboBoxHeight); + m_bendScale.setBounds(x1, 42 + yDist, comboBoxWidth, comboBoxHeight); + m_cat1.setBounds(x1, m_bendScale.getY() + yDist - 1, comboBoxWidth, comboBoxHeight); + m_bendDown.setBounds(x2, 42, comboBoxWidth, comboBoxHeight); + m_smoothMode.setBounds(x2, m_bendScale.getY(), comboBoxWidth, comboBoxHeight); + m_cat2.setBounds(x2, m_cat1.getY(), comboBoxWidth, comboBoxHeight); + + _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_secondaryOutput, 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); +} +#endif +\ 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,70 @@ +#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, private juce::Timer +{ +public: + ArpEditor(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef); + static constexpr auto kPartGroupId = 0x3FBBC; +private: + void updateMidiInput(int index); + void updateMidiOutput(int index); + + //WorkingMode + juce::ComboBox m_WorkingMode; + + //Channels + void changePart(uint8_t _part); + void setPlayMode(uint8_t _mode); + void timerCallback() override; + bool bRunning; + Virus::Controller &m_controller; + VirusParameterBinding &m_parameterBinding; + + Buttons::Button3 m_partSelect[16]; + //juce::Label m_partLabels[16]; + Buttons::PresetButtonDown m_PresetPatch[16]; + juce::Label m_presetNames[16]; + Buttons::PresetButtonRight m_nextPatch[16]; + Buttons::PresetButtonLeft m_prevPatch[16]; + juce::Slider m_partVolumes[16]; + juce::Slider m_partPans[16]; + + //juce::TextButton m_btSingleMode; + //juce::TextButton m_btMultiSingleMode; + //juce::TextButton m_btMultiMode; + + //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_softknob1, m_softknob2; + + //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,579 @@ +#include "../VirusParameterBinding.h" +#include "BinaryData.h" +#include "Ui_Utils.h" +#include "Virus_Panel5_PatchBrowser.h" +#include <juce_gui_extra/juce_gui_extra.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::canSelectFiles, File::getSpecialLocation(File::SpecialLocationType::currentApplicationFile), &m_fileFilter, NULL) +{ + setupBackground(*this, m_background, BinaryData::panel_5_png, BinaryData::panel_5_pngSize); + bRunning = false; + + auto bankDir = m_properties->getValue("virus_bank_dir", ""); + if (bankDir != "" && juce::File(bankDir).isDirectory()) + { + m_bankList.setRoot(bankDir); + } + + setLookAndFeel(&m_landf); + + setBounds(0, 0, kPanelWidth, kPanelHeight); + + m_modeIndex = m_properties->getIntValue("patch_mode", 1); + + m_bankList.setBounds(0, 50/fBrowserScaleFactor , 1030/fBrowserScaleFactor , 935/fBrowserScaleFactor ); + //m_bankList.setLookAndFeel (&m_landf); + + + //PatchList + m_patchList.setBounds(1049/fBrowserScaleFactor , 50/fBrowserScaleFactor , 1010/fBrowserScaleFactor , 930/fBrowserScaleFactor ); + //m_patchList.setLookAndFeel (&m_landf); + + m_patchList.getHeader().addColumn("#", ColumnsPatch::INDEX, 32); + m_patchList.getHeader().addColumn("Name", ColumnsPatch::NAME, 140); + m_patchList.getHeader().addColumn("Category 1", ColumnsPatch::CAT1, 90); + m_patchList.getHeader().addColumn("Category 2", ColumnsPatch::CAT2, 90); + m_patchList.getHeader().addColumn("Arp", ColumnsPatch::ARP, 32); + m_patchList.getHeader().addColumn("Uni", ColumnsPatch::UNI, 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); + + //ROM Bank + m_romBankList.setBounds(10, 50/fBrowserScaleFactor , 970/fBrowserScaleFactor , 935/fBrowserScaleFactor ); + m_romBankList.getHeader().addColumn("ROM BANK", ColumnsRomBanks::ROM_BANK_NAME, 450); + m_romBankList.setTransform(AffineTransform::scale(fBrowserScaleFactor)); + + RomBank rRomBank; + for (int i=1;i<=m_controller.getBankCount();i++) + { + rRomBank.m_RomNumber = static_cast<virusLib::BankNumber>(static_cast<int>(rRomBank.m_RomNumber) + 1) ; + m_romBankes.add(rRomBank); + } + + m_romBankList.setModel(this); + m_romBankList.updateContent(); + m_romBankList.deselectAllRows(); + m_romBankList.repaint(); // force repaint since row number doesn't often change + m_romBankList.selectRow(0); + + + //Show Options Buttons/Cmb + addAndMakeVisible(m_LoadBank); + addAndMakeVisible(m_SavePreset); + addAndMakeVisible(m_Mode); + addAndMakeVisible(m_romBankList); + addAndMakeVisible(m_bankList); + + m_LoadBank.setBounds(2195 - m_LoadBank.kWidth / 2, 182 - m_LoadBank.kHeight / 2, m_LoadBank.kWidth, m_LoadBank.kHeight); + m_SavePreset.setBounds(2195 - m_SavePreset.kWidth / 2, 241 - m_SavePreset.kHeight / 2, m_SavePreset.kWidth, m_SavePreset.kHeight); + m_Mode.setBounds(2206+comboBoxXMargin - comboBox3Width / 2, 83 - comboBox3Height / 2, comboBox3Width+5, comboBox3Height-5); + + m_Mode.setLookAndFeel(&m_lookAndFeel); + m_Mode.addItem("ROM",1); + m_Mode.addItem("FILE",2); + + m_Mode.onChange = [this]() + { + if(bRunning) + { + if (m_Mode.getSelectedItemIndex()==0) + { + //ROM + m_romBankList.setVisible(true); + m_bankList.setVisible(false); + m_romBankList.updateContent(); + m_romBankList.deselectAllRows(); + m_romBankList.repaint(); // force repaint since row number doesn't often change + m_romBankList.selectRow(0); + } + else + { + //FILE + m_romBankList.setVisible(false); + m_bankList.setVisible(true); + } + m_properties->setValue("patch_mode", m_Mode.getSelectedItemIndex()); + m_properties->save(); + m_modeIndex = m_Mode.getSelectedItemIndex(); + } + }; + + m_Mode.setSelectedItemIndex(m_modeIndex); + + m_LoadBank.onClick = [this]() { loadFile(); }; + m_SavePreset.onClick = [this]() { savePreset(); }; + + startTimerHz(4); +} + +void PatchBrowser::timerCallback() +{ + +} + +void PatchBrowser::selectionChanged() +{ + bRunning=true; //ugly +} + +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; + +} +void PatchBrowser::fileClicked(const juce::File &file, const juce::MouseEvent &e) +{ + auto ext = file.getFileExtension().toLowerCase(); + auto path = file.getParentDirectory().getFullPathName(); + juce::MemoryBlock data; + + file.loadFileAsData(data); + + m_properties->setValue("virus_bank_dir", path); + if (ext == ".syx") + { + m_patches.clear(); + loadBankFileToRom(file); + + int index = 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]; + 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); + } + m_patches.add(patch); + index++; + } + } + + m_patchList.updateContent(); + m_patchList.deselectAllRows(); + m_patchList.repaint(); // force repaint since row number doesn't often change + m_patchList.selectRow(0); + } + else if (ext == ".mid" || ext == ".midi") + { + m_patches.clear(); + loadBankFileToRom(file); + + uint8_t index = 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]; + 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); + } + 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]; + 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); + } + m_patches.add(patch); + index++; + + it += 266; + } + + } + + + } + + m_patchList.updateContent(); + m_patchList.deselectAllRows(); + m_patchList.repaint(); + m_patchList.selectRow(0); + } +} + +void PatchBrowser::fileDoubleClicked(const juce::File &file) {} + +void PatchBrowser::browserRootChanged(const File &newRoot) {} + +int PatchBrowser::getNumRows() +{ + if(m_modeIndex==1) + { + return m_patches.size(); + } + else if (m_modeIndex==0) + { + return m_romBankes.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 + if(m_modeIndex==1) + { + g.setColour(rowIsSelected ? juce::Colours::darkblue: getLookAndFeel().findColour(juce::ListBox::textColourId)); // [5] + juce::String text = ""; + + auto rowElement = m_patches[rowNumber]; + //auto text = rowElement.name; + + if (columnId == ColumnsPatch::INDEX) + text = juce::String(rowElement.progNumber); + 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::VER) + { + if(rowElement.model < ModelList.size()) + text = ModelList[rowElement.model]; + } + g.drawText(text, 2, 0, width - 4, 20, juce::Justification::centredLeft, true); // [6] + g.setColour(getLookAndFeel().findColour(juce::ListBox::backgroundColourId)); + g.fillRect(width - 1, 0, 1, 20); // [7] + } + else if (m_modeIndex==0) + { + g.setColour(rowIsSelected ? juce::Colours::darkblue: getLookAndFeel().findColour(juce::ListBox::textColourId)); // [5] + juce::String text = ""; + + auto rowElement = m_romBankes[rowNumber]; + juce::String sBankName="ERR"; + text = "Bank: "; + + switch (virusLib::BankNumber(rowNumber+1)) + { + case virusLib::BankNumber::A: text += "A"; break; + case virusLib::BankNumber::B: text += "B"; break; + case virusLib::BankNumber::C: text += "C"; break; + case virusLib::BankNumber::D: text += "D"; break; + case virusLib::BankNumber::E: text += "E"; break; + case virusLib::BankNumber::F: text += "F"; break; + case virusLib::BankNumber::G: text += "G"; break; + case virusLib::BankNumber::H: text += "H"; break; + } + + g.drawText(text, 2, 0, width - 4, 20, juce::Justification::centredLeft, true); // [6] + g.setColour(getLookAndFeel().findColour(juce::ListBox::backgroundColourId)); + g.fillRect(width - 1, 0, 1, 20); // [7] + } +} + +void PatchBrowser::selectedRowsChanged(int lastRowSelected) { + auto idx = m_patchList.getSelectedRow(); + + if (idx == -1) { + return; + } + + m_controller.setCurrentPartPreset(m_controller.getCurrentPart(),m_controller.getCurrentPartBank(m_controller.getCurrentPart()),lastRowSelected); + + /*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_patches[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 +} + +void PatchBrowser::cellDoubleClicked(int rowNumber, int columnId, const juce::MouseEvent &) +{ + + if(m_modeIndex==1) + { + if(rowNumber == m_patchList.getSelectedRow()) + { + selectedRowsChanged(0); + } + } + else if (m_modeIndex==0) + { + if(rowNumber == m_romBankList.getSelectedRow()) + { + selectedRowsChanged(0); + } + } +} + + + +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) + { + //Load first patch + 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.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,146 @@ +#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 Virus::LookAndFeelPatchBrowser; + +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; +}; + +struct RomBank +{ + virusLib::BankNumber m_RomNumber; +}; + +class PatchBrowser : public juce::Component, juce::FileBrowserListener, juce::TableListBoxModel, private juce::Timer +{ + +public: + PatchBrowser(VirusParameterBinding &_parameterBinding, AudioPluginAudioProcessor &_processorRef); + void loadFile(); + void loadBankFileToRom(const juce::File &file); + void savePreset(); + +private: + void timerCallback() override; + bool bRunning; + + Virus::LookAndFeelPatchBrowser m_landf; + Virus::LookAndFeel m_lookAndFeel; + + 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::TableListBox m_patchList; + juce::TableListBox m_romBankList; + juce::Array<RomBank> m_romBankes; + juce::Array<Patch> m_patches; + juce::PropertiesFile *m_properties; + + Buttons::OptionButtonLoadBank m_LoadBank; + Buttons::OptionButtonSavePreset m_SavePreset; + juce::ComboBox m_Mode; + int m_modeIndex; + + + juce::String m_previousPath; + + // 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, + VER = 7 + }; + + enum ColumnsRomBanks { + ROM_BANK_NAME = 1, + }; + + 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); + } + return direction * (first.progNumber - second.progNumber); + } + +private: + int attributeToSort; + int direction; +}; + +