diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md
index 0fddbf7694..88f5a1bda3 100644
--- a/BREAKING_CHANGES.md
+++ b/BREAKING_CHANGES.md
@@ -37,6 +37,27 @@ The Javascript implementation increases compilation times while being required
by only a select number of projects.
+## Change
+
+The return type for VST3ClientExtensions::getCompatibleClasses() has changed
+from a String to an array of 16 bytes.
+
+**Possible Issues**
+
+Any inherited classes overriding this method might fail to compile.
+
+**Workaround**
+
+Either explicitly switch to creating a 16-byte std::array or use
+VST3ClientExtensions::toInterfaceId() to convert a string to a 16-byte array.
+
+**Rationale**
+
+As part of adding functionality to support migrating parameter IDs from
+compatible plugins it was useful to switch to a safer type for representing
+VST3 interface IDs that closer matches the VST3 SDK types.
+
+
## Change
The VBlankAttachment class' inheritance from the ComponentPeer::VBlankListener
diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
index 62f2831a24..23f6137865 100644
--- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
+++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
@@ -860,6 +860,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
@@ -3455,6 +3456,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
index 377388730c..9247d2cf1c 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
@@ -1095,6 +1095,9 @@
true
+
+ true
+
true
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
index 6f25f4f2bb..25877052c3 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
@@ -1804,6 +1804,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
index 0aa583c9d3..d97ebc4639 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
@@ -1095,6 +1095,9 @@
true
+
+ true
+
true
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
index 730cd91e51..c1ba09e988 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
@@ -1804,6 +1804,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
index 34171f6197..f9d6992c03 100644
--- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
@@ -815,6 +815,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
@@ -3070,6 +3071,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
index 435dc4331a..e64a688c9c 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
@@ -1055,6 +1055,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
index 7a475a3df1..b05a019507 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
@@ -1594,6 +1594,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
index 35e7501e7e..a88bfc6b68 100644
--- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
@@ -848,6 +848,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
@@ -3256,6 +3257,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
index f44f8ff9db..c37d716ce5 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
@@ -1063,6 +1063,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
index c5886faeb6..957d187b47 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
@@ -1669,6 +1669,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
index e68c5fab18..b28adb2059 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
@@ -1063,6 +1063,9 @@
true
+
+ true
+
true
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
index aad4602684..5e4d2e64e9 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
@@ -1669,6 +1669,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
index e46ad7a010..be29f69e17 100644
--- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
+++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
@@ -819,6 +819,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
@@ -3154,6 +3155,7 @@ set_source_files_properties(
"../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h"
+ "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp"
"../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h"
"../../../../../modules/juce_audio_processors/juce_audio_processors.cpp"
"../../../../../modules/juce_audio_processors/juce_audio_processors.mm"
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
index 761d9fbd53..56d8b7707e 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
@@ -1055,6 +1055,9 @@
true
+
+ true
+
true
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
index f3ddc3d536..b954c7a5de 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
@@ -1624,6 +1624,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
index d7db6fd365..6451d6a7fc 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
@@ -1071,6 +1071,9 @@
true
+
+ true
+
true
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
index 399197e763..3082400365 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -1717,6 +1717,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
index 84f1c9922d..7a0e63f8eb 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
@@ -1071,6 +1071,9 @@
true
+
+ true
+
true
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
index 97053ec167..182b38de9c 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -1717,6 +1717,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj
index 0ce5ac00ff..28d7d579de 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj
@@ -1054,6 +1054,9 @@
true
+
+ true
+
true
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters
index b6dcc7091b..343fe5911f 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters
@@ -1621,6 +1621,9 @@
JUCE Modules\juce_audio_processors\utilities
+
+ JUCE Modules\juce_audio_processors\utilities
+
JUCE Modules\juce_audio_processors
diff --git a/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h b/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h
index 1e7744f94c..ca2193dd96 100644
--- a/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h
+++ b/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h
@@ -66,88 +66,6 @@ struct PluginUtilities
return hostType;
}
- // NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code.
- static void getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16])
- {
- #if JUCE_WINDOWS
- const auto juce_sprintf = [] (auto&& head, auto&&... tail) { sprintf_s (head, (size_t) numElementsInArray (head), tail...); };
- const auto juce_strcpy = [] (auto&& head, auto&&... tail) { strcpy_s (head, (size_t) numElementsInArray (head), tail...); };
- const auto juce_strcat = [] (auto&& head, auto&&... tail) { strcat_s (head, (size_t) numElementsInArray (head), tail...); };
- const auto juce_sscanf = [] (auto&&... args) { sscanf_s (args...); };
- #else
- const auto juce_sprintf = [] (auto&& head, auto&&... tail) { snprintf (head, (size_t) numElementsInArray (head), tail...); };
- const auto juce_strcpy = [] (auto&&... args) { strcpy (args...); };
- const auto juce_strcat = [] (auto&&... args) { strcat (args...); };
- const auto juce_sscanf = [] (auto&&... args) { sscanf (args...); };
- #endif
-
- char uidString[33];
-
- const int vstfxid = (('V' << 16) | ('S' << 8) | (forControllerUID ? 'E' : 'T'));
- char vstfxidStr[7] = { 0 };
- juce_sprintf (vstfxidStr, "%06X", vstfxid);
-
- juce_strcpy (uidString, vstfxidStr);
-
- char uidStr[9] = { 0 };
- juce_sprintf (uidStr, "%08X", JucePlugin_VSTUniqueID);
- juce_strcat (uidString, uidStr);
-
- char nameidStr[3] = { 0 };
- const size_t len = strlen (JucePlugin_Name);
-
- for (size_t i = 0; i <= 8; ++i)
- {
- juce::uint8 c = i < len ? static_cast (JucePlugin_Name[i]) : 0;
-
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
-
- juce_sprintf (nameidStr, "%02X", c);
- juce_strcat (uidString, nameidStr);
- }
-
- unsigned long p0;
- unsigned int p1, p2;
- unsigned int p3[8];
-
- juce_sscanf (uidString, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X",
- &p0, &p1, &p2, &p3[0], &p3[1], &p3[2], &p3[3], &p3[4], &p3[5], &p3[6], &p3[7]);
-
- union q0_u {
- uint32 word;
- uint8 bytes[4];
- } q0;
-
- union q1_u {
- uint16 half;
- uint8 bytes[2];
- } q1, q2;
-
- q0.word = static_cast (p0);
- q1.half = static_cast (p1);
- q2.half = static_cast (p2);
-
- // VST3 doesn't use COM compatible UUIDs on non windows platforms
- #if ! JUCE_WINDOWS
- q0.word = ByteOrder::swap (q0.word);
- q1.half = ByteOrder::swap (q1.half);
- q2.half = ByteOrder::swap (q2.half);
- #endif
-
- for (int i = 0; i < 4; ++i)
- uuid[i+0] = q0.bytes[i];
-
- for (int i = 0; i < 2; ++i)
- uuid[i+4] = q1.bytes[i];
-
- for (int i = 0; i < 2; ++i)
- uuid[i+6] = q2.bytes[i];
-
- for (int i = 0; i < 8; ++i)
- uuid[i+8] = static_cast (p3[i]);
- }
-
#if JucePlugin_Build_VST
static bool handleManufacturerSpecificVST2Opcode ([[maybe_unused]] int32 index,
[[maybe_unused]] pointer_sized_int value,
@@ -158,9 +76,10 @@ struct PluginUtilities
if ((index == (int32) ByteOrder::bigEndianInt ("stCA") || index == (int32) ByteOrder::bigEndianInt ("stCa"))
&& value == (int32) ByteOrder::bigEndianInt ("FUID") && ptr != nullptr)
{
- uint8 fuid[16];
- getUUIDForVST2ID (false, fuid);
- ::memcpy (ptr, fuid, 16);
+ const auto uidString = VST3ClientExtensions::convertVST2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, VST3ClientExtensions::InterfaceType::component);
+ MemoryBlock uidValue;
+ uidValue.loadFromHexString (uidString);
+ uidValue.copyTo (ptr, 0, uidValue.getSize());
return true;
}
#endif
diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp
index aa6b9800be..63045e3dbd 100644
--- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp
+++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp
@@ -38,6 +38,38 @@
//==============================================================================
#if JucePlugin_Build_VST3
+#if JUCE_VST3_CAN_REPLACE_VST2 && ! JUCE_FORCE_USE_LEGACY_PARAM_IDS && ! JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING
+
+ // If you encounter this error there may be an issue migrating parameter
+ // automation between sessions saved using the VST2 and VST3 versions of this
+ // plugin.
+ //
+ // If you have released neither a VST2 or VST3 version of the plugin,
+ // consider only releasing a VST3 version and disabling JUCE_VST3_CAN_REPLACE_VST2.
+ //
+ // If you have released a VST2 version of the plugin but have not yet released
+ // a VST3 version of the plugin, consider enabling JUCE_FORCE_USE_LEGACY_PARAM_IDS.
+ // This will ensure that the parameter IDs remain compatible between both the
+ // VST2 and VST3 versions of the plugin in all hosts.
+ //
+ // If you have released a VST3 version of the plugin but have not released a
+ // VST2 version of the plugin, enable JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING.
+ // DO NOT change the JUCE_VST3_CAN_REPLACE_VST2 or JUCE_FORCE_USE_LEGACY_PARAM_IDS
+ // values as this will break compatibility with currently released VST3
+ // versions of the plugin.
+ //
+ // If you have already released a VST2 and VST3 version of the plugin you may
+ // find in some hosts when a session containing automation data is saved using
+ // the VST2 or VST3 version, and is later loaded using the other version, the
+ // automation data will fail to control any of the parameters in the plugin as
+ // the IDs for these parameters are different. To fix parameter automation for
+ // the VST3 plugin when a session was saved with the VST2 plugin, implement
+ // VST3ClientExtensions::getCompatibleParameterIds() and enable
+ // JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING.
+
+ #error You may have a conflict with parameter automation between VST2 and VST3 versions of your plugin. See the comment above for more details.
+#endif
+
JUCE_BEGIN_NO_SANITIZE ("vptr")
#if JUCE_PLUGINHOST_VST3
@@ -97,23 +129,33 @@ JUCE_BEGIN_NO_SANITIZE ("vptr")
namespace juce
{
-JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4310)
-JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wall")
-
-#if JUCE_VST3_CAN_REPLACE_VST2
- static Steinberg::FUID getFUIDForVST2ID (bool forControllerUID)
- {
- Steinberg::TUID uuid;
- detail::PluginUtilities::getUUIDForVST2ID (forControllerUID, (uint8*) uuid);
- return Steinberg::FUID (uuid);
- }
-#endif
-
-JUCE_END_IGNORE_WARNINGS_MSVC
-JUCE_END_IGNORE_WARNINGS_GCC_LIKE
+using VST3InterfaceType = VST3ClientExtensions::InterfaceType;
+using VST3InterfaceId = VST3ClientExtensions::InterfaceId;
using namespace Steinberg;
+static FUID toSteinbergUID (const VST3InterfaceId& uid)
+{
+ return FUID::fromTUID ((const char*) (uid.data()));
+}
+
+static VST3InterfaceId toVST3InterfaceId (const TUID uid)
+{
+ VST3InterfaceId iid;
+ std::memcpy (iid.data(), uid, iid.size());
+ return iid;
+}
+
+static VST3InterfaceId getInterfaceId (VST3InterfaceType interfaceType)
+{
+ #if JUCE_VST3_CAN_REPLACE_VST2
+ if (interfaceType == VST3InterfaceType::controller || interfaceType == VST3InterfaceType::component)
+ return VST3ClientExtensions::convertVST2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, interfaceType);
+ #endif
+
+ return VST3ClientExtensions::convertJucePluginId (JucePlugin_ManufacturerCode, JucePlugin_PluginCode, interfaceType);
+}
+
//==============================================================================
#if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE
double getScaleFactorForWindow (HWND);
@@ -502,13 +544,15 @@ public:
#if JUCE_FORCE_USE_LEGACY_PARAM_IDS
return static_cast (paramIndex);
#else
+ jassert (paramIndex < vstParamIDs.size());
return vstParamIDs.getReference (paramIndex);
#endif
}
AudioProcessorParameter* getParamForVSTParamID (Vst::ParamID paramID) const noexcept
{
- return paramMap[static_cast (paramID)];
+ const auto iter = paramMap.find (paramID);
+ return iter != paramMap.end() ? iter->second : nullptr;
}
AudioProcessorParameter* getBypassParameter() const noexcept
@@ -561,8 +605,61 @@ public:
bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); }
+ std::map getParameterMap (const VST3InterfaceId& pluginId) const
+ {
+ const auto iter = compatibleParameterIdMap.find (pluginId);
+ return iter != compatibleParameterIdMap.end() ? iter->second
+ : std::map{};
+ }
+
+ AudioProcessorParameter* getParameter (const String& juceParamId) const
+ {
+ const auto iter = juceIdParameterMap.find (juceParamId);
+ return iter != juceIdParameterMap.end() ? iter->second : nullptr;
+ }
+
+ void updateParameterMapping()
+ {
+ static const auto currentPluginId = getInterfaceId (VST3InterfaceType::component);
+
+ compatibleParameterIdMap = {};
+ compatibleParameterIdMap[currentPluginId] = paramMap;
+
+ if (const auto* ext = audioProcessor->getVST3ClientExtensions())
+ {
+ for (auto& compatibleClass : ext->getCompatibleClasses())
+ {
+ auto& parameterIdMap = compatibleParameterIdMap[compatibleClass];
+
+ for (auto [oldParamId, newParamId] : ext->getCompatibleParameterIds (compatibleClass))
+ {
+ auto* parameter = getParameter (newParamId);
+ parameterIdMap[oldParamId] = parameter;
+
+ // This means a parameter ID returned by getCompatibleParameterIds()
+ // does not match any parameters declared in the plugin. All IDs must
+ // match an existing parameter, or return an empty string to indicate
+ // there is no parameter to map to.
+ jassert (parameter != nullptr || newParamId.isEmpty());
+
+ // This means getCompatibleParameterIds() returned a parameter mapping
+ // that will hide a parameter in the current plugin! If this is due to
+ // an ID collision between plugin versions, you may be able to determine
+ // the mapping to report based on setStateInformation(). If you've
+ // already done this you can safely ignore this warning. If there is no
+ // way to determine the difference between the two plugin versions in
+ // setStateInformation() the best course of action is to remove the
+ // problematic parameter from the mapping.
+ jassert (compatibleClass != currentPluginId
+ || getParamForVSTParamID (oldParamId) == nullptr
+ || parameter == getParamForVSTParamID (oldParamId));
+ }
+ }
+ }
+ }
+
//==============================================================================
- inline static const FUID iid { TUID INLINE_UID (0x0101ABAB, 0xABCDEF01, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) };
+ inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::processor));
private:
//==============================================================================
@@ -570,7 +667,7 @@ private:
{
parameterGroups = audioProcessor->getParameterTree().getSubgroups (true);
- #if JUCE_DEBUG
+ #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED
auto allGroups = parameterGroups;
allGroups.add (&audioProcessor->getParameterTree());
std::unordered_set unitIDs;
@@ -633,7 +730,8 @@ private:
}
vstParamIDs.add (vstParamID);
- paramMap.set (static_cast (vstParamID), juceParam);
+ paramMap[vstParamID] = juceParam;
+ juceIdParameterMap[LegacyAudioParameter::getParamID (juceParam, false)] = juceParam;
}
auto numPrograms = audioProcessor->getNumPrograms();
@@ -650,7 +748,7 @@ private:
programParamID = static_cast (i++);
vstParamIDs.add (programParamID);
- paramMap.set (static_cast (programParamID), ownedProgramParameter.get());
+ paramMap[programParamID] = ownedProgramParameter.get();
}
cachedParamValues = CachedParamValues { { vstParamIDs.begin(), vstParamIDs.end() } };
@@ -658,20 +756,13 @@ private:
Vst::ParamID generateVSTParamIDForParam (const AudioProcessorParameter* param)
{
- auto juceParamID = LegacyAudioParameter::getParamID (param, false);
+ const auto juceParamID = LegacyAudioParameter::getParamID (param, false);
- #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
+ #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
return static_cast (juceParamID.getIntValue());
- #else
- auto paramHash = static_cast (juceParamID.hashCode());
-
- #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
- // studio one doesn't like negative parameters
- paramHash &= ~(((Vst::ParamID) 1) << (sizeof (Vst::ParamID) * 8 - 1));
+ #else
+ return VST3ClientExtensions::convertJuceParameterId (juceParamID, JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS);
#endif
-
- return paramHash;
- #endif
}
//==============================================================================
@@ -679,6 +770,8 @@ private:
CachedParamValues cachedParamValues;
Vst::ParamID bypassParamID = 0, programParamID = static_cast (paramPreset);
bool bypassIsRegularParameter = false;
+ std::map> compatibleParameterIdMap;
+ std::map juceIdParameterMap;
//==============================================================================
std::atomic refCount { 0 };
@@ -686,7 +779,7 @@ private:
//==============================================================================
LegacyAudioParametersWrapper juceParameters;
- HashMap paramMap;
+ std::map paramMap;
std::unique_ptr ownedBypassParameter, ownedProgramParameter;
Array parameterGroups;
@@ -764,6 +857,7 @@ static void setValueAndNotifyIfChanged (AudioProcessorParameter& param, float ne
class JuceVST3EditController final : public Vst::EditController,
public Vst::IMidiMapping,
public Vst::IUnitInfo,
+ public Vst::IRemapParamID,
public Vst::ChannelContext::IInfoListener,
#if JucePlugin_Enable_ARA
public Presonus::IPlugInViewEmbedding,
@@ -783,12 +877,7 @@ public:
}
//==============================================================================
-
- #if JUCE_VST3_CAN_REPLACE_VST2
- inline static const FUID iid = getFUIDForVST2ID (true);
- #else
- inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0x1234ABCD, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) };
- #endif
+ inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::controller));
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Winconsistent-missing-override")
@@ -1028,6 +1117,30 @@ public:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProgramChangeParameter)
};
+ //==============================================================================
+ tresult PLUGIN_API getCompatibleParamID (const TUID pluginToReplaceUID,
+ Vst::ParamID oldParamID,
+ Vst::ParamID& newParamID) override
+ {
+ const auto parameterMap = audioProcessor->getParameterMap (toVST3InterfaceId (pluginToReplaceUID));
+ const auto iter = parameterMap.find (oldParamID);
+
+ if (iter == parameterMap.end())
+ {
+ // This suggests a host is trying to load a plugin and parameter ID
+ // combination that hasn't been accounted for in getCompatibleParameterIds().
+ // Override this method in VST3ClientExtensions and return a suitable
+ // parameter mapping to silence this warning.
+ jassertfalse;
+ return kResultFalse;
+ }
+
+ const auto* parameter = iter->second;
+ newParamID = parameter != nullptr ? audioProcessor->getVSTParamIDForIndex (parameter->getParameterIndex())
+ : 0xffffffff;
+ return kResultTrue;
+ }
+
//==============================================================================
tresult PLUGIN_API setChannelContextInfos (Vst::IAttributeList* list) override
{
@@ -1101,8 +1214,10 @@ public:
}
}
+ audioProcessor->updateParameterMapping();
+
if (auto* handler = getComponentHandler())
- handler->restartComponent (Vst::kParamValuesChanged);
+ handler->restartComponent (Vst::kParamValuesChanged | Vst::kParamIDMappingChanged);
return kResultOk;
}
@@ -1549,6 +1664,7 @@ private:
UniqueBase{},
UniqueBase{},
UniqueBase{},
+ UniqueBase{},
UniqueBase{},
SharedBase{},
UniqueBase{},
@@ -1557,6 +1673,9 @@ private:
#endif
SharedBase{});
+ if (targetIID == Vst::IRemapParamID::iid)
+ jassertfalse;
+
if (result.isOk())
return result;
@@ -2508,7 +2627,7 @@ private:
return createARAFactory();
}
- inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0xA1B2C3D4, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) };
+ inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::ara));
private:
//==============================================================================
@@ -2581,11 +2700,7 @@ public:
AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
//==============================================================================
- #if JUCE_VST3_CAN_REPLACE_VST2
- inline static const FUID iid = getFUIDForVST2ID (false);
- #else
- inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0x9182FAEB, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) };
- #endif
+ inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::component));
JUCE_DECLARE_VST3_COM_REF_METHODS
@@ -3998,15 +4113,7 @@ public:
Array oldArray;
for (const auto& uid : extensions->getCompatibleClasses())
- {
- // All UIDs returned from getCompatibleClasses should be 32 characters long
- jassert (uid.length() == 32);
-
- // All UIDs returned from getCompatibleClasses should be in hex notation
- jassert (uid.containsOnly ("ABCDEF0123456789"));
-
- oldArray.add (uid);
- }
+ oldArray.add (String::toHexString (uid.data(), (int) uid.size(), 0));
return oldArray;
}());
@@ -4034,7 +4141,7 @@ public:
return kNotImplemented;
}
- inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0xC0DEF00D, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) };
+ inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::compatibility));
private:
std::atomic refCount { 1 };
diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp
index 873ca3ade0..a3d5d7ed9e 100644
--- a/modules/juce_audio_processors/juce_audio_processors.cpp
+++ b/modules/juce_audio_processors/juce_audio_processors.cpp
@@ -231,6 +231,7 @@ private:
#include "utilities/juce_PluginHostType.cpp"
#include "utilities/juce_AAXClientExtensions.cpp"
#include "utilities/juce_VST2ClientExtensions.cpp"
+#include "utilities/juce_VST3ClientExtensions.cpp"
#include "utilities/ARA/juce_ARA_utils.cpp"
#include "format_types/juce_LV2PluginFormat.cpp"
diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
index 74376b5096..103d303c12 100644
--- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h
+++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h
@@ -1154,7 +1154,11 @@ public:
See also the helper function getXmlFromBinary() for loading settings as XML.
- @see setCurrentProgramStateInformation
+ VST3ClientExtensions::getCompatibleParameterIds() will always be called after
+ setStateInformation() therefore you can use information from the plugin state
+ to determine which parameter mapping to use if necessary.
+
+ @see setCurrentProgramStateInformation, VST3ClientExtensions::getCompatibleParameterIds
*/
virtual void setStateInformation (const void* data, int sizeInBytes) = 0;
diff --git a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp
new file mode 100644
index 0000000000..b3cd133bb8
--- /dev/null
+++ b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp
@@ -0,0 +1,167 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE framework.
+ Copyright (c) Raw Material Software Limited
+
+ JUCE is an open source framework subject to commercial or open source
+ licensing.
+
+ By downloading, installing, or using the JUCE framework, or combining the
+ JUCE framework with any other source code, object code, content or any other
+ copyrightable work, you agree to the terms of the JUCE End User Licence
+ Agreement, and all incorporated terms including the JUCE Privacy Policy and
+ the JUCE Website Terms of Service, as applicable, which will bind you. If you
+ do not agree to the terms of these agreements, we will not license the JUCE
+ framework to you, and you must discontinue the installation or download
+ process and cease use of the JUCE framework.
+
+ JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
+ JUCE Privacy Policy: https://juce.com/juce-privacy-policy
+ JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
+
+ Or:
+
+ You may also use this code under the terms of the AGPLv3:
+ https://www.gnu.org/licenses/agpl-3.0.en.html
+
+ THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
+ WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
+
+ ==============================================================================
+*/
+
+namespace juce
+{
+
+std::map VST3ClientExtensions::getCompatibleParameterIds (const InterfaceId&) const
+{
+ return {};
+}
+
+VST3ClientExtensions::InterfaceId VST3ClientExtensions::convertJucePluginId (uint32_t manufacturerCode,
+ uint32_t pluginCode,
+ InterfaceType interfaceType)
+{
+ const auto word0 = std::invoke ([&]() -> uint32_t
+ {
+ switch (interfaceType)
+ {
+ case InterfaceType::ara: [[fallthrough]];
+ case InterfaceType::controller: [[fallthrough]];
+ case InterfaceType::compatibility: [[fallthrough]];
+ case InterfaceType::component: return 0xABCDEF01;
+ case InterfaceType::processor: return 0x0101ABAB;
+ }
+
+ jassertfalse;
+ return 0;
+ });
+
+ const auto word1 = std::invoke ([&]() -> uint32_t
+ {
+ switch (interfaceType)
+ {
+ case InterfaceType::ara: return 0xA1B2C3D4;
+ case InterfaceType::controller: return 0x1234ABCD;
+ case InterfaceType::compatibility: return 0xC0DEF00D;
+ case InterfaceType::component: return 0x9182FAEB;
+ case InterfaceType::processor: return 0xABCDEF01;
+ }
+
+ jassertfalse;
+ return 0;
+ });
+
+ std::array data;
+ std::memcpy (&data[0], &word0, sizeof (word0));
+ std::memcpy (&data[4], &word1, sizeof (word1));
+ std::memcpy (&data[8], &manufacturerCode, sizeof (manufacturerCode));
+ std::memcpy (&data[12], &pluginCode, sizeof (pluginCode));
+
+ return data;
+}
+
+VST3ClientExtensions::InterfaceId VST3ClientExtensions::convertVST2PluginId (uint32_t pluginCode,
+ const String& pluginName,
+ InterfaceType interfaceType)
+{
+ VST3ClientExtensions::InterfaceId iid{};
+
+ iid[0] = (std::byte) 'V';
+ iid[1] = (std::byte) 'S';
+ iid[2] = (std::byte) std::invoke ([&]
+ {
+ switch (interfaceType)
+ {
+ case InterfaceType::controller: return 'E';
+ case InterfaceType::component: return 'T';
+ case InterfaceType::ara: [[fallthrough]];
+ case InterfaceType::compatibility: [[fallthrough]];
+ case InterfaceType::processor: break;
+ }
+
+ // A VST2 plugin only has two interfaces
+ // - component (the audio effect)
+ // - controller (the editor/UI)
+ jassertfalse;
+ return '\0';
+ });
+ iid[3] = (std::byte) (pluginCode >> 24);
+ iid[4] = (std::byte) (pluginCode >> 16);
+ iid[5] = (std::byte) (pluginCode >> 8);
+ iid[6] = (std::byte) pluginCode;
+
+ for (const auto [index, character] : enumerate (pluginName, (size_t) 7))
+ {
+ if (index >= iid.size())
+ break;
+
+ iid[index] = (std::byte) CharacterFunctions::toLowerCase (character);
+ }
+
+ #if JUCE_WINDOWS
+ std::swap (iid[0], iid[3]);
+ std::swap (iid[1], iid[2]);
+ std::swap (iid[4], iid[5]);
+ std::swap (iid[6], iid[7]);
+ #endif
+
+ return iid;
+}
+
+uint32_t VST3ClientExtensions::convertJuceParameterId (const String& parameterId, bool studioOneCompatible)
+{
+ auto hash = (uint32_t) (parameterId.hashCode());
+
+ if (studioOneCompatible)
+ hash &= 0x7fffffff;
+
+ return hash;
+}
+
+VST3ClientExtensions::InterfaceId VST3ClientExtensions::toInterfaceId (const String& interfaceIdString)
+{
+ jassert (interfaceIdString.length() == 32);
+ jassert (interfaceIdString.containsOnly ("0123456789abcdefABCDEF"));
+
+ return { (std::byte) interfaceIdString.substring ( 0, 2).getHexValue32(),
+ (std::byte) interfaceIdString.substring ( 2, 4).getHexValue32(),
+ (std::byte) interfaceIdString.substring ( 4, 6).getHexValue32(),
+ (std::byte) interfaceIdString.substring ( 6, 8).getHexValue32(),
+ (std::byte) interfaceIdString.substring ( 8, 10).getHexValue32(),
+ (std::byte) interfaceIdString.substring (10, 12).getHexValue32(),
+ (std::byte) interfaceIdString.substring (12, 14).getHexValue32(),
+ (std::byte) interfaceIdString.substring (14, 16).getHexValue32(),
+ (std::byte) interfaceIdString.substring (16, 18).getHexValue32(),
+ (std::byte) interfaceIdString.substring (18, 20).getHexValue32(),
+ (std::byte) interfaceIdString.substring (20, 22).getHexValue32(),
+ (std::byte) interfaceIdString.substring (22, 24).getHexValue32(),
+ (std::byte) interfaceIdString.substring (24, 26).getHexValue32(),
+ (std::byte) interfaceIdString.substring (26, 28).getHexValue32(),
+ (std::byte) interfaceIdString.substring (28, 30).getHexValue32(),
+ (std::byte) interfaceIdString.substring (30, 32).getHexValue32() };
+}
+
+} // namespace juce
diff --git a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h
index 09c8cdb73a..cee4e6a305 100644
--- a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h
+++ b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h
@@ -104,16 +104,156 @@ struct VST3ClientExtensions
*/
virtual bool getPluginHasMainInput() const { return true; }
- /** This function should return the UIDs of any compatible VST2 plug-ins.
+ using InterfaceId = std::array;
- Each item in the vector should be a 32-character string consisting only
- of the characters 0-9 and A-F.
+ /** This function should return the UIDs of any compatible VST2 or VST3
+ plug-ins.
This information will be used to implement the IPluginCompatibility
interface. Hosts can use this interface to determine whether this VST3
is capable of replacing a given VST2.
+
+ Each compatible class is a 16-byte array that corresponds to the VST3
+ interface ID for the class implementing the IComponent interface.
+ For VST2 or JUCE plugins these IDs can be determined in the following
+ ways:
+ - Use convertVST2PluginId() for VST2 plugins or JUCE VST3 plugins with
+ JUCE_VST3_CAN_REPLACE_VST3 enabled
+ - Use convertJucePluginId() for any other JUCE VST3 plugins
+
+ If JUCE_VST3_CAN_REPLACE_VST2 is enabled the VST3 plugin will have the
+ same identifier as the VST2 plugin and therefore there will be no need
+ to implement this function.
+
+ If the parameter IDs between compatible versions differ
+ getCompatibleParameterIds() should also be overridden. However, unlike
+ getCompatibleParameterIds() this function should remain constant and
+ always return the same IDs.
+
+ @see getCompatibleParameterIds()
*/
- virtual std::vector getCompatibleClasses() const { return {}; }
+ virtual std::vector getCompatibleClasses() const { return {}; }
+
+ /** This function should return a map of VST3 parameter IDs and the JUCE
+ parameters they map to.
+
+ This information is used to implement the IRemapParameter interface.
+ Hosts can use this to preserve automation data when a session was saved
+ using a compatible plugin that has different parameter IDs.
+
+ Not all hosts will take this information into account. Therefore,
+ parameter IDs should be maintained between plugin versions. For JUCE
+ plugins migrating from VST2 to VST3 the best method for achieving this
+ is enabling JUCE_FORCE_LEGACY_PARAM_IDS. However, if a plugin has
+ already been released without enabling this flag, this method offers an
+ alternative approach that won't cause any further compatibility issues.
+
+ The key in the map is a VST3 parameter identifier or Vst::ParamID. For
+ VST2 or JUCE plugins these IDs can be determined in the following ways
+ - Use the parameter index for
+ - VST2 plugins
+ - JUCE VST3 plugins with JUCE_FORCE_LEGACY_PARAM_IDS enabled
+ - Any parameter that doesn't inherit from HostedAudioProcessorParameter
+ - Use convertJuceParameterId() for JUCE VST3 plugins where
+ JUCE_FORCE_LEGACY_PARAM_IDS is disabled
+
+ The value in the map is the JUCE parameter ID for the parameter to map
+ to, or an empty string to indicate that there is no parameter to map to.
+ If a parameter doesn't inherit from HostedAudioProcessorParameter its ID
+ will be the parameter index as a string, for example "1". Otherwise
+ always use the actual parameter ID (even if JUCE_FORCE_LEGACY_PARAM_IDS
+ is enabled).
+
+ In the unlikely event that two plugins share the same plugin ID, and
+ both have a different parameters that share the same parameter ID, it
+ may be possible to determine which version of the plugin is being loaded
+ during setStateInformation(). This method will always be called after
+ setStateInformation(), so that the map with the correct mapping can be
+ provided when queried.
+
+ Below is an example of how you might implement this function for a JUCE
+ VST3 plugin where JUCE_VST3_CAN_REPLACE_VST2 is enabled, but
+ JUCE_FORCE_LEGACY_PARAM_IDS is disabled.
+
+ @code
+ std::map getCompatibleParameterIds (const String&) const override
+ {
+ return { { 0, "Frequency" },
+ { 1, "CutOff" },
+ { 2, "Gain" },
+ { 3, "Bypass" } };
+ }
+ @endcode
+
+ @param compatibleClass A plugin identifier, either for the current
+ plugin or one listed in getCompatibleClasses().
+ This parameter allows the implementation to
+ return a different parameter map for each
+ compatible class. Use convertJucePluginId() and
+ convertVST2PluginId() to determine the class IDs
+ used by JUCE plugins.
+
+ @returns A map where each key is a VST3 parameter ID in the compatible
+ plugin, and the value is the unique JUCE parameter ID in the
+ current plugin that it should be mapped to.
+
+ @see getCompatibleClasses, convertJucePluginId, convertVST2PluginId, convertJuceParameterId
+ */
+ virtual std::map getCompatibleParameterIds (const InterfaceId& compatibleClass) const;
+
+ /** An enum indicating the various VST3 interface types.
+
+ In most cases users shouldn't need to concern themselves with any
+ interfaces other than the component, which is used to report the actual
+ audio effect.
+ */
+ enum class InterfaceType
+ {
+ ara,
+ controller,
+ compatibility,
+ component,
+ processor
+ };
+
+ /** Returns a 16-byte array indicating the VST3 interface ID used for a
+ given JUCE VST3 plugin.
+
+ Internally this is what JUCE will use to assign an ID to each VST3
+ interface, unless JUCE_VST3_CAN_REPLACE_VST2 is enabled.
+
+ @see convertVST2PluginId, getCompatibleClasses, getCompatibleParameterIds
+ */
+ static InterfaceId convertJucePluginId (uint32_t manufacturerCode,
+ uint32_t pluginCode,
+ InterfaceType interfaceType = InterfaceType::component);
+
+ /** Returns a 16-byte array indicating the VST3 interface ID used for a
+ given VST2 plugin.
+
+ Internally JUCE will use this method to assign an ID for the component
+ and controller interfaces when JUCE_VST3_CAN_REPLACE_VST2 is enabled.
+
+ @convertJucePluginId, getCompatibleClasses, getCompatibleParameterIds
+ */
+ static InterfaceId convertVST2PluginId (uint32_t pluginCode,
+ const String& pluginName,
+ InterfaceType interfaceType = InterfaceType::component);
+
+ /** Returns the VST3 compatible parameter ID reported for a given JUCE
+ parameter.
+
+ Internally JUCE will use this method to determine the Vst::ParamID for
+ a HostedAudioProcessorParameter, unless JUCE_FORCE_LEGACY_PARAM_IDS is
+ enabled, in which case it will use the parameter index.
+
+ @see getCompatibleParameterIds
+ */
+ static uint32_t convertJuceParameterId (const String& parameterId,
+ bool studioOneCompatible = true);
+
+ /** Converts a 32-character hex notation string to a VST3 interface ID. */
+ static InterfaceId toInterfaceId (const String& interfaceIdString);
};
} // namespace juce