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 14b092463004a6cabe52a8fcc284f891fc7a6868
parent 5124f5e4ec9efa0adabb6ce57be2652e35c70b77
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Sun, 23 Apr 2023 17:12:15 +0200

Osirus 1.2.30

Diffstat:
MCMakeLists.txt | 38+++++++++++++++++++++++---------------
Mbuild_linux.sh | 3---
Mbuild_mac.sh | 1-
Mbuild_win32.bat | 1-
Mbuild_win64.bat | 2--
Mbuild_win64_vs19.bat | 2--
Mlinux_dependencies.sh | 3++-
Msource/juce.cmake | 12++++++++++++
Msource/jucePlugin/ui3/VirusEditor.cpp | 208+++++--------------------------------------------------------------------------
Msource/jucePlugin/ui3/VirusEditor.h | 25++++++++++---------------
Msource/jucePluginEditorLib/CMakeLists.txt | 2++
Asource/jucePluginEditorLib/focusedParameter.cpp | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/jucePluginEditorLib/focusedParameter.h | 39+++++++++++++++++++++++++++++++++++++++
Asource/jucePluginEditorLib/focusedParameterTooltip.cpp | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/jucePluginEditorLib/focusedParameterTooltip.h | 19+++++++++++++++++++
Msource/jucePluginEditorLib/pluginEditor.cpp | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msource/jucePluginEditorLib/pluginEditor.h | 20+++++++++++++++++---
Msource/jucePluginLib/CMakeLists.txt | 1+
Msource/jucePluginLib/controller.cpp | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msource/jucePluginLib/controller.h | 47++++++++++++++++++++++++++++-------------------
Asource/jucePluginLib/event.cpp | 5+++++
Asource/jucePluginLib/event.h | 44++++++++++++++++++++++++++++++++++++++++++++
Msource/jucePluginLib/midipacket.cpp | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msource/jucePluginLib/midipacket.h | 15+++++++++++++++
Msource/jucePluginLib/parameterbinding.cpp | 2+-
Msource/jucePluginLib/parameterbinding.h | 2+-
Msource/juceUiLib/condition.cpp | 6++++++
Msource/juceUiLib/condition.h | 1+
Msource/juceUiLib/uiObject.cpp | 3+++
Asource/runAuValidation.cmake | 39+++++++++++++++++++++++++++++++++++++++
Msource/synthLib/configFile.cpp | 2+-
Msource/synthLib/os.cpp | 12++++++++++--
Msource/synthLib/plugin.cpp | 2+-
Msource/virusLib/dspSingle.cpp | 2+-
Msource/virusLib/midiFileToRomData.cpp | 5+++--
35 files changed, 747 insertions(+), 276 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,9 +3,14 @@ cmake_minimum_required(VERSION 3.15) # build a fat binary that runs on both intel and the new Apple M1 chip set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "OS X Architectures") -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") +# Xcode 14+ can not build for anything < High Sierra anymore +if(CMAKE_GENERATOR STREQUAL Xcode AND XCODE_VERSION VERSION_GREATER_EQUAL 14.0.0) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version") +else() + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") +endif() -project(gearmulator VERSION 1.2.29) +project(gearmulator VERSION 1.2.30) include(base.cmake) include(CTest) @@ -13,14 +18,7 @@ include(CTest) option(${PROJECT_NAME}_BUILD_JUCEPLUGIN "Build Juce plugin" on) option(${PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP "Build CLAP version of Juce plugin" on) -# ----------------- source - -add_subdirectory(source) - -# ----------------- CPack - -get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) -list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") +# ----------------- CPack basic parameters message("CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME}) message("CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR}) @@ -31,22 +29,32 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") set(CPACK_SYSTEM_NAME "MacOS") endif() -set(CPACK_COMPONENTS_GROUPING IGNORE) -set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) - set(CPACK_PACKAGE_CONTACT "The Usual Suspects") set(CPACK_PACKAGE_VENDOR "The Usual Suspects") set(CPACK_PACKAGE_NAME "DSP56300Emu") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "DSP 56300 family emulator audio plugin") -set(CPACK_DEB_COMPONENT_INSTALL ON) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "The Usual Suspects") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://dsp56300.wordpress.com") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) -set(CPACK_RPM_COMPONENT_INSTALL ON) set(CPACK_RPM_PACKAGE_AUTOREQ "yes") set(CPACK_RPM_PACKAGE_URL ${CPACK_DEBIAN_PACKAGE_HOMEPAGE}) set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) +# ----------------- source + +add_subdirectory(source) + +# ----------------- CPack parameters based on source + +get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) +list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") + +set(CPACK_COMPONENTS_GROUPING IGNORE) + +set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) +set(CPACK_DEB_COMPONENT_INSTALL ON) +set(CPACK_RPM_COMPONENT_INSTALL ON) + include(CPack) diff --git a/build_linux.sh b/build_linux.sh @@ -4,6 +4,3 @@ cmake --build . --config Release cpack -G DEB cpack -G RPM cpack -G ZIP -mv *.deb ../../deploy/ -mv *.rpm ../../deploy/ -mv *.zip ../../deploy/ diff --git a/build_mac.sh b/build_mac.sh @@ -2,4 +2,3 @@ cmake -G Xcode -S . -B ./temp/cmake cd ./temp/cmake cmake --build . --config Release cpack -G ZIP -mv *.zip ../../deploy/ diff --git a/build_win32.bat b/build_win32.bat @@ -12,4 +12,3 @@ IF %ERRORLEVEL% NEQ 0 ( ) cpack -G ZIP popd -move /y %outdir%*.zip deploy\ diff --git a/build_win64.bat b/build_win64.bat @@ -12,4 +12,3 @@ IF %ERRORLEVEL% NEQ 0 ( ) cpack -G ZIP popd -move /y %outdir%*.zip deploy\ -\ No newline at end of file diff --git a/build_win64_vs19.bat b/build_win64_vs19.bat @@ -12,4 +12,3 @@ IF %ERRORLEVEL% NEQ 0 ( ) cmake -P ../../scripts/pack.cmake popd -move /y %outdir%*.zip deploy\ -\ No newline at end of file diff --git a/linux_dependencies.sh b/linux_dependencies.sh @@ -1,2 +1,3 @@ -sudo apt-get -y install gcc g++ cmake rpm +sudo apt-get -y install gcc g++ cmake rpm git sudo apt-get -y install libfreetype6-dev libx11-dev libxinerama-dev libxrandr-dev libxcursor-dev mesa-common-dev libasound2-dev freeglut3-dev libxcomposite-dev +sudo apt-get -y install pkg-config diff --git a/source/juce.cmake b/source/juce.cmake @@ -1,6 +1,7 @@ option(${CMAKE_PROJECT_NAME}_BUILD_FX_PLUGIN "Build FX plugin variants" off) set(USE_CLAP ${CMAKE_PROJECT_NAME}_BUILD_JUCEPLUGIN_CLAP) +set(JUCE_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) if(JUCE_GLOBAL_VST2_SDK_PATH) set(VST "VST") @@ -93,6 +94,17 @@ macro(createJucePlugin targetName productName isSynth plugin4CC binaryDataProjec install(TARGETS ${targetName}_CLAP LIBRARY DESTINATION /usr/local/lib/clap/ COMPONENT ${productName}-CLAP) endif() endif() + + if(APPLE AND ${isSynth}) + add_test(NAME ${targetName}_AU_Validate COMMAND ${CMAKE_COMMAND} + -DIDCOMPANY=TusP + -DIDPLUGIN=${plugin4CC} + -DBINDIR=${CMAKE_BINARY_DIR} + -DCOMPONENT_NAME=${productName} + -DCPACK_FILE=${CPACK_PACKAGE_NAME}-${CMAKE_PROJECT_VERSION}-${CPACK_SYSTEM_NAME}-${productName}-AU.zip + -P ${JUCE_CMAKE_DIR}/runAuValidation.cmake) + set_tests_properties(${targetName}_AU_Validate PROPERTIES LABELS "PluginTest") + endif() endmacro() macro(createJucePluginWithFX targetName productName plugin4CCSynth plugin4CCFX binaryDataProject synthLibProject) diff --git a/source/jucePlugin/ui3/VirusEditor.cpp b/source/jucePlugin/ui3/VirusEditor.cpp @@ -10,7 +10,6 @@ #include "../../jucePluginLib/parameterbinding.h" #include "../../synthLib/os.h" -#include "../../synthLib/sysexToMidi.h" namespace genericVirusUI { @@ -41,12 +40,8 @@ namespace genericVirusUI m_patchBrowser.reset(new PatchBrowser(*this)); m_presetName = findComponentT<juce::Label>("PatchName"); - m_focusedParameterName = findComponentT<juce::Label>("FocusedParameterName"); - m_focusedParameterValue = findComponentT<juce::Label>("FocusedParameterValue"); - m_focusedParameterTooltip = findComponentT<juce::Label>("FocusedParameterTooltip", false); - if(m_focusedParameterTooltip) - m_focusedParameterTooltip->setVisible(false); + m_focusedParameter.reset(new jucePluginEditorLib::FocusedParameter(getController(), m_parameterBinding, *this)); m_romSelector = findComponentT<juce::ComboBox>("RomSelector"); @@ -78,9 +73,6 @@ namespace genericVirusUI addMouseListener(this, true); - m_focusedParameterName->setVisible(false); - m_focusedParameterValue->setVisible(false); - if(auto* versionInfo = findComponentT<juce::Label>("VersionInfo", false)) { const std::string message = "DSP 56300 Emulator Version " + std::string(g_pluginVersionString) + " - " __DATE__ " " __TIME__; @@ -122,31 +114,11 @@ namespace genericVirusUI updatePresetName(); updatePlayModeButtons(); - updateControlLabel(nullptr); - - for (auto& params : getController().getExposedParameters()) - { - for (const auto& param : params.second) - { - m_boundParameters.push_back(param); - - param->onValueChanged.emplace_back(1, [this, param]() - { - if (param->getChangeOrigin() == pluginLib::Parameter::ChangedBy::PresetChange || - param->getChangeOrigin() == pluginLib::Parameter::ChangedBy::Derived) - return; - auto* comp = m_parameterBinding.getBoundComponent(param); - if(comp) - updateControlLabel(comp); - }); - } - } } VirusEditor::~VirusEditor() { - for (auto* p : m_boundParameters) - p->removeListener(1); + m_focusedParameter.reset(); m_parameterBinding.clearBindings(); @@ -206,103 +178,7 @@ namespace genericVirusUI void VirusEditor::mouseEnter(const juce::MouseEvent& event) { - if(event.eventComponent && event.eventComponent->getProperties().contains("parameter")) - updateControlLabel(event.eventComponent); - } - - void VirusEditor::timerCallback() - { - updateControlLabel(nullptr); - } - - void VirusEditor::updateControlLabel(juce::Component* _component) - { - stopTimer(); - - if(_component) - { - // combo boxes report the child label as event source, try the parent in this case - if(!_component->getProperties().contains("parameter")) - _component = _component->getParentComponent(); - } - - if(!_component || !_component->getProperties().contains("parameter")) - { - m_focusedParameterName->setVisible(false); - m_focusedParameterValue->setVisible(false); - if(m_focusedParameterTooltip) - m_focusedParameterTooltip->setVisible(false); - return; - } - - const auto& props = _component->getProperties(); - const int v = props["parameter"]; - - const int part = props.contains("part") ? static_cast<int>(props["part"]) : static_cast<int>(getController().getCurrentPart()); - - const auto* p = getController().getParameter(v, part); - - if(!p) - { - m_focusedParameterName->setVisible(false); - m_focusedParameterValue->setVisible(false); - if(m_focusedParameterTooltip) - m_focusedParameterTooltip->setVisible(false); - return; - } - - const auto value = p->getText(p->getValue(), 0); - - const auto& desc = p->getDescription(); - - m_focusedParameterName->setText(desc.displayName, juce::dontSendNotification); - m_focusedParameterValue->setText(value, juce::dontSendNotification); - - m_focusedParameterName->setVisible(true); - m_focusedParameterValue->setVisible(true); - - if(m_focusedParameterTooltip && dynamic_cast<juce::Slider*>(_component) && _component->isShowing()) - { - int x = _component->getX(); - int y = _component->getY(); - - // local to global - auto parent = _component->getParentComponent(); - - while(parent && parent != this) - { - x += parent->getX(); - y += parent->getY(); - parent = parent->getParentComponent(); - } - - x += (_component->getWidth()>>1) - (m_focusedParameterTooltip->getWidth()>>1); - y += _component->getHeight() + (m_focusedParameterTooltip->getHeight()>>1); - - // global to local of tooltip parent - parent = m_focusedParameterTooltip->getParentComponent(); - - while(parent && parent != this) - { - x -= parent->getX(); - y -= parent->getY(); - parent = parent->getParentComponent(); - } - - if(m_focusedParameterTooltip->getProperties().contains("offsetY")) - y += static_cast<int>(m_focusedParameterTooltip->getProperties()["offsetY"]); - - m_focusedParameterTooltip->setTopLeftPosition(x,y); - m_focusedParameterTooltip->setText(value, juce::dontSendNotification); - m_focusedParameterTooltip->setVisible(true); - m_focusedParameterTooltip->toFront(false); - } - else if(m_focusedParameterTooltip) - { - m_focusedParameterTooltip->setVisible(false); - } - - startTimer(3000); + m_focusedParameter->onMouseEnter(event); } void VirusEditor::updatePresetName() const @@ -389,27 +265,13 @@ namespace genericVirusUI 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) + Editor::loadPreset([this](const juce::File& _result) { - if (chooser.getResults().isEmpty()) - return; - - const auto result = chooser.getResult(); - m_previousPath = result.getParentDirectory().getFullPathName(); - const auto ext = result.getFileExtension().toLowerCase(); + const auto ext = _result.getFileExtension().toLowerCase(); PatchBrowser::PatchList patches; - m_patchBrowser->loadBankFile(patches, nullptr, result); + m_patchBrowser->loadBankFile(patches, nullptr, _result); if (patches.empty()) return; @@ -432,8 +294,7 @@ namespace genericVirusUI } getController().onStateLoaded(); - }; - m_fileChooser->launchAsync(flags, onFileChooser); + }); } void VirusEditor::setPlayMode(uint8_t _playMode) @@ -456,31 +317,12 @@ namespace genericVirusUI void VirusEditor::savePresets(SaveType _saveType, FileType _fileType, uint8_t _bankNumber/* = 0*/) { - const auto path = m_processor.getConfig().getValue("virus_bank_dir", ""); - m_fileChooser = std::make_unique<juce::FileChooser>( - "Save preset(s) as syx or mid", - m_previousPath.isEmpty() - ? (path.isEmpty() ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() : juce::File(path)) - : m_previousPath, - "*.syx,*.mid", true); - - constexpr auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::FileChooserFlags::canSelectFiles; - - auto onFileChooser = [this, _saveType, _bankNumber](const juce::FileChooser& chooser) + Editor::savePreset([this, _saveType, _bankNumber, _fileType](const juce::File& _result) { - if (chooser.getResults().isEmpty()) - return; - - const auto result = chooser.getResult(); - m_previousPath = result.getParentDirectory().getFullPathName(); - const auto ext = result.getFileExtension().toLowerCase(); - - if (!result.existsAsFile() || juce::NativeMessageBox::showYesNoBox(juce::AlertWindow::WarningIcon, "File exists", "Do you want to overwrite the existing file?") == 1) - { - savePresets(result.getFullPathName().toStdString(), _saveType, ext.endsWith("mid") ? FileType::Mid : FileType::Syx, _bankNumber); - } - }; - m_fileChooser->launchAsync(flags, onFileChooser); + FileType fileType = _fileType; + const auto file = createValidFilename(fileType, _result); + savePresets(file, _saveType, fileType, _bankNumber); + }); } bool VirusEditor::savePresets(const std::string& _pathName, SaveType _saveType, FileType _fileType, uint8_t _bankNumber/* = 0*/) const @@ -521,31 +363,7 @@ namespace genericVirusUI return false; } - if(messages.empty()) - return false; - - if(_fileType == FileType::Mid) - { - return synthLib::SysexToMidi::write(_pathName.c_str(), messages); - } - - FILE* hFile = fopen(_pathName.c_str(), "wb"); - - if(!hFile) - return false; - - for (const auto& message : messages) - { - const auto written = fwrite(&message[0], 1, message.size(), hFile); - - if(written != message.size()) - { - fclose(hFile); - return false; - } - } - fclose(hFile); - return true; + return Editor::savePresets(_fileType, _pathName, messages); } void VirusEditor::setPart(size_t _part) diff --git a/source/jucePlugin/ui3/VirusEditor.h b/source/jucePlugin/ui3/VirusEditor.h @@ -2,6 +2,7 @@ #include "../../jucePluginEditorLib/midiPorts.h" #include "../../jucePluginEditorLib/pluginEditor.h" +#include "../../jucePluginEditorLib/focusedParameter.h" #include "Parts.h" #include "Tabs.h" @@ -9,6 +10,11 @@ #include "PatchBrowser.h" #include "ControllerLinks.h" +namespace jucePluginEditorLib +{ + class FocusedParameter; +} + namespace pluginLib { class Parameter; @@ -19,15 +25,9 @@ class AudioPluginAudioProcessor; namespace genericVirusUI { - class VirusEditor : public jucePluginEditorLib::Editor, juce::Timer + class VirusEditor : public jucePluginEditorLib::Editor { public: - enum class FileType - { - Syx, - Mid - }; - enum class SaveType { CurrentSingle, @@ -57,9 +57,7 @@ namespace genericVirusUI void onCurrentPartChanged(); void mouseEnter(const juce::MouseEvent& event) override; - void timerCallback() override; - void updateControlLabel(juce::Component* _component); void updatePresetName() const; void updatePlayModeButtons() const; @@ -84,9 +82,9 @@ namespace genericVirusUI std::unique_ptr<ControllerLinks> m_controllerLinks; juce::Label* m_presetName = nullptr; - juce::Label* m_focusedParameterName = nullptr; - juce::Label* m_focusedParameterValue = nullptr; - juce::Label* m_focusedParameterTooltip = nullptr; + + std::unique_ptr<jucePluginEditorLib::FocusedParameter> m_focusedParameter; + juce::ComboBox* m_romSelector = nullptr; juce::Button* m_playModeSingle = nullptr; @@ -97,9 +95,6 @@ namespace genericVirusUI juce::TooltipWindow m_tooltipWindow; - std::unique_ptr<juce::FileChooser> m_fileChooser; - juce::String m_previousPath; std::function<void()> m_openMenuCallback; - std::vector<pluginLib::Parameter*> m_boundParameters; }; } diff --git a/source/jucePluginEditorLib/CMakeLists.txt b/source/jucePluginEditorLib/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.15) project(jucePluginEditorLib VERSION ${CMAKE_PROJECT_VERSION}) set(SOURCES + focusedParameter.cpp focusedParameter.h + focusedParameterTooltip.cpp focusedParameterTooltip.h midiPorts.cpp midiPorts.h patchbrowser.cpp patchbrowser.h pluginEditor.cpp pluginEditor.h diff --git a/source/jucePluginEditorLib/focusedParameter.cpp b/source/jucePluginEditorLib/focusedParameter.cpp @@ -0,0 +1,126 @@ +#include "focusedParameter.h" + +#include "pluginEditor.h" + +#include "../jucePluginLib/controller.h" +#include "../jucePluginLib/parameterbinding.h" + +namespace jucePluginEditorLib +{ + static constexpr uint32_t g_listenerId = 1; + + FocusedParameter::FocusedParameter(const pluginLib::Controller& _controller, const pluginLib::ParameterBinding& _parameterBinding, const genericUI::Editor& _editor) + : m_parameterBinding(_parameterBinding) + , m_controller(_controller) + { + m_focusedParameterName = _editor.findComponentT<juce::Label>("FocusedParameterName", false); + m_focusedParameterValue = _editor.findComponentT<juce::Label>("FocusedParameterValue", false); + + if (m_focusedParameterName) + m_focusedParameterName->setVisible(false); + if (m_focusedParameterValue) + m_focusedParameterValue->setVisible(false); + + m_tooltip.reset(new FocusedParameterTooltip(_editor.findComponentT<juce::Label>("FocusedParameterTooltip", false))); + + updateControlLabel(nullptr); + + for (auto& params : m_controller.getExposedParameters()) + { + for (const auto& param : params.second) + { + m_boundParameters.push_back(param); + + param->onValueChanged.emplace_back(g_listenerId, [this, param]() + { + if (param->getChangeOrigin() == pluginLib::Parameter::ChangedBy::PresetChange || + param->getChangeOrigin() == pluginLib::Parameter::ChangedBy::Derived) + return; + auto* comp = m_parameterBinding.getBoundComponent(param); + if(comp) + updateControlLabel(comp); + }); + } + } + } + + FocusedParameter::~FocusedParameter() + { + m_tooltip.reset(); + + for (auto* p : m_boundParameters) + p->removeListener(g_listenerId); + } + + void FocusedParameter::onMouseEnter(const juce::MouseEvent& _event) + { + auto* component = _event.eventComponent; + + if(component && component->getProperties().contains("parameter")) + updateControlLabel(component); + } + + void FocusedParameter::timerCallback() + { + updateControlLabel(nullptr); + } + + void FocusedParameter::updateControlLabel(juce::Component* _component) + { + stopTimer(); + + if(_component) + { + // combo boxes report the child label as event source, try the parent in this case + if(!_component->getProperties().contains("parameter")) + _component = _component->getParentComponent(); + } + + if(!_component || !_component->getProperties().contains("parameter")) + { + if (m_focusedParameterName) + m_focusedParameterName->setVisible(false); + if (m_focusedParameterValue) + m_focusedParameterValue->setVisible(false); + m_tooltip->setVisible(false); + return; + } + + const auto& props = _component->getProperties(); + const int v = props["parameter"]; + + const int part = props.contains("part") ? static_cast<int>(props["part"]) : static_cast<int>(m_controller.getCurrentPart()); + + const auto* p = m_controller.getParameter(v, static_cast<uint8_t>(part)); + + if(!p) + { + if (m_focusedParameterName) + m_focusedParameterName->setVisible(false); + if (m_focusedParameterValue) + m_focusedParameterValue->setVisible(false); + m_tooltip->setVisible(false); + return; + } + + const auto value = p->getText(p->getValue(), 0); + + const auto& desc = p->getDescription(); + + if (m_focusedParameterName) + { + m_focusedParameterName->setText(desc.displayName, juce::dontSendNotification); + m_focusedParameterName->setVisible(true); + } + + if (m_focusedParameterValue) + { + m_focusedParameterValue->setText(value, juce::dontSendNotification); + m_focusedParameterValue->setVisible(true); + } + + m_tooltip->initialize(_component, value); + + startTimer(3000); + } +} diff --git a/source/jucePluginEditorLib/focusedParameter.h b/source/jucePluginEditorLib/focusedParameter.h @@ -0,0 +1,39 @@ +#pragma once + +#include "focusedParameterTooltip.h" + +namespace genericUI +{ + class Editor; +} + +namespace pluginLib +{ + class Parameter; + class Controller; + class ParameterBinding; +} + +namespace jucePluginEditorLib +{ + class FocusedParameter : juce::Timer + { + public: + FocusedParameter(const pluginLib::Controller& _controller, const pluginLib::ParameterBinding& _parameterBinding, const genericUI::Editor& _editor); + ~FocusedParameter() override; + + void onMouseEnter(const juce::MouseEvent& _event); + + private: + void timerCallback() override; + void updateControlLabel(juce::Component* _component); + + const pluginLib::ParameterBinding& m_parameterBinding; + const pluginLib::Controller& m_controller; + + juce::Label* m_focusedParameterName = nullptr; + juce::Label* m_focusedParameterValue = nullptr; + std::unique_ptr<FocusedParameterTooltip> m_tooltip; + std::vector<pluginLib::Parameter*> m_boundParameters; + }; +} diff --git a/source/jucePluginEditorLib/focusedParameterTooltip.cpp b/source/jucePluginEditorLib/focusedParameterTooltip.cpp @@ -0,0 +1,62 @@ +#include "focusedParameterTooltip.h" + +namespace jucePluginEditorLib +{ + FocusedParameterTooltip::FocusedParameterTooltip(juce::Label* _label) : m_label(_label) + { + setVisible(false); + } + + void FocusedParameterTooltip::setVisible(bool _visible) const + { + if (isValid()) + m_label->setVisible(_visible); + } + + void FocusedParameterTooltip::initialize(juce::Component* _component, const juce::String& _value) const + { + if (!isValid()) + return; + + if(dynamic_cast<juce::Slider*>(_component) && _component->isShowing()) + { + int x = _component->getX(); + int y = _component->getY(); + + // local to global + auto parent = _component->getParentComponent(); + + while(parent && parent != m_label->getParentComponent()) + { + x += parent->getX(); + y += parent->getY(); + parent = parent->getParentComponent(); + } + + x += (_component->getWidth()>>1) - (m_label->getWidth()>>1); + y += _component->getHeight() + (m_label->getHeight()>>1); + /* + // global to local of tooltip parent + parent = m_label->getParentComponent(); + + while(parent && parent != this) + { + x -= parent->getX(); + y -= parent->getY(); + parent = parent->getParentComponent(); + } + */ + if(m_label->getProperties().contains("offsetY")) + y += static_cast<int>(m_label->getProperties()["offsetY"]); + + m_label->setTopLeftPosition(x,y); + m_label->setText(_value, juce::dontSendNotification); + m_label->setVisible(true); + m_label->toFront(false); + } + else if(m_label) + { + m_label->setVisible(false); + } + } +} diff --git a/source/jucePluginEditorLib/focusedParameterTooltip.h b/source/jucePluginEditorLib/focusedParameterTooltip.h @@ -0,0 +1,19 @@ +#pragma once + +#include <juce_audio_processors/juce_audio_processors.h> + +namespace jucePluginEditorLib +{ + class FocusedParameterTooltip + { + public: + FocusedParameterTooltip(juce::Label* _label); + + bool isValid() const { return m_label != nullptr; } + void setVisible(bool _visible) const; + void initialize(juce::Component* _component, const juce::String& _value) const; + + private: + juce::Label* m_label = nullptr; + }; +} diff --git a/source/jucePluginEditorLib/pluginEditor.cpp b/source/jucePluginEditorLib/pluginEditor.cpp @@ -1,13 +1,15 @@ #include "pluginEditor.h" #include "pluginProcessor.h" + #include "../jucePluginLib/parameterbinding.h" #include "../synthLib/os.h" +#include "../synthLib/sysexToMidi.h" namespace jucePluginEditorLib { - Editor::Editor(pluginLib::Processor& _processor, pluginLib::ParameterBinding& _binding, std::string _skinFolder) + Editor::Editor(Processor& _processor, pluginLib::ParameterBinding& _binding, std::string _skinFolder) : genericUI::Editor(static_cast<EditorInterface&>(*this)) , m_processor(_processor) , m_binding(_binding) @@ -15,6 +17,99 @@ namespace jucePluginEditorLib { } + void Editor::loadPreset(const std::function<void(const juce::File&)>& _callback) + { + const auto path = m_processor.getConfig().getValue("load_path", ""); + + m_fileChooser = std::make_unique<juce::FileChooser>( + "Choose syx/midi banks to import", + path.isEmpty() ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() : path, + "*.syx,*.mid,*.midi", true); + + constexpr auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::FileChooserFlags::canSelectFiles; + + const std::function onFileChosen = [this, _callback](const juce::FileChooser& _chooser) + { + if (_chooser.getResults().isEmpty()) + return; + + const auto result = _chooser.getResult(); + + m_processor.getConfig().setValue("load_path", result.getParentDirectory().getFullPathName()); + + _callback(result); + }; + m_fileChooser->launchAsync(flags, onFileChosen); + } + + void Editor::savePreset(const std::function<void(const juce::File&)>& _callback) + { + const auto path = m_processor.getConfig().getValue("save_path", ""); + + m_fileChooser = std::make_unique<juce::FileChooser>( + "Save preset(s) as syx or mid", + path.isEmpty() ? juce::File::getSpecialLocation(juce::File::currentApplicationFile).getParentDirectory() : path, + "*.syx,*.mid,*.midi", true); + + constexpr auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::FileChooserFlags::canSelectFiles; + + auto onFileChosen = [this, _callback](const juce::FileChooser& _chooser) + { + if (_chooser.getResults().isEmpty()) + return; + + const auto result = _chooser.getResult(); + m_processor.getConfig().setValue("save_path", result.getParentDirectory().getFullPathName()); + + if (!result.existsAsFile() || juce::NativeMessageBox::showYesNoBox(juce::AlertWindow::WarningIcon, "File exists", "Do you want to overwrite the existing file?") == 1) + { + _callback(result); + } + }; + m_fileChooser->launchAsync(flags, onFileChosen); + } + + bool Editor::savePresets(const FileType _type, const std::string& _pathName, const std::vector<std::vector<uint8_t>>& _presets) const + { + if (_presets.empty()) + return false; + + if (_type == FileType::Mid) + return synthLib::SysexToMidi::write(_pathName.c_str(), _presets); + + FILE* hFile = fopen(_pathName.c_str(), "wb"); + + if (!hFile) + return false; + + for (const auto& message : _presets) + { + const auto written = fwrite(&message.front(), 1, message.size(), hFile); + + if (written != message.size()) + { + fclose(hFile); + return false; + } + } + fclose(hFile); + return true; + } + + std::string Editor::createValidFilename(FileType& _type, const juce::File& _file) + { + const auto ext = _file.getFileExtension(); + auto file = _file.getFullPathName().toStdString(); + + if (ext.endsWithIgnoreCase("mid")) + _type = FileType::Mid; + else if (ext.endsWithIgnoreCase("syx")) + _type = FileType::Syx; + else + file += _type == FileType::Mid ? ".mid" : ".syx"; + return file; + } + const char* Editor::getResourceByFilename(const std::string& _name, uint32_t& _dataSize) { if(!m_skinFolder.empty()) @@ -31,7 +126,7 @@ namespace jucePluginEditorLib return &it->second.front(); }; - auto* res = readFromCache(); + const auto* res = readFromCache(); if(res) return res; diff --git a/source/jucePluginEditorLib/pluginEditor.h b/source/jucePluginEditorLib/pluginEditor.h @@ -4,19 +4,31 @@ namespace pluginLib { - class Processor; class ParameterBinding; } namespace jucePluginEditorLib { + class Processor; + class Editor : public genericUI::Editor, genericUI::EditorInterface { public: - Editor(pluginLib::Processor& _processor, pluginLib::ParameterBinding& _binding, std::string _skinFolder); + enum class FileType + { + Syx, + Mid + }; + + Editor(Processor& _processor, pluginLib::ParameterBinding& _binding, std::string _skinFolder); virtual const char* findResourceByFilename(const std::string& _filename, uint32_t& _size) = 0; + void loadPreset(const std::function<void(const juce::File&)>& _callback); + void savePreset(const std::function<void(const juce::File&)>& _callback); + bool savePresets(FileType _type, const std::string& _pathName, const std::vector<std::vector<uint8_t>>& _presets) const; + static std::string createValidFilename(FileType& _type, const juce::File& _file); + private: const char* getResourceByFilename(const std::string& _name, uint32_t& _dataSize) override; int getParameterIndexByName(const std::string& _name) override; @@ -25,11 +37,13 @@ namespace jucePluginEditorLib bool bindParameter(juce::Slider& _target, int _parameterIndex) override; juce::Value* getParameterValue(int _parameterIndex, uint8_t _part) override; - pluginLib::Processor& m_processor; + Processor& m_processor; pluginLib::ParameterBinding& m_binding; const std::string m_skinFolder; std::map<std::string, std::vector<char>> m_fileCache; + + std::unique_ptr<juce::FileChooser> m_fileChooser; }; } diff --git a/source/jucePluginLib/CMakeLists.txt b/source/jucePluginLib/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.15) project(jucePluginLib VERSION ${CMAKE_PROJECT_VERSION}) set(SOURCES + event.cpp event.h controller.cpp controller.h midipacket.cpp midipacket.h parameter.cpp parameter.h diff --git a/source/jucePluginLib/controller.cpp b/source/jucePluginLib/controller.cpp @@ -4,9 +4,15 @@ #include "parameter.h" #include "processor.h" +#include "dsp56kEmu/logging.h" namespace pluginLib { + uint8_t getParameterValue(Parameter* _p) + { + return static_cast<uint8_t>(roundToInt(_p->getValueObject().getValue())); + } + Controller::Controller(pluginLib::Processor& _processor, const std::string& _parameterDescJson) : m_processor(_processor), m_descriptions(_parameterDescJson) { } @@ -45,7 +51,10 @@ namespace pluginLib const auto& existingParams = findSynthParam(idx); for (auto& existingParam : existingParams) - existingParam->addDerivedParameter(p.get()); + { + if(isDerivedParameter(*existingParam, *p)) + existingParam->addDerivedParameter(p.get()); + } } const bool isNonPartExclusive = desc.isNonPartSensitive(); @@ -111,9 +120,83 @@ namespace pluginLib synthLib::SMidiEvent ev; ev.sysex = msg; ev.source = synthLib::MidiEventSourceEditor; - m_processor.addMidiEvent(ev); + sendMidiEvent(ev); } + void Controller::sendMidiEvent(const synthLib::SMidiEvent& _ev) const + { + m_processor.addMidiEvent(_ev); + } + + void Controller::sendMidiEvent(const uint8_t _a, const uint8_t _b, const uint8_t _c, const uint32_t _offset/* = 0*/, const synthLib::MidiEventSource _source/* = synthLib::MidiEventSourceEditor*/) const + { + m_processor.addMidiEvent(synthLib::SMidiEvent(_a, _b, _c, _offset, _source)); + } + + bool Controller::combineParameterChange(uint8_t& _result, const std::string& _midiPacket, const Parameter& _parameter, uint8_t _value) const + { + const auto &desc = _parameter.getDescription(); + + std::map<MidiDataType, uint8_t> data; + + const auto *packet = getMidiPacket(_midiPacket); + + if (!packet) + { + LOG("Failed to find midi packet " << _midiPacket); + return false; + } + + const ParamIndex idx = {static_cast<uint8_t>(desc.page), _parameter.getPart(), desc.index}; + + const auto params = findSynthParam(idx); + + uint32_t byte = MidiPacket::InvalidIndex; + + for (auto param : params) + { + byte = packet->getByteIndexForParameterName(param->getDescription().name); + if (byte != MidiPacket::InvalidIndex) + break; + } + + if (byte == MidiPacket::InvalidIndex) + { + LOG("Failed to find byte index for parameter " << desc.name); + return false; + } + + std::vector<const MidiPacket::MidiDataDefinition*> definitions; + + if(!packet->getDefinitionsForByteIndex(definitions, byte)) + return false; + + if (definitions.size() == 1) + { + _result = _value; + return true; + } + + _result = 0; + + for (const auto& it : definitions) + { + uint32_t i = 0; + + if(!m_descriptions.getIndexByName(i, it->paramName)) + { + LOG("Failed to find index for parameter " << it->paramName); + return false; + } + + auto* p = getParameter(i, _parameter.getPart()); + const auto v = p == &_parameter ? _value : getParameterValue(p); + _result |= it->getMaskedValue(v); + } + + return true; + } + bool Controller::sendSysEx(const std::string& _packetName) const { const std::map<pluginLib::MidiDataType, uint8_t> params; @@ -131,14 +214,14 @@ namespace pluginLib return true; } - const Controller::ParameterList& Controller::findSynthParam(const uint8_t _part, const uint8_t _page, const uint8_t _paramIndex) + const Controller::ParameterList& Controller::findSynthParam(const uint8_t _part, const uint8_t _page, const uint8_t _paramIndex) const { const ParamIndex paramIndex{ _page, _part, _paramIndex }; return findSynthParam(paramIndex); } - const Controller::ParameterList& Controller::findSynthParam(const ParamIndex& _paramIndex) + const Controller::ParameterList& Controller::findSynthParam(const ParamIndex& _paramIndex) const { const auto it = m_synthParams.find(_paramIndex); @@ -156,7 +239,7 @@ namespace pluginLib return iti->second; } - juce::Value* Controller::getParamValueObject(const uint32_t _index, uint8_t _part) + juce::Value* Controller::getParamValueObject(const uint32_t _index, uint8_t _part) const { const auto res = getParameter(_index, _part); return res ? &res->getValueObject() : nullptr; @@ -209,7 +292,7 @@ namespace pluginLib if(!p) return false; - auto v = roundToInt(p->getValueObject().getValue()); + const auto v = getParameterValue(p); paramValues.insert(std::make_pair(std::make_pair(index.first, p->getDescription().name), v)); } } diff --git a/source/jucePluginLib/controller.h b/source/jucePluginLib/controller.h @@ -3,12 +3,9 @@ #include "parameterdescriptions.h" #include "parameter.h" -#include <string> +#include "../synthLib/midiTypes.h" -namespace synthLib -{ - struct SMidiEvent; -} +#include <string> namespace pluginLib { @@ -25,7 +22,7 @@ namespace pluginLib virtual void sendParameterChange(const Parameter& _parameter, uint8_t _value) = 0; - juce::Value* getParamValueObject(uint32_t _index, uint8_t _part); + juce::Value* getParamValueObject(uint32_t _index, uint8_t _part) const; Parameter* getParameter(uint32_t _index) const; Parameter* getParameter(uint32_t _index, uint8_t _part) const; @@ -39,7 +36,7 @@ namespace pluginLib bool parseMidiPacket(const std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const; bool parseMidiPacket(std::string& _name, MidiPacket::Data& _data, MidiPacket::ParamValues& _parameterValues, const std::vector<uint8_t>& _src) const; - const auto& getExposedParameters() { return m_synthParams; } + const auto& getExposedParameters() const { return m_synthParams; } uint8_t getCurrentPart() const { return m_currentPart; } void setCurrentPart(const uint8_t _part) { m_currentPart = _part; } @@ -58,30 +55,42 @@ namespace pluginLib void sendSysEx(const pluginLib::SysEx &) const; bool sendSysEx(const std::string& _packetName) const; bool sendSysEx(const std::string& _packetName, const std::map<pluginLib::MidiDataType, uint8_t>& _params) const; + void sendMidiEvent(const synthLib::SMidiEvent& _ev) const; + void sendMidiEvent(uint8_t _a, uint8_t _b, uint8_t _c, uint32_t _offset = 0, synthLib::MidiEventSource _source = synthLib::MidiEventSourceEditor) const; - private: - Processor& m_processor; - ParameterDescriptions m_descriptions; + bool combineParameterChange(uint8_t& _result, const std::string& _midiPacket, const Parameter& _parameter, uint8_t _value) const; + + virtual bool isDerivedParameter(Parameter& _derived, Parameter& _base) const { return true; } struct ParamIndex { uint8_t page; uint8_t partNum; uint8_t paramNum; - bool operator<(ParamIndex const &rhs) const + + bool operator<(const ParamIndex& _p) const { - if (page < rhs.page) return false; - if (page > rhs.page) return true; - if (partNum < rhs.partNum) return false; - if (partNum > rhs.partNum) return true; - if (paramNum < rhs.paramNum) return false; - if (paramNum > rhs.paramNum) return true; + if (page < _p.page) return false; + if (page > _p.page) return true; + if (partNum < _p.partNum) return false; + if (partNum > _p.partNum) return true; + if (paramNum < _p.paramNum) return false; + if (paramNum > _p.paramNum) return true; return false; } + + bool operator==(const ParamIndex& _p) const + { + return page == _p.page && partNum == _p.partNum && paramNum && _p.paramNum; + } }; using ParameterList = std::vector<Parameter*>; + private: + Processor& m_processor; + ParameterDescriptions m_descriptions; + uint8_t m_currentPart = 0; std::mutex m_pluginMidiOutLock; @@ -89,8 +98,8 @@ namespace pluginLib protected: // tries to find synth param in both internal and host - const ParameterList& findSynthParam(uint8_t _part, uint8_t _page, uint8_t _paramIndex); - const ParameterList& findSynthParam(const ParamIndex& _paramIndex); + const ParameterList& findSynthParam(uint8_t _part, uint8_t _page, uint8_t _paramIndex) const; + const ParameterList& findSynthParam(const ParamIndex& _paramIndex) const; std::map<ParamIndex, ParameterList> m_synthInternalParams; std::map<ParamIndex, ParameterList> m_synthParams; // exposed and managed by audio processor diff --git a/source/jucePluginLib/event.cpp b/source/jucePluginLib/event.cpp @@ -0,0 +1,5 @@ +#include "event.h" + +namespace pluginLib +{ +} diff --git a/source/jucePluginLib/event.h b/source/jucePluginLib/event.h @@ -0,0 +1,44 @@ +#pragma once + +#include <functional> +#include <map> + +namespace pluginLib +{ + class Event + { + public: + using Callback = std::function<void()>; + + Event() = default; + + void addListener(size_t _id, const Callback& _callback) + { + m_listeners.insert(std::make_pair(_id, _callback)); + } + + void removeListener(const size_t _id) + { + m_listeners.erase(_id); + } + + void clear() + { + m_listeners.clear(); + } + + void invoke() const + { + for (const auto& it : m_listeners) + it.second(); + } + + void operator ()() const + { + invoke(); + } + + private: + std::map<size_t, Callback> m_listeners; + }; +} diff --git a/source/jucePluginLib/midipacket.cpp b/source/jucePluginLib/midipacket.cpp @@ -34,7 +34,7 @@ namespace pluginLib m_definitionToByteIndex.insert(std::make_pair(i, byteIndex)); if(byteIndex >= m_byteToDefinitionIndex.size()) - m_byteToDefinitionIndex.push_back({}); + m_byteToDefinitionIndex.emplace_back(); m_byteToDefinitionIndex[byteIndex].push_back(i); @@ -77,7 +77,7 @@ namespace pluginLib LOG("Failed to find value for parameter " << d.paramName << ", part " << d.paramPart); return false; } - _dst[i] |= (it->second & d.paramMask) << d.paramShift; + _dst[i] |= d.getMaskedValue(it->second); } break; case MidiDataType::Checksum: @@ -207,6 +207,45 @@ namespace pluginLib return true; } + bool MidiPacket::getDefinitionsForByteIndex(std::vector<const MidiDataDefinition*>& _result, uint32_t _byteIndex) const + { + if (_byteIndex >= m_byteToDefinitionIndex.size()) + return false; + + const auto& defs = m_byteToDefinitionIndex[_byteIndex]; + + for (const auto idx : defs) + { + const auto &d = m_definitions[idx]; + _result.emplace_back(&d); + } + return true; + } + + bool MidiPacket::getParameterIndicesForByteIndex(std::vector<ParamIndex>& _result, const ParameterDescriptions& _parameters, const uint32_t _byteIndex) const + { + if (_byteIndex >= m_byteToDefinitionIndex.size()) + return false; + + const auto& defs = m_byteToDefinitionIndex[_byteIndex]; + + for (const auto idx : defs) + { + const auto &d = m_definitions[idx]; + + uint32_t paramIdx; + + if(!_parameters.getIndexByName(paramIdx, d.paramName)) + { + LOG("Failed to retrieve index for parameter " << d.paramName); + return false; + } + + _result.emplace_back(d.paramPart, paramIdx); + } + return true; + } + uint32_t MidiPacket::getByteIndexForType(MidiDataType _type) const { for(uint32_t i=0; i<m_definitions.size(); ++i) @@ -237,6 +276,41 @@ namespace pluginLib return InvalidIndex; } + const MidiPacket::MidiDataDefinition* MidiPacket::getDefinitionByParameterName(const std::string& _name) const + { + for (const auto& definition : m_definitions) + { + if (definition.paramName == _name) + return &definition; + } + + return nullptr; + } + + bool MidiPacket::updateChecksums(Sysex& _data) const + { + if (_data.size() != m_byteSize) + return false; + + bool res = false; + + for (uint32_t i=0; i<m_definitions.size(); ++i) + { + const auto& def = m_definitions[i]; + + if (def.type != MidiDataType::Checksum) + continue; + + const auto byteIndex = m_definitionToByteIndex.find(i)->second; + + const auto c = calcChecksum(def, _data); + _data[byteIndex] = c; + res = true; + } + + return res; + } + uint8_t MidiPacket::calcChecksum(const MidiDataDefinition& _d, const Sysex& _src) { auto checksum = _d.checksumInitValue; diff --git a/source/jucePluginLib/midipacket.h b/source/jucePluginLib/midipacket.h @@ -46,6 +46,16 @@ namespace pluginLib uint32_t checksumFirstIndex = 0; uint32_t checksumLastIndex = 0; uint8_t checksumInitValue = 0; + + uint8_t getMaskedValue(const uint8_t _unmasked) const + { + return static_cast<uint8_t>((_unmasked & paramMask) << paramShift); + } + + bool doMasksOverlap(const MidiDataDefinition& _d) const + { + return (getMaskedValue(0xff) & _d.getMaskedValue(0xff)) != 0; + } }; using Data = std::map<MidiDataType, uint8_t>; @@ -75,9 +85,14 @@ namespace pluginLib bool create(std::vector<uint8_t>& _dst, const Data& _data) const; bool parse(Data& _data, ParamValues& _parameterValues, const ParameterDescriptions& _parameters, const Sysex& _src, bool _ignoreChecksumErrors = true) const; bool getParameterIndices(ParamIndices& _indices, const ParameterDescriptions& _parameters) const; + bool getDefinitionsForByteIndex(std::vector<const MidiDataDefinition*>& _result, uint32_t _byteIndex) const; + bool getParameterIndicesForByteIndex(std::vector<ParamIndex>& _result, const ParameterDescriptions& _parameters, uint32_t _byteIndex) const; uint32_t getByteIndexForType(MidiDataType _type) const; uint32_t getByteIndexForParameterName(const std::string& _name) const; + const MidiDataDefinition *getDefinitionByParameterName(const std::string& _name) const; + + bool updateChecksums(Sysex& _data) const; private: static uint8_t calcChecksum(const MidiDataDefinition& _d, const Sysex& _src); diff --git a/source/jucePluginLib/parameterbinding.cpp b/source/jucePluginLib/parameterbinding.cpp @@ -158,7 +158,7 @@ namespace pluginLib addBinding(p); } - juce::Component* ParameterBinding::getBoundComponent(const pluginLib::Parameter* _parameter) + juce::Component* ParameterBinding::getBoundComponent(const Parameter* _parameter) const { const auto it = m_boundParameters.find(_parameter); if(it == m_boundParameters.end()) diff --git a/source/jucePluginLib/parameterbinding.h b/source/jucePluginLib/parameterbinding.h @@ -58,7 +58,7 @@ namespace pluginLib void enableBindings(); const auto& getBindings() const { return m_bindings; } - juce::Component* getBoundComponent(const pluginLib::Parameter* _parameter); + juce::Component* getBoundComponent(const pluginLib::Parameter* _parameter) const; private: void removeMouseListener(juce::Slider& _slider); diff --git a/source/juceUiLib/condition.cpp b/source/juceUiLib/condition.cpp @@ -44,4 +44,10 @@ namespace genericUI m_value->removeListener(this); m_value = nullptr; } + + void Condition::refresh() + { + if(m_value) + valueChanged(*m_value); + } } diff --git a/source/juceUiLib/condition.h b/source/juceUiLib/condition.h @@ -21,6 +21,7 @@ namespace genericUI void bind(juce::Value* _value); void unbind(); int32_t getParameterIndex() const { return m_parameterIndex; } + void refresh(); private: juce::Component& m_target; const int32_t m_parameterIndex; diff --git a/source/juceUiLib/uiObject.cpp b/source/juceUiLib/uiObject.cpp @@ -46,6 +46,9 @@ namespace genericUI _parent.addAndMakeVisible(obj); + if(ch->m_condition) + ch->m_condition->refresh(); + ch->createChildObjects(_editor, *obj); } } diff --git a/source/runAuValidation.cmake b/source/runAuValidation.cmake @@ -0,0 +1,39 @@ +message(STATUS "Testing AU Validation for Company ID ${IDCOMPANY}, Plugin ID ${IDPLUGIN}") +message(STATUS "cmake binary dir ${CMAKE_BINARY_DIR}") +message(STATUS "cmake current binary dir ${CMAKE_CURRENT_BINARY_DIR}") +message(STATUS "cmake current source dir ${CMAKE_CURRENT_SOURCE_DIR}") +message(STATUS "BINDIR ${BINDIR}") +message(STATUS "CPACK_FILE ${CPACK_FILE}") + +set(SOURCE_DIR ${BINDIR}) +set(TARGET_DIR "$ENV{HOME}/Library/Audio/Plug-Ins/Components") + +set(SOURCE_ZIP ${SOURCE_DIR}/${CPACK_FILE}) +set(TARGET_ZIP ${TARGET_DIR}/${CPACK_FILE}) + +set(TARGET_COMPONENT ${TARGET_DIR}/${COMPONENT_NAME}.component) + +message(STATUS "Copying AU plugin to ${TARGET_DIR}") + +if(EXISTS ${TARGET_COMPONENT}) + execute_process(COMMAND rm -R ${TARGET_COMPONENT} COMMAND_ECHO STDOUT RESULT_VARIABLE REMOVE_RESULT) + if(REMOVE_RESULT) + message(FATAL_ERROR "Failed to remove ${TARGET_COMPONENT}") + endif() +endif() + +execute_process(COMMAND unzip -o ${SOURCE_ZIP} -d ${TARGET_DIR} COMMAND_ECHO STDOUT RESULT_VARIABLE UNZIP_RESULT) +if(UNZIP_RESULT) + message(FATAL_ERROR "Failed to unzip ${TARGET_ZIP}") +endif() + +execute_process(COMMAND sleep 5 COMMAND_ECHO STDOUT RESULT_VARIABLE SLEEP_RESULT) +if(SLEEP_RESULT) + message(FATAL_ERROR "Failed to sleep") +endif() + +#auvaltool -v aumu Tmqs TusP +execute_process(COMMAND auvaltool -v aumu ${IDPLUGIN} ${IDCOMPANY} COMMAND_ECHO STDOUT RESULT_VARIABLE AUVALTOOL_RESULT) +if(AUVALTOOL_RESULT) + message(FATAL_ERROR "AU validation failed") +endif() diff --git a/source/synthLib/configFile.cpp b/source/synthLib/configFile.cpp @@ -55,7 +55,7 @@ namespace synthLib const auto key = trim(line.substr(0, posEq)); const auto val = trim(line.substr(posEq + 1)); - m_values.emplace_back(std::make_pair(key, val)); + m_values.emplace_back(key, val); } } } \ No newline at end of file diff --git a/source/synthLib/os.cpp b/source/synthLib/os.cpp @@ -185,9 +185,9 @@ namespace synthLib return {}; } - std::string findFile(const std::string& _extension, const size_t _minSize, const size_t _maxSize) + std::string findFile(const std::string& _extension, const size_t _minSize, const size_t _maxSize, const bool _stripPluginComponentFolders) { - std::string path = getModulePath(); + std::string path = getModulePath(_stripPluginComponentFolders); if(path.empty()) path = getCurrentDirectory(); @@ -195,6 +195,14 @@ namespace synthLib return findFile(path, _extension, _minSize, _maxSize); } + std::string findFile(const std::string& _extension, const size_t _minSize, const size_t _maxSize) + { + auto res = findFile(_extension, _minSize, _maxSize, true); + if (!res.empty()) + return res; + return findFile(_extension, _minSize, _maxSize, false); + } + std::string findFile(const std::string& _rootPath, const std::string& _extension, const size_t _minSize, const size_t _maxSize) { std::vector<std::string> files; diff --git a/source/synthLib/plugin.cpp b/source/synthLib/plugin.cpp @@ -28,7 +28,7 @@ namespace synthLib if(m_midiInRingBuffer.full()) { - std::lock_guard lock(m_lock); + std::lock_guard l(m_lock); processMidiInEvent(m_midiInRingBuffer.pop_front()); } m_midiInRingBuffer.push_back(_ev); diff --git a/source/virusLib/dspSingle.cpp b/source/virusLib/dspSingle.cpp @@ -10,7 +10,7 @@ namespace virusLib { constexpr dsp56k::TWord g_externalMemStart = 0x020000; - DspSingle::DspSingle(uint32_t _memorySize, bool _use56367Peripherals/* = false*/, const char* _name/* = nullptr*/) : m_name(_name ? _name : std::string()), m_periphX(&m_periphY) + DspSingle::DspSingle(uint32_t _memorySize, bool _use56367Peripherals/* = false*/, const char* _name/* = nullptr*/) : m_name(_name ? _name : std::string()), m_periphX(_use56367Peripherals ? &m_periphY : nullptr) { const size_t requiredMemSize = dsp56k::alignedSize<dsp56k::DSP>() + diff --git a/source/virusLib/midiFileToRomData.cpp b/source/virusLib/midiFileToRomData.cpp @@ -45,7 +45,8 @@ namespace virusLib switch (cmd) { case 0x50: // Virus A - case 0x55: // Virus B + case 0x53: // Virus B OS + case 0x55: // Virus B Demo case 0x57: // Virus C { const auto msb = _packet[6]; // packet number MSB @@ -132,7 +133,7 @@ namespace virusLib if(lsb != m_expectedSector) { - if(lsb == 127) + if(lsb == 127 || lsb == 126) return setCompleted(); return packetInvalid(); }