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 6015028cc667da1a1e8f81616eeab864daa2e0fb
parent 4b1da5c976c9c2c97af72f9b69e58e3b1c03344c
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu, 30 May 2024 16:51:29 +0200

add copy/paste support for parameter regions

Diffstat:
Msource/jucePluginEditorLib/pluginEditor.cpp | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msource/jucePluginEditorLib/pluginEditor.h | 4++++
Msource/jucePluginLib/controller.cpp | 22++++++++++++++++++++++
Msource/jucePluginLib/controller.h | 2++
Msource/jucePluginLib/parameter.cpp | 6++++++
Msource/jucePluginLib/parameter.h | 1+
6 files changed, 158 insertions(+), 2 deletions(-)

diff --git a/source/jucePluginEditorLib/pluginEditor.cpp b/source/jucePluginEditorLib/pluginEditor.cpp @@ -257,8 +257,129 @@ namespace jucePluginEditorLib if(!_event || !_event->originalComponent) return false; - // TODO - return false; + const auto* param = m_binding.getBoundParameter(_event->originalComponent); + if(!param) + return false; + + const auto& controller = m_processor.getController(); + + const auto& regions = controller.getParameterDescriptions().getRegions(); + const auto paramRegionIds = controller.getRegionIdsForParameter(param); + + if(paramRegionIds.empty()) + return false; + + juce::PopupMenu menu; + + for (const auto& regionId : paramRegionIds) + { + const auto& regionName = regions.find(regionId)->second.getName(); + + menu.addItem(std::string("Copy region '") + regionName + "'", [this, regionId] + { + copyRegionToClipboard(regionId); + }); + } + + const auto data = pluginLib::Clipboard::getDataFromString(m_processor, juce::SystemClipboard::getTextFromClipboard().toStdString()); + + if(!data.parameterValuesByRegion.empty()) + { + bool haveSeparator = false; + + for (const auto& paramRegionId : paramRegionIds) + { + const auto it = data.parameterValuesByRegion.find(paramRegionId); + + if(it == data.parameterValuesByRegion.end()) + continue; + + // if region is not fully covered, skip it + const auto& region = regions.find(it->first)->second; + if(it->second.size() < region.getParams().size()) + continue; + + const auto& parameterValues = it->second; + + if(!haveSeparator) + { + menu.addSeparator(); + haveSeparator = true; + } + + const auto& regionName = regions.find(paramRegionId)->second.getName(); + + menu.addItem("Paste region '" + regionName + "'", [this, parameterValues] + { + setParameters(parameterValues); + }); + } + + menu.addSeparator(); + + const auto& desc = param->getDescription(); + const auto& paramName = desc.name; + + const auto itParam = data.parameterValues.find(paramName); + + if(itParam != data.parameterValues.end()) + { + const auto& paramValue = itParam->second; + + const auto& valueText = desc.valueList.valueToText(paramValue); + + menu.addItem("Paste value '" + valueText + "' for parameter '" + desc.displayName + "'", [this, paramName, paramValue] + { + pluginLib::Clipboard::Data::ParameterValues params; + params.insert({paramName, paramValue}); + setParameters(params); + }); + } + } + + menu.showMenuAsync({}); + + return true; + } + + bool Editor::copyRegionToClipboard(const std::string& _regionId) const + { + const auto& regions = m_processor.getController().getParameterDescriptions().getRegions(); + const auto it = regions.find(_regionId); + if(it == regions.end()) + return false; + + const auto& region = it->second; + + const auto& params = region.getParams(); + + std::vector<std::string> paramsList; + paramsList.reserve(params.size()); + + for (const auto& p : params) + paramsList.push_back(p.first); + + return copyParametersToClipboard(paramsList, _regionId); + } + + bool Editor::copyParametersToClipboard(const std::vector<std::string>& _params, const std::string& _regionId) const + { + const auto result = pluginLib::Clipboard::parametersToString(m_processor, _params, _regionId); + + if(result.empty()) + return false; + + juce::SystemClipboard::copyTextToClipboard(result); + + return true; + } + + bool Editor::setParameters(const std::map<std::string, uint8_t>& _paramValues) const + { + if(_paramValues.empty()) + return false; + + return getProcessor().getController().setParameters(_paramValues, m_processor.getController().getCurrentPart(), pluginLib::Parameter::ChangedBy::Ui); } bool Editor::keyPressed(const juce::KeyPress& _key) diff --git a/source/jucePluginEditorLib/pluginEditor.h b/source/jucePluginEditorLib/pluginEditor.h @@ -72,6 +72,10 @@ namespace jucePluginEditorLib virtual void openMenu(juce::MouseEvent* _event); virtual bool openContextMenuForParameter(const juce::MouseEvent* _event); + bool copyRegionToClipboard(const std::string& _regionId) const; + bool copyParametersToClipboard(const std::vector<std::string>& _params, const std::string& _regionId = {}) const; + bool setParameters(const std::map<std::string, uint8_t>& _paramValues) const; + private: bool keyPressed(const juce::KeyPress& _key) override; diff --git a/source/jucePluginLib/controller.cpp b/source/jucePluginLib/controller.cpp @@ -301,6 +301,28 @@ namespace pluginLib return m_descriptions.getIndexByName(index, _name) ? index : InvalidParameterIndex; } + bool Controller::setParameters(const std::map<std::string, uint8_t>& _values, const uint8_t _part, const Parameter::ChangedBy _changedBy) const + { + bool res = false; + + for (const auto& it : _values) + { + const auto& name = it.first; + const auto& value = it.second; + + const auto index = getParameterIndexByName(name); + auto* param = getParameter(index, _part); + + if(param) + { + res = true; + param->setUnnormalizedValue(value, _changedBy); + } + } + + return res; + } + const MidiPacket* Controller::getMidiPacket(const std::string& _name) const { return m_descriptions.getMidiPacket(_name); diff --git a/source/jucePluginLib/controller.h b/source/jucePluginLib/controller.h @@ -36,6 +36,8 @@ namespace pluginLib uint32_t getParameterIndexByName(const std::string& _name) const; + bool setParameters(const std::map<std::string, uint8_t>& _values, uint8_t _part, Parameter::ChangedBy _changedBy) const; + const MidiPacket* getMidiPacket(const std::string& _name) const; bool createNamedParamValues(MidiPacket::NamedParamValues& _params, const std::string& _packetName, uint8_t _part) const; diff --git a/source/jucePluginLib/parameter.cpp b/source/jucePluginLib/parameter.cpp @@ -155,6 +155,12 @@ namespace pluginLib m_changingDerivedValues = false; } + void Parameter::setUnnormalizedValue(const int _newValue, const ChangedBy _origin) + { + const auto v = convertTo0to1(static_cast<float>(_newValue)); + setValue(v, _origin); + } + void Parameter::setValueFromSynth(int newValue, const bool notifyHost, ChangedBy _origin) { const auto clampedValue = juce::roundToInt(m_range.getRange().clipValue(static_cast<float>(newValue))); diff --git a/source/jucePluginLib/parameter.h b/source/jucePluginLib/parameter.h @@ -42,6 +42,7 @@ namespace pluginLib int getUnnormalizedValue() const { return juce::roundToInt(m_value.getValue()); } void setValue(float _newValue) override; void setValue(float _newValue, ChangedBy _origin); + void setUnnormalizedValue(int _newValue, ChangedBy _origin); void setValueFromSynth(int newValue, bool notifyHost, ChangedBy _origin); bool isDiscrete() const override { return m_desc.isDiscrete; }