gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit 3b8936105eacc1593a2593ab742a306213f42c73
parent f2fb41db86f780b47d0e3d312e60247587e89181
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Tue, 15 Mar 2022 21:38:37 +0100

add load/save preset functionality

Diffstat:
Msource/jucePlugin/genericUI/assets/VirusC.json | 29+++++++++++------------------
Msource/jucePlugin/genericUI/assets/assets.cmake | 36++++++++++++++++++------------------
Msource/jucePlugin/ui3/VirusEditor.cpp | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msource/jucePlugin/ui3/VirusEditor.h | 5+++++
4 files changed, 135 insertions(+), 36 deletions(-)

diff --git a/source/jucePlugin/genericUI/assets/VirusC.json b/source/jucePlugin/genericUI/assets/VirusC.json @@ -21,7 +21,7 @@ "name" : "singleLabel", "label" : { "text" : "SINGLE", - "textHeight" : "16", + "textHeight" : "18", "color" : "EBEBEBFF", "alignH" : "R", "alignV" : "C", @@ -76,7 +76,7 @@ "name" : "multiLabel", "label" : { "text" : "MULTI", - "textHeight" : "16", + "textHeight" : "18", "color" : "EBEBEBFF", "alignH" : "L", "alignV" : "C", @@ -2064,7 +2064,7 @@ "y" : "761", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenRed_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -2249,7 +2249,7 @@ "y" : "90,5", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenPol_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -2266,7 +2266,7 @@ "y" : "90,5", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenRed_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -2283,7 +2283,7 @@ "y" : "329,5", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenRed_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -2300,7 +2300,7 @@ "y" : "329,5", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenRed_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -2334,7 +2334,7 @@ "y" : "764", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenRed_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -2664,7 +2664,7 @@ "y" : "327", "width" : "140", "height" : "140", - "texture" : "Gen_140x140_x2", + "texture" : "GenPol_140x140_x2", "tileSizeX" : "140", "tileSizeY" : "140" } @@ -5616,11 +5616,7 @@ "x" : "954,88", "y" : "1116", "width" : "124,2466", - "height" : "36", - "tooltip" : "Select an additional physical Midi Input device for " - }, - "parameterAttachment" : { - "parameter" : "Category1" + "height" : "36" } }, { @@ -5634,9 +5630,6 @@ "y" : "1116", "width" : "124,2466", "height" : "36" - }, - "parameterAttachment" : { - "parameter" : "Category1" } } ] @@ -5671,7 +5664,7 @@ "color" : "FF7180FF", "x" : "74", "y" : "1543", - "width" : "730", + "width" : "627,718", "height" : "45" } }, diff --git a/source/jucePlugin/genericUI/assets/assets.cmake b/source/jucePlugin/genericUI/assets/assets.cmake @@ -1,29 +1,29 @@ set(ASSETS_VirusC - ${CMAKE_CURRENT_LIST_DIR}/VC_x2_N.png - ${CMAKE_CURRENT_LIST_DIR}/presest_btn_select_86x60_x2.png - ${CMAKE_CURRENT_LIST_DIR}/OSC_282x52_x2.png - ${CMAKE_CURRENT_LIST_DIR}/LFO_282x52_x2.png - ${CMAKE_CURRENT_LIST_DIR}/FX_282x52_x2.png + ${CMAKE_CURRENT_LIST_DIR}/ARP_2034x1238_x2.png ${CMAKE_CURRENT_LIST_DIR}/ARP_282x52_x2.png - ${CMAKE_CURRENT_LIST_DIR}/multi_btn_select_72x72_x2.png - ${CMAKE_CURRENT_LIST_DIR}/Mult_36x36_x2i.png - ${CMAKE_CURRENT_LIST_DIR}/OSC_2034x1238_x2.png + ${CMAKE_CURRENT_LIST_DIR}/arp_72x37_X2.png + ${CMAKE_CURRENT_LIST_DIR}/arpall_104x432_X2.png + ${CMAKE_CURRENT_LIST_DIR}/env_pol_100x68_x2.png + ${CMAKE_CURRENT_LIST_DIR}/FX_2034x1238_x2_D.png + ${CMAKE_CURRENT_LIST_DIR}/FX_282x52_x2.png + ${CMAKE_CURRENT_LIST_DIR}/FX_reverbOverlay.png ${CMAKE_CURRENT_LIST_DIR}/Gen_140x140_x2.png - ${CMAKE_CURRENT_LIST_DIR}/sync2_108x100_x2.png + ${CMAKE_CURRENT_LIST_DIR}/GenPol_140x140_x2.png + ${CMAKE_CURRENT_LIST_DIR}/GenRed_140x140_x2.png ${CMAKE_CURRENT_LIST_DIR}/handle_72x94_x2.png - ${CMAKE_CURRENT_LIST_DIR}/env_pol_100x68_x2.png - ${CMAKE_CURRENT_LIST_DIR}/link_horizon_48x72_x2.png ${CMAKE_CURRENT_LIST_DIR}/LFO_2034x1238_x2.png + ${CMAKE_CURRENT_LIST_DIR}/LFO_282x52_x2.png ${CMAKE_CURRENT_LIST_DIR}/lfo_btn_46x76_x2.png + ${CMAKE_CURRENT_LIST_DIR}/link_horizon_48x72_x2.png ${CMAKE_CURRENT_LIST_DIR}/link_vert_72x48_x2.png - ${CMAKE_CURRENT_LIST_DIR}/GenPol_140x140_x2.png - ${CMAKE_CURRENT_LIST_DIR}/FX_2034x1238_x2_D.png - ${CMAKE_CURRENT_LIST_DIR}/GenRed_140x140_x2.png - ${CMAKE_CURRENT_LIST_DIR}/FX_reverbOverlay.png - ${CMAKE_CURRENT_LIST_DIR}/ARP_2034x1238_x2.png - ${CMAKE_CURRENT_LIST_DIR}/arpall_104x432_X2.png - ${CMAKE_CURRENT_LIST_DIR}/arp_72x37_X2.png + ${CMAKE_CURRENT_LIST_DIR}/Mult_36x36_x2i.png + ${CMAKE_CURRENT_LIST_DIR}/multi_btn_select_72x72_x2.png + ${CMAKE_CURRENT_LIST_DIR}/OSC_2034x1238_x2.png + ${CMAKE_CURRENT_LIST_DIR}/OSC_282x52_x2.png ${CMAKE_CURRENT_LIST_DIR}/Pres_2034x1238_x2.png + ${CMAKE_CURRENT_LIST_DIR}/presest_btn_select_86x60_x2.png + ${CMAKE_CURRENT_LIST_DIR}/sync2_108x100_x2.png + ${CMAKE_CURRENT_LIST_DIR}/VC_x2_N.png ${CMAKE_CURRENT_LIST_DIR}/VirusC.json ) diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp @@ -49,6 +49,11 @@ namespace genericVirusUI versionInfo->setText(message, juce::dontSendNotification); } + auto* presetSave = findComponentT<juce::Button>("PresetSave"); + presetSave->onClick = [this] { savePreset(); }; + + auto* presetLoad = findComponentT<juce::Button>("PresetLoad"); + presetLoad->onClick = [this] { loadPreset(); }; } void VirusEditor::onProgramChange() @@ -138,6 +143,102 @@ namespace genericVirusUI m_playModeMulti->setToggleState(getController().isMultiMode(), juce::dontSendNotification); } + void VirusEditor::savePreset() + { + const auto path = getController().getConfig()->getValue("virus_bank_dir", ""); + m_fileChooser = std::make_unique<juce::FileChooser>( + "Save preset as syx", + m_previousPath.isEmpty() + ? (path.isEmpty() ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() : juce::File(path)) + : m_previousPath, + "*.syx", true); + + constexpr auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::FileChooserFlags::canSelectFiles; + + auto onFileChooser = [this](const juce::FileChooser& chooser) + { + if (chooser.getResults().isEmpty()) + return; + + 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 }; + constexpr 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++) + { + const auto param = getController().getParamValue(getController().getCurrentPart(), i < 128 ? 0 : 1, i & 127); + + data[i] = param ? static_cast<int>(param->getValue()) : 0; + cs += data[i]; + } + cs = cs & 0x7f; + + result.deleteFile(); + result.create(); + result.appendData(syxHeader, 9); + result.appendData(data, 256); + result.appendData(&cs, 1); + result.appendData(syxEof, 1); + }; + m_fileChooser->launchAsync(flags, onFileChooser); + } + + void VirusEditor::loadPreset() + { + m_fileChooser = std::make_unique<juce::FileChooser>( + "Choose syx/midi banks to import", + m_previousPath.isEmpty() + ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() + : m_previousPath, + "*.syx,*.mid,*.midi", true); + + constexpr auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::FileChooserFlags::canSelectFiles; + + const std::function onFileChooser = [this](const juce::FileChooser& chooser) + { + if (chooser.getResults().isEmpty()) + return; + + const auto result = chooser.getResult(); + m_previousPath = result.getParentDirectory().getFullPathName(); + const auto ext = result.getFileExtension().toLowerCase(); + + std::vector<Patch> patches; + ::PatchBrowser::loadBankFile(patches, nullptr, result); + + if (patches.empty()) + return; + + if (patches.size() == 1) + { + // load to edit buffer of current part + auto data = patches.front().sysex; + data[7] = virusLib::toMidiByte(virusLib::BankNumber::EditBuffer); + if (getController().isMultiMode()) + data[8] = getController().getCurrentPart(); + else + data[8] = virusLib::SINGLE; + getController().sendSysEx(data); + } + else + { + // load to bank A + for (const auto& p : patches) + { + auto data = p.sysex; + data[7] = virusLib::toMidiByte(virusLib::BankNumber::A); + getController().sendSysEx(data); + } + } + + getController().onStateLoaded(); + }; + m_fileChooser->launchAsync(flags, onFileChooser); + } + void VirusEditor::setPlayMode(uint8_t _playMode) { getController().getParameter(Virus::Param_PlayMode)->setValue(_playMode); diff --git a/source/jucePlugin/ui3/VirusEditor.h b/source/jucePlugin/ui3/VirusEditor.h @@ -32,6 +32,8 @@ namespace genericVirusUI void updatePresetName() const; void updatePlayModeButtons() const; + void savePreset(); + void loadPreset(); void setPlayMode(uint8_t _playMode); @@ -48,5 +50,8 @@ namespace genericVirusUI juce::TooltipWindow m_tooltipWindow; PatchBrowser m_patchBrowser; + + std::unique_ptr<juce::FileChooser> m_fileChooser; + juce::String m_previousPath; }; }