From f5245eb557119f3ca195f67f3609ab734dfef9b7 Mon Sep 17 00:00:00 2001 From: michalcourson Date: Tue, 4 Nov 2025 20:04:07 -0500 Subject: [PATCH] closes #7 Processor editor and processor in seperate files. slider paramters now can be added by simply adding them in the processor, everything else is dynamic --- Assets/web/src/App.js | 8 +- .../Harmonizer_SharedCode.vcxproj | 2865 ++++++++--------- .../Harmonizer_SharedCode.vcxproj.filters | 169 +- Builds/VisualStudio2022/olddemo.h | 714 ++++ Harmonizer.jucer | 9 + Source/CircularBuffer.h | 86 + Source/PluginEditor.cpp | 185 ++ Source/PluginEditor.h | 93 + Source/PluginProcessor.cpp | 96 + Source/PluginProcessor.h | 131 + Source/WebViewPluginDemo.h | 647 +--- 11 files changed, 2771 insertions(+), 2232 deletions(-) create mode 100644 Builds/VisualStudio2022/olddemo.h create mode 100644 Source/CircularBuffer.h create mode 100644 Source/PluginEditor.cpp create mode 100644 Source/PluginEditor.h create mode 100644 Source/PluginProcessor.cpp create mode 100644 Source/PluginProcessor.h diff --git a/Assets/web/src/App.js b/Assets/web/src/App.js index 61da4db..8269b1a 100644 --- a/Assets/web/src/App.js +++ b/Assets/web/src/App.js @@ -48,10 +48,10 @@ function App() { return (
- - - - + + + +
diff --git a/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj b/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj index 8e0acd6..be5ca3a 100644 --- a/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj +++ b/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj @@ -1,8 +1,5 @@ - - - + + Debug @@ -16,29 +13,25 @@ {13753206-F62C-286C-51BE-24A788A74DCC} - - + + StaticLibrary false false v143 10.0 - + StaticLibrary false true v143 10.0 - - + + - + <_ProjectFileVersion>10.0.30319.1 @@ -62,13 +55,13 @@ true true Win32 - + Disabled ProgramDatabase ..\..\..\..\..\Downloads\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\..\Downloads\JUCE\modules;C:\Users\mickl\Downloads\juce-8.0.4-windows\JUCE\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name="Harmonizer";JucePlugin_Desc="Harmonizer";JucePlugin_Manufacturer="yourcompany";JucePlugin_ManufacturerWebsite="www.yourcompany.com";JucePlugin_ManufacturerEmail="";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString="1.0.0";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category="Fx";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted="HarmonizerAU";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName="yourcompany: Harmonizer";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID="com.yourcompany.Harmonizer.factory";JucePlugin_ARADocumentArchiveID="com.yourcompany.Harmonizer.aradocumentarchive.1.0.0";JucePlugin_ARACompatibleArchiveIDs="";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name="Harmonizer";JucePlugin_Desc="Harmonizer";JucePlugin_Manufacturer="yourcompany";JucePlugin_ManufacturerWebsite="www.yourcompany.com";JucePlugin_ManufacturerEmail="";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString="1.0.0";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category="Fx";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted="HarmonizerAU";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName="yourcompany: Harmonizer";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID="com.yourcompany.Harmonizer.factory";JucePlugin_ARADocumentArchiveID="com.yourcompany.Harmonizer.aradocumentarchive.1.0.0";JucePlugin_ARACompatibleArchiveIDs="";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -82,7 +75,7 @@ ..\..\..\..\..\Downloads\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\..\Downloads\JUCE\modules;C:\Users\mickl\Downloads\juce-8.0.4-windows\JUCE\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name=\"Harmonizer\";JucePlugin_Desc=\"Harmonizer\";JucePlugin_Manufacturer=\"yourcompany\";JucePlugin_ManufacturerWebsite=\"www.yourcompany.com\";JucePlugin_ManufacturerEmail=\"\";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString=\"1.0.0\";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category=\"Fx\";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted=\"HarmonizerAU\";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName=\"yourcompany: Harmonizer\";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID=\"com.yourcompany.Harmonizer.factory\";JucePlugin_ARADocumentArchiveID=\"com.yourcompany.Harmonizer.aradocumentarchive.1.0.0\";JucePlugin_ARACompatibleArchiveIDs=\"\";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name=\"Harmonizer\";JucePlugin_Desc=\"Harmonizer\";JucePlugin_Manufacturer=\"yourcompany\";JucePlugin_ManufacturerWebsite=\"www.yourcompany.com\";JucePlugin_ManufacturerEmail=\"\";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString=\"1.0.0\";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category=\"Fx\";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted=\"HarmonizerAU\";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName=\"yourcompany: Harmonizer\";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID=\"com.yourcompany.Harmonizer.factory\";JucePlugin_ARADocumentArchiveID=\"com.yourcompany.Harmonizer.aradocumentarchive.1.0.0\";JucePlugin_ARACompatibleArchiveIDs=\"\";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) $(OutDir)\Harmonizer.lib @@ -104,13 +97,13 @@ true true Win32 - + Full ProgramDatabase ..\..\..\..\..\Downloads\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\..\Downloads\JUCE\modules;C:\Users\mickl\Downloads\juce-8.0.4-windows\JUCE\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name="Harmonizer";JucePlugin_Desc="Harmonizer";JucePlugin_Manufacturer="yourcompany";JucePlugin_ManufacturerWebsite="www.yourcompany.com";JucePlugin_ManufacturerEmail="";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString="1.0.0";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category="Fx";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted="HarmonizerAU";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName="yourcompany: Harmonizer";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID="com.yourcompany.Harmonizer.factory";JucePlugin_ARADocumentArchiveID="com.yourcompany.Harmonizer.aradocumentarchive.1.0.0";JucePlugin_ARACompatibleArchiveIDs="";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name="Harmonizer";JucePlugin_Desc="Harmonizer";JucePlugin_Manufacturer="yourcompany";JucePlugin_ManufacturerWebsite="www.yourcompany.com";JucePlugin_ManufacturerEmail="";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString="1.0.0";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category="Fx";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted="HarmonizerAU";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName="yourcompany: Harmonizer";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID="com.yourcompany.Harmonizer.factory";JucePlugin_ARADocumentArchiveID="com.yourcompany.Harmonizer.aradocumentarchive.1.0.0";JucePlugin_ARACompatibleArchiveIDs="";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) MultiThreadedDLL true NotUsing @@ -124,7 +117,7 @@ ..\..\..\..\..\Downloads\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\..\Downloads\JUCE\modules;C:\Users\mickl\Downloads\juce-8.0.4-windows\JUCE\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name=\"Harmonizer\";JucePlugin_Desc=\"Harmonizer\";JucePlugin_Manufacturer=\"yourcompany\";JucePlugin_ManufacturerWebsite=\"www.yourcompany.com\";JucePlugin_ManufacturerEmail=\"\";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString=\"1.0.0\";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category=\"Fx\";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted=\"HarmonizerAU\";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName=\"yourcompany: Harmonizer\";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID=\"com.yourcompany.Harmonizer.factory\";JucePlugin_ARADocumentArchiveID=\"com.yourcompany.Harmonizer.aradocumentarchive.1.0.0\";JucePlugin_ARACompatibleArchiveIDs=\"\";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x8000a;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_plugin_client=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_VST3_CAN_REPLACE_VST2=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=1;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=1;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;JucePlugin_Enable_IAA=0;JucePlugin_Enable_ARA=0;JucePlugin_Name=\"Harmonizer\";JucePlugin_Desc=\"Harmonizer\";JucePlugin_Manufacturer=\"yourcompany\";JucePlugin_ManufacturerWebsite=\"www.yourcompany.com\";JucePlugin_ManufacturerEmail=\"\";JucePlugin_ManufacturerCode=0x4d616e75;JucePlugin_PluginCode=0x456d6377;JucePlugin_IsSynth=0;JucePlugin_WantsMidiInput=1;JucePlugin_ProducesMidiOutput=0;JucePlugin_IsMidiEffect=0;JucePlugin_EditorRequiresKeyboardFocus=0;JucePlugin_Version=1.0.0;JucePlugin_VersionCode=0x10000;JucePlugin_VersionString=\"1.0.0\";JucePlugin_VSTUniqueID=JucePlugin_PluginCode;JucePlugin_VSTCategory=kPlugCategEffect;JucePlugin_Vst3Category=\"Fx\";JucePlugin_AUMainType='aufx';JucePlugin_AUSubType=JucePlugin_PluginCode;JucePlugin_AUExportPrefix=HarmonizerAU;JucePlugin_AUExportPrefixQuoted=\"HarmonizerAU\";JucePlugin_AUManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_CFBundleIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXIdentifier=com.yourcompany.Harmonizer;JucePlugin_AAXManufacturerCode=JucePlugin_ManufacturerCode;JucePlugin_AAXProductId=JucePlugin_PluginCode;JucePlugin_AAXCategory=0;JucePlugin_AAXDisableBypass=0;JucePlugin_AAXDisableMultiMono=0;JucePlugin_IAAType=0x6175726d;JucePlugin_IAASubType=JucePlugin_PluginCode;JucePlugin_IAAName=\"yourcompany: Harmonizer\";JucePlugin_VSTNumMidiInputs=16;JucePlugin_VSTNumMidiOutputs=16;JucePlugin_ARAContentTypes=0;JucePlugin_ARATransformationFlags=0;JucePlugin_ARAFactoryID=\"com.yourcompany.Harmonizer.factory\";JucePlugin_ARADocumentArchiveID=\"com.yourcompany.Harmonizer.aradocumentarchive.1.0.0\";JucePlugin_ARACompatibleArchiveIDs=\"\";JUCE_STANDALONE_APPLICATION=JucePlugin_Build_Standalone;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcVXNlcnNcbWlja2xcRG93bmxvYWRzXGp1Y2UtOC4wLjQtd2luZG93c1xKVUNFXGV4YW1wbGVz;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=1.0.0;JUCE_APP_VERSION_HEX=0x10000;JUCE_SHARED_CODE=1;_LIB;%(PreprocessorDefinitions) $(OutDir)\Harmonizer.lib @@ -144,13 +137,15 @@ - - - - - - - + + + + + + + + + true @@ -2926,1431 +2921,1433 @@ true - - - - - + + + + + /bigobj %(AdditionalOptions) - - - + + + /bigobj %(AdditionalOptions) - - - - + + + + /bigobj %(AdditionalOptions) - - + + /bigobj %(AdditionalOptions) - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + Static - + \ No newline at end of file diff --git a/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj.filters b/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj.filters index ef34919..ba44df9 100644 --- a/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj.filters +++ b/Builds/VisualStudio2022/Harmonizer_SharedCode.vcxproj.filters @@ -1,5 +1,4 @@ - - + @@ -685,6 +684,12 @@ + + Harmonizer\Source + + + Harmonizer\Source + Harmonizer\Source @@ -838,9 +843,6 @@ JUCE Modules\juce_audio_basics - - JUCE Modules\juce_audio_basics - JUCE Modules\juce_audio_devices\audio_io @@ -1066,9 +1068,6 @@ JUCE Modules\juce_audio_devices\native - - JUCE Modules\juce_audio_devices\native - JUCE Modules\juce_audio_devices\native @@ -1102,9 +1101,6 @@ JUCE Modules\juce_audio_devices - - JUCE Modules\juce_audio_devices - JUCE Modules\juce_audio_formats\codecs\flac\libFLAC\deduplication @@ -1291,9 +1287,6 @@ JUCE Modules\juce_audio_formats - - JUCE Modules\juce_audio_formats - JUCE Modules\juce_audio_plugin_client @@ -1477,9 +1470,6 @@ JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting - JUCE Modules\juce_audio_processors\format_types\VST3_SDK\public.sdk\source\vst\hosting @@ -1525,9 +1515,6 @@ JUCE Modules\juce_audio_processors\format_types - - JUCE Modules\juce_audio_processors\format_types - JUCE Modules\juce_audio_processors\format_types @@ -1648,9 +1635,6 @@ JUCE Modules\juce_audio_processors - - JUCE Modules\juce_audio_processors - JUCE Modules\juce_audio_processors @@ -1684,33 +1668,21 @@ JUCE Modules\juce_audio_utils\gui - - JUCE Modules\juce_audio_utils\native - JUCE Modules\juce_audio_utils\native JUCE Modules\juce_audio_utils\native - - JUCE Modules\juce_audio_utils\native - JUCE Modules\juce_audio_utils\native JUCE Modules\juce_audio_utils\native - - JUCE Modules\juce_audio_utils\native - JUCE Modules\juce_audio_utils\native - - JUCE Modules\juce_audio_utils\native - JUCE Modules\juce_audio_utils\native @@ -1723,9 +1695,6 @@ JUCE Modules\juce_audio_utils - - JUCE Modules\juce_audio_utils - JUCE Modules\juce_core\containers @@ -1864,9 +1833,6 @@ JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native @@ -1888,42 +1854,27 @@ JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native @@ -1936,9 +1887,6 @@ JUCE Modules\juce_core\native - - JUCE Modules\juce_core\native - JUCE Modules\juce_core\native @@ -2107,9 +2055,6 @@ JUCE Modules\juce_core - - JUCE Modules\juce_core - JUCE Modules\juce_core @@ -2143,9 +2088,6 @@ JUCE Modules\juce_data_structures - - JUCE Modules\juce_data_structures - JUCE Modules\juce_dsp\containers @@ -2251,9 +2193,6 @@ JUCE Modules\juce_dsp - - JUCE Modules\juce_dsp - JUCE Modules\juce_events\broadcasters @@ -2293,12 +2232,6 @@ JUCE Modules\juce_events\messages - - JUCE Modules\juce_events\native - - - JUCE Modules\juce_events\native - JUCE Modules\juce_events\native @@ -2323,9 +2256,6 @@ JUCE Modules\juce_events - - JUCE Modules\juce_events - JUCE Modules\juce_graphics\colour @@ -2848,9 +2778,6 @@ JUCE Modules\juce_graphics\images - - JUCE Modules\juce_graphics\native - JUCE Modules\juce_graphics\native @@ -2881,9 +2808,6 @@ JUCE Modules\juce_graphics\native - - JUCE Modules\juce_graphics\native - JUCE Modules\juce_graphics\native @@ -2983,9 +2907,6 @@ JUCE Modules\juce_graphics - - JUCE Modules\juce_graphics - JUCE Modules\juce_graphics @@ -3262,21 +3183,12 @@ JUCE Modules\juce_gui_basics\native\accessibility - - JUCE Modules\juce_gui_basics\native\accessibility - - - JUCE Modules\juce_gui_basics\native\accessibility - JUCE Modules\juce_gui_basics\native\accessibility JUCE Modules\juce_gui_basics\native\accessibility - - JUCE Modules\juce_gui_basics\native\accessibility - JUCE Modules\juce_gui_basics\native\accessibility @@ -3298,63 +3210,33 @@ JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native @@ -3364,15 +3246,9 @@ JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native - - JUCE Modules\juce_gui_basics\native - JUCE Modules\juce_gui_basics\native @@ -3514,9 +3390,6 @@ JUCE Modules\juce_gui_basics - - JUCE Modules\juce_gui_basics - JUCE Modules\juce_gui_extra\code_editor @@ -3577,15 +3450,9 @@ JUCE Modules\juce_gui_extra\native - - JUCE Modules\juce_gui_extra\native - JUCE Modules\juce_gui_extra\native - - JUCE Modules\juce_gui_extra\native - JUCE Modules\juce_gui_extra\native @@ -3604,18 +3471,12 @@ JUCE Modules\juce_gui_extra\native - - JUCE Modules\juce_gui_extra\native - JUCE Modules\juce_gui_extra\native JUCE Modules\juce_gui_extra\native - - JUCE Modules\juce_gui_extra\native - JUCE Modules\juce_gui_extra\native @@ -3625,9 +3486,6 @@ JUCE Modules\juce_gui_extra - - JUCE Modules\juce_gui_extra - JUCE Library Code @@ -3690,6 +3548,15 @@ Harmonizer\Assets + + Harmonizer\Source + + + Harmonizer\Source + + + Harmonizer\Source + Harmonizer\Source @@ -7847,6 +7714,6 @@ JUCE Modules\juce_graphics\unicode\sheenbidi - + - + \ No newline at end of file diff --git a/Builds/VisualStudio2022/olddemo.h b/Builds/VisualStudio2022/olddemo.h new file mode 100644 index 0000000..9802480 --- /dev/null +++ b/Builds/VisualStudio2022/olddemo.h @@ -0,0 +1,714 @@ +#pragma once +/* + ============================================================================== + + This file is part of the JUCE framework examples. + Copyright (c) Raw Material Software Limited + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + + ============================================================================== +*/ + +/******************************************************************************* + The block below describes the properties of this PIP. A PIP is a short snippet + of code that can be read by the Projucer and used to generate a JUCE project. + + BEGIN_JUCE_PIP_METADATA + + name: WebViewPluginDemo + version: 1.0.0 + vendor: JUCE + website: http://juce.com + description: Filtering audio plugin using an HTML/JS user interface + + dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, + juce_audio_plugin_client, juce_audio_processors, juce_dsp, + juce_audio_utils, juce_core, juce_data_structures, + juce_events, juce_graphics, juce_gui_basics, juce_gui_extra + exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone + + moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1, JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1 + + type: AudioProcessor + mainClass: WebViewPluginAudioProcessorWrapper + + useLocalCopy: 1 + + END_JUCE_PIP_METADATA + +*******************************************************************************/ + +#pragma once + +#include "DemoUtilities.h" +#include +#include "Shifter.h" + +//using namespace juce::dsp; + +namespace ID +{ +#define PARAMETER_ID(str) static const ParameterID str { #str, 1 }; + + PARAMETER_ID(formantPreserve) + PARAMETER_ID(autoTuneSpeed) + PARAMETER_ID(autoTuneDepth) + PARAMETER_ID(portTime) + PARAMETER_ID(mute) + PARAMETER_ID(filterType) + +#undef PARAMETER_ID +} + +class CircularBuffer +{ +public: + CircularBuffer(int numChannels, int numSamples) + : buffer(data, (size_t)numChannels, (size_t)numSamples) + { + } + + template + void push(dsp::AudioBlock b) + { + jassert(b.getNumChannels() == buffer.getNumChannels()); + + const auto trimmed = b.getSubBlock(b.getNumSamples() + - std::min(b.getNumSamples(), buffer.getNumSamples())); + + const auto bufferLength = (int64)buffer.getNumSamples(); + + for (auto samplesRemaining = (int64)trimmed.getNumSamples(); samplesRemaining > 0;) + { + const auto writeOffset = writeIx % bufferLength; + const auto numSamplesToWrite = std::min(samplesRemaining, bufferLength - writeOffset); + + auto destSubBlock = buffer.getSubBlock((size_t)writeOffset, (size_t)numSamplesToWrite); + const auto sourceSubBlock = trimmed.getSubBlock(trimmed.getNumSamples() - (size_t)samplesRemaining, + (size_t)numSamplesToWrite); + + destSubBlock.copyFrom(sourceSubBlock); + + samplesRemaining -= numSamplesToWrite; + writeIx += numSamplesToWrite; + } + } + + template + void push(Span s) + { + auto* ptr = s.begin(); + dsp::AudioBlock b(&ptr, 1, s.size()); + push(b); + } + + void read(int64 readIx, dsp::AudioBlock output) const + { + const auto numChannelsToUse = std::min(buffer.getNumChannels(), output.getNumChannels()); + + jassert(output.getNumChannels() == buffer.getNumChannels()); + + const auto bufferLength = (int64)buffer.getNumSamples(); + + for (auto outputOffset = (size_t)0; outputOffset < output.getNumSamples();) + { + const auto inputOffset = (size_t)((readIx + (int64)outputOffset) % bufferLength); + const auto numSamplesToRead = std::min(output.getNumSamples() - outputOffset, + (size_t)bufferLength - inputOffset); + + auto destSubBlock = output.getSubBlock(outputOffset, numSamplesToRead) + .getSubsetChannelBlock(0, numChannelsToUse); + + destSubBlock.copyFrom(buffer.getSubBlock(inputOffset, numSamplesToRead) + .getSubsetChannelBlock(0, numChannelsToUse)); + + outputOffset += numSamplesToRead; + } + } + + int64 getWriteIndex() const noexcept { return writeIx; } + +private: + HeapBlock data; + dsp::AudioBlock buffer; + int64 writeIx = 0; +}; + +//class SpectralBars +//{ +//public: +// //template +// void push(int data) +// { +// testQueue.push(data); +// } +// +// void compute(Span output) { +// int index = 0; +// for (auto it = output.begin(); it != output.end(); ++it) { +// *it = testQueue.get(index++); +// } +// } +// +// +//private: +// circ_queue testQueue; +//}; + +//============================================================================== +class WebViewPluginAudioProcessor : public AudioProcessor +{ +public: + //============================================================================== + WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout); + + //============================================================================== + void prepareToPlay(double sampleRate, int samplesPerBlock) override; + void releaseResources() override {} + + bool isBusesLayoutSupported(const BusesLayout& layouts) const override; + + void processBlock(AudioBuffer&, MidiBuffer&) override; + using AudioProcessor::processBlock; + + //============================================================================== + const String getName() const override { return JucePlugin_Name; } + + bool acceptsMidi() const override { return false; } + bool producesMidi() const override { return false; } + bool isMidiEffect() const override { return false; } + double getTailLengthSeconds() const override { return 0.0; } + + //============================================================================== + int getNumPrograms() override { return 1; } + int getCurrentProgram() override { return 0; } + void setCurrentProgram(int) override {} + const String getProgramName(int) override { return {}; } + void changeProgramName(int, const String&) override {} + + //============================================================================== + void getStateInformation(MemoryBlock& destData) override; + void setStateInformation(const void* data, int sizeInBytes) override; + bool new_midi = false; + + struct Parameters + { + public: + explicit Parameters(AudioProcessorValueTreeState::ParameterLayout& layout) + : formantPreserve(addToLayout(layout, + ID::formantPreserve, + "Formant Preserve", + NormalisableRange {0.0f, 1.0f, .01f}, + .5f)), + + autoTuneSpeed(addToLayout(layout, + ID::autoTuneSpeed, + "AutoTune Speed", + NormalisableRange {0.001f, 0.1f, .001f}, + .5f)), + autoTuneDepth(addToLayout(layout, + ID::autoTuneDepth, + "AutoTune Depth", + NormalisableRange {0.0f, 1.1f, .01f}, + .5f)), + portTime(addToLayout(layout, + ID::portTime, + "Portamento Speed", + NormalisableRange {0.001f, 0.2f, .001f}, + .01f)), + mute(addToLayout(layout, ID::mute, "Mute", false)), + filterType(addToLayout(layout, + ID::filterType, + "Filter type", + StringArray{ "Low-pass", "High-pass", "Band-pass" }, + 0)) + { + } + + AudioParameterFloat& formantPreserve; + AudioParameterFloat& autoTuneSpeed; + AudioParameterFloat& autoTuneDepth; + AudioParameterFloat& portTime; + AudioParameterBool& mute; + AudioParameterChoice& filterType; + + private: + template + static void add(AudioProcessorParameterGroup& group, std::unique_ptr param) + { + group.addChild(std::move(param)); + } + + template + static void add(AudioProcessorValueTreeState::ParameterLayout& group, std::unique_ptr param) + { + group.add(std::move(param)); + } + + template + static Param& addToLayout(Group& layout, Ts&&... ts) + { + auto param = std::make_unique(std::forward(ts)...); + auto& ref = *param; + add(layout, std::move(param)); + return ref; + } + }; + + Parameters parameters; + AudioProcessorValueTreeState state; + SpinLock midiLock; + + /*std::vector spectrumData = [] { return std::vector(256, 0.0f); }(); + SpinLock spectrumDataLock; + + SpectralBars spectralBars;*/ + + dsp::LadderFilter filter; + Shifter shifter; + +private: + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessor) + +}; + +//============================================================================== +WebViewPluginAudioProcessor::WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout) + : AudioProcessor(BusesProperties() +#if ! JucePlugin_IsMidiEffect +#if ! JucePlugin_IsSynth + .withInput("Input", juce::AudioChannelSet::stereo(), true) +#endif + .withOutput("Output", juce::AudioChannelSet::stereo(), true) +#endif + ), + parameters(layout), + state(*this, nullptr, "STATE", std::move(layout)) +{ + shifter.Init(48000.0f, 48); +} + +//============================================================================== +void WebViewPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) +{ + const auto channels = std::max(getTotalNumInputChannels(), getTotalNumOutputChannels()); + shifter.Init((float)sampleRate, samplesPerBlock); + if (channels == 0) + return; + + filter.prepare({ sampleRate, (uint32_t)samplesPerBlock, (uint32_t)channels }); + filter.reset(); +} + +bool WebViewPluginAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const +{ + if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() + && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) + return false; + + if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) + return false; + + return true; +} + +void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer& buffer, + juce::MidiBuffer& midi) +{ + juce::ScopedNoDenormals noDenormals; + + const auto totalNumInputChannels = getTotalNumInputChannels(); + const auto totalNumOutputChannels = getTotalNumOutputChannels(); + + for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) + buffer.clear(i, 0, buffer.getNumSamples()); + shifter.SetFormantPreserve(parameters.formantPreserve.get()); + shifter.SetAutoTuneSpeed(parameters.autoTuneSpeed.get()); + shifter.SetAutoTuneDepth(parameters.autoTuneDepth.get()); + shifter.SetPortamentoTime(parameters.portTime.get()); + juce::AudioBuffer const_buff; + const_buff.makeCopyOf(buffer); + shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples()); + + for (const auto metadata : midi) + { + const auto msg = metadata.getMessage(); + if (msg.isNoteOn()) { + shifter.AddMidiNote(msg.getNoteNumber()); + new_midi = true; + //editor.webComponent.emitEventIfBrowserIsVisible("midNoteData", var{}); + + } + else if (msg.isNoteOff()) { + shifter.RemoveMidiNote(msg.getNoteNumber()); + new_midi = true; + //editor.webComponent.emitEventIfBrowserIsVisible("midNoteData", var{}); + } + } + + + { + //DBG(shifter.out_midi[MAX_VOICES]); + //push midi note + //spectralBars.push(shifter.out_midi[MAX_VOICES]); + const SpinLock::ScopedTryLockType lock(midiLock); + + if (!lock.isLocked()) + return; + + } + + + /*for(auto i = 0; i < buffer.getNumSamples(); ++i) + { + bool process = (i % 256) == 0 && i != 0; + + for(auto j = 0; j < totalNumInputChannels; ++j) + { + input[j][i] = buffer.getReadPointer(j)[i]; + } + } + + filter.setCutoffFrequencyHz (parameters.cutoffFreqHz.get()); + + const auto filterMode = [this] + { + switch (parameters.filterType.getIndex()) + { + case 0: + return dsp::LadderFilter::Mode::LPF12; + + case 1: + return dsp::LadderFilter::Mode::HPF12; + + default: + return dsp::LadderFilter::Mode::BPF12; + } + }(); + + filter.setMode (filterMode); + + auto outBlock = dsp::AudioBlock { buffer }.getSubsetChannelBlock (0, (size_t) getTotalNumOutputChannels()); + + if (parameters.mute.get()) + outBlock.clear(); + + filter.process (dsp::ProcessContextReplacing (outBlock)); + + spectralBars.push (Span { buffer.getReadPointer (0), (size_t) buffer.getNumSamples() }); + + { + const SpinLock::ScopedTryLockType lock (spectrumDataLock); + + if (! lock.isLocked()) + return; + + spectralBars.compute ({ spectrumData.data(), spectrumData.size() }); + }*/ +} + +//============================================================================== +void WebViewPluginAudioProcessor::getStateInformation(juce::MemoryBlock& destData) +{ + juce::ignoreUnused(destData); +} + +void WebViewPluginAudioProcessor::setStateInformation(const void* data, int sizeInBytes) +{ + juce::ignoreUnused(data, sizeInBytes); +} + +extern const String localDevServerAddress; + +std::optional getResource(const String& url); + +//============================================================================== +struct SinglePageBrowser : WebBrowserComponent +{ + using WebBrowserComponent::WebBrowserComponent; + + // Prevent page loads from navigating away from our single page web app + bool pageAboutToLoad(const String& newURL) override; +}; + +//============================================================================== +class WebViewPluginAudioProcessorEditor : public AudioProcessorEditor, private Timer +{ +public: + explicit WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor&); + + std::optional getResource(const String& url); + + //============================================================================== + void paint(Graphics&) override; + void resized() override; + + int getControlParameterIndex(Component&) override + { + return controlParameterIndexReceiver.getControlParameterIndex(); + } + + void timerCallback() override + { + static constexpr size_t numFramesBuffered = 5; + + SpinLock::ScopedLockType lock{ processorRef.midiLock }; + + static int64 callbackCounter = 0; + processorRef.new_midi = false; + juce::Array notes; + int voice_num = 0; + for (auto& voice : processorRef.shifter.voices) { + if (voice.onoff_) { + auto obj = new DynamicObject(); + obj->setProperty("voice", voice_num); + obj->setProperty("midi", voice.GetMidiNote()); + notes.add(var(obj)); + } + voice_num++; + } + + DynamicObject::Ptr d(new DynamicObject()); + d->setProperty("notes", notes); + d->setProperty("input_pitch", processorRef.shifter.getInputPitch()); + d->setProperty("output_pitch", processorRef.shifter.getOutputPitch()); + webComponent.emitEventIfBrowserIsVisible("midNoteData", d.get()); + } + +private: + WebViewPluginAudioProcessor& processorRef; + + WebSliderRelay formantSliderRelay{ "formantSlider" }; + WebSliderRelay autoTuneSpeedSliderRelay{ "autoTuneSpeedSlider" }; + WebSliderRelay autoTuneDepthSliderRelay{ "autoTuneDepthSlider" }; + WebSliderRelay portTimeSliderRelay{ "portTimeSlider" }; + WebToggleButtonRelay muteToggleRelay{ "muteToggle" }; + WebComboBoxRelay filterTypeComboRelay{ "filterTypeCombo" }; + + WebControlParameterIndexReceiver controlParameterIndexReceiver; + + SinglePageBrowser webComponent{ WebBrowserComponent::Options{} + .withBackend(WebBrowserComponent::Options::Backend::webview2) + .withWinWebView2Options(WebBrowserComponent::Options::WinWebView2{} + .withUserDataFolder(File::getSpecialLocation(File::SpecialLocationType::tempDirectory))) + .withNativeIntegrationEnabled() + .withOptionsFrom(formantSliderRelay) + .withOptionsFrom(autoTuneSpeedSliderRelay) + .withOptionsFrom(autoTuneDepthSliderRelay) + .withOptionsFrom(portTimeSliderRelay) + .withOptionsFrom(muteToggleRelay) + .withOptionsFrom(filterTypeComboRelay) + .withOptionsFrom(controlParameterIndexReceiver) + .withNativeFunction("sayHello", [](auto& var, auto complete) + { + complete("Hello " + var[0].toString()); + }) + .withResourceProvider([this](const auto& url) + { + return getResource(url); + }, + URL { localDevServerAddress }.getOrigin()) }; + + WebSliderParameterAttachment formantAttachment; + WebSliderParameterAttachment autoTuneSpeedAttachment; + WebSliderParameterAttachment autoTuneDepthAttachment; + WebSliderParameterAttachment portTimeAttachment; + WebToggleButtonParameterAttachment muteAttachment; + WebComboBoxParameterAttachment filterTypeAttachment; + + std::deque> spectrumDataFrames; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessorEditor) +}; + +static ZipFile* getZipFile() +{ + static auto stream = createAssetInputStream("webviewplugin-gui_1.0.0.zip", AssertAssetExists::no); + + if (stream == nullptr) + return nullptr; + + static ZipFile f{ stream.get(), false }; + return &f; +} + +static const char* getMimeForExtension(const String& extension) +{ + static const std::unordered_map mimeMap = + { + { { "htm" }, "text/html" }, + { { "html" }, "text/html" }, + { { "txt" }, "text/plain" }, + { { "jpg" }, "image/jpeg" }, + { { "jpeg" }, "image/jpeg" }, + { { "svg" }, "image/svg+xml" }, + { { "ico" }, "image/vnd.microsoft.icon" }, + { { "json" }, "application/json" }, + { { "png" }, "image/png" }, + { { "css" }, "text/css" }, + { { "map" }, "application/json" }, + { { "js" }, "text/javascript" }, + { { "woff2" }, "font/woff2" } + }; + + if (const auto it = mimeMap.find(extension.toLowerCase()); it != mimeMap.end()) + return it->second; + + jassertfalse; + return ""; +} + +static String getExtension(String filename) +{ + return filename.fromLastOccurrenceOf(".", false, false); +} + +static auto streamToVector(InputStream& stream) +{ + std::vector result((size_t)stream.getTotalLength()); + stream.setPosition(0); + [[maybe_unused]] const auto bytesRead = stream.read(result.data(), result.size()); + jassert(bytesRead == (ssize_t)result.size()); + return result; +} + +std::optional WebViewPluginAudioProcessorEditor::getResource(const String& url) +{ + const auto urlToRetrive = url == "/" ? String{ "index.html" } + : url.fromFirstOccurrenceOf("/", false, false); + + if (auto* archive = getZipFile()) + { + if (auto* entry = archive->getEntry(urlToRetrive)) + { + auto stream = rawToUniquePtr(archive->createStreamForEntry(*entry)); + auto v = streamToVector(*stream); + auto mime = getMimeForExtension(getExtension(entry->filename).toLowerCase()); + return WebBrowserComponent::Resource{ std::move(v), + std::move(mime) }; + } + } + + if (urlToRetrive == "index.html") + { + auto fallbackIndexHtml = createAssetInputStream("webviewplugin-gui-fallback.html"); + return WebBrowserComponent::Resource{ streamToVector(*fallbackIndexHtml), + String { "text/html" } }; + } + + if (urlToRetrive == "data.txt") + { + WebBrowserComponent::Resource resource; + static constexpr char testData[] = "testdata"; + MemoryInputStream stream{ testData, numElementsInArray(testData) - 1, false }; + return WebBrowserComponent::Resource{ streamToVector(stream), String { "text/html" } }; + } + + if (urlToRetrive == "midNoteData.json") + { + + juce::Array notes; + int voice_num = 0; + for (auto& voice : processorRef.shifter.voices) { + if (voice.onoff_) { + auto obj = new DynamicObject(); + obj->setProperty("voice", voice_num); + obj->setProperty("midi", voice.GetMidiNote()); + notes.add(var(obj)); + } + voice_num++; + } + + DynamicObject::Ptr d(new DynamicObject()); + d->setProperty("notes", notes); + + const auto s = JSON::toString(d.get()); + MemoryInputStream stream{ s.getCharPointer(), s.getNumBytesAsUTF8(), false }; + return WebBrowserComponent::Resource{ streamToVector(stream), String { "application/json" } }; + } + + return std::nullopt; +} + +#if JUCE_ANDROID +// The localhost is available on this address to the emulator +const String localDevServerAddress = "http://10.0.2.2:3000/"; +#else +const String localDevServerAddress = "http://localhost:3000/"; +#endif + +bool SinglePageBrowser::pageAboutToLoad(const String& newURL) +{ + return newURL == localDevServerAddress || newURL == getResourceProviderRoot(); +} + +//============================================================================== +WebViewPluginAudioProcessorEditor::WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor& p) + : AudioProcessorEditor(&p), processorRef(p), + formantAttachment(*processorRef.state.getParameter(ID::formantPreserve.getParamID()), + formantSliderRelay, + processorRef.state.undoManager), + autoTuneSpeedAttachment(*processorRef.state.getParameter(ID::autoTuneSpeed.getParamID()), + autoTuneSpeedSliderRelay, + processorRef.state.undoManager), + autoTuneDepthAttachment(*processorRef.state.getParameter(ID::autoTuneDepth.getParamID()), + autoTuneDepthSliderRelay, + processorRef.state.undoManager), + portTimeAttachment(*processorRef.state.getParameter(ID::portTime.getParamID()), + portTimeSliderRelay, + processorRef.state.undoManager), + muteAttachment(*processorRef.state.getParameter(ID::mute.getParamID()), + muteToggleRelay, + processorRef.state.undoManager), + filterTypeAttachment(*processorRef.state.getParameter(ID::filterType.getParamID()), + filterTypeComboRelay, + processorRef.state.undoManager) +{ + addAndMakeVisible(webComponent); + + webComponent.goToURL(localDevServerAddress); + //webComponent.goToURL (WebBrowserComponent::getResourceProviderRoot()); + + setSize(500, 500); + + startTimerHz(60); +} + +//============================================================================== +void WebViewPluginAudioProcessorEditor::paint(Graphics& g) +{ + // (Our component is opaque, so we must completely fill the background with a solid colour) + g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); +} + +void WebViewPluginAudioProcessorEditor::resized() +{ + webComponent.setBounds(getLocalBounds()); +} + +class WebViewPluginAudioProcessorWrapper : public WebViewPluginAudioProcessor +{ +public: + WebViewPluginAudioProcessorWrapper() : WebViewPluginAudioProcessor({}) + { + } + + bool hasEditor() const override { return true; } + AudioProcessorEditor* createEditor() override { + return new WebViewPluginAudioProcessorEditor(*this); + } +}; diff --git a/Harmonizer.jucer b/Harmonizer.jucer index f78cda7..60d9a2f 100644 --- a/Harmonizer.jucer +++ b/Harmonizer.jucer @@ -10,6 +10,15 @@ resource="1" file="Assets/webviewplugin-gui-fallback.html"/> + + + + + diff --git a/Source/CircularBuffer.h b/Source/CircularBuffer.h new file mode 100644 index 0000000..85c26f3 --- /dev/null +++ b/Source/CircularBuffer.h @@ -0,0 +1,86 @@ +/* + ============================================================================== + + CircularBuffer.h + Created: 4 Nov 2025 6:20:15pm + Author: mickl + + ============================================================================== +*/ + +#pragma once +#include + +class CircularBuffer +{ +public: + CircularBuffer(int numChannels, int numSamples) + : buffer(data, (size_t)numChannels, (size_t)numSamples) + { + } + + template + void push(dsp::AudioBlock b) + { + jassert(b.getNumChannels() == buffer.getNumChannels()); + + const auto trimmed = b.getSubBlock(b.getNumSamples() + - std::min(b.getNumSamples(), buffer.getNumSamples())); + + const auto bufferLength = (int64)buffer.getNumSamples(); + + for (auto samplesRemaining = (int64)trimmed.getNumSamples(); samplesRemaining > 0;) + { + const auto writeOffset = writeIx % bufferLength; + const auto numSamplesToWrite = std::min(samplesRemaining, bufferLength - writeOffset); + + auto destSubBlock = buffer.getSubBlock((size_t)writeOffset, (size_t)numSamplesToWrite); + const auto sourceSubBlock = trimmed.getSubBlock(trimmed.getNumSamples() - (size_t)samplesRemaining, + (size_t)numSamplesToWrite); + + destSubBlock.copyFrom(sourceSubBlock); + + samplesRemaining -= numSamplesToWrite; + writeIx += numSamplesToWrite; + } + } + + template + void push(Span s) + { + auto* ptr = s.begin(); + dsp::AudioBlock b(&ptr, 1, s.size()); + push(b); + } + + void read(int64 readIx, dsp::AudioBlock output) const + { + const auto numChannelsToUse = std::min(buffer.getNumChannels(), output.getNumChannels()); + + jassert(output.getNumChannels() == buffer.getNumChannels()); + + const auto bufferLength = (int64)buffer.getNumSamples(); + + for (auto outputOffset = (size_t)0; outputOffset < output.getNumSamples();) + { + const auto inputOffset = (size_t)((readIx + (int64)outputOffset) % bufferLength); + const auto numSamplesToRead = std::min(output.getNumSamples() - outputOffset, + (size_t)bufferLength - inputOffset); + + auto destSubBlock = output.getSubBlock(outputOffset, numSamplesToRead) + .getSubsetChannelBlock(0, numChannelsToUse); + + destSubBlock.copyFrom(buffer.getSubBlock(inputOffset, numSamplesToRead) + .getSubsetChannelBlock(0, numChannelsToUse)); + + outputOffset += numSamplesToRead; + } + } + + int64 getWriteIndex() const noexcept { return writeIx; } + +private: + HeapBlock data; + dsp::AudioBlock buffer; + int64 writeIx = 0; +}; \ No newline at end of file diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp new file mode 100644 index 0000000..d4bd0d3 --- /dev/null +++ b/Source/PluginEditor.cpp @@ -0,0 +1,185 @@ +/* + ============================================================================== + + PluginEditor.cpp + Created: 4 Nov 2025 6:20:46pm + Author: mickl + + ============================================================================== +*/ + +#include "PluginEditor.h" +#include "DemoUtilities.h" + + + +static ZipFile* getZipFile() +{ + static auto stream = createAssetInputStream("webviewplugin-gui_1.0.0.zip", AssertAssetExists::no); + + if (stream == nullptr) + return nullptr; + + static ZipFile f{ stream.get(), false }; + return &f; +} + +static const char* getMimeForExtension(const String& extension) +{ + static const std::unordered_map mimeMap = + { + { { "htm" }, "text/html" }, + { { "html" }, "text/html" }, + { { "txt" }, "text/plain" }, + { { "jpg" }, "image/jpeg" }, + { { "jpeg" }, "image/jpeg" }, + { { "svg" }, "image/svg+xml" }, + { { "ico" }, "image/vnd.microsoft.icon" }, + { { "json" }, "application/json" }, + { { "png" }, "image/png" }, + { { "css" }, "text/css" }, + { { "map" }, "application/json" }, + { { "js" }, "text/javascript" }, + { { "woff2" }, "font/woff2" } + }; + + if (const auto it = mimeMap.find(extension.toLowerCase()); it != mimeMap.end()) + return it->second; + + jassertfalse; + return ""; +} + +static String getExtension(String filename) +{ + return filename.fromLastOccurrenceOf(".", false, false); +} + +static auto streamToVector(InputStream& stream) +{ + std::vector result((size_t)stream.getTotalLength()); + stream.setPosition(0); + [[maybe_unused]] const auto bytesRead = stream.read(result.data(), result.size()); + jassert(bytesRead == (ssize_t)result.size()); + return result; +} + +std::optional WebViewPluginAudioProcessorEditor::getResource(const String& url) +{ + const auto urlToRetrive = url == "/" ? String{ "index.html" } + : url.fromFirstOccurrenceOf("/", false, false); + + if (auto* archive = getZipFile()) + { + if (auto* entry = archive->getEntry(urlToRetrive)) + { + auto stream = rawToUniquePtr(archive->createStreamForEntry(*entry)); + auto v = streamToVector(*stream); + auto mime = getMimeForExtension(getExtension(entry->filename).toLowerCase()); + return WebBrowserComponent::Resource{ std::move(v), + std::move(mime) }; + } + } + + if (urlToRetrive == "index.html") + { + auto fallbackIndexHtml = createAssetInputStream("webviewplugin-gui-fallback.html"); + return WebBrowserComponent::Resource{ streamToVector(*fallbackIndexHtml), + String { "text/html" } }; + } + + if (urlToRetrive == "data.txt") + { + WebBrowserComponent::Resource resource; + static constexpr char testData[] = "testdata"; + MemoryInputStream stream{ testData, numElementsInArray(testData) - 1, false }; + return WebBrowserComponent::Resource{ streamToVector(stream), String { "text/html" } }; + } + + if (urlToRetrive == "midNoteData.json") + { + + juce::Array notes; + int voice_num = 0; + for (auto& voice : processorRef.shifter.voices) { + if (voice.onoff_) { + auto obj = new DynamicObject(); + obj->setProperty("voice", voice_num); + obj->setProperty("midi", voice.GetMidiNote()); + notes.add(var(obj)); + } + voice_num++; + } + + DynamicObject::Ptr d(new DynamicObject()); + d->setProperty("notes", notes); + + const auto s = JSON::toString(d.get()); + MemoryInputStream stream{ s.getCharPointer(), s.getNumBytesAsUTF8(), false }; + return WebBrowserComponent::Resource{ streamToVector(stream), String { "application/json" } }; + } + + return std::nullopt; +} + +#if JUCE_ANDROID +// The localhost is available on this address to the emulator +const String localDevServerAddress = "http://10.0.2.2:3000/"; +#else +const String localDevServerAddress = "http://localhost:3000/"; +#endif + +bool SinglePageBrowser::pageAboutToLoad(const String& newURL) +{ + return newURL == localDevServerAddress || newURL == getResourceProviderRoot(); +} + + +WebViewPluginAudioProcessorEditor::WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor& p) + : AudioProcessorEditor(&p), processorRef(p) +{ + auto options = WebBrowserComponent::Options{} + .withBackend(WebBrowserComponent::Options::Backend::webview2) + .withWinWebView2Options(WebBrowserComponent::Options::WinWebView2{} + .withUserDataFolder(File::getSpecialLocation(File::SpecialLocationType::tempDirectory))) + .withNativeIntegrationEnabled() + .withOptionsFrom(controlParameterIndexReceiver) + .withResourceProvider([this](const auto& url) + { + return getResource(url); + }, + URL{ localDevServerAddress }.getOrigin()); + + + for (auto& sliderId : p.parameters.sliderIds) { + slider_relays.push_back(new WebSliderRelay{ sliderId }); + slider_attatchments.push_back(new + WebSliderParameterAttachment( + *processorRef.state.getParameter(sliderId), + *slider_relays.back(), + processorRef.state.undoManager)); + options = options.withOptionsFrom(*slider_relays.back()); + } + + webComponent = new SinglePageBrowser(options); + addAndMakeVisible(*webComponent); + + webComponent->goToURL(localDevServerAddress); + //webComponent.goToURL (WebBrowserComponent::getResourceProviderRoot()); + + setSize(500, 500); + + startTimerHz(60); +} + +void WebViewPluginAudioProcessorEditor::paint(Graphics& g) +{ + // (Our component is opaque, so we must completely fill the background with a solid colour) + g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); +} + +void WebViewPluginAudioProcessorEditor::resized() +{ + if (webComponent == nullptr) return; + webComponent->setBounds(getLocalBounds()); +} diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h new file mode 100644 index 0000000..01eaf0d --- /dev/null +++ b/Source/PluginEditor.h @@ -0,0 +1,93 @@ +/* + ============================================================================== + + PluginEditor.h + Created: 4 Nov 2025 6:20:46pm + Author: mickl + + ============================================================================== +*/ + +#pragma once +#include +#include "PluginProcessor.h" +extern const String localDevServerAddress; + +std::optional getResource(const String& url); + +//============================================================================== +class SinglePageBrowser : public WebBrowserComponent +{ +public: + using WebBrowserComponent::WebBrowserComponent; + + // Prevent page loads from navigating away from our single page web app + bool pageAboutToLoad(const String& newURL) override; +}; + +//============================================================================== +class WebViewPluginAudioProcessorEditor : public AudioProcessorEditor, private Timer +{ +public: + explicit WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor&); + ~WebViewPluginAudioProcessorEditor() { + delete webComponent; + for (auto& attatchments : slider_attatchments) { + delete attatchments; + } + for (auto& relays : slider_relays) { + delete relays; + } + } + + std::optional getResource(const String& url); + + //============================================================================== + void paint(Graphics&) override; + void resized() override; + + int getControlParameterIndex(Component&) override + { + return controlParameterIndexReceiver.getControlParameterIndex(); + } + + void timerCallback() override + { + static constexpr size_t numFramesBuffered = 5; + + SpinLock::ScopedLockType lock{ processorRef.midiLock }; + + static int64 callbackCounter = 0; + processorRef.new_midi = false; + juce::Array notes; + int voice_num = 0; + for (auto& voice : processorRef.shifter.voices) { + if (voice.onoff_) { + auto obj = new DynamicObject(); + obj->setProperty("voice", voice_num); + obj->setProperty("midi", voice.GetMidiNote()); + notes.add(var(obj)); + } + voice_num++; + } + + DynamicObject::Ptr d(new DynamicObject()); + d->setProperty("notes", notes); + d->setProperty("input_pitch", processorRef.shifter.getInputPitch()); + d->setProperty("output_pitch", processorRef.shifter.getOutputPitch()); + webComponent->emitEventIfBrowserIsVisible("midNoteData", d.get()); + } + +private: + WebViewPluginAudioProcessor& processorRef; + std::vector slider_relays; + std::vector< WebSliderParameterAttachment*> slider_attatchments; + + WebControlParameterIndexReceiver controlParameterIndexReceiver; + + SinglePageBrowser* webComponent = nullptr; + + std::deque> spectrumDataFrames; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessorEditor) +}; \ No newline at end of file diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp new file mode 100644 index 0000000..99f5e77 --- /dev/null +++ b/Source/PluginProcessor.cpp @@ -0,0 +1,96 @@ +/* + ============================================================================== + + PluginProcessor.cpp + Created: 4 Nov 2025 6:20:37pm + Author: mickl + + ============================================================================== +*/ + +#include "PluginProcessor.h" + + + +//============================================================================== +WebViewPluginAudioProcessor::WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout) + : AudioProcessor(BusesProperties() + .withInput("Input", juce::AudioChannelSet::stereo(), true) + .withOutput("Output", juce::AudioChannelSet::stereo(), true) + ), + parameters(layout), + state(*this, nullptr, "STATE", std::move(layout)) +{ + shifter.Init(48000.0f, 48); + shifter.SetFormantPreserve(state.getParameterAsValue("formantPreserve").getValue()); + shifter.SetAutoTuneSpeed(state.getParameterAsValue("autoTuneSpeed").getValue()); + shifter.SetAutoTuneDepth(state.getParameterAsValue("autoTuneDepth").getValue()); + shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue()); +} + +//============================================================================== +void WebViewPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) +{ + const auto channels = std::max(getTotalNumInputChannels(), getTotalNumOutputChannels()); + shifter.Init((float)sampleRate, samplesPerBlock); + if (channels == 0) + return; + + filter.prepare({ sampleRate, (uint32_t)samplesPerBlock, (uint32_t)channels }); + filter.reset(); +} + +bool WebViewPluginAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const +{ + if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() + && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) + return false; + + if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) + return false; + + return true; +} + +void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer& buffer, + juce::MidiBuffer& midi) +{ + juce::ScopedNoDenormals noDenormals; + + const auto totalNumInputChannels = getTotalNumInputChannels(); + const auto totalNumOutputChannels = getTotalNumOutputChannels(); + + for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) + buffer.clear(i, 0, buffer.getNumSamples()); + shifter.SetFormantPreserve(state.getParameterAsValue("formantPreserve").getValue()); + shifter.SetAutoTuneSpeed(state.getParameterAsValue("autoTuneSpeed").getValue()); + shifter.SetAutoTuneDepth(state.getParameterAsValue("autoTuneDepth").getValue()); + shifter.SetPortamentoTime(state.getParameterAsValue("portTime").getValue()); + juce::AudioBuffer const_buff; + const_buff.makeCopyOf(buffer); + shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples()); + + for (const auto metadata : midi) + { + const auto msg = metadata.getMessage(); + if (msg.isNoteOn()) { + shifter.AddMidiNote(msg.getNoteNumber()); + new_midi = true; + } + else if (msg.isNoteOff()) { + shifter.RemoveMidiNote(msg.getNoteNumber()); + new_midi = true; + } + } +} + +//============================================================================== +void WebViewPluginAudioProcessor::getStateInformation(juce::MemoryBlock& destData) +{ + juce::ignoreUnused(destData); +} + +void WebViewPluginAudioProcessor::setStateInformation(const void* data, int sizeInBytes) +{ + juce::ignoreUnused(data, sizeInBytes); +} diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h new file mode 100644 index 0000000..e31a8ae --- /dev/null +++ b/Source/PluginProcessor.h @@ -0,0 +1,131 @@ +/* + ============================================================================== + + PluginProcessor.h + Created: 4 Nov 2025 6:20:37pm + Author: mickl + + ============================================================================== +*/ + +#pragma once +#include +#include "Shifter.h" + +class WebViewPluginAudioProcessor : public AudioProcessor +{ +public: + //============================================================================== + WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout); + + //============================================================================== + void prepareToPlay(double sampleRate, int samplesPerBlock) override; + void releaseResources() override {} + + bool isBusesLayoutSupported(const BusesLayout& layouts) const override; + + void processBlock(AudioBuffer&, MidiBuffer&) override; + using AudioProcessor::processBlock; + + //============================================================================== + const String getName() const override { return JucePlugin_Name; } + + bool acceptsMidi() const override { return false; } + bool producesMidi() const override { return false; } + bool isMidiEffect() const override { return false; } + double getTailLengthSeconds() const override { return 0.0; } + + //============================================================================== + int getNumPrograms() override { return 1; } + int getCurrentProgram() override { return 0; } + void setCurrentProgram(int) override {} + const String getProgramName(int) override { return {}; } + void changeProgramName(int, const String&) override {} + + //============================================================================== + void getStateInformation(MemoryBlock& destData) override; + void setStateInformation(const void* data, int sizeInBytes) override; + bool new_midi = false; + + struct Parameters + { + public: + explicit Parameters(AudioProcessorValueTreeState::ParameterLayout& layout) + { + sliderIds.push_back("formantPreserve"); + addToLayout(layout, + ParameterID{ "formantPreserve" }, + "Formant Preserve", + NormalisableRange {0.0f, 1.0f, .01f}, + .5f); + + sliderIds.push_back("autoTuneDepth"); + addToLayout(layout, + ParameterID("autoTuneDepth"), + "AutoTune Depth", + NormalisableRange {0.0f, 1.1f, .01f}, + .5f); + + sliderIds.push_back("autoTuneSpeed"); + addToLayout(layout, + ParameterID("autoTuneSpeed"), + "AutoTune Speed", + NormalisableRange {0.001f, 0.1f, .001f}, + .5f); + sliderIds.push_back("portTime"); + addToLayout(layout, + ParameterID("portTime"), + "Portamento Speed", + NormalisableRange {0.001f, 0.2f, .001f}, + .01f); + + } + + /*AudioParameterFloat& formantPreserve; + AudioParameterFloat& autoTuneSpeed; + AudioParameterFloat& autoTuneDepth; + AudioParameterFloat& portTime;*/ + std::vector sliderIds; + /*AudioParameterBool& mute; + AudioParameterChoice& filterType;*/ + + private: + template + static void add(AudioProcessorParameterGroup& group, std::unique_ptr param) + { + group.addChild(std::move(param)); + } + + template + static void add(AudioProcessorValueTreeState::ParameterLayout& group, std::unique_ptr param) + { + group.add(std::move(param)); + } + + template + static Param& addToLayout(Group& layout, Ts&&... ts) + { + auto param = std::make_unique(std::forward(ts)...); + auto& ref = *param; + add(layout, std::move(param)); + return ref; + } + }; + + Parameters parameters; + AudioProcessorValueTreeState state; + SpinLock midiLock; + + /*std::vector spectrumData = [] { return std::vector(256, 0.0f); }(); + SpinLock spectrumDataLock; + + SpectralBars spectralBars;*/ + + dsp::LadderFilter filter; + Shifter shifter; + +private: + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessor) + +}; \ No newline at end of file diff --git a/Source/WebViewPluginDemo.h b/Source/WebViewPluginDemo.h index a1bf101..5a3e462 100644 --- a/Source/WebViewPluginDemo.h +++ b/Source/WebViewPluginDemo.h @@ -52,652 +52,12 @@ #pragma once -#include "DemoUtilities.h" + #include -#include "Shifter.h" +#include "PluginEditor.h" +#include "PluginProcessor.h" -//using namespace juce::dsp; -namespace ID -{ -#define PARAMETER_ID(str) static const ParameterID str { #str, 1 }; - - PARAMETER_ID(formantPreserve) - PARAMETER_ID(autoTuneSpeed) - PARAMETER_ID(autoTuneDepth) - PARAMETER_ID(portTime) - PARAMETER_ID(mute) - PARAMETER_ID(filterType) - -#undef PARAMETER_ID -} - -class CircularBuffer -{ -public: - CircularBuffer(int numChannels, int numSamples) - : buffer(data, (size_t)numChannels, (size_t)numSamples) - { - } - - template - void push(dsp::AudioBlock b) - { - jassert(b.getNumChannels() == buffer.getNumChannels()); - - const auto trimmed = b.getSubBlock(b.getNumSamples() - - std::min(b.getNumSamples(), buffer.getNumSamples())); - - const auto bufferLength = (int64)buffer.getNumSamples(); - - for (auto samplesRemaining = (int64)trimmed.getNumSamples(); samplesRemaining > 0;) - { - const auto writeOffset = writeIx % bufferLength; - const auto numSamplesToWrite = std::min(samplesRemaining, bufferLength - writeOffset); - - auto destSubBlock = buffer.getSubBlock((size_t)writeOffset, (size_t)numSamplesToWrite); - const auto sourceSubBlock = trimmed.getSubBlock(trimmed.getNumSamples() - (size_t)samplesRemaining, - (size_t)numSamplesToWrite); - - destSubBlock.copyFrom(sourceSubBlock); - - samplesRemaining -= numSamplesToWrite; - writeIx += numSamplesToWrite; - } - } - - template - void push(Span s) - { - auto* ptr = s.begin(); - dsp::AudioBlock b(&ptr, 1, s.size()); - push(b); - } - - void read(int64 readIx, dsp::AudioBlock output) const - { - const auto numChannelsToUse = std::min(buffer.getNumChannels(), output.getNumChannels()); - - jassert(output.getNumChannels() == buffer.getNumChannels()); - - const auto bufferLength = (int64)buffer.getNumSamples(); - - for (auto outputOffset = (size_t)0; outputOffset < output.getNumSamples();) - { - const auto inputOffset = (size_t)((readIx + (int64)outputOffset) % bufferLength); - const auto numSamplesToRead = std::min(output.getNumSamples() - outputOffset, - (size_t)bufferLength - inputOffset); - - auto destSubBlock = output.getSubBlock(outputOffset, numSamplesToRead) - .getSubsetChannelBlock(0, numChannelsToUse); - - destSubBlock.copyFrom(buffer.getSubBlock(inputOffset, numSamplesToRead) - .getSubsetChannelBlock(0, numChannelsToUse)); - - outputOffset += numSamplesToRead; - } - } - - int64 getWriteIndex() const noexcept { return writeIx; } - -private: - HeapBlock data; - dsp::AudioBlock buffer; - int64 writeIx = 0; -}; - -//class SpectralBars -//{ -//public: -// //template -// void push(int data) -// { -// testQueue.push(data); -// } -// -// void compute(Span output) { -// int index = 0; -// for (auto it = output.begin(); it != output.end(); ++it) { -// *it = testQueue.get(index++); -// } -// } -// -// -//private: -// circ_queue testQueue; -//}; - -//============================================================================== -class WebViewPluginAudioProcessor : public AudioProcessor -{ -public: - //============================================================================== - WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout); - - //============================================================================== - void prepareToPlay(double sampleRate, int samplesPerBlock) override; - void releaseResources() override {} - - bool isBusesLayoutSupported(const BusesLayout& layouts) const override; - - void processBlock(AudioBuffer&, MidiBuffer&) override; - using AudioProcessor::processBlock; - - //============================================================================== - const String getName() const override { return JucePlugin_Name; } - - bool acceptsMidi() const override { return false; } - bool producesMidi() const override { return false; } - bool isMidiEffect() const override { return false; } - double getTailLengthSeconds() const override { return 0.0; } - - //============================================================================== - int getNumPrograms() override { return 1; } - int getCurrentProgram() override { return 0; } - void setCurrentProgram(int) override {} - const String getProgramName(int) override { return {}; } - void changeProgramName(int, const String&) override {} - - //============================================================================== - void getStateInformation(MemoryBlock& destData) override; - void setStateInformation(const void* data, int sizeInBytes) override; - bool new_midi = false; - - struct Parameters - { - public: - explicit Parameters(AudioProcessorValueTreeState::ParameterLayout& layout) - : formantPreserve(addToLayout(layout, - ID::formantPreserve, - "Formant Preserve", - NormalisableRange {0.0f, 1.0f, .01f}, - .5f)), - - autoTuneSpeed(addToLayout(layout, - ID::autoTuneSpeed, - "AutoTune Speed", - NormalisableRange {0.001f, 0.1f, .001f}, - .5f)), - autoTuneDepth(addToLayout(layout, - ID::autoTuneDepth, - "AutoTune Depth", - NormalisableRange {0.0f, 1.1f, .01f}, - .5f)), - portTime(addToLayout(layout, - ID::portTime, - "Portamento Speed", - NormalisableRange {0.001f, 0.2f, .001f}, - .01f)), - mute(addToLayout(layout, ID::mute, "Mute", false)), - filterType(addToLayout(layout, - ID::filterType, - "Filter type", - StringArray{ "Low-pass", "High-pass", "Band-pass" }, - 0)) - { - } - - AudioParameterFloat& formantPreserve; - AudioParameterFloat& autoTuneSpeed; - AudioParameterFloat& autoTuneDepth; - AudioParameterFloat& portTime; - AudioParameterBool& mute; - AudioParameterChoice& filterType; - - private: - template - static void add(AudioProcessorParameterGroup& group, std::unique_ptr param) - { - group.addChild(std::move(param)); - } - - template - static void add(AudioProcessorValueTreeState::ParameterLayout& group, std::unique_ptr param) - { - group.add(std::move(param)); - } - - template - static Param& addToLayout(Group& layout, Ts&&... ts) - { - auto param = std::make_unique(std::forward(ts)...); - auto& ref = *param; - add(layout, std::move(param)); - return ref; - } - }; - - Parameters parameters; - AudioProcessorValueTreeState state; - SpinLock midiLock; - - /*std::vector spectrumData = [] { return std::vector(256, 0.0f); }(); - SpinLock spectrumDataLock; - - SpectralBars spectralBars;*/ - - dsp::LadderFilter filter; - Shifter shifter; - -private: - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessor) - -}; - -//============================================================================== -WebViewPluginAudioProcessor::WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout) - : AudioProcessor(BusesProperties() -#if ! JucePlugin_IsMidiEffect -#if ! JucePlugin_IsSynth - .withInput("Input", juce::AudioChannelSet::stereo(), true) -#endif - .withOutput("Output", juce::AudioChannelSet::stereo(), true) -#endif - ), - parameters(layout), - state(*this, nullptr, "STATE", std::move(layout)) -{ - shifter.Init(48000.0f, 48); -} - -//============================================================================== -void WebViewPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) -{ - const auto channels = std::max(getTotalNumInputChannels(), getTotalNumOutputChannels()); - shifter.Init((float)sampleRate, samplesPerBlock); - if (channels == 0) - return; - - filter.prepare({ sampleRate, (uint32_t)samplesPerBlock, (uint32_t)channels }); - filter.reset(); -} - -bool WebViewPluginAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const -{ - if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() - && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) - return false; - - if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) - return false; - - return true; -} - -void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer& buffer, - juce::MidiBuffer& midi) -{ - juce::ScopedNoDenormals noDenormals; - - const auto totalNumInputChannels = getTotalNumInputChannels(); - const auto totalNumOutputChannels = getTotalNumOutputChannels(); - - for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i) - buffer.clear(i, 0, buffer.getNumSamples()); - shifter.SetFormantPreserve(parameters.formantPreserve.get()); - shifter.SetAutoTuneSpeed(parameters.autoTuneSpeed.get()); - shifter.SetAutoTuneDepth(parameters.autoTuneDepth.get()); - shifter.SetPortamentoTime(parameters.portTime.get()); - juce::AudioBuffer const_buff; - const_buff.makeCopyOf(buffer); - shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples()); - - for (const auto metadata : midi) - { - const auto msg = metadata.getMessage(); - if (msg.isNoteOn()) { - shifter.AddMidiNote(msg.getNoteNumber()); - new_midi = true; - //editor.webComponent.emitEventIfBrowserIsVisible("midNoteData", var{}); - - } - else if (msg.isNoteOff()) { - shifter.RemoveMidiNote(msg.getNoteNumber()); - new_midi = true; - //editor.webComponent.emitEventIfBrowserIsVisible("midNoteData", var{}); - } - } - - - { - //DBG(shifter.out_midi[MAX_VOICES]); - //push midi note - //spectralBars.push(shifter.out_midi[MAX_VOICES]); - const SpinLock::ScopedTryLockType lock(midiLock); - - if (!lock.isLocked()) - return; - - } - - - /*for(auto i = 0; i < buffer.getNumSamples(); ++i) - { - bool process = (i % 256) == 0 && i != 0; - - for(auto j = 0; j < totalNumInputChannels; ++j) - { - input[j][i] = buffer.getReadPointer(j)[i]; - } - } - - filter.setCutoffFrequencyHz (parameters.cutoffFreqHz.get()); - - const auto filterMode = [this] - { - switch (parameters.filterType.getIndex()) - { - case 0: - return dsp::LadderFilter::Mode::LPF12; - - case 1: - return dsp::LadderFilter::Mode::HPF12; - - default: - return dsp::LadderFilter::Mode::BPF12; - } - }(); - - filter.setMode (filterMode); - - auto outBlock = dsp::AudioBlock { buffer }.getSubsetChannelBlock (0, (size_t) getTotalNumOutputChannels()); - - if (parameters.mute.get()) - outBlock.clear(); - - filter.process (dsp::ProcessContextReplacing (outBlock)); - - spectralBars.push (Span { buffer.getReadPointer (0), (size_t) buffer.getNumSamples() }); - - { - const SpinLock::ScopedTryLockType lock (spectrumDataLock); - - if (! lock.isLocked()) - return; - - spectralBars.compute ({ spectrumData.data(), spectrumData.size() }); - }*/ -} - -//============================================================================== -void WebViewPluginAudioProcessor::getStateInformation(juce::MemoryBlock& destData) -{ - juce::ignoreUnused(destData); -} - -void WebViewPluginAudioProcessor::setStateInformation(const void* data, int sizeInBytes) -{ - juce::ignoreUnused(data, sizeInBytes); -} - -extern const String localDevServerAddress; - -std::optional getResource(const String& url); - -//============================================================================== -struct SinglePageBrowser : WebBrowserComponent -{ - using WebBrowserComponent::WebBrowserComponent; - - // Prevent page loads from navigating away from our single page web app - bool pageAboutToLoad(const String& newURL) override; -}; - -//============================================================================== -class WebViewPluginAudioProcessorEditor : public AudioProcessorEditor, private Timer -{ -public: - explicit WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor&); - - std::optional getResource(const String& url); - - //============================================================================== - void paint(Graphics&) override; - void resized() override; - - int getControlParameterIndex(Component&) override - { - return controlParameterIndexReceiver.getControlParameterIndex(); - } - - void timerCallback() override - { - static constexpr size_t numFramesBuffered = 5; - - SpinLock::ScopedLockType lock{ processorRef.midiLock }; - - static int64 callbackCounter = 0; - processorRef.new_midi = false; - juce::Array notes; - int voice_num = 0; - for (auto& voice : processorRef.shifter.voices) { - if (voice.onoff_) { - auto obj = new DynamicObject(); - obj->setProperty("voice", voice_num); - obj->setProperty("midi", voice.GetMidiNote()); - notes.add(var(obj)); - } - voice_num++; - } - - DynamicObject::Ptr d(new DynamicObject()); - d->setProperty("notes", notes); - d->setProperty("input_pitch", processorRef.shifter.getInputPitch()); - d->setProperty("output_pitch", processorRef.shifter.getOutputPitch()); - webComponent.emitEventIfBrowserIsVisible("midNoteData", d.get()); - } - -private: - WebViewPluginAudioProcessor& processorRef; - - WebSliderRelay formantSliderRelay{ "formantSlider" }; - WebSliderRelay autoTuneSpeedSliderRelay{ "autoTuneSpeedSlider" }; - WebSliderRelay autoTuneDepthSliderRelay{ "autoTuneDepthSlider" }; - WebSliderRelay portTimeSliderRelay{ "portTimeSlider" }; - WebToggleButtonRelay muteToggleRelay{ "muteToggle" }; - WebComboBoxRelay filterTypeComboRelay{ "filterTypeCombo" }; - - WebControlParameterIndexReceiver controlParameterIndexReceiver; - - SinglePageBrowser webComponent{ WebBrowserComponent::Options{} - .withBackend(WebBrowserComponent::Options::Backend::webview2) - .withWinWebView2Options(WebBrowserComponent::Options::WinWebView2{} - .withUserDataFolder(File::getSpecialLocation(File::SpecialLocationType::tempDirectory))) - .withNativeIntegrationEnabled() - .withOptionsFrom(formantSliderRelay) - .withOptionsFrom(autoTuneSpeedSliderRelay) - .withOptionsFrom(autoTuneDepthSliderRelay) - .withOptionsFrom(portTimeSliderRelay) - .withOptionsFrom(muteToggleRelay) - .withOptionsFrom(filterTypeComboRelay) - .withOptionsFrom(controlParameterIndexReceiver) - .withNativeFunction("sayHello", [](auto& var, auto complete) - { - complete("Hello " + var[0].toString()); - }) - .withResourceProvider([this](const auto& url) - { - return getResource(url); - }, - URL { localDevServerAddress }.getOrigin()) }; - - WebSliderParameterAttachment formantAttachment; - WebSliderParameterAttachment autoTuneSpeedAttachment; - WebSliderParameterAttachment autoTuneDepthAttachment; - WebSliderParameterAttachment portTimeAttachment; - WebToggleButtonParameterAttachment muteAttachment; - WebComboBoxParameterAttachment filterTypeAttachment; - - std::deque> spectrumDataFrames; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessorEditor) -}; - -static ZipFile* getZipFile() -{ - static auto stream = createAssetInputStream("webviewplugin-gui_1.0.0.zip", AssertAssetExists::no); - - if (stream == nullptr) - return nullptr; - - static ZipFile f{ stream.get(), false }; - return &f; -} - -static const char* getMimeForExtension(const String& extension) -{ - static const std::unordered_map mimeMap = - { - { { "htm" }, "text/html" }, - { { "html" }, "text/html" }, - { { "txt" }, "text/plain" }, - { { "jpg" }, "image/jpeg" }, - { { "jpeg" }, "image/jpeg" }, - { { "svg" }, "image/svg+xml" }, - { { "ico" }, "image/vnd.microsoft.icon" }, - { { "json" }, "application/json" }, - { { "png" }, "image/png" }, - { { "css" }, "text/css" }, - { { "map" }, "application/json" }, - { { "js" }, "text/javascript" }, - { { "woff2" }, "font/woff2" } - }; - - if (const auto it = mimeMap.find(extension.toLowerCase()); it != mimeMap.end()) - return it->second; - - jassertfalse; - return ""; -} - -static String getExtension(String filename) -{ - return filename.fromLastOccurrenceOf(".", false, false); -} - -static auto streamToVector(InputStream& stream) -{ - std::vector result((size_t)stream.getTotalLength()); - stream.setPosition(0); - [[maybe_unused]] const auto bytesRead = stream.read(result.data(), result.size()); - jassert(bytesRead == (ssize_t)result.size()); - return result; -} - -std::optional WebViewPluginAudioProcessorEditor::getResource(const String& url) -{ - const auto urlToRetrive = url == "/" ? String{ "index.html" } - : url.fromFirstOccurrenceOf("/", false, false); - - if (auto* archive = getZipFile()) - { - if (auto* entry = archive->getEntry(urlToRetrive)) - { - auto stream = rawToUniquePtr(archive->createStreamForEntry(*entry)); - auto v = streamToVector(*stream); - auto mime = getMimeForExtension(getExtension(entry->filename).toLowerCase()); - return WebBrowserComponent::Resource{ std::move(v), - std::move(mime) }; - } - } - - if (urlToRetrive == "index.html") - { - auto fallbackIndexHtml = createAssetInputStream("webviewplugin-gui-fallback.html"); - return WebBrowserComponent::Resource{ streamToVector(*fallbackIndexHtml), - String { "text/html" } }; - } - - if (urlToRetrive == "data.txt") - { - WebBrowserComponent::Resource resource; - static constexpr char testData[] = "testdata"; - MemoryInputStream stream{ testData, numElementsInArray(testData) - 1, false }; - return WebBrowserComponent::Resource{ streamToVector(stream), String { "text/html" } }; - } - - if (urlToRetrive == "midNoteData.json") - { - - juce::Array notes; - int voice_num = 0; - for (auto& voice : processorRef.shifter.voices) { - if (voice.onoff_) { - auto obj = new DynamicObject(); - obj->setProperty("voice", voice_num); - obj->setProperty("midi", voice.GetMidiNote()); - notes.add(var(obj)); - } - voice_num++; - } - - DynamicObject::Ptr d(new DynamicObject()); - d->setProperty("notes", notes); - - const auto s = JSON::toString(d.get()); - MemoryInputStream stream{ s.getCharPointer(), s.getNumBytesAsUTF8(), false }; - return WebBrowserComponent::Resource{ streamToVector(stream), String { "application/json" } }; - } - - return std::nullopt; -} - -#if JUCE_ANDROID -// The localhost is available on this address to the emulator -const String localDevServerAddress = "http://10.0.2.2:3000/"; -#else -const String localDevServerAddress = "http://localhost:3000/"; -#endif - -bool SinglePageBrowser::pageAboutToLoad(const String& newURL) -{ - return newURL == localDevServerAddress || newURL == getResourceProviderRoot(); -} - -//============================================================================== -WebViewPluginAudioProcessorEditor::WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor& p) - : AudioProcessorEditor(&p), processorRef(p), - formantAttachment(*processorRef.state.getParameter(ID::formantPreserve.getParamID()), - formantSliderRelay, - processorRef.state.undoManager), - autoTuneSpeedAttachment(*processorRef.state.getParameter(ID::autoTuneSpeed.getParamID()), - autoTuneSpeedSliderRelay, - processorRef.state.undoManager), - autoTuneDepthAttachment(*processorRef.state.getParameter(ID::autoTuneDepth.getParamID()), - autoTuneDepthSliderRelay, - processorRef.state.undoManager), - portTimeAttachment(*processorRef.state.getParameter(ID::portTime.getParamID()), - portTimeSliderRelay, - processorRef.state.undoManager), - muteAttachment(*processorRef.state.getParameter(ID::mute.getParamID()), - muteToggleRelay, - processorRef.state.undoManager), - filterTypeAttachment(*processorRef.state.getParameter(ID::filterType.getParamID()), - filterTypeComboRelay, - processorRef.state.undoManager) -{ - addAndMakeVisible(webComponent); - - webComponent.goToURL(localDevServerAddress); - //webComponent.goToURL (WebBrowserComponent::getResourceProviderRoot()); - - setSize(500, 500); - - startTimerHz(60); -} - -//============================================================================== -void WebViewPluginAudioProcessorEditor::paint(Graphics& g) -{ - // (Our component is opaque, so we must completely fill the background with a solid colour) - g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); -} - -void WebViewPluginAudioProcessorEditor::resized() -{ - webComponent.setBounds(getLocalBounds()); -} class WebViewPluginAudioProcessorWrapper : public WebViewPluginAudioProcessor { @@ -711,3 +71,4 @@ public: return new WebViewPluginAudioProcessorEditor(*this); } }; +