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);
}
};
+