From 2c606d6612468d1b86b5d4d307839d4d4870dbd4 Mon Sep 17 00:00:00 2001 From: Anthony Nicholls Date: Fri, 9 Aug 2024 13:05:36 +0100 Subject: [PATCH] VST3: Fix an issue migrating VST2 to VST3 states in Reaper Reaper uses an undocumented version 0 of the fxbank struct, this could be handled by JUCE but instead this solution switches to using methods provided by the VST3 SDK to save and load VST2 states. This also has the added benefit of no longer requiring the VST2 SDK to build a VST3 plugin that supports saving and loading VST2 states. --- .../Builds/Android/app/CMakeLists.txt | 4 + .../VisualStudio2019/DemoRunner_App.vcxproj | 4 + .../DemoRunner_App.vcxproj.filters | 6 + .../VisualStudio2022/DemoRunner_App.vcxproj | 4 + .../DemoRunner_App.vcxproj.filters | 6 + .../Builds/Android/app/CMakeLists.txt | 4 + .../AudioPerformanceTest_App.vcxproj | 4 + .../AudioPerformanceTest_App.vcxproj.filters | 6 + .../Builds/Android/app/CMakeLists.txt | 4 + .../AudioPluginHost_App.vcxproj | 4 + .../AudioPluginHost_App.vcxproj.filters | 6 + .../AudioPluginHost_App.vcxproj | 4 + .../AudioPluginHost_App.vcxproj.filters | 6 + extras/Build/CMake/JUCEHelperTargets.cmake | 1 + .../Builds/Android/app/CMakeLists.txt | 4 + .../NetworkGraphicsDemo_App.vcxproj | 4 + .../NetworkGraphicsDemo_App.vcxproj.filters | 6 + .../ProjectSaving/jucer_ProjectExporter.cpp | 1 + .../UnitTestRunner_ConsoleApp.vcxproj | 4 + .../UnitTestRunner_ConsoleApp.vcxproj.filters | 6 + .../UnitTestRunner_ConsoleApp.vcxproj | 4 + .../UnitTestRunner_ConsoleApp.vcxproj.filters | 6 + .../WindowsDLL_StaticLibrary.vcxproj | 4 + .../WindowsDLL_StaticLibrary.vcxproj.filters | 6 + .../detail/juce_PluginUtilities.h | 4 - .../juce_audio_plugin_client_VST3.cpp | 208 ++---- .../source/vst/utility/vst2persistence.cpp | 633 ++++++++++++++++++ .../source/vst/utility/vst2persistence.h | 123 ++++ .../format_types/juce_VST3Headers.h | 5 + 29 files changed, 923 insertions(+), 158 deletions(-) create mode 100644 modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp create mode 100644 modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt index 35379d7a5e..6a032fa594 100644 --- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt +++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt @@ -748,6 +748,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" @@ -3263,6 +3265,8 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj index d34954a154..f80394e608 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj @@ -947,6 +947,9 @@ true + + true + true @@ -3552,6 +3555,7 @@ + diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters index 243b2c5399..8a42e5619d 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters @@ -1639,6 +1639,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -5250,6 +5253,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj index b862217d2c..7b3c5bc01a 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj @@ -947,6 +947,9 @@ true + + true + true @@ -3552,6 +3555,7 @@ + diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters index 0d8aaf63bc..0269f158b0 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters @@ -1639,6 +1639,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -5250,6 +5253,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt index 682db777df..d14a0bf159 100644 --- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt @@ -703,6 +703,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" @@ -2901,6 +2903,8 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj index bb4feb7fec..723a414e57 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj @@ -907,6 +907,9 @@ true + + true + true @@ -3124,6 +3127,7 @@ + diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters index 6ee29f4608..d9891e52d2 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters @@ -1456,6 +1456,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4608,6 +4611,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt index b4b8f706f5..9e780dd435 100644 --- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt @@ -736,6 +736,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" @@ -3087,6 +3089,8 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj index bc03d70f9f..0e15fa3ef8 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj @@ -915,6 +915,9 @@ true + + true + true @@ -3304,6 +3307,7 @@ + diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters index 116cf3c080..c9c303abc8 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters @@ -1531,6 +1531,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4884,6 +4887,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj index 6ddce8d84f..8defa769a0 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj @@ -915,6 +915,9 @@ true + + true + true @@ -3304,6 +3307,7 @@ + diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters index 08e12d981f..00323eac0b 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters @@ -1531,6 +1531,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4884,6 +4887,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/Build/CMake/JUCEHelperTargets.cmake b/extras/Build/CMake/JUCEHelperTargets.cmake index 30ed5284fd..e7002ae1c7 100644 --- a/extras/Build/CMake/JUCEHelperTargets.cmake +++ b/extras/Build/CMake/JUCEHelperTargets.cmake @@ -99,6 +99,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") -Wno-implicit-fallthrough -Wno-maybe-uninitialized -Wno-ignored-qualifiers + -Wno-multichar -Wswitch-enum -Wredundant-decls -Wno-strict-overflow diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt index 984b630ad2..8c6cb29e72 100644 --- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt +++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt @@ -707,6 +707,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" @@ -2985,6 +2987,8 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/stringconvert.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp" + "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.cpp" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstbus.h" "../../../../../modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/vstcomponent.cpp" diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj index 3569fecde8..dbd231cf59 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj @@ -907,6 +907,9 @@ true + + true + true @@ -3215,6 +3218,7 @@ + diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters index 259e43ed02..8540038cb4 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters @@ -1486,6 +1486,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4749,6 +4752,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp index 5e91e9bfe7..4b298ffc3a 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp @@ -954,6 +954,7 @@ ProjectExporter::BuildConfiguration::BuildConfiguration (Project& p, const Value "-Wno-maybe-uninitialized", "-Wredundant-decls", "-Wno-strict-overflow", + "-Wno-multichar", "-Wshadow" }); } diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj index 47d67d5b44..b29b374f90 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj @@ -923,6 +923,9 @@ true + + true + true @@ -3400,6 +3403,7 @@ + diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters index 43aedc6ab7..557924d8bf 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1552,6 +1552,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4998,6 +5001,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj index 3159363a5a..c5f60508cd 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj @@ -923,6 +923,9 @@ true + + true + true @@ -3400,6 +3403,7 @@ + diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters index f2c1c612b7..977ae82575 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1552,6 +1552,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4998,6 +5001,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj index 283f2ea7c9..7a022809df 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj @@ -906,6 +906,9 @@ true + + true + true @@ -3191,6 +3194,7 @@ + diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters index 5c908a7ecc..7ea6ca93da 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters @@ -1483,6 +1483,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst @@ -4716,6 +4719,9 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\utility + JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst diff --git a/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h b/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h index c73ef16a79..1e7744f94c 100644 --- a/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h +++ b/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h @@ -66,10 +66,6 @@ struct PluginUtilities return hostType; } - #ifndef JUCE_VST3_CAN_REPLACE_VST2 - #define JUCE_VST3_CAN_REPLACE_VST2 1 - #endif - // NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code. static void getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16]) { 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 568d0c6778..db7eea776a 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 @@ -64,24 +64,6 @@ JUCE_BEGIN_NO_SANITIZE ("vptr") #include #include -#ifndef JUCE_VST3_CAN_REPLACE_VST2 - #define JUCE_VST3_CAN_REPLACE_VST2 1 -#endif - -#if JUCE_VST3_CAN_REPLACE_VST2 - - #if ! JUCE_MSVC && ! defined (__cdecl) - #define __cdecl - #endif - - namespace Vst2 - { - struct AEffect; - #include "pluginterfaces/vst2.x/vstfxstore.h" - } - -#endif - #ifndef JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS #if JucePlugin_WantsMidiInput #define JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS 1 @@ -2824,102 +2806,22 @@ public: } //============================================================================== - #if JUCE_VST3_CAN_REPLACE_VST2 - bool loadVST2VstWBlock (const char* data, int size) - { - jassert (ByteOrder::bigEndianInt ("VstW") == htonl ((uint32) readUnaligned (data))); - jassert (1 == htonl ((uint32) readUnaligned (data + 8))); // version should be 1 according to Steinberg's docs - - auto headerLen = (int) htonl ((uint32) readUnaligned (data + 4)) + 8; - return loadVST2CcnKBlock (data + headerLen, size - headerLen); - } - - bool loadVST2CcnKBlock (const char* data, int size) - { - auto* bank = reinterpret_cast (data); - - jassert (ByteOrder::bigEndianInt ("CcnK") == htonl ((uint32) bank->chunkMagic)); - jassert (ByteOrder::bigEndianInt ("FBCh") == htonl ((uint32) bank->fxMagic)); - jassert (htonl ((uint32) bank->version) == 1 || htonl ((uint32) bank->version) == 2); - jassert (JucePlugin_VSTUniqueID == htonl ((uint32) bank->fxID)); - - setStateInformation (bank->content.data.chunk, - jmin ((int) (size - (bank->content.data.chunk - data)), - (int) htonl ((uint32) bank->content.data.size))); - return true; - } - - bool loadVST3PresetFile (const char* data, int size) - { - if (size < 48) - return false; - - // At offset 4 there's a little-endian version number which seems to typically be 1 - // At offset 8 there's 32 bytes the SDK calls "ASCII-encoded class id" - auto chunkListOffset = (int) ByteOrder::littleEndianInt (data + 40); - jassert (memcmp (data + chunkListOffset, "List", 4) == 0); - auto entryCount = (int) ByteOrder::littleEndianInt (data + chunkListOffset + 4); - jassert (entryCount > 0); - - for (int i = 0; i < entryCount; ++i) - { - auto entryOffset = chunkListOffset + 8 + 20 * i; - - if (entryOffset + 20 > size) - return false; - - if (memcmp (data + entryOffset, "Comp", 4) == 0) - { - // "Comp" entries seem to contain the data. - auto chunkOffset = ByteOrder::littleEndianInt64 (data + entryOffset + 4); - auto chunkSize = ByteOrder::littleEndianInt64 (data + entryOffset + 12); - - if (static_cast (chunkOffset + chunkSize) > static_cast (size)) - { - jassertfalse; - return false; - } - - loadVST2VstWBlock (data + chunkOffset, (int) chunkSize); - } - } - - return true; - } - - bool loadVST2CompatibleState (const char* data, int size) - { - if (size < 4) - return false; - - auto header = htonl ((uint32) readUnaligned (data)); - - if (header == ByteOrder::bigEndianInt ("VstW")) - return loadVST2VstWBlock (data, size); - - if (header == ByteOrder::bigEndianInt ("CcnK")) - return loadVST2CcnKBlock (data, size); - - if (memcmp (data, "VST3", 4) == 0) - { - // In Cubase 5, when loading VST3 .vstpreset files, - // we get the whole content of the files to load. - // In Cubase 7 we get just the contents within and - // we go directly to the loadVST2VstW codepath instead. - return loadVST3PresetFile (data, size); - } - - return false; - } - #endif - - void loadStateData (const void* data, int size) + bool shouldTryToLoadVst2State() { #if JUCE_VST3_CAN_REPLACE_VST2 - if (loadVST2CompatibleState ((const char*) data, size)) - return; + return true; + #else + return false; + #endif + } + + bool shouldWriteStateWithVst2Compatibility() + { + #if JUCE_VST3_CAN_REPLACE_VST2 + return true; + #else + return false; #endif - setStateInformation (data, size); } bool readFromMemoryStream (IBStream* state) @@ -2952,7 +2854,7 @@ public: if (block.getSize() >= 5 && memcmp (block.getData(), "VC2!E", 5) == 0) return false; - loadStateData (block.getData(), (int) block.getSize()); + setStateInformation (block.getData(), (int) block.getSize()); return true; } @@ -2984,10 +2886,21 @@ public: if (dataSize <= 0 || dataSize >= 0x7fffffff) return false; - loadStateData (allData.getData(), (int) dataSize); + setStateInformation (allData.getData(), (int) dataSize); return true; } + bool readVst2State (IBStream* state) + { + if (auto vst2State = VST3::tryVst2StateLoad (*state)) + { + setStateInformation (vst2State->chunk.data(), (int) vst2State->chunk.size()); + return true; + } + + return false; + } + tresult PLUGIN_API setState (IBStream* state) override { // The VST3 spec requires that this function is called from the UI thread. @@ -2999,36 +2912,41 @@ public: FUnknownPtr stateRefHolder (state); // just in case the caller hasn't properly ref-counted the stream object - if (state->seek (0, IBStream::kIBSeekSet, nullptr) == kResultTrue) + const auto seekToBeginningOfStream = [&] { - if (! detail::PluginUtilities::getHostType().isFruityLoops() && readFromMemoryStream (state)) - return kResultTrue; + return state->seek (0, IBStream::kIBSeekSet, nullptr) == kResultTrue; + }; - if (readFromUnknownStream (state)) - return kResultTrue; - } + if (seekToBeginningOfStream() && shouldTryToLoadVst2State() && readVst2State (state)) + return kResultTrue; + + if (seekToBeginningOfStream() && ! detail::PluginUtilities::getHostType().isFruityLoops() && readFromMemoryStream (state)) + return kResultTrue; + + if (seekToBeginningOfStream() && readFromUnknownStream (state)) + return kResultTrue; return kResultFalse; } - #if JUCE_VST3_CAN_REPLACE_VST2 - static tresult writeVST2Header (IBStream* state, bool bypassed) + tresult getStateWithVst2Compatibility (const MemoryBlock& dataChunk, IBStream& outState) { - auto writeVST2IntToState = [state] (uint32 n) - { - auto t = (int32) htonl (n); - return state->write (&t, 4); - }; + VST3::Vst2xState vst2State; - auto status = writeVST2IntToState (ByteOrder::bigEndianInt ("VstW")); + vst2State.chunk.resize (dataChunk.getSize()); + std::copy (dataChunk.begin(), dataChunk.end(), vst2State.chunk.begin()); - if (status == kResultOk) status = writeVST2IntToState (8); // header size - if (status == kResultOk) status = writeVST2IntToState (1); // version - if (status == kResultOk) status = writeVST2IntToState (bypassed ? 1 : 0); // bypass + vst2State.fxUniqueID = JucePlugin_VSTUniqueID; + vst2State.fxVersion = JucePlugin_VersionCode; + vst2State.isBypassed = isBypassed(); - return status; + if (VST3::writeVst2State (vst2State, outState)) + return kResultTrue; + + // Please inform the JUCE team if you hit this assertion + jassertfalse; + return kResultFalse; } - #endif tresult PLUGIN_API getState (IBStream* state) override { @@ -3038,29 +2956,11 @@ public: MemoryBlock mem; getStateInformation (mem); - #if JUCE_VST3_CAN_REPLACE_VST2 - tresult status = writeVST2Header (state, isBypassed()); + if (mem.isEmpty()) + return kResultFalse; - if (status != kResultOk) - return status; - - const int bankBlockSize = 160; - Vst2::fxBank bank; - - zerostruct (bank); - bank.chunkMagic = (int32) htonl (ByteOrder::bigEndianInt ("CcnK")); - bank.byteSize = (int32) htonl (bankBlockSize - 8 + (unsigned int) mem.getSize()); - bank.fxMagic = (int32) htonl (ByteOrder::bigEndianInt ("FBCh")); - bank.version = (int32) htonl (2); - bank.fxID = (int32) htonl (JucePlugin_VSTUniqueID); - bank.fxVersion = (int32) htonl (JucePlugin_VersionCode); - bank.content.data.size = (int32) htonl ((unsigned int) mem.getSize()); - - status = state->write (&bank, bankBlockSize); - - if (status != kResultOk) - return status; - #endif + if (shouldWriteStateWithVst2Compatibility()) + return getStateWithVst2Compatibility (mem, *state); return state->write (mem.getData(), (Steinberg::int32) mem.getSize()); } diff --git a/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp b/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp new file mode 100644 index 0000000000..a6ccdb0098 --- /dev/null +++ b/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.cpp @@ -0,0 +1,633 @@ +//------------------------------------------------------------------------ +// Flags : clang-format SMTGSequencer +// Project : VST3 SDK +// Filename : public.sdk/source/vst/utility/vst2persistence.cpp +// Created by : Steinberg, 12/2019 +// Description : vst2 persistence helper +// +//------------------------------------------------------------------------ +// LICENSE +// (c) 2024, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#include "public.sdk/source/vst/utility/vst2persistence.h" +#include "pluginterfaces/base/fplatform.h" +#include + +//------------------------------------------------------------------------ +namespace VST3 { +namespace { +namespace IO { + +//------------------------------------------------------------------------ +enum class Error +{ + NoError, + Unknown, + EndOfFile, + BufferToBig, + NotAllowed, + InvalidArgument, +}; + +//------------------------------------------------------------------------ +enum class SeekMode +{ + Set, + End, + Current +}; + +//------------------------------------------------------------------------ +struct Result +{ + Error error {Error::Unknown}; + uint64_t bytes {0u}; + + Result () noexcept = default; + Result (Error error, uint64_t bytes = 0) noexcept : error (error), bytes (bytes) {} + + operator bool () const noexcept { return error == Error::NoError; } +}; + +//------------------------------------------------------------------------ +struct ReadBufferDesc +{ + const uint64_t bytes; + void* ptr; +}; + +//------------------------------------------------------------------------ +struct WriteBufferDesc +{ + const uint64_t bytes; + const void* ptr; +}; + +//------------------------------------------------------------------------ +template +class ByteOrderStream +{ +public: +//------------------------------------------------------------------------ + ByteOrderStream (Steinberg::IBStream& stream) noexcept : stream (stream) {} + ByteOrderStream (ByteOrderStream&&) noexcept = delete; + ByteOrderStream& operator= (ByteOrderStream&&) noexcept = delete; + ByteOrderStream (const ByteOrderStream&) noexcept = delete; + ByteOrderStream& operator= (const ByteOrderStream&) noexcept = delete; + + inline Result operator<< (const std::string& input) noexcept; + inline Result operator>> (std::string& output) noexcept; + + template + inline Result operator<< (const T& input) noexcept; + template + inline Result operator>> (T& output) const noexcept; + + inline Result read (const ReadBufferDesc& buffer) const noexcept; + inline Result write (const WriteBufferDesc& buffer) noexcept; + inline Result seek (SeekMode mode, int64_t bytes) const noexcept; + inline Result tell () const noexcept; + +//------------------------------------------------------------------------ +private: + template + inline Result swapAndWrite (const uint8_t* buffer) noexcept; + inline void swap (uint8_t* buffer, uint64_t size) const noexcept; + Steinberg::IBStream& stream; +}; + +//------------------------------------------------------------------------ +using LittleEndianStream = ByteOrderStream; +using BigEndianStream = ByteOrderStream; +using NativeEndianStream = ByteOrderStream; + +//------------------------------------------------------------------------ +template +inline Result ByteOrderStream::read (const ReadBufferDesc& buffer) const noexcept +{ + if (buffer.bytes > static_cast (std::numeric_limits::max ())) + return Result (Error::BufferToBig); + Steinberg::int32 readBytes = 0; + auto tres = stream.read (buffer.ptr, static_cast (buffer.bytes), &readBytes); + if (tres != Steinberg::kResultTrue) + return Result (Error::Unknown); + assert (readBytes >= 0); + return Result {Error::NoError, static_cast (readBytes)}; +} + +//------------------------------------------------------------------------ +template +inline Result ByteOrderStream::write (const WriteBufferDesc& buffer) noexcept +{ + if (buffer.bytes > static_cast (std::numeric_limits::max ())) + return Result (Error::BufferToBig); + Steinberg::int32 writtenBytes = 0; + auto tres = stream.write (const_cast (buffer.ptr), + static_cast (buffer.bytes), &writtenBytes); + if (tres != Steinberg::kResultTrue) + return Result (Error::Unknown); + assert (writtenBytes >= 0); + return Result {Error::NoError, static_cast (writtenBytes)}; +} + +//------------------------------------------------------------------------ +template +inline Result ByteOrderStream::seek (SeekMode mode, int64_t bytes) const noexcept +{ + Steinberg::int32 seekMode = 0; + switch (mode) + { + case SeekMode::Set: seekMode = Steinberg::IBStream::kIBSeekSet; break; + case SeekMode::Current: seekMode = Steinberg::IBStream::kIBSeekCur; break; + case SeekMode::End: seekMode = Steinberg::IBStream::kIBSeekEnd; break; + } + Steinberg::int64 seekRes = 0; + auto tres = stream.seek (static_cast (bytes), seekMode, &seekRes); + if (tres != Steinberg::kResultTrue || seekRes < 0) + return Result {Error::Unknown}; + return Result (Error::NoError, static_cast (seekRes)); +} + +//------------------------------------------------------------------------ +template +inline Result ByteOrderStream::tell () const noexcept +{ + Steinberg::int64 tellRes = 0; + auto tres = stream.tell (&tellRes); + if (tres != Steinberg::kResultTrue || tellRes < 0) + return Result {Error::Unknown}; + return Result {Error::NoError, static_cast (tellRes)}; +} + +//------------------------------------------------------------------------ +template +inline Result ByteOrderStream::operator<< (const std::string& input) noexcept +{ + auto res = *this << static_cast (input.length ()); + if (!res) + return res; + res = stream.write (const_cast (static_cast (input.data ())), + static_cast (input.length ())); + res.bytes += sizeof (uint64_t); + return res; +} + +//------------------------------------------------------------------------ +template +inline Result ByteOrderStream::operator>> (std::string& output) noexcept +{ + uint64_t length; + auto res = *this >> length; + if (!res) + return res; + output.resize (length); + if (length > 0) + { + res = stream.read (&output.front (), static_cast (length)); + res.bytes += sizeof (uint64_t); + } + return res; +} + +//------------------------------------------------------------------------ +template +template +inline Result ByteOrderStream::operator<< (const T& input) noexcept +{ + static_assert (std::is_standard_layout::value, "Only standard layout types allowed"); + // with C++17: if constexpr (StreamByteOrder == BYTEORDER) + if (constexpr bool tmp = (StreamByteOrder == BYTEORDER)) + return write (WriteBufferDesc {sizeof (T), static_cast (&input)}); + + return swapAndWrite (reinterpret_cast (&input)); +} + +//------------------------------------------------------------------------ +template +template +inline Result ByteOrderStream::operator>> (T& output) const noexcept +{ + static_assert (std::is_standard_layout::value, "Only standard layout types allowed"); + auto res = read (ReadBufferDesc {sizeof (T), &output}); + // with C++17: if constexpr (StreamByteOrder == BYTEORDER) + if (constexpr bool tmp = (StreamByteOrder == BYTEORDER)) + return res; + + swap (reinterpret_cast (&output), res.bytes); + return res; +} + +//------------------------------------------------------------------------ +template +template +inline Result ByteOrderStream::swapAndWrite (const uint8_t* buffer) noexcept +{ + // with C++17: if constexpr (_size > 1) + if (constexpr bool tmp2 = (_size > 1)) + { + int8_t tmp[_size]; + + constexpr auto halfSize = _size / 2; + auto size = _size; + auto low = buffer; + auto high = buffer + size - 1; + + while (size > halfSize) + { + tmp[size - 2] = buffer[(_size - size) + 1]; + tmp[(_size - size) + 1] = buffer[size - 2]; + tmp[_size - size] = *high; + tmp[size - 1] = *low; + low += 2; + high -= 2; + size -= 2; + } + return write (WriteBufferDesc {_size, tmp}); + } + return write (WriteBufferDesc {1, buffer}); +} + +//------------------------------------------------------------------------ +template +inline void ByteOrderStream::swap (uint8_t* buffer, uint64_t size) const noexcept +{ + if (size < 2) + return; + auto low = buffer; + auto high = buffer + size - 1; + while (size >= 2) + { + auto tmp = *low; + *low = *high; + *high = tmp; + low += 2; + high -= 2; + size -= 2; + } +} + +//------------------------------------------------------------------------ +} // IO + +//------------------------------------------------------------------------ +constexpr int32_t cMagic = 'CcnK'; +constexpr int32_t bankMagic = 'FxBk'; +constexpr int32_t privateChunkID = 'VstW'; +constexpr int32_t chunkBankMagic = 'FBCh'; +constexpr int32_t programMagic = 'FxCk'; +constexpr int32_t chunkProgramMagic = 'FPCh'; + +//------------------------------------------------------------------------ +Optional loadProgram (const IO::BigEndianStream& state, + const Optional& vst2xUniqueID) +{ + Vst2xProgram program; + int32_t id; + if (!(state >> id)) + return {}; + if (id != cMagic) + return {}; + int32_t bankSize; + if (!(state >> bankSize)) + return {}; + int32_t fxMagic; + if (!(state >> fxMagic)) + return {}; + if (!(fxMagic == programMagic || fxMagic == chunkProgramMagic)) + return {}; + int32_t formatVersion; + if (!(state >> formatVersion)) + return {}; + int32_t fxId; + if (!(state >> fxId)) + return {}; + if (vst2xUniqueID && fxId != *vst2xUniqueID) + return {}; + int32_t fxVersion; + if (!(state >> fxVersion)) + return {}; + int32_t numParams; + if (!(state >> numParams)) + return {}; + if (numParams < 0) + return {}; + char name[29]; + if (!state.read ({28, name})) + return {}; + name[28] = 0; + program.name = name; + program.fxUniqueID = fxId; + program.fxVersion = fxVersion; + if (fxMagic == chunkProgramMagic) + { + uint32_t chunkSize; + if (!(state >> chunkSize)) + return {}; + program.chunk.resize (chunkSize); + if (!state.read ({chunkSize, program.chunk.data ()})) + return {}; + } + else + { + program.values.resize (numParams); + float paramValue; + for (int32_t i = 0; i < numParams; ++i) + { + if (!(state >> paramValue)) + return {}; + program.values[i] = paramValue; + } + } + return {std::move (program)}; +} + +//------------------------------------------------------------------------ +bool loadPrograms (Steinberg::IBStream& stream, Vst2xState::Programs& programs, + const Optional& vst2xUniqueID) +{ + IO::BigEndianStream state (stream); + + for (auto& program : programs) + { + if (auto prg = loadProgram (state, vst2xUniqueID)) + std::swap (program, *prg); + else + return false; + } + return true; +} + +//------------------------------------------------------------------------ +template +IO::Error streamSizeWriter (StreamT& stream, Proc proc) +{ + auto startPos = stream.tell (); + if (startPos.error != IO::Error::NoError) + return startPos.error; + auto res = stream << static_cast (0); // placeholder + if (!res) + return res.error; + auto procRes = proc (); + if (procRes != IO::Error::NoError) + return procRes; + auto endPos = stream.tell (); + if (endPos.error != IO::Error::NoError) + return endPos.error; + auto size = (endPos.bytes - startPos.bytes) - 4; + auto typeSize = static_cast (size); + if (size != static_cast (typeSize)) + return IO::Error::Unknown; + res = stream.seek (IO::SeekMode::Set, startPos.bytes); + if (!res) + return res.error; + res = (stream << typeSize); + if (!res) + return res.error; + res = stream.seek (IO::SeekMode::Set, endPos.bytes); + return res.error; +} + +//------------------------------------------------------------------------ +template +IO::Error writePrograms (StreamT& stream, const Vst2xState::Programs& programs) +{ + for (const auto& program : programs) + { + auto res = stream << cMagic; + if (!res) + return res.error; + res = streamSizeWriter (stream, [&] () { + bool writeChunk = !program.chunk.empty (); + if (!(res = stream << (writeChunk ? chunkProgramMagic : programMagic))) + return res.error; + int32_t version = 1; + if (!(res = stream << version)) + return res.error; + if (!(res = stream << program.fxUniqueID)) + return res.error; + int32_t fxVersion = program.fxVersion; + if (!(res = stream << fxVersion)) + return res.error; + uint32_t numParams = static_cast (program.values.size ()); + if (!(res = stream << numParams)) + return res.error; + auto programName = program.name; + programName.resize (28); + for (auto c : programName) + { + if (!(res = stream << c)) + return res.error; + } + if (writeChunk) + { + if (!(res = stream << static_cast (program.chunk.size ()))) + return res.error; + if (!(res = stream.write ({program.chunk.size (), program.chunk.data ()}))) + return res.error; + } + else + { + for (auto value : program.values) + { + if (!(res = stream << value)) + return res.error; + } + } + return IO::Error::NoError; + }); + if (res.error != IO::Error::NoError) + return res.error; + } + return IO::Error::NoError; +} + +//------------------------------------------------------------------------ +} // anonymous + +//------------------------------------------------------------------------ +Optional tryVst2StateLoad (Steinberg::IBStream& stream, + Optional vst2xUniqueID) noexcept +{ + Vst2xState result; + + IO::BigEndianStream state (stream); + int32_t version; + int32_t size; + int32_t id; + if (!(state >> id)) + return {}; + if (id == privateChunkID) + { + if (!(state >> size)) + return {}; + if (!(state >> version)) + return {}; + int32_t bypass; + if (!(state >> bypass)) + return {}; + result.isBypassed = bypass ? true : false; + if (!(state >> id)) + return {}; + } + if (id != cMagic) + return {}; + int32_t bankSize; + if (!(state >> bankSize)) + return {}; + int32_t fxMagic; + if (!(state >> fxMagic)) + return {}; + if (!(fxMagic == bankMagic || fxMagic == chunkBankMagic)) + return {}; + int32_t bankVersion; + if (!(state >> bankVersion)) + return {}; + int32_t fxId; + if (!(state >> fxId)) + return {}; + if (vst2xUniqueID && fxId != *vst2xUniqueID) + return {}; + result.fxUniqueID = fxId; + int32_t fxVersion; + if (!(state >> fxVersion)) + return {}; + result.fxVersion = fxVersion; + + int32_t numPrograms; + if (!(state >> numPrograms)) + return {}; + if (numPrograms < 1) + return {}; + + int32_t currentProgram = 0; + if (bankVersion >= 1) + { + if (!(state >> currentProgram)) + return {}; + state.seek (IO::SeekMode::Current, 124); // future + } + result.currentProgram = currentProgram; + if (fxMagic == bankMagic) + { + result.programs.resize (numPrograms); + if (!loadPrograms (stream, result.programs, vst2xUniqueID)) + return {}; + assert (static_cast (result.programs.size ()) > currentProgram); + } + else + { + uint32_t chunkSize; + if (!(state >> chunkSize)) + return {}; + if (chunkSize == 0) + return {}; + result.chunk.resize (chunkSize); + if (!state.read ({chunkSize, result.chunk.data ()})) + return {}; + } + return {std::move (result)}; +} + +//------------------------------------------------------------------------ +bool writeVst2State (const Vst2xState& state, Steinberg::IBStream& _stream, + bool writeBypassState) noexcept +{ + IO::BigEndianStream stream (_stream); + if (writeBypassState) + { + if (!(stream << privateChunkID)) + return false; + if (streamSizeWriter (stream, [&] () { + uint32_t version = 1; + auto res = (stream << version); + if (!res) + return res.error; + int32_t bypass = state.isBypassed ? 1 : 0; + return (stream << bypass).error; + }) != IO::Error::NoError) + { + return false; + } + } + if (!(stream << cMagic)) + { + return false; + } + if (streamSizeWriter (stream, [&] () { + bool writeChunk = !state.chunk.empty (); + IO::Result res; + if (!(res = (stream << (writeChunk ? chunkBankMagic : bankMagic)))) + return res.error; + int32_t bankVersion = 2; + if (!(res = (stream << bankVersion))) + return res.error; + if (!(res = (stream << state.fxUniqueID))) + return res.error; + if (!(res = (stream << state.fxVersion))) + return res.error; + int32_t numPrograms = writeChunk ? 1 : static_cast (state.programs.size ()); + if (!(res = (stream << numPrograms))) + return res.error; + if (bankVersion > 1) + { + if (!(res = (stream << state.currentProgram))) + return res.error; + // write 124 zero bytes + uint8_t byte = 0; + for (uint32_t i = 0; i < 124; ++i) + if (!(res = (stream << byte))) + return res.error; + } + if (writeChunk) + { + auto chunkSize = static_cast (state.chunk.size ()); + if (!(res = (stream << chunkSize))) + return res.error; + stream.write ({state.chunk.size (), state.chunk.data ()}); + } + else + { + writePrograms (stream, state.programs); + } + return IO::Error::NoError; + }) != IO::Error::NoError) + { + return false; + } + return true; +} + +//------------------------------------------------------------------------ +Optional tryVst2ProgramLoad (Steinberg::IBStream& stream, + Optional vst2xUniqueID) noexcept +{ + IO::BigEndianStream state (stream); + return loadProgram (state, vst2xUniqueID); +} + +//------------------------------------------------------------------------ +} // VST3 diff --git a/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h b/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h new file mode 100644 index 0000000000..b731cc6970 --- /dev/null +++ b/modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/vst2persistence.h @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------ +// Flags : clang-format SMTGSequencer +// Project : VST3 SDK +// Filename : public.sdk/source/vst/utility/vst2persistence.h +// Created by : Steinberg, 12/2019 +// Description : vst2 persistence helper +// +//------------------------------------------------------------------------ +// LICENSE +// (c) 2024, Steinberg Media Technologies GmbH, All Rights Reserved +//----------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Steinberg Media Technologies nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +#pragma once + +#include "public.sdk/source/vst/utility/optional.h" +#include "pluginterfaces/base/ibstream.h" +#include +#include + +//------------------------------------------------------------------------ +namespace VST3 { + +//------------------------------------------------------------------------ +using Vst2xChunk = std::vector; + +//------------------------------------------------------------------------ +/** structure holding the content of a vst2 fxp format stream + * + * either the values member is valid or the chunk member but not both + */ +struct Vst2xProgram +{ + using ProgramValues = std::vector; + ProgramValues values; + Vst2xChunk chunk; + int32_t fxUniqueID {0}; + int32_t fxVersion {0}; + std::string name; +}; + +//------------------------------------------------------------------------ +/** structure holding the content of a vst2 fxb format stream + * + * either the programs member is valid or the chunk member but not both + */ +struct Vst2xState +{ + using Programs = std::vector; + Programs programs; + Vst2xChunk chunk; + + int32_t fxUniqueID {0}; + int32_t fxVersion {0}; + int32_t currentProgram {0}; + bool isBypassed {false}; +}; + +//------------------------------------------------------------------------ +/** Try loading the state from an old vst2 fxb format stream + * + * If successfully loaded, the state has either a chunk or programs but not both + * The Vst2xState::isBypassed boolean will be set if a Steinberg host has written the state into a + * project and the plug-in was bypassed. + * + * @param stream the input stream + * @param vst2xUniqueID vst2 unique id expected to be stored in the stream [optional]. If present + * the fxb unique id header entry must be the same as this otherwise the + * return value is empty. + * @return on success the optional has a Vst2xState object with the data + */ +Optional tryVst2StateLoad (Steinberg::IBStream& stream, + Optional vst2xUniqueID = {}) noexcept; + +//------------------------------------------------------------------------ +/** Write a vst2 fxb stream + * + * Writes the state into stream as a vst2 fxb format + * + * @param state the state which should be written + * @param stream the stream where the state should be written into + * @param writeBypassState write extra chunk with bypass state + * @return true on success + */ +bool writeVst2State (const Vst2xState& state, Steinberg::IBStream& stream, + bool writeBypassState = true) noexcept; + +//------------------------------------------------------------------------ +/** Try loading the state from on old vst2 fxp format stream + * + * If successfully loaded, the program has either a chunk or plain values but not both + * + * @param stream the input stream + * @param vst2xUniqueID vst2 unique id expected to be stored in the stream + * @return on success the optional has a Vst2xProgram object with the data + */ +Optional tryVst2ProgramLoad (Steinberg::IBStream& stream, + Optional vst2xUniqueID) noexcept; + +//------------------------------------------------------------------------ +} // VST3 diff --git a/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/modules/juce_audio_processors/format_types/juce_VST3Headers.h index 2d757df6da..b8eb6dcddd 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -162,6 +162,8 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #pragma push_macro ("False") #undef False + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmultichar", "-Wfour-char-constants") + #include #include #include @@ -176,6 +178,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #include #include #include + #include #include #include #include @@ -185,6 +188,8 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-W#warnings", #include #include + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + #pragma pop_macro ("True") #pragma pop_macro ("False")