From 3a0af69eff840958c53185fd0ff79df226d68e25 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 1 Mar 2021 16:43:01 +0000 Subject: [PATCH] AudioPluginHost: Added an application-level setting for scaling plug-ins on Windows --- extras/AudioPluginHost/Source/HostStartup.cpp | 113 ++++++++++++++++++ .../Source/Plugins/PluginGraph.cpp | 41 ++++--- .../Source/UI/GraphEditorPanel.cpp | 31 ++--- .../Source/UI/MainHostWindow.cpp | 74 ++++++++---- .../Source/UI/MainHostWindow.h | 36 +++++- .../native/juce_win32_Windowing.cpp | 4 +- .../juce_ScopedDPIAwarenessDisabler.h | 3 - modules/juce_gui_extra/juce_gui_extra.cpp | 6 + 8 files changed, 233 insertions(+), 75 deletions(-) diff --git a/extras/AudioPluginHost/Source/HostStartup.cpp b/extras/AudioPluginHost/Source/HostStartup.cpp index 2aca5df3d7..07a9a19daf 100644 --- a/extras/AudioPluginHost/Source/HostStartup.cpp +++ b/extras/AudioPluginHost/Source/HostStartup.cpp @@ -155,5 +155,118 @@ bool isOnTouchDevice() return isTouch; } +//============================================================================== +static AutoScale autoScaleFromString (StringRef str) +{ + if (str.isEmpty()) return AutoScale::useDefault; + if (str == CharPointer_ASCII { "0" }) return AutoScale::scaled; + if (str == CharPointer_ASCII { "1" }) return AutoScale::unscaled; + + jassertfalse; + return AutoScale::useDefault; +} + +static const char* autoScaleToString (AutoScale autoScale) +{ + if (autoScale == AutoScale::scaled) return "0"; + if (autoScale == AutoScale::unscaled) return "1"; + + return {}; +} + +AutoScale getAutoScaleValueForPlugin (const String& identifier) +{ + if (identifier.isNotEmpty()) + { + auto plugins = StringArray::fromLines (getAppProperties().getUserSettings()->getValue ("autoScalePlugins")); + plugins.removeEmptyStrings(); + + for (auto& plugin : plugins) + { + auto fromIdentifier = plugin.fromFirstOccurrenceOf (identifier, false, false); + + if (fromIdentifier.isNotEmpty()) + return autoScaleFromString (fromIdentifier.fromFirstOccurrenceOf (":", false, false)); + } + } + + return AutoScale::useDefault; +} + +void setAutoScaleValueForPlugin (const String& identifier, AutoScale s) +{ + auto plugins = StringArray::fromLines (getAppProperties().getUserSettings()->getValue ("autoScalePlugins")); + plugins.removeEmptyStrings(); + + auto index = [identifier, plugins] + { + auto it = std::find_if (plugins.begin(), plugins.end(), + [&] (const String& str) { return str.startsWith (identifier); }); + + return (int) std::distance (plugins.begin(), it); + }(); + + if (s == AutoScale::useDefault && index != plugins.size()) + { + plugins.remove (index); + } + else + { + auto str = identifier + ":" + autoScaleToString (s); + + if (index != plugins.size()) + plugins.getReference (index) = str; + else + plugins.add (str); + } + + getAppProperties().getUserSettings()->setValue ("autoScalePlugins", plugins.joinIntoString ("\n")); +} + +bool shouldAutoScalePlugin (const String& identifier) +{ + if (! autoScaleOptionAvailable) + return false; + + const auto scaleValue = getAutoScaleValueForPlugin (identifier); + + return (scaleValue == AutoScale::scaled + || (scaleValue == AutoScale::useDefault + && getAppProperties().getUserSettings()->getBoolValue ("autoScalePluginWindows"))); +} + +void addPluginAutoScaleOptionsSubMenu (AudioPluginInstance* pluginInstance, + PopupMenu& menu) +{ + if (pluginInstance == nullptr) + return; + + auto description = pluginInstance->getPluginDescription(); + + if (! description.pluginFormatName.contains ("VST")) + return; + + auto identifier = description.fileOrIdentifier; + + PopupMenu autoScaleMenu; + + autoScaleMenu.addItem ("Default", + true, + getAutoScaleValueForPlugin (identifier) == AutoScale::useDefault, + [identifier] { setAutoScaleValueForPlugin (identifier, AutoScale::useDefault); }); + + autoScaleMenu.addItem ("Enabled", + true, + getAutoScaleValueForPlugin (identifier) == AutoScale::scaled, + [identifier] { setAutoScaleValueForPlugin (identifier, AutoScale::scaled); }); + + autoScaleMenu.addItem ("Disabled", + true, + getAutoScaleValueForPlugin (identifier) == AutoScale::unscaled, + [identifier] { setAutoScaleValueForPlugin (identifier, AutoScale::unscaled); }); + + menu.addSubMenu ("Auto-scale window", autoScaleMenu); +} + // This kicks the whole thing off.. START_JUCE_APPLICATION (PluginHostApp) diff --git a/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp b/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp index 0cedbcac32..127405419c 100644 --- a/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp +++ b/extras/AudioPluginHost/Source/Plugins/PluginGraph.cpp @@ -29,6 +29,11 @@ #include "InternalPlugins.h" #include "../UI/GraphEditorPanel.h" +static std::unique_ptr makeDPIAwarenessDisablerForPlugin (StringRef identifier) +{ + return shouldAutoScalePlugin (identifier) ? std::make_unique() + : nullptr; +} //============================================================================== PluginGraph::PluginGraph (AudioPluginFormatManager& fm) @@ -76,10 +81,12 @@ AudioProcessorGraph::Node::Ptr PluginGraph::getNodeForName (const String& name) void PluginGraph::addPlugin (const PluginDescription& desc, Point pos) { + std::shared_ptr dpiDisabler = makeDPIAwarenessDisablerForPlugin (desc.fileOrIdentifier); + formatManager.createPluginInstanceAsync (desc, graph.getSampleRate(), graph.getBlockSize(), - [this, pos] (std::unique_ptr instance, const String& error) + [this, pos, dpiDisabler] (std::unique_ptr instance, const String& error) { addPluginCallback (std::move (instance), error, pos); }); @@ -156,18 +163,10 @@ PluginWindow* PluginGraph::getOrCreateWindowFor (AudioProcessorGraph::Node* node getCommandManager().invokeDirectly (CommandIDs::showAudioSettings, false); return nullptr; } - } - #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE - if (! node->properties["DPIAware"] - && ! node->getProcessor()->getName().contains ("Kontakt")) // Kontakt doesn't behave correctly in DPI unaware mode... - { - ScopedDPIAwarenessDisabler disableDPIAwareness; + auto localDpiDisabler = makeDPIAwarenessDisablerForPlugin (description.fileOrIdentifier); return activePluginWindows.add (new PluginWindow (node, type, activePluginWindows)); } - #endif - - return activePluginWindows.add (new PluginWindow (node, type, activePluginWindows)); } return nullptr; @@ -332,9 +331,6 @@ static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcep e->setAttribute ("uid", (int) node->nodeID.uid); e->setAttribute ("x", node->properties ["x"].toString()); e->setAttribute ("y", node->properties ["y"].toString()); - #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE - e->setAttribute ("DPIAware", node->properties["DPIAware"].toString()); - #endif for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i) { @@ -383,10 +379,16 @@ void PluginGraph::createNodeFromXml (const XmlElement& xml) break; } - String errorMessage; + auto createInstance = [this, pd] + { + String errorMessage; - if (auto instance = formatManager.createPluginInstance (pd, graph.getSampleRate(), - graph.getBlockSize(), errorMessage)) + auto localDpiDisabler = makeDPIAwarenessDisablerForPlugin (pd.fileOrIdentifier); + return formatManager.createPluginInstance (pd, graph.getSampleRate(), + graph.getBlockSize(), errorMessage); + }; + + if (auto instance = createInstance()) { if (auto* layoutEntity = xml.getChildByName ("LAYOUT")) { @@ -408,11 +410,8 @@ void PluginGraph::createNodeFromXml (const XmlElement& xml) node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize()); } - node->properties.set ("x", xml.getDoubleAttribute ("x")); - node->properties.set ("y", xml.getDoubleAttribute ("y")); - #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE - node->properties.set ("DPIAware", xml.getDoubleAttribute ("DPIAware")); - #endif + node->properties.set ("x", xml.getDoubleAttribute ("x")); + node->properties.set ("y", xml.getDoubleAttribute ("y")); for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i) { diff --git a/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp b/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp index 9591c88ac8..f7a5b30880 100644 --- a/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp +++ b/extras/AudioPluginHost/Source/UI/GraphEditorPanel.cpp @@ -403,21 +403,14 @@ struct GraphEditorPanel::PluginComponent : public Component, menu->addItem (2, "Disconnect all pins"); menu->addItem (3, "Toggle Bypass"); - if (getProcessor()->hasEditor()) - { - menu->addSeparator(); - menu->addItem (10, "Show plugin GUI"); - menu->addItem (11, "Show all programs"); - menu->addItem (12, "Show all parameters"); - #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE - auto isTicked = false; - if (auto* node = graph.graph.getNodeForId (pluginID)) - isTicked = node->properties["DPIAware"]; + menu->addSeparator(); + menu->addItem (10, "Show plugin GUI"); + menu->addItem (11, "Show all programs"); + menu->addItem (12, "Show all parameters"); + menu->addItem (13, "Show debug log"); - menu->addItem (13, "Enable DPI awareness", true, isTicked); - #endif - menu->addItem (14, "Show debug log"); - } + if (autoScaleOptionAvailable) + addPluginAutoScaleOptionsSubMenu (dynamic_cast (getProcessor()), *menu); menu->addSeparator(); menu->addItem (20, "Configure Audio I/O"); @@ -441,15 +434,7 @@ struct GraphEditorPanel::PluginComponent : public Component, case 10: showWindow (PluginWindow::Type::normal); break; case 11: showWindow (PluginWindow::Type::programs); break; case 12: showWindow (PluginWindow::Type::generic) ; break; - #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE - case 13: - { - if (auto* node = graph.graph.getNodeForId (pluginID)) - node->properties.set ("DPIAware", ! node->properties ["DPIAware"]); - break; - } - #endif - case 14: showWindow (PluginWindow::Type::debug); break; + case 13: showWindow (PluginWindow::Type::debug); break; case 20: showWindow (PluginWindow::Type::audioIO); break; case 21: testStateSaveLoad(); break; diff --git a/extras/AudioPluginHost/Source/UI/MainHostWindow.cpp b/extras/AudioPluginHost/Source/UI/MainHostWindow.cpp index dfd4927c85..8695f50601 100644 --- a/extras/AudioPluginHost/Source/UI/MainHostWindow.cpp +++ b/extras/AudioPluginHost/Source/UI/MainHostWindow.cpp @@ -277,9 +277,9 @@ PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& // "Plugins" menu PopupMenu pluginsMenu; addPluginsToMenu (pluginsMenu); - menu.addSubMenu ("Create plugin", pluginsMenu); + menu.addSubMenu ("Create Plug-in", pluginsMenu); menu.addSeparator(); - menu.addItem (250, "Delete all plugins"); + menu.addItem (250, "Delete All Plug-ins"); } else if (topLevelMenuIndex == 2) { @@ -288,17 +288,20 @@ PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& menu.addCommandItem (&getCommandManager(), CommandIDs::showPluginListEditor); PopupMenu sortTypeMenu; - sortTypeMenu.addItem (200, "List plugins in default order", true, pluginSortMethod == KnownPluginList::defaultOrder); - sortTypeMenu.addItem (201, "List plugins in alphabetical order", true, pluginSortMethod == KnownPluginList::sortAlphabetically); - sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory); - sortTypeMenu.addItem (203, "List plugins by manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer); - sortTypeMenu.addItem (204, "List plugins based on the directory structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation); - menu.addSubMenu ("Plugin menu type", sortTypeMenu); + sortTypeMenu.addItem (200, "List Plug-ins in Default Order", true, pluginSortMethod == KnownPluginList::defaultOrder); + sortTypeMenu.addItem (201, "List Plug-ins in Alphabetical Order", true, pluginSortMethod == KnownPluginList::sortAlphabetically); + sortTypeMenu.addItem (202, "List Plug-ins by Category", true, pluginSortMethod == KnownPluginList::sortByCategory); + sortTypeMenu.addItem (203, "List Plug-ins by Manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer); + sortTypeMenu.addItem (204, "List Plug-ins Based on the Directory Structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation); + menu.addSubMenu ("Plug-in Menu Type", sortTypeMenu); menu.addSeparator(); menu.addCommandItem (&getCommandManager(), CommandIDs::showAudioSettings); menu.addCommandItem (&getCommandManager(), CommandIDs::toggleDoublePrecision); + if (autoScaleOptionAvailable) + menu.addCommandItem (&getCommandManager(), CommandIDs::autoScalePluginWindows); + menu.addSeparator(); menu.addCommandItem (&getCommandManager(), CommandIDs::aboutBox); } @@ -414,7 +417,8 @@ void MainHostWindow::getAllCommands (Array& commands) CommandIDs::showAudioSettings, CommandIDs::toggleDoublePrecision, CommandIDs::aboutBox, - CommandIDs::allWindowsForward + CommandIDs::allWindowsForward, + CommandIDs::autoScalePluginWindows }; commands.addArray (ids, numElementsInArray (ids)); @@ -451,12 +455,12 @@ void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationComma #endif case CommandIDs::showPluginListEditor: - result.setInfo ("Edit the list of available plug-Ins...", String(), category, 0); + result.setInfo ("Edit the List of Available Plug-ins...", {}, category, 0); result.addDefaultKeypress ('p', ModifierKeys::commandModifier); break; case CommandIDs::showAudioSettings: - result.setInfo ("Change the audio device settings", String(), category, 0); + result.setInfo ("Change the Audio Device Settings", {}, category, 0); result.addDefaultKeypress ('a', ModifierKeys::commandModifier); break; @@ -465,7 +469,7 @@ void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationComma break; case CommandIDs::aboutBox: - result.setInfo ("About...", String(), category, 0); + result.setInfo ("About...", {}, category, 0); break; case CommandIDs::allWindowsForward: @@ -473,6 +477,10 @@ void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationComma result.addDefaultKeypress ('w', ModifierKeys::commandModifier); break; + case CommandIDs::autoScalePluginWindows: + updateAutoScaleMenuItem (result); + break; + default: break; } @@ -518,20 +526,30 @@ bool MainHostWindow::perform (const InvocationInfo& info) case CommandIDs::toggleDoublePrecision: if (auto* props = getAppProperties().getUserSettings()) { - bool newIsDoublePrecision = ! isDoublePrecisionProcessing(); + auto newIsDoublePrecision = ! isDoublePrecisionProcessingEnabled(); props->setValue ("doublePrecisionProcessing", var (newIsDoublePrecision)); - { - ApplicationCommandInfo cmdInfo (info.commandID); - updatePrecisionMenuItem (cmdInfo); - menuItemsChanged(); - } + ApplicationCommandInfo cmdInfo (info.commandID); + updatePrecisionMenuItem (cmdInfo); + menuItemsChanged(); if (graphHolder != nullptr) graphHolder->setDoublePrecision (newIsDoublePrecision); } break; + case CommandIDs::autoScalePluginWindows: + if (auto* props = getAppProperties().getUserSettings()) + { + auto newAutoScale = ! isAutoScalePluginWindowsEnabled(); + props->setValue ("autoScalePluginWindows", var (newAutoScale)); + + ApplicationCommandInfo cmdInfo (info.commandID); + updateAutoScaleMenuItem (cmdInfo); + menuItemsChanged(); + } + break; + case CommandIDs::aboutBox: // TODO break; @@ -633,7 +651,7 @@ void MainHostWindow::filesDropped (const StringArray& files, int x, int y) } } -bool MainHostWindow::isDoublePrecisionProcessing() +bool MainHostWindow::isDoublePrecisionProcessingEnabled() { if (auto* props = getAppProperties().getUserSettings()) return props->getBoolValue ("doublePrecisionProcessing", false); @@ -641,8 +659,22 @@ bool MainHostWindow::isDoublePrecisionProcessing() return false; } +bool MainHostWindow::isAutoScalePluginWindowsEnabled() +{ + if (auto* props = getAppProperties().getUserSettings()) + return props->getBoolValue ("autoScalePluginWindows", false); + + return false; +} + void MainHostWindow::updatePrecisionMenuItem (ApplicationCommandInfo& info) { - info.setInfo ("Double floating point precision rendering", String(), "General", 0); - info.setTicked (isDoublePrecisionProcessing()); + info.setInfo ("Double Floating-Point Precision Rendering", {}, "General", 0); + info.setTicked (isDoublePrecisionProcessingEnabled()); +} + +void MainHostWindow::updateAutoScaleMenuItem (ApplicationCommandInfo& info) +{ + info.setInfo ("Auto-Scale Plug-in Windows", {}, "General", 0); + info.setTicked (isAutoScalePluginWindowsEnabled()); } diff --git a/extras/AudioPluginHost/Source/UI/MainHostWindow.h b/extras/AudioPluginHost/Source/UI/MainHostWindow.h index 313a41bdbc..9cebce02c3 100644 --- a/extras/AudioPluginHost/Source/UI/MainHostWindow.h +++ b/extras/AudioPluginHost/Source/UI/MainHostWindow.h @@ -43,12 +43,34 @@ namespace CommandIDs static const int aboutBox = 0x30300; static const int allWindowsForward = 0x30400; static const int toggleDoublePrecision = 0x30500; + static const int autoScalePluginWindows = 0x30600; } +//============================================================================== ApplicationCommandManager& getCommandManager(); ApplicationProperties& getAppProperties(); bool isOnTouchDevice(); +//============================================================================== +enum class AutoScale +{ + scaled, + unscaled, + useDefault +}; + +constexpr bool autoScaleOptionAvailable = + #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE + true; + #else + false; + #endif + +AutoScale getAutoScaleValueForPlugin (const String&); +void setAutoScaleValueForPlugin (const String&, AutoScale); +bool shouldAutoScalePlugin (const String&); +void addPluginAutoScaleOptionsSubMenu (AudioPluginInstance*, PopupMenu&); + //============================================================================== class MainHostWindow : public DocumentWindow, public MenuBarModel, @@ -88,12 +110,18 @@ public: void addPluginsToMenu (PopupMenu&); PluginDescription getChosenType (int menuID) const; - bool isDoublePrecisionProcessing(); - void updatePrecisionMenuItem (ApplicationCommandInfo& info); - std::unique_ptr graphHolder; private: + //============================================================================== + static bool isDoublePrecisionProcessingEnabled(); + static bool isAutoScalePluginWindowsEnabled(); + + static void updatePrecisionMenuItem (ApplicationCommandInfo& info); + static void updateAutoScaleMenuItem (ApplicationCommandInfo& info); + + void showAudioSettings(); + //============================================================================== AudioDeviceManager deviceManager; AudioPluginFormatManager formatManager; @@ -106,7 +134,5 @@ private: class PluginListWindow; std::unique_ptr pluginListWindow; - void showAudioSettings(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainHostWindow) }; diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 43eeb6657d..571ae637d7 100755 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -27,7 +27,7 @@ #include #endif -#if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra +#if JUCE_MODULE_AVAILABLE_juce_gui_extra #include #endif @@ -470,7 +470,7 @@ static double getGlobalDPI() } //============================================================================== -#if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra +#if JUCE_MODULE_AVAILABLE_juce_gui_extra ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { if (! isPerMonitorDPIAwareThread()) diff --git a/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h b/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h index cb9644b782..d3c8893049 100644 --- a/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h +++ b/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h @@ -26,8 +26,6 @@ namespace juce { -#if (JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE) || DOXYGEN - //============================================================================== /** A Windows-specific class that temporarily sets the DPI awareness context of @@ -52,6 +50,5 @@ public: private: void* previousContext = nullptr; }; -#endif } // namespace juce diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp index 9cf367411b..9e813c0e7a 100644 --- a/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/modules/juce_gui_extra/juce_gui_extra.cpp @@ -188,3 +188,9 @@ #include "native/juce_android_WebBrowserComponent.cpp" #endif #endif + +//============================================================================== +#if ! JUCE_WINDOWS + juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } + juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} +#endif