From 1a375b0ad574d37277d3add7ed0eee1e7eb808d3 Mon Sep 17 00:00:00 2001 From: Roland Rabien Date: Wed, 23 Aug 2023 08:45:23 -0700 Subject: [PATCH] Switch to cmake --- .gitignore | 1 + CMakeLists.txt | 232 ++++++++++++ CMakePresets.json | 13 + ci/build.sh | 135 +++---- ci/config_cmake.sh | 16 + modules/gin | 2 +- modules/juce | 2 +- plugin/Resources/Wavetables/WINDOW_S.WAV | Bin 32812 -> 0 bytes plugin/Resources/placeholder.txt | 0 plugin/Source/Boxes.h | 421 ---------------------- plugin/Source/Cfg.h | 5 +- plugin/Source/Panels.h | 435 +++++++++++++++++++++++ plugin/Source/PluginEditor.cpp | 71 +--- plugin/Source/PluginEditor.h | 173 +++++++-- plugin/Source/PluginProcessor.cpp | 276 ++++++-------- plugin/Source/PluginProcessor.h | 38 +- plugin/Source/WavetableVoice.cpp | 116 ++---- plugin/Source/WavetableVoice.h | 16 +- plugin/Wavetable.jucer | 142 -------- 19 files changed, 1085 insertions(+), 1009 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100755 ci/config_cmake.sh delete mode 100644 plugin/Resources/Wavetables/WINDOW_S.WAV create mode 100644 plugin/Resources/placeholder.txt delete mode 100644 plugin/Source/Boxes.h create mode 100644 plugin/Source/Panels.h delete mode 100644 plugin/Wavetable.jucer diff --git a/.gitignore b/.gitignore index b3bacf5..cda1704 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ xcuserdata Builds JuceLibraryCode +ci/bin diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8f8e145 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,232 @@ +cmake_minimum_required (VERSION 3.24.0 FATAL_ERROR) + +# +# Set for each plugin +# +set (PLUGIN_NAME Wavetable) +set (PLUGIN_VERSION 1.0.0) +set (BUNDLE_ID com.socalabs.Wavetable) +set (AU_ID WavetableAU) +set (LV2_URI https://socalabs.com/wavetable/) +set (PLUGIN_CODE Wave) + +set (CMAKE_POLICY_DEFAULT_CMP0077 NEW) +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +set (CMAKE_SUPPRESS_REGENERATION true) +set (CMAKE_SKIP_INSTALL_RULES YES) +set_property (GLOBAL PROPERTY DEBUG_CONFIGURATIONS "Debug") + +set (CMAKE_C_FLAGS_DEVELOPMENT ${CMAKE_C_FLAGS_RELEASE}) +set (CMAKE_CXX_FLAGS_DEVELOPMENT ${CMAKE_CXX_FLAGS_RELEASE}) + +project (${PLUGIN_NAME} VERSION ${PLUGIN_VERSION} LANGUAGES CXX C HOMEPAGE_URL "https://socalabs.com/") + +include (CMakeDependentOption) + +set_property (DIRECTORY APPEND PROPERTY LABELS ${PLUGIN_NAME}) + +set_property (DIRECTORY APPEND PROPERTY LABELS SocaLabs) +set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PLUGIN_NAME}_Standalone) + +set (CMAKE_OSX_DEPLOYMENT_TARGET 10.9) + +set (CMAKE_EXPORT_COMPILE_COMMANDS ON) +set (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION ON) +set (CMAKE_CXX_STANDARD 20) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set (CMAKE_CXX_EXTENSIONS OFF) +set (CMAKE_OBJCXX_STANDARD 20) +set (CMAKE_OBJCXX_STANDARD_REQUIRED ON) + set (CMAKE_CXX_VISIBILITY_PRESET hidden) + set (CMAKE_VISIBILITY_INLINES_HIDDEN ON) +set (CMAKE_MINSIZEREL_POSTFIX -rm) +set (CMAKE_RELWITHDEBINFO_POSTFIX -rd) +set (CMAKE_OPTIMIZE_DEPENDENCIES OFF) + +set (BUILD_SHARED_LIBS OFF) + +if(APPLE) + set (CMAKE_OSX_ARCHITECTURES arm64 x86_64) +endif() + +if (WIN32) + set (FORMATS Standalone VST VST3 LV2) +else() + set (FORMATS Standalone VST VST3 AU LV2) +endif() + +set_property (GLOBAL PROPERTY USE_FOLDERS YES) +set_property (GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER utility) +set_property (GLOBAL PROPERTY REPORT_UNDEFINED_PROPERTIES "${CMAKE_BINARY_DIR}/undefined_properties.log") +set_property (GLOBAL PROPERTY JUCE_COPY_PLUGIN_AFTER_BUILD YES) + +set_property (DIRECTORY APPEND PROPERTY LABELS External) + +# JUCE + +set (JUCE_MODULES_ONLY OFF) +set (JUCE_ENABLE_MODULE_SOURCE_GROUPS ON) +set (JUCE_BUILD_EXTRAS OFF) +set (JUCE_BUILD_EXAMPLES OFF) + +add_subdirectory (modules/juce) + +set_property (DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/modules/juce" APPEND PROPERTY LABELS JUCE) + +# + +# Gin modules + +foreach(module_name IN ITEMS gin gin_dsp gin_graphics gin_gui gin_metadata gin_network gin_plugin gin_webp) + juce_add_module ( + "${CMAKE_CURRENT_LIST_DIR}/modules/gin/modules/${module_name}" + ) + + set_property (TARGET "${module_name}" APPEND PROPERTY LABELS Gin) +endforeach() + +# Binary Data + +set_property (DIRECTORY APPEND PROPERTY LABELS Assets) + +juce_add_binary_data (${PLUGIN_NAME}_Assets SOURCES + "plugin/Resources/placeholder.txt" + ) + +set_target_properties(${PLUGIN_NAME}_Assets PROPERTIES UNITY_BUILD ON UNITY_BUILD_MODE BATCH UNITY_BUILD_BATCH_SIZE 50) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +juce_set_vst2_sdk_path (${CMAKE_SOURCE_DIR}/modules/plugin_sdk/vstsdk2.4) + +juce_add_plugin (${PLUGIN_NAME} + PRODUCT_NAME ${PLUGIN_NAME} + VERSION ${PLUGIN_VERSION} + COMPANY_NAME SocaLabs + COMPANY_WEBSITE "https://socalabs.com/" + BUNDLE_ID ${BUNDLE_ID} + FORMATS ${FORMATS} + PLUGIN_MANUFACTURER_CODE Soca + PLUGIN_CODE ${PLUGIN_CODE} + IS_SYNTH ON + NEEDS_MIDI_INPUT ON + EDITOR_WANTS_KEYBOARD_FOCUS ON + VST2_CATEGORY kPlugCategSynth + VST3_CATEGORIES Instrument + AU_MAIN_TYPE kAudioUnitType_MusicDevice + AU_EXPORT_PREFIX ${AU_ID} + AU_SANDBOX_SAFE FALSE + LV2URI LV2_URI) + +file (GLOB_RECURSE source_files CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.cc + ${CMAKE_CURRENT_SOURCE_DIR}/plugin/*.h) + +target_sources (${PLUGIN_NAME} PRIVATE ${source_files}) +source_group (TREE ${CMAKE_CURRENT_SOURCE_DIR}/plugin PREFIX Source FILES ${source_files}) + +file (GLOB_RECURSE asset_files CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/Assets/*) + +target_sources (${PLUGIN_NAME} PRIVATE ${asset_files}) +source_group (TREE ${CMAKE_CURRENT_SOURCE_DIR}/Assets PREFIX Assets FILES ${asset_files}) + +target_link_libraries (${PLUGIN_NAME} PRIVATE + ${PLUGIN_NAME}_Assets + + gin + gin_dsp + gin_graphics + gin_gui + gin_plugin + + juce::juce_audio_basics + juce::juce_audio_devices + juce::juce_audio_formats + juce::juce_audio_plugin_client + juce::juce_audio_processors + juce::juce_audio_utils + juce::juce_core + juce::juce_cryptography + juce::juce_data_structures + juce::juce_events + juce::juce_graphics + juce::juce_gui_basics + juce::juce_gui_extra + + juce::juce_recommended_config_flags + ) + +target_include_directories (${PLUGIN_NAME} PRIVATE modules/fmt/include) +target_include_directories (${PLUGIN_NAME} PRIVATE modules/ASIO/common) +target_include_directories (${PLUGIN_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/Definitions) + +juce_generate_juce_header (${PLUGIN_NAME}) + +target_compile_definitions (${PLUGIN_NAME} PRIVATE + JUCE_DISPLAY_SPLASH_SCREEN=0 + JUCE_COREGRAPHICS_DRAW_ASYNC=1 + JUCE_MODAL_LOOPS_PERMITTED=1 + JUCE_WEB_BROWSER=0 + JUCE_USE_FLAC=0 + JUCE_USE_CURL=1 + JUCE_USE_MP3AUDIOFORMAT=0 + JUCE_USE_LAME_AUDIO_FORMAT=0 + JUCE_USE_WINDOWS_MEDIA_FORMAT=0 + JucePlugin_PreferredChannelConfigurations={0,2} + _CRT_SECURE_NO_WARNINGS + ) + +if (APPLE) + set_target_properties("juce_vst3_helper" PROPERTIES XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO) + + foreach(t ${FORMATS} "Assets" "All" "") + set(tgt ${CMAKE_PROJECT_NAME}) + if (NOT t STREQUAL "") + set(tgt ${tgt}_${t}) + endif() + if (TARGET ${tgt}) + set_target_properties(${tgt} PROPERTIES + XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME NO + #XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] YES + XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] "YES" + ) + if (NOT t STREQUAL "All") + target_compile_options(${tgt} PRIVATE + -Wall -Wstrict-aliasing -Wunused-parameter -Wconditional-uninitialized -Woverloaded-virtual -Wreorder -Wconstant-conversion -Wbool-conversion -Wextra-semi + -Wunreachable-code -Winconsistent-missing-destructor-override -Wshift-sign-overflow -Wnullable-to-nonnull-conversion -Wuninitialized -Wno-missing-field-initializers + -Wno-ignored-qualifiers -Wno-missing-braces -Wno-char-subscripts -Wno-unused-private-field -fno-aligned-allocation -Wunused-private-field -Wunreachable-code + -Wenum-compare -Wshadow -Wfloat-conversion -Wshadow-uncaptured-local -Wshadow-field -Wsign-compare -Wdeprecated-this-capture -Wimplicit-float-conversion + -ffast-math -fno-finite-math-only) + endif() + endif() + endforeach() +endif() + +if (WIN32) + foreach(t ${FORMATS} "Assets" "All" "") + set(tgt ${CMAKE_PROJECT_NAME}) + if (NOT t STREQUAL "") + set(tgt ${tgt}_${t}) + endif() + if (TARGET ${tgt}) + set_property(TARGET ${tgt} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /INCREMENTAL:NO") + set_target_properties(${tgt} PROPERTIES LINK_FLAGS "/ignore:4099") + endif() + endforeach() +endif() + +if(UNIX AND NOT APPLE) + target_link_libraries (${PLUGIN_NAME} PRIVATE curl) +endif() + + +if(WIN32) + set (dest "Program Files") +else() + set (dest "Applications") +endif() + +install (TARGETS ${PLUGIN_NAME} DESTINATION "${dest}") diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..edcdeae --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,13 @@ +{ + "cmakeMinimumRequired": { + "major": 3, + "minor": 24, + "patch": 0 + }, + "version": 5, + "include": [ + "modules/gin/ci/toolchains/xcode.json", + "modules/gin/ci/toolchains/vs.json", + "modules/gin/ci/toolchains/gcc.json" + ] +} diff --git a/ci/build.sh b/ci/build.sh index 77a3044..2f554b7 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -3,19 +3,13 @@ PLUGIN="Wavetable" # linux specific stiff -if [ $OS = "linux" ]; then - export GDK_BACKEND=x11 - +if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then sudo apt-get update - sudo apt-get install clang git ladspa-sdk freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 juce-tools xvfb - - Xvfb :99 & - export DISPLAY=:99 - sleep 5 + sudo apt-get install clang git ninja-build ladspa-sdk freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 juce-tools xvfb fi # mac specific stuff -if [ $OS = "mac" ]; then +if [ "$(uname)" == "Darwin" ]; then # Create a temp keychain if [ -n "$GITHUB_ACTIONS" ]; then echo "Create a keychain" @@ -41,109 +35,84 @@ ROOT=$(cd "$(dirname "$0")/.."; pwd) cd "$ROOT" echo "$ROOT" +BRANCH=${GITHUB_REF##*/} +echo "$BRANCH" + cd "$ROOT/ci" rm -Rf bin mkdir bin -# Get the hash -cd "$ROOT/modules/juce" -HASH=`git rev-parse HEAD` -echo "Hash: $HASH" - -# Get the Projucer -cd "$ROOT/ci/bin" -while true -do - PROJUCER_URL=$(curl -s -S "https://projucer.rabien.com/get_projucer.php?hash=$HASH&os=$OS&key=$APIKEY") - echo "Response: $PROJUCER_URL" - if [[ $PROJUCER_URL == http* ]]; then - curl -s -S $PROJUCER_URL -o "$ROOT/ci/bin/Projucer.zip" - unzip Projucer.zip - break - fi - sleep 15 -done - -# Resave jucer file -if [ "$OS" = "mac" ]; then - "$ROOT/ci/bin/Projucer.app/Contents/MacOS/Projucer" --resave "$ROOT/plugin/$PLUGIN.jucer" -elif [ "$OS" = "linux" ]; then - "$ROOT/ci/bin/Projucer" --resave "$ROOT/plugin/$PLUGIN.jucer" -else - "$ROOT/ci/bin/Projucer.exe" --resave "$ROOT/plugin/$PLUGIN.jucer" -fi - # Build mac version -if [ "$OS" = "mac" ]; then - cd "$ROOT/plugin/Builds/MacOSX" - xcodebuild -configuration Release || exit 1 +if [ "$(uname)" == "Darwin" ]; then + cd "$ROOT" + cmake --preset xcode + cmake --build --preset xcode --config Release - cp -R ~/Library/Audio/Plug-Ins/VST/$PLUGIN.vst "$ROOT/ci/bin" - cp -R ~/Library/Audio/Plug-Ins/Components/$PLUGIN.component "$ROOT/ci/bin" + cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/AU/$PLUGIN.component" "$ROOT/ci/bin" + cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST/$PLUGIN.vst" "$ROOT/ci/bin" + cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin" cd "$ROOT/ci/bin" - for filename in ./*.vst; do - codesign -s "$DEV_APP_ID" -v "$filename" --options=runtime --timestamp - done - for filename in ./*.component; do - codesign -s "$DEV_APP_ID" -v "$filename" --options=runtime --timestamp - done - - # Build notarize tool - cd "$ROOT/modules/gin/tools/notarize" - "$ROOT/ci/bin/Projucer.app/Contents/MacOS/Projucer" --set-global-search-path osx defaultJuceModulePath "$ROOT/modules/juce/modules" - "$ROOT/ci/bin/Projucer.app/Contents/MacOS/Projucer" --resave "notarize.jucer" - cd Builds/MacOSX - xcodebuild -configuration Release || exit 1 - cd build/Release - cp notarize "$ROOT/ci/bin" + codesign -s "$DEV_APP_ID" -v $PLUGIN.vst --options=runtime --timestamp --force + codesign -s "$DEV_APP_ID" -v $PLUGIN.vst3 --options=runtime --timestamp --force + codesign -s "$DEV_APP_ID" -v $PLUGIN.component --options=runtime --timestamp --force # Notarize cd "$ROOT/ci/bin" - zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.component + zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.vst3 $PLUGIN.component - "$ROOT/ci/bin/notarize" -ns ${PLUGIN}_Mac.zip $APPLE_USER $APPLE_PASS com.figbug.$PLUGIN.vst + if [[ -n "$APPLE_USER" ]]; then + xcrun notarytool submit --verbose --apple-id "$APPLE_USER" --password "$APPLE_PASS" --team-id "3FS7DJDG38" --wait --timeout 30m ${PLUGIN}_Mac.zip + fi rm ${PLUGIN}_Mac.zip xcrun stapler staple $PLUGIN.vst + xcrun stapler staple $PLUGIN.vst3 xcrun stapler staple $PLUGIN.component - zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.component - - curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY" + zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.vst3 $PLUGIN.component + + if [ "$BRANCH" = "release" ]; then + curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY" + fi fi # Build linux version -if [ "$OS" = "linux" ]; then - cd "$ROOT/plugin/Builds/LinuxMakefile" - make CONFIG=Release +if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + cd "$ROOT" + + cmake --preset ninja-gcc + cmake --build --preset ninja-gcc --config Release - cd "$ROOT/plugin/Builds/LinuxMakefile" - cp ./build/$PLUGIN.so "$ROOT/ci/bin" + cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/LV2/$PLUGIN.lv2" "$ROOT/ci/bin" + cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST/lib$PLUGIN.so" "$ROOT/ci/bin/$PLUGIN.so" + cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin" cd "$ROOT/ci/bin" - rm -Rf ${PLUGIN}_Linux.zip - zip -r ${PLUGIN}_Linux.zip $PLUGIN.so - curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY" + # Upload + cd "$ROOT/ci/bin" + zip -r ${PLUGIN}_Linux.zip $PLUGIN.so $PLUGIN.vst3 $PLUGIN.lv2 + + if [ "$BRANCH" = "release" ]; then + curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY" + fi fi # Build Win version -if [ "$OS" = "win" ]; then - VS_WHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" - - MSBUILD_EXE=$("$VS_WHERE" -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe") - echo $MSBUILD_EXE +if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then + cd "$ROOT" - cd "$ROOT/plugin/Builds/VisualStudio2019" - "$MSBUILD_EXE" "$PLUGIN.sln" "//p:VisualStudioVersion=16.0" "//m" "//t:Build" "//p:Configuration=Release64" "//p:Platform=x64" "//p:PreferredToolArchitecture=x64" - "$MSBUILD_EXE" "$PLUGIN.sln" "//p:VisualStudioVersion=16.0" "//m" "//t:Build" "//p:Configuration=Release" "//p:PlatformTarget=x86" "//p:PreferredToolArchitecture=x64" + cmake --preset vs + cmake --build --preset vs --config Release cd "$ROOT/ci/bin" - cp "$ROOT/plugin/Builds/VisualStudio2019/x64/Release64/VST/${PLUGIN}.dll" . - cp "$ROOT/plugin/Builds/VisualStudio2019/Win32/Release/VST/${PLUGIN}_32b.dll" . + cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST/$PLUGIN.dll" "$ROOT/ci/bin" + cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin" - 7z a ${PLUGIN}_Win.zip ${PLUGIN}.dll ${PLUGIN}_32b.dll + 7z a ${PLUGIN}_Win.zip $PLUGIN.dll $PLUGIN.vst3 - curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY" -fi \ No newline at end of file + if [ "$BRANCH" = "release" ]; then + curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY" + fi +fi diff --git a/ci/config_cmake.sh b/ci/config_cmake.sh new file mode 100755 index 0000000..233babd --- /dev/null +++ b/ci/config_cmake.sh @@ -0,0 +1,16 @@ +#!/bin/bash -e + +ROOT=$(cd "$(dirname "$0")/.."; pwd) +cd "$ROOT" + +export PATH=$PATH:"/c/Program Files/CMake/bin" + +if [ "$(uname)" == "Darwin" ]; then + TOOLCHAIN="xcode" +elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + TOOLCHAIN="ninja-gcc" +elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then + TOOLCHAIN="vs" +fi + +cmake --preset $TOOLCHAIN -D BUILD_EXTRAS=OFF -D JUCE_COPY_PLUGIN_AFTER_BUILD=ON diff --git a/modules/gin b/modules/gin index d8ead26..170e6fb 160000 --- a/modules/gin +++ b/modules/gin @@ -1 +1 @@ -Subproject commit d8ead26e5775c0b11bd5067399d3a38beb7602b2 +Subproject commit 170e6fb30e8581fe0f5370fa23c7cab522c7aeb2 diff --git a/modules/juce b/modules/juce index 83b1436..2a27ebc 160000 --- a/modules/juce +++ b/modules/juce @@ -1 +1 @@ -Subproject commit 83b1436c6a21f82ffdc4125592836f21dbd7b1e7 +Subproject commit 2a27ebcfae7ca7f6eb62b29d5f002ceefdaadbdb diff --git a/plugin/Resources/Wavetables/WINDOW_S.WAV b/plugin/Resources/Wavetables/WINDOW_S.WAV deleted file mode 100644 index e6f7134ac3df7ee4ec70611d3318ee93388803ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32812 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 w<5P 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 DTa|*c diff --git a/plugin/Resources/placeholder.txt b/plugin/Resources/placeholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/plugin/Source/Boxes.h b/plugin/Source/Boxes.h deleted file mode 100644 index ec7bf19..0000000 --- a/plugin/Source/Boxes.h +++ /dev/null @@ -1,421 +0,0 @@ -#pragma once - -#include -#include "PluginProcessor.h" -#include "Cfg.h" - -//============================================================================== -class CommonBox : public gin::PagedControlBox -{ -public: - CommonBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_) - : gin::PagedControlBox (e), proc (proc_) - { - auto& g = proc.globalParams; - - addPage ("Main", 3, 2); - - addControl (0, new gin::Knob (g.level), 0, 0); - addControl (0, new gin::Switch (g.mono), 1, 0); - addControl (0, new gin::Select (g.glideMode), 2, 0); - - addControl (0, v = new gin::Knob (g.voices), 0, 1); - addControl (0, l = new gin::Switch (g.legato), 1, 1); - addControl (0, s = new gin::Knob (g.glideRate), 2, 1); - - watchParam (g.mono); - watchParam (g.glideMode); - - addPage ("Control", 1, 2); - - addControl (1, new gin::Switch (g.mpe), 0, 0); - } - - void paramChanged() override - { - gin::PagedControlBox::paramChanged(); - - auto& g = proc.globalParams; - v->setEnabled (! g.mono->isOn()); - l->setEnabled (g.glideMode->getProcValue() != 0.0f); - s->setEnabled (g.glideMode->getProcValue() != 0.0f); - } - - WavetableAudioProcessor& proc; - ParamComponentPtr v, l, s; -}; - -//============================================================================== -class OscillatorBox : public gin::PagedControlBox -{ -public: - OscillatorBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_) - : gin::PagedControlBox (e), proc (proc_) - { - for ( int i = 0; i < numElementsInArray (proc.wtParams); i++) - { - auto& wt = proc.wtParams[i]; - - addPage ("WT " + String (i + 1), 3, 4); - addPageEnable (i * 2, wt.enable); - - addControl (i * 2, new gin::Knob (wt.table), 0, 0); - addControl (i * 2, new gin::Knob (wt.tune, true), 1, 0); - addControl (i * 2, new gin::Knob (wt.finetune, true), 2, 0); - addControl (i * 2, new gin::Knob (wt.pan, true), 0, 1); - addControl (i * 2, new gin::Knob (wt.level), 1, 1); - - addPage ("Unison " + String (i + 1), 2, 4); - addControl (i * 2 + 1, new gin::Knob (wt.voices), 0, 0); - addControl (i * 2 + 1, trans[i] = new gin::Knob (wt.voicesTrns, true), 1, 0); - addControl (i * 2 + 1, detune[i] = new gin::Knob (wt.detune), 0, 1); - addControl (i * 2 + 1, spread[i] = new gin::Knob (wt.spread), 1, 1); - - watchParam (wt.voices); - } - - for ( int i = 0; i < numElementsInArray (proc.oscParams); i++) - { - auto& osc = proc.oscParams[i]; - - addPage ("OSC " + String (i + 1), 3, 4); - addPageEnable (Cfg::numWTs * 2 + i * 2, osc.enable); - - addControl (Cfg::numWTs * 2 + i * 2, new gin::Select (osc.wave), 0, 0); - addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.tune, true), 1, 0); - addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.finetune, true), 2, 0); - addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.pan, true), 0, 1); - addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.level), 1, 1); - addControl (Cfg::numWTs * 2 + i * 2, pw[Cfg::numWTs + i] = new gin::Knob (osc.pulsewidth), 2, 1); - - watchParam (osc.wave); - - addPage ("Unison " + String (i + 1), 2, 4); - addControl (Cfg::numWTs * 2 + i * 2 + 1, new gin::Knob (osc.voices), 0, 0); - addControl (Cfg::numWTs * 2 + i * 2 + 1, trans[Cfg::numWTs + i] = new gin::Knob (osc.voicesTrns, true), 1, 0); - addControl (Cfg::numWTs * 2 + i * 2 + 1, detune[Cfg::numWTs + i] = new gin::Knob (osc.detune), 0, 1); - addControl (Cfg::numWTs * 2 + i * 2 + 1, spread[Cfg::numWTs + i] = new gin::Knob (osc.spread), 1, 1); - - watchParam (osc.voices); - } - - setPageOpen (0, true); - setPageOpen (2, true); - setPageOpen (4, true); - } - - void paramChanged() override - { - gin::PagedControlBox::paramChanged(); - - for ( int i = 0; i < numElementsInArray (proc.wtParams); i++) - { - auto& wt = proc.oscParams[i]; - - trans[i]->setEnabled (wt.voices->getProcValue() > 1); - detune[i]->setEnabled (wt.voices->getProcValue() > 1); - spread[i]->setEnabled (wt.voices->getProcValue() > 1); - } - - for ( int i = 0; i < numElementsInArray (proc.oscParams); i++) - { - auto& osc = proc.oscParams[i]; - pw[Cfg::numWTs + i]->setEnabled ((gin::Wave) int (osc.wave->getProcValue()) == gin::Wave::pulse); - - trans[Cfg::numWTs + i]->setEnabled (osc.voices->getProcValue() > 1); - detune[Cfg::numWTs + i]->setEnabled (osc.voices->getProcValue() > 1); - spread[Cfg::numWTs + i]->setEnabled (osc.voices->getProcValue() > 1); - } - } - - WavetableAudioProcessor& proc; - ParamComponentPtr pw[Cfg::numOSCs + Cfg::numWTs], trans[Cfg::numOSCs + Cfg::numWTs], - detune[Cfg::numOSCs + Cfg::numWTs], spread[Cfg::numOSCs + Cfg::numWTs]; -}; - -//============================================================================== -class FilterAmpBox : public gin::PagedControlBox -{ -public: - FilterAmpBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_) - : gin::PagedControlBox (e), proc (proc_) - { - for (int i = 0; i < numElementsInArray (proc.filterParams); i++) - { - auto& flt = proc.filterParams[i]; - - addPage ("Filter " + String (i + 1), 8, 2); - addBottomButton (i, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcFilter[i], true)); - addPageEnable (i, flt.enable); - - addControl (i, new gin::Select (flt.type), 0, 0); - auto freq = new gin::Knob (flt.frequency); - addControl (i, freq, 1, 0); - addControl (i, new gin::Knob (flt.resonance), 2, 0); - - addControl (i, new gin::Knob (flt.keyTracking), 0, 1); - addControl (i, new gin::Knob (flt.amount, true), 1, 1); - addControl (i, v[i] = new gin::Knob (flt.velocityTracking), 2, 1); - - adsr[i] = new gin::ADSRComponent (); - adsr[i]->setParams (flt.attack, flt.decay, flt.sustain, flt.release); - addControl (i, adsr[i], 3, 0, 3, 2); - - addControl (i, a[i] = new gin::Knob (flt.attack), 6, 0); - addControl (i, d[i] = new gin::Knob (flt.decay), 7, 0); - addControl (i, s[i] = new gin::Knob (flt.sustain), 6, 1); - addControl (i, r[i] = new gin::Knob (flt.release), 7, 1); - - watchParam (flt.amount); - - freq->setLiveValuesCallback ([this, i] () - { - if (proc.filterParams[i].amount->getUserValue() != 0.0f || - proc.filterParams[i].keyTracking->getUserValue() != 0.0f || - proc.modMatrix.isModulated (gin::ModDstId (proc.filterParams[i].frequency->getModIndex()))) - return proc.getLiveFilterCutoff (i); - return Array(); - }); - - int n = numElementsInArray (proc.filterParams); - auto& env = proc.adsrParams; - - addPage ("ADSR", 8, 2); - addControl (n, new gin::Knob (env.attack), 0, 0); - addControl (n, new gin::Knob (env.decay), 1, 0); - addControl (n, new gin::Knob (env.sustain), 0, 1); - addControl (n, new gin::Knob (env.release), 1, 1); - addControl (n, new gin::Knob (env.velocityTracking), 2, 0); - - auto g = new gin::ADSRComponent (); - g->setParams (env.attack, env.decay, env.sustain, env.release); - addControl (n, g, 3, 0, 3, 2); - } - } - - void paramChanged () override - { - gin::PagedControlBox::paramChanged (); - - for ( int i = 0; i < numElementsInArray (proc.filterParams); i++) - { - auto& flt = proc.filterParams[i]; - v[i]->setEnabled (flt.amount->getUserValue() != 0.0f); - a[i]->setEnabled (flt.amount->getUserValue() != 0.0f); - d[i]->setEnabled (flt.amount->getUserValue() != 0.0f); - s[i]->setEnabled (flt.amount->getUserValue() != 0.0f); - r[i]->setEnabled (flt.amount->getUserValue() != 0.0f); - adsr[i]->setEnabled (flt.amount->getUserValue() != 0.0f); - } - } - - WavetableAudioProcessor& proc; - ParamComponentPtr v[Cfg::numFilters], a[Cfg::numFilters], d[Cfg::numFilters], s[Cfg::numFilters], r[Cfg::numFilters]; - gin::ADSRComponent* adsr[Cfg::numFilters]; -}; - -//============================================================================== -class ModulationBox : public gin::PagedControlBox -{ -public: - ModulationBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_) - : gin::PagedControlBox (e), proc (proc_) - { - int cnt = 0; - for (int i = 0; i < numElementsInArray (proc.lfoParams); i++, cnt++) - { - auto& lfo = proc.lfoParams[i]; - - addPage ("LFO" + String (i + 1), 5, 2); - addPageEnable (cnt, lfo.enable); - addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcLFO[i], true)); - addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcMonoLFO[i], false)); - - addControl (cnt, new gin::Select (lfo.wave), 0, 0); - addControl (cnt, new gin::Switch (lfo.sync), 1, 0); - addControl (cnt, r[i] = new gin::Knob (lfo.rate), 2, 0); - addControl (cnt, b[i] = new gin::Select (lfo.beat), 2, 0); - - addControl (cnt, new gin::Knob (lfo.depth, true), 0, 1); - addControl (cnt, new gin::Knob (lfo.phase, true), 1, 1); - addControl (cnt, new gin::Knob (lfo.offset, true), 2, 1); - addControl (cnt, new gin::Knob (lfo.fade, true), 3, 1); - addControl (cnt, new gin::Knob (lfo.delay), 4, 1); - - auto l = new gin::LFOComponent(); - l->setParams (lfo.wave, lfo.sync, lfo.rate, lfo.beat, lfo.depth, lfo.offset, lfo.phase, lfo.enable); - addControl (cnt, l, 3, 0, 2, 1); - - watchParam (lfo.sync); - } - - for (int i = 0; i < numElementsInArray (proc.envParams); i++, cnt++) - { - auto& env = proc.envParams[i]; - - addPage ("ENV" + String (i + 1), 5, 2); - addPageEnable (cnt, env.enable); - addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcEnv[i], true)); - - addControl (cnt, new gin::Knob (env.attack), 0, 0); - addControl (cnt, new gin::Knob (env.decay), 1, 0); - addControl (cnt, new gin::Knob (env.sustain), 0, 1); - addControl (cnt, new gin::Knob (env.release), 1, 1); - - auto adsr = new gin::ADSRComponent (); - adsr->setParams (env.attack, env.decay, env.sustain, env.release); - addControl (cnt, adsr, 2, 0, 3, 2); - } - - auto& stp = proc.stepLfoParams; - addPage ("Step", 6, 2); - addPageEnable (cnt, stp.enable); - addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcStep, true)); - addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcMonoStep, false)); - - addControl (cnt, new gin::Select (stp.beat), 0, 0); - addControl (cnt, new gin::Knob (stp.length), 0, 1); - - auto s = new gin::StepLFOComponent(); - s->setParams (stp.beat, stp.length, stp.level, stp.enable); - addControl (cnt, s, 1, 0, 5, 2); - cnt++; - - addPage ("All", 5, 2); - addControl (cnt, new gin::ModSrcListBox (proc.modMatrix), 0, 0, 5, 2); - cnt++; - - addPage ("Mod Matrix", 5, 2); - addControl (cnt, new gin::ModMatrixBox (proc, proc.modMatrix), 0, 0, 5, 2); - cnt++; - } - - void paramChanged () override - { - gin::PagedControlBox::paramChanged (); - - for (int i = 0; i < numElementsInArray (proc.lfoParams); i++) - { - auto& lfo = proc.lfoParams[i]; - r[i]->setVisible (! lfo.sync->isOn()); - b[i]->setVisible (lfo.sync->isOn()); - } - } - - WavetableAudioProcessor& proc; - ParamComponentPtr r[Cfg::numLFOs], b[Cfg::numLFOs]; -}; - -//============================================================================== -class EffectsBox : public gin::PagedControlBox -{ -public: - EffectsBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_) - : gin::PagedControlBox (e), proc (proc_) - { - int idx = 0; - - addPage ("Gate", 8, 2); - addPageEnable (idx, proc.gateParams.enable); - addControl (idx, new gin::Select (proc.gateParams.beat), 0, 0); - addControl (idx, new gin::Knob (proc.gateParams.length), 1, 0); - addControl (idx, new gin::Knob (proc.gateParams.attack), 0, 1); - addControl (idx, new gin::Knob (proc.gateParams.release), 1, 1); - auto g = new gin::GateEffectComponent (); - g->setParams (proc.gateParams.length, proc.gateParams.l, proc.gateParams.r, proc.gateParams.enable); - addControl (idx, g, 2, 0, 6, 2); - idx++; - - addPage ("Chorus", 3, 2); - addPageEnable (idx, proc.chorusParams.enable); - addControl (idx, new gin::Knob (proc.chorusParams.delay), 0, 0); - addControl (idx, new gin::Knob (proc.chorusParams.rate), 1, 0); - addControl (idx, new gin::Knob (proc.chorusParams.depth), 2, 0); - addControl (idx, new gin::Knob (proc.chorusParams.width), 0, 1); - addControl (idx, new gin::Knob (proc.chorusParams.mix), 1, 1); - idx++; - - addPage ("Distortion", 2, 2); - addPageEnable (idx, proc.distortionParams.enable); - addControl (idx, new gin::Knob (proc.distortionParams.amount), 0, 0); - addControl (idx, new gin::Knob (proc.distortionParams.highpass), 1, 0); - addControl (idx, new gin::Knob (proc.distortionParams.output), 0, 1); - addControl (idx, new gin::Knob (proc.distortionParams.mix), 1, 1); - idx++; - - addPage ("EQ", 6, 2); - addPageEnable (idx, proc.eqParams.enable); - addControl (idx, new gin::Knob (proc.eqParams.loFreq), 0, 0); - addControl (idx, new gin::Knob (proc.eqParams.loGain), 1, 0); - addControl (idx, new gin::Knob (proc.eqParams.loQ), 2, 0); - addControl (idx, new gin::Knob (proc.eqParams.mid1Freq), 3, 0); - addControl (idx, new gin::Knob (proc.eqParams.mid1Gain), 4, 0); - addControl (idx, new gin::Knob (proc.eqParams.mid1Q), 5, 0); - addControl (idx, new gin::Knob (proc.eqParams.mid2Freq), 0, 1); - addControl (idx, new gin::Knob (proc.eqParams.mid2Gain), 1, 1); - addControl (idx, new gin::Knob (proc.eqParams.mid2Q), 2, 1); - addControl (idx, new gin::Knob (proc.eqParams.hiFreq), 3, 1); - addControl (idx, new gin::Knob (proc.eqParams.hiGain), 4, 1); - addControl (idx, new gin::Knob (proc.eqParams.hiQ), 5, 1); - idx++; - - addPage ("Comp", 3, 2); - addPageEnable (idx, proc.compressorParams.enable); - addControl (idx, new gin::Knob (proc.compressorParams.attack), 0, 0); - addControl (idx, new gin::Knob (proc.compressorParams.release), 1, 0); - addControl (idx, new gin::Knob (proc.compressorParams.ratio), 2, 0); - addControl (idx, new gin::Knob (proc.compressorParams.threshold), 0, 1); - addControl (idx, new gin::Knob (proc.compressorParams.gain), 1, 1); - idx++; - - addPage ("Delay", 3, 2); - addPageEnable (idx, proc.delayParams.enable); - addControl (idx, new gin::Switch (proc.delayParams.sync), 0, 0); - addControl (idx, t = new gin::Knob (proc.delayParams.time), 1, 0); - addControl (idx, b = new gin::Select (proc.delayParams.beat), 1, 0); - addControl (idx, new gin::Knob (proc.delayParams.fb), 2, 0); - addControl (idx, new gin::Knob (proc.delayParams.cf), 1, 1); - addControl (idx, new gin::Knob (proc.delayParams.mix), 2, 1); - idx++; - - watchParam (proc.delayParams.sync); - - addPage ("Reverb", 3, 2); - addPageEnable (idx, proc.reverbParams.enable); - addControl (idx, new gin::Knob (proc.reverbParams.damping), 0, 0); - addControl (idx, new gin::Knob (proc.reverbParams.freezeMode), 1, 0); - addControl (idx, new gin::Knob (proc.reverbParams.roomSize), 2, 0); - addControl (idx, new gin::Knob (proc.reverbParams.width), 0, 1); - addControl (idx, new gin::Knob (proc.reverbParams.mix), 1, 1); - idx++; - - addPage ("Limiter", 2, 2); - addPageEnable (idx, proc.limiterParams.enable); - addControl (idx, new gin::Knob (proc.limiterParams.attack), 0, 0); - addControl (idx, new gin::Knob (proc.limiterParams.release), 1, 0); - addControl (idx, new gin::Knob (proc.limiterParams.threshold), 0, 1); - addControl (idx, new gin::Knob (proc.limiterParams.gain), 1, 1); - idx++; - - addPage ("Scope", 8, 2); - auto scope = new gin::TriggeredScope (proc.fifo); - scope->setNumChannels (2); - scope->setTriggerMode (gin::TriggeredScope::TriggerMode::Up); - scope->setColour (gin::TriggeredScope::lineColourId, Colours::transparentBlack); - addControl (idx, scope, 0, 0, 8, 2); - idx++; - - setPageOpen (0, false); - } - - void paramChanged () override - { - gin::PagedControlBox::paramChanged (); - - t->setVisible (! proc.delayParams.sync->isOn()); - b->setVisible (proc.delayParams.sync->isOn()); - } - - WavetableAudioProcessor& proc; - ParamComponentPtr t, b; -}; diff --git a/plugin/Source/Cfg.h b/plugin/Source/Cfg.h index d4a413c..1343c51 100644 --- a/plugin/Source/Cfg.h +++ b/plugin/Source/Cfg.h @@ -2,9 +2,8 @@ namespace Cfg { - constexpr static int numWTs = 2; - constexpr static int numOSCs = 1; - constexpr static int numFilters = 1; + constexpr static int numOSCs = 4; + constexpr static int numFilters = 2; constexpr static int numENVs = 3; constexpr static int numLFOs = 3; } diff --git a/plugin/Source/Panels.h b/plugin/Source/Panels.h new file mode 100644 index 0000000..192b470 --- /dev/null +++ b/plugin/Source/Panels.h @@ -0,0 +1,435 @@ +#pragma once + +#include +#include "PluginProcessor.h" +#include "Cfg.h" + +//============================================================================== +class OscillatorBox : public gin::ParamBox +{ +public: + OscillatorBox (const juce::String& name, WavetableAudioProcessor& proc_, int idx_) + : gin::ParamBox (name), proc (proc_), idx (idx_) + { + auto& osc = proc.oscParams[idx]; + + addEnable (osc.enable); + + addControl (new gin::Select (osc.wave), 0, 0); + addControl (new gin::Knob (osc.tune, true), 1, 0); + addControl (new gin::Select (osc.voices), 2, 0); + addControl (detune = new gin::Knob (osc.detune), 3, 0); + + addControl (pw = new gin::Knob (osc.pulsewidth), 0, 1); + addControl (new gin::Knob (osc.finetune, true), 1, 1); + addControl (spread = new gin::Knob (osc.spread), 2, 1); + addControl (trans = new gin::Knob (osc.voicesTrns, true), 3, 1); + + watchParam (osc.wave); + watchParam (osc.voices); + } + + void paramChanged() override + { + gin::ParamBox::paramChanged(); + + auto& osc = proc.oscParams[idx]; + pw->setEnabled ((gin::Wave) int (osc.wave->getProcValue()) == gin::Wave::pulse); + + trans->setEnabled (osc.voices->getProcValue() > 1); + detune->setEnabled (osc.voices->getProcValue() > 1); + spread->setEnabled (osc.voices->getProcValue() > 1); + } + + WavetableAudioProcessor& proc; + int idx = 0; + gin::ParamComponent::Ptr pw, trans, detune, spread; +}; + +//============================================================================== +class FilterBox : public gin::ParamBox +{ +public: + FilterBox (const juce::String& name, WavetableAudioProcessor& proc_, int idx_) + : gin::ParamBox (name), proc (proc_), idx (idx_) + { + auto& flt = proc.filterParams[idx]; + + addEnable (flt.enable); + + auto freq = new gin::Knob (flt.frequency); + addControl (freq, 0, 0); + addControl (new gin::Knob (flt.resonance), 1, 0); + addControl (new gin::Knob (flt.amount, true), 2, 0); + + addControl (new gin::Knob (flt.keyTracking), 0, 1); + addControl (new gin::Select (flt.type), 1, 1); + addControl (v = new gin::Knob (flt.velocityTracking), 2, 1); + + freq->setLiveValuesCallback ([this] () + { + if (proc.filterParams[idx].amount->getUserValue() != 0.0f || + proc.filterParams[idx].keyTracking->getUserValue() != 0.0f || + proc.modMatrix.isModulated (gin::ModDstId (proc.filterParams[idx].frequency->getModIndex()))) + return proc.getLiveFilterCutoff (idx); + return juce::Array(); + }); + } + + void paramChanged () override + { + gin::ParamBox::paramChanged (); + + auto& flt = proc.filterParams[idx]; + v->setEnabled (flt.amount->getUserValue() != 0.0f); + } + + WavetableAudioProcessor& proc; + int idx = 0; + gin::ParamComponent::Ptr v; +}; + +//============================================================================== +class FilterADSRArea : public gin::ParamArea +{ +public: + FilterADSRArea (WavetableAudioProcessor& proc_, int idx_) + : proc (proc_), idx (idx_) + { + auto& flt = proc.filterParams[idx]; + + adsr = new gin::ADSRComponent (); + adsr->setParams (flt.attack, flt.decay, flt.sustain, flt.release); + addControl (adsr); + + addControl (a = new gin::Knob (flt.attack)); + addControl (d = new gin::Knob (flt.decay)); + addControl (s = new gin::Knob (flt.sustain)); + addControl (r = new gin::Knob (flt.release)); + + watchParam (flt.amount); + } + + void paramChanged() override + { + gin::ParamArea::paramChanged (); + + auto& flt = proc.filterParams[idx]; + a->setEnabled (flt.amount->getUserValue() != 0.0f); + d->setEnabled (flt.amount->getUserValue() != 0.0f); + s->setEnabled (flt.amount->getUserValue() != 0.0f); + r->setEnabled (flt.amount->getUserValue() != 0.0f); + adsr->setEnabled (flt.amount->getUserValue() != 0.0f); + } + + WavetableAudioProcessor& proc; + int idx; + gin::ParamComponent::Ptr a, d, s, r; + gin::ADSRComponent* adsr; +}; + +//============================================================================== +class MixBox : public gin::ParamBox +{ +public: + MixBox (const juce::String& name, WavetableAudioProcessor& proc_) + : gin::ParamBox (name), proc (proc_) + { + } + + void paramChanged () override + { + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class LFOBox : public gin::ParamBox +{ +public: + LFOBox (const juce::String& name, WavetableAudioProcessor& proc_, int idx_) + : gin::ParamBox (name), proc (proc_), idx (idx_) + { + auto& lfo = proc.lfoParams[idx]; + + addEnable (lfo.enable); + + addControl (r = new gin::Knob (lfo.rate), 0, 0); + addControl (b = new gin::Select (lfo.beat), 0, 0); + addControl (new gin::Knob (lfo.depth, true), 1, 0); + addControl (new gin::Knob (lfo.fade, true), 0, 1); + addControl (new gin::Knob (lfo.delay), 1, 1); + + watchParam (lfo.sync); + + setSize (112, 163); + } + + void paramChanged () override + { + gin::ParamBox::paramChanged (); + + auto& lfo = proc.lfoParams[idx]; + r->setVisible (! lfo.sync->isOn()); + b->setVisible (lfo.sync->isOn()); + } + + WavetableAudioProcessor& proc; + int idx; + gin::ParamComponent::Ptr r, b; +}; + +//============================================================================== +class LFOArea : public gin::ParamArea +{ +public: + LFOArea (WavetableAudioProcessor& proc_, int idx_) + : proc (proc_), idx (idx_) + { + auto& lfo = proc.lfoParams[idx]; + + addControl (new gin::Select (lfo.wave)); + addControl (new gin::Switch (lfo.sync)); + addControl (new gin::Knob (lfo.phase, true)); + addControl (new gin::Knob (lfo.offset, true)); + + auto l = new gin::LFOComponent(); + l->setParams (lfo.wave, lfo.sync, lfo.rate, lfo.beat, lfo.depth, lfo.offset, lfo.phase, lfo.enable); + addControl (l); + + setSize (186, 163); + } + + void paramChanged () override + { + gin::ParamArea::paramChanged (); + } + + WavetableAudioProcessor& proc; + int idx; +}; + +//============================================================================== +class GateBox : public gin::ParamBox +{ +public: + GateBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("Gate"), proc (proc_) + { + addControl (new gin::Select (proc.gateParams.beat), 0, 0); + addControl (new gin::Knob (proc.gateParams.length), 1, 0); + addControl (new gin::Knob (proc.gateParams.attack), 0, 1); + addControl (new gin::Knob (proc.gateParams.release), 1, 1); + + setSize (112, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class GateArea : public gin::ParamArea +{ +public: + GateArea (WavetableAudioProcessor& proc_) + : gin::ParamArea ("Pattern"), proc (proc_) + { + g = new gin::GateEffectComponent(); + g->setParams (proc.gateParams.length, proc.gateParams.l, proc.gateParams.r, proc.gateParams.enable); + addControl (g); + + setSize (272, 163); + } + + void paramChanged () override + { + gin::ParamArea::paramChanged (); + } + + void resized() override + { + g->setBounds (getLocalBounds().withSizeKeepingCentre (getWidth(), 128)); + } + + WavetableAudioProcessor& proc; + gin::GateEffectComponent* g; +}; + +//============================================================================== +class ChorusBox : public gin::ParamBox +{ +public: + ChorusBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("Chorus"), proc (proc_) + { + addControl (new gin::Knob (proc.chorusParams.delay), 0, 0); + addControl (new gin::Knob (proc.chorusParams.rate), 1, 0); + addControl (new gin::Knob (proc.chorusParams.mix), 2, 0); + + addControl (new gin::Knob (proc.chorusParams.depth), 0.5f, 1.0f); + addControl (new gin::Knob (proc.chorusParams.width), 1.5f, 1.0f); + + setSize (168, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class DistortBox : public gin::ParamBox +{ +public: + DistortBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("Distort"), proc (proc_) + { + addControl (new gin::Knob (proc.distortionParams.amount), 0, 0); + addControl (new gin::Knob (proc.distortionParams.highpass), 1, 0); + addControl (new gin::Knob (proc.distortionParams.output), 0, 1); + addControl (new gin::Knob (proc.distortionParams.mix), 1, 1); + + setSize (112, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class EQBox : public gin::ParamBox +{ +public: + EQBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("EQ"), proc (proc_) + { + addControl (new gin::Knob (proc.eqParams.loFreq), 0, 0); + addControl (new gin::Knob (proc.eqParams.loGain), 1, 0); + addControl (new gin::Knob (proc.eqParams.loQ), 2, 0); + addControl (new gin::Knob (proc.eqParams.mid1Freq), 3, 0); + addControl (new gin::Knob (proc.eqParams.mid1Gain), 4, 0); + addControl (new gin::Knob (proc.eqParams.mid1Q), 5, 0); + addControl (new gin::Knob (proc.eqParams.mid2Freq), 0, 1); + addControl (new gin::Knob (proc.eqParams.mid2Gain), 1, 1); + addControl (new gin::Knob (proc.eqParams.mid2Q), 2, 1); + addControl (new gin::Knob (proc.eqParams.hiFreq), 3, 1); + addControl (new gin::Knob (proc.eqParams.hiGain), 4, 1); + addControl (new gin::Knob (proc.eqParams.hiQ), 5, 1); + + setSize (308, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class CompressBox : public gin::ParamBox +{ +public: + CompressBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("Compress"), proc (proc_) + { + addControl (new gin::Knob (proc.compressorParams.ratio), 0, 0); + addControl (new gin::Knob (proc.compressorParams.threshold), 1, 0); + addControl (new gin::Knob (proc.compressorParams.gain), 2, 0); + + addControl (new gin::Knob (proc.compressorParams.attack), 0.5f, 1.0f); + addControl (new gin::Knob (proc.compressorParams.release), 1.5f, 1.0f); + + setSize (168, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class DelayBox : public gin::ParamBox +{ +public: + DelayBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("Delay"), proc (proc_) + { + addControl (t = new gin::Knob (proc.delayParams.time), 0, 0); + addControl (b = new gin::Select (proc.delayParams.beat), 0, 0); + addControl (new gin::Knob (proc.delayParams.fb), 1, 0); + addControl (new gin::Knob (proc.delayParams.cf), 2, 0); + + addControl (new gin::Switch (proc.delayParams.sync), 0, 1); + addControl (new gin::Knob (proc.delayParams.mix), 1.5f, 1.0f); + + watchParam (proc.delayParams.sync); + + setSize (168, 163); + } + + void paramChanged () override + { + gin::ParamBox::paramChanged(); + + t->setVisible (! proc.delayParams.sync->isOn()); + b->setVisible (proc.delayParams.sync->isOn()); + } + + WavetableAudioProcessor& proc; + gin::ParamComponent::Ptr t, b; +}; + +//============================================================================== +class ReverbBox : public gin::ParamBox +{ +public: + ReverbBox (WavetableAudioProcessor& proc_) + : gin::ParamBox ("Reverb"), proc (proc_) + { + addControl (new gin::Knob (proc.reverbParams.damping), 0, 0); + addControl (new gin::Knob (proc.reverbParams.freezeMode), 1, 0); + addControl (new gin::Knob (proc.reverbParams.roomSize), 2, 0); + addControl (new gin::Knob (proc.reverbParams.width), 0.5f, 1.0f); + addControl (new gin::Knob (proc.reverbParams.mix), 1.5f, 1.0f); + + setSize (168, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class LimitBox : public gin::ParamBox +{ +public: + LimitBox (WavetableAudioProcessor& proc_) + : gin::ParamBox (""), proc (proc_) + { + addControl (new gin::Knob (proc.limiterParams.attack), 0, 0); + addControl (new gin::Knob (proc.limiterParams.release), 1, 0); + addControl (new gin::Knob (proc.limiterParams.threshold), 0, 1); + addControl (new gin::Knob (proc.limiterParams.gain), 1, 1); + + setSize (112, 163); + } + + WavetableAudioProcessor& proc; +}; + +//============================================================================== +class ScopeArea : public gin::ParamArea +{ +public: + ScopeArea (WavetableAudioProcessor& proc_) + : gin::ParamArea ("Scope"), proc (proc_) + { + scope = new gin::TriggeredScope (proc.fifo); + scope->setNumChannels (2); + scope->setTriggerMode (gin::TriggeredScope::TriggerMode::Up); + scope->setColour (gin::TriggeredScope::lineColourId, juce::Colours::transparentBlack); + addControl (scope); + + setSize (272, 163); + } + + void resized() override + { + scope->setBounds (getLocalBounds().withSizeKeepingCentre (getWidth(), 140)); + } + + WavetableAudioProcessor& proc; + gin::TriggeredScope* scope; +}; diff --git a/plugin/Source/PluginEditor.cpp b/plugin/Source/PluginEditor.cpp index 33e4f03..0e37624 100644 --- a/plugin/Source/PluginEditor.cpp +++ b/plugin/Source/PluginEditor.cpp @@ -1,28 +1,13 @@ #include "PluginProcessor.h" #include "PluginEditor.h" -using namespace gin; - //============================================================================== WavetableAudioProcessorEditor::WavetableAudioProcessorEditor (WavetableAudioProcessor& p) - : ProcessorEditor (p, 50, 50 + 15), proc (p) + : ProcessorEditor (p), vaProc (p) { - oscHeaders.addChildComponent (modOverview); + addAndMakeVisible (editor); - gin::addAndMakeVisible (*this, { &commonHeader, &common, &unisonHeader }); - - gin::addAndMakeVisible (*this, { &oscHeaders }); - gin::addAndMakeVisible (*this, { &oscillators }); - - gin::addAndMakeVisible (*this, { &FiltersHeader }); - gin::addAndMakeVisible (*this, { &Filters }); - - gin::addAndMakeVisible (*this, { &modulationHeader, &modulation }); - gin::addAndMakeVisible (*this, { &effectsHeader, &effects }); - - oscHeaders.addAndMakeVisible ( usage ); - - setGridSize (14, 8, 0, 3 * 25); + setSize (901, 753); } WavetableAudioProcessorEditor::~WavetableAudioProcessorEditor() @@ -30,59 +15,21 @@ WavetableAudioProcessorEditor::~WavetableAudioProcessorEditor() } //============================================================================== -void WavetableAudioProcessorEditor::paint (Graphics& g) +void WavetableAudioProcessorEditor::paint (juce::Graphics& g) { ProcessorEditor::paint (g); - g.setColour (Colours::white.withAlpha (0.2f)); + titleBar.setShowBrowser (true); - auto rc = getFullGridArea(); - g.drawRect (rc.expanded (1)); + g.fillAll (findColour (gin::PluginLookAndFeel::blackColourId)); } void WavetableAudioProcessorEditor::resized() { ProcessorEditor::resized (); - auto rc = getFullGridArea(); + auto rc = getLocalBounds().reduced (1); + rc.removeFromTop (40); - int hh = 25; - - int gx = getGridWidth(); - int gy = getGridHeight(); - - // Oscillators - { - auto rHeaders = rc.removeFromTop (hh); - oscHeaders.setBounds (rHeaders.removeFromLeft (gx * 14)); - - auto rOscs = rc.removeFromTop (gy * 4); - oscillators.setBounds (rOscs.removeFromLeft (gx * 14)); - - modOverview.setBounds (4, 4, 200, hh - 8); - - usage.setBounds ( oscHeaders.getLocalBounds().removeFromRight (14 * 5 + 2).withSizeKeepingCentre (14 * 5 + 2, 16)); - } - - // ADSR and mod - { - auto rHeaders = rc.removeFromTop (hh); - ampFiltersHeader.setBounds (rHeaders.removeFromLeft (gx * 6)); - modulationHeader.setBounds (rHeaders.removeFromLeft (gx * 8)); - - auto rFilters = rc.removeFromTop (gy * 2); - ampFilters.setBounds (rFilters.removeFromLeft (gx * 6)); - modulation.setBounds (rFilters.removeFromLeft (gx * 8)); - } - - // Effects & Common - { - auto rHeaders = rc.removeFromTop (hh); - effectsHeader.setBounds (rHeaders.removeFromLeft (gx * 10)); - commonHeader.setBounds (rHeaders.removeFromLeft (gx * 4)); - - auto rControls = rc.removeFromTop (gy * 2); - effects.setBounds (rControls.removeFromLeft (gx * 10)); - common.setBounds (rControls.removeFromLeft (gx * 4)); - } + editor.setBounds (rc); } diff --git a/plugin/Source/PluginEditor.h b/plugin/Source/PluginEditor.h index 4546bbb..da47090 100644 --- a/plugin/Source/PluginEditor.h +++ b/plugin/Source/PluginEditor.h @@ -2,7 +2,153 @@ #include #include "PluginProcessor.h" -#include "Boxes.h" +#include "Panels.h" + +//============================================================================== +class Editor : public juce::Component +{ +public: + Editor (WavetableAudioProcessor& proc_) + : proc ( proc_ ) + { + for (auto& o : oscillators) addAndMakeVisible (o); + for (auto& f : filters) addAndMakeVisible (f); + for (auto& a : fltADSR) addAndMakeVisible (a); + for (auto& l : lfos) addAndMakeVisible (l); + for (auto& l : lfoGraphs) addAndMakeVisible (l); + + addAndMakeVisible (mix); + + for (auto& i : modItems) + modHeader.addItem (i); + + addAndMakeVisible (modHeader); + + for (int i = 0; i < Cfg::numLFOs; i++) + { + lfoBox.addBox (i, &lfos[i]); + lfoBox.addBox (i, &lfoGraphs[i]); + } + addAndMakeVisible (lfoBox); + lfoBox.setPage (0); + + for (auto& i : fxItems) + fxHeader.addItem (i); + + addAndMakeVisible (fxHeader); + + effects.addBox (&gate); + effects.addBox (&pattern); + effects.addBox (&chorus); + effects.addBox (&distort); + effects.addBox (&eq); + effects.addBox (&compress); + effects.addBox (&delay); + effects.addBox (&reverb); + effects.addBox (&limit); + effects.addBox (&scope); + + addAndMakeVisible (effects); + + setupCallbacks(); + } + + void setupCallbacks() + { + // LFO mod items + for (int i = 0; i < Cfg::numLFOs; i++) + { + modItems[i].onClick = [this, i] + { + modItems[0].setSelected (i == 0); + modItems[1].setSelected (i == 1); + modItems[2].setSelected (i == 2); + + lfoBox.setPage (i); + }; + } + modItems[0].onClick(); + } + + void resized() override + { + auto rc = getLocalBounds(); + + auto rcOsc = rc.removeFromTop (163); + for (auto& o : oscillators) { o.setBounds (rcOsc.removeFromLeft (224)); rcOsc.removeFromLeft (1); }; + + rc.removeFromTop (1); + + auto rcFlt = rc.removeFromTop (163); + filters[0].setBounds (rcFlt.removeFromLeft (168)); rcFlt.removeFromLeft (1); + fltADSR[0].setBounds (rcFlt.removeFromLeft (186)); rcFlt.removeFromLeft (1); + mix.setBounds (rcFlt.removeFromLeft (187)); rcFlt.removeFromLeft (1); + fltADSR[1].setBounds (rcFlt.removeFromLeft (186)); rcFlt.removeFromLeft (1); + filters[1].setBounds (rcFlt.removeFromLeft (168)); rcFlt.removeFromLeft (1); + + auto rcB1 = rc.removeFromTop (26); + modHeader.setBounds (rcB1); + + auto rcMod = rc.removeFromTop (163); + lfoBox.setBounds (rcMod); + + auto rcB2 = rc.removeFromTop (26); + fxHeader.setBounds (rcB2); + + auto rcFX = rc.removeFromTop (163); + effects.setBounds (rcFX); + } + + WavetableAudioProcessor& proc; + + OscillatorBox oscillators[Cfg::numOSCs] { { "oscillator 1", proc, 0 }, { "oscillator 2", proc, 1 }, + { "oscillator 3", proc, 2 }, { "oscillator 4", proc, 3 } }; + + FilterBox filters[Cfg::numFilters] { { "filter 1", proc, 0 }, { "filter 2", proc, 1 } }; + + FilterADSRArea fltADSR[Cfg::numFilters] { { proc, 0 }, { proc, 1 } }; + + MixBox mix { "osc mix", proc }; + + LFOBox lfos[Cfg::numLFOs] { { "LFO 1", proc, 0 }, { "LFO 2", proc, 1 }, { "LFO 3", proc, 2 } }; + LFOArea lfoGraphs[Cfg::numLFOs] { { proc, 0 }, { proc, 1 }, { proc, 2 } }; + + gin::HeaderItem modItems[8] { { "LFO 1", proc.lfoParams[0].enable, proc.modMatrix, proc.modSrcMonoLFO[0], proc.modSrcLFO[0] }, + { "LFO 2", proc.lfoParams[1].enable, proc.modMatrix, proc.modSrcMonoLFO[1], proc.modSrcLFO[1] }, + { "LFO 3", proc.lfoParams[2].enable, proc.modMatrix, proc.modSrcMonoLFO[2], proc.modSrcLFO[2] }, + { "ENV 1", proc.envParams[0].enable, proc.modMatrix, {}, proc.modSrcEnv[0] }, + { "ENV 2", proc.envParams[1].enable, proc.modMatrix, {}, proc.modSrcEnv[1] }, + { "ENV 3", proc.envParams[2].enable, proc.modMatrix, {}, proc.modSrcEnv[2] }, + { "STEP", proc.stepLfoParams.enable, proc.modMatrix, proc.modSrcMonoStep, proc.modSrcStep }, + { "MIDI", nullptr } }; + gin::HeaderRow modHeader; + + gin::HeaderItem fxItems[8] { { "GATE", proc.gateParams.enable }, + { "CHORUS", proc.chorusParams.enable }, + { "DISTORT", proc.distortionParams.enable }, + { "EQ", proc.eqParams.enable }, + { "COMPRESS", proc.compressorParams.enable }, + { "DELAY", proc.delayParams.enable }, + { "REVERB", proc.reverbParams.enable }, + { "LIMIT", proc.limiterParams.enable } }; + + gin::HeaderRow fxHeader; + + + GateBox gate { proc }; + GateArea pattern { proc }; + ChorusBox chorus { proc }; + DistortBox distort { proc }; + EQBox eq { proc }; + CompressBox compress { proc }; + DelayBox delay { proc }; + ReverbBox reverb { proc }; + LimitBox limit { proc }; + ScopeArea scope { proc }; + + gin::BoxArea lfoBox; + gin::BoxArea effects; +}; //============================================================================== class WavetableAudioProcessorEditor : public gin::ProcessorEditor @@ -12,32 +158,13 @@ public: ~WavetableAudioProcessorEditor() override; //============================================================================== - void paint (Graphics&) override; + void paint (juce::Graphics&) override; void resized() override; private: - WavetableAudioProcessor& proc; + WavetableAudioProcessor& vaProc; - gin::ModulationOverview modOverview { proc.modMatrix }; - - gin::ControlHeader commonHeader { "Common" }; - gin::ControlHeader unisonHeader { "Unison" }; - - CommonBox common { *this, proc }; - - gin::ControlHeader oscHeaders = { "Oscillators" }; - OscillatorBox oscillators = { *this, proc }; - - gin::ControlHeader ampFiltersHeader { "Filter / ADSR" }; - FilterAmpBox ampFilters { *this, proc }; - - gin::ControlHeader modulationHeader { "Modulation" }; - ModulationBox modulation { *this, proc }; - - gin::ControlHeader effectsHeader { "Effects" }; - EffectsBox effects { *this, proc }; - - gin::SynthesiserUsage usage { proc }; + Editor editor { vaProc }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavetableAudioProcessorEditor) }; diff --git a/plugin/Source/PluginProcessor.cpp b/plugin/Source/PluginProcessor.cpp index fc01145..7f53906 100644 --- a/plugin/Source/PluginProcessor.cpp +++ b/plugin/Source/PluginProcessor.cpp @@ -2,69 +2,69 @@ #include "PluginEditor.h" #include "WavetableVoice.h" -using namespace gin; - -static String waveTextFunction (const Parameter&, float v) +static juce::String waveTextFunction (const gin::Parameter&, float v) { - switch ((Wave)int (v)) + switch ((gin::Wave)int (v)) { - case Wave::sine: return "Sine"; - case Wave::triangle: return "Triangle"; - case Wave::sawUp: return "Saw (Up)"; - case Wave::sawDown: return "Saw (Down)"; - case Wave::pulse: return "Pulse"; - case Wave::square: return "Square"; - case Wave::noise: return "Noise"; + case gin::Wave::silence: return "Off"; + case gin::Wave::sine: return "Sine"; + case gin::Wave::triangle: return "Triangle"; + case gin::Wave::sawUp: return "Saw (Up)"; + case gin::Wave::sawDown: return "Saw (Down)"; + case gin::Wave::pulse: return "Pulse"; + case gin::Wave::square: return "Square"; + case gin::Wave::noise: return "Noise"; + case gin::Wave::wavetable: default: jassertfalse; return {}; } } -static String lfoTextFunction (const Parameter&, float v) +static juce::String lfoTextFunction (const gin::Parameter&, float v) { - switch ((LFO::WaveShape)int (v)) + switch ((gin::LFO::WaveShape)int (v)) { - case LFO::WaveShape::none: return "None"; - case LFO::WaveShape::sine: return "Sine"; - case LFO::WaveShape::triangle: return "Triangle"; - case LFO::WaveShape::sawUp: return "Saw Up"; - case LFO::WaveShape::sawDown: return "Saw Down"; - case LFO::WaveShape::square: return "Square"; - case LFO::WaveShape::squarePos: return "Square+"; - case LFO::WaveShape::sampleAndHold: return "S&H"; - case LFO::WaveShape::noise: return "Noise"; - case LFO::WaveShape::stepUp3: return "Step Up 3"; - case LFO::WaveShape::stepUp4: return "Step Up 4"; - case LFO::WaveShape::stepup8: return "Step Up 8"; - case LFO::WaveShape::stepDown3: return "Step Down 3"; - case LFO::WaveShape::stepDown4: return "Step Down 4"; - case LFO::WaveShape::stepDown8: return "Step Down 8"; - case LFO::WaveShape::pyramid3: return "Pyramid 3"; - case LFO::WaveShape::pyramid5: return "Pyramid 5"; - case LFO::WaveShape::pyramid9: return "Pyramid 9"; + case gin::LFO::WaveShape::none: return "None"; + case gin::LFO::WaveShape::sine: return "Sine"; + case gin::LFO::WaveShape::triangle: return "Triangle"; + case gin::LFO::WaveShape::sawUp: return "Saw Up"; + case gin::LFO::WaveShape::sawDown: return "Saw Down"; + case gin::LFO::WaveShape::square: return "Square"; + case gin::LFO::WaveShape::squarePos: return "Square+"; + case gin::LFO::WaveShape::sampleAndHold: return "S&H"; + case gin::LFO::WaveShape::noise: return "Noise"; + case gin::LFO::WaveShape::stepUp3: return "Step Up 3"; + case gin::LFO::WaveShape::stepUp4: return "Step Up 4"; + case gin::LFO::WaveShape::stepup8: return "Step Up 8"; + case gin::LFO::WaveShape::stepDown3: return "Step Down 3"; + case gin::LFO::WaveShape::stepDown4: return "Step Down 4"; + case gin::LFO::WaveShape::stepDown8: return "Step Down 8"; + case gin::LFO::WaveShape::pyramid3: return "Pyramid 3"; + case gin::LFO::WaveShape::pyramid5: return "Pyramid 5"; + case gin::LFO::WaveShape::pyramid9: return "Pyramid 9"; default: jassertfalse; return {}; } } -static String enableTextFunction (const Parameter&, float v) +static juce::String enableTextFunction (const gin::Parameter&, float v) { return v > 0.0f ? "On" : "Off"; } -static String durationTextFunction (const Parameter&, float v) +static juce::String durationTextFunction (const gin::Parameter&, float v) { - return NoteDuration::getNoteDurations()[size_t (v)].getName(); + return gin::NoteDuration::getNoteDurations()[size_t (v)].getName(); } -static String distortionAmountTextFunction (const Parameter&, float v) +static juce::String distortionAmountTextFunction (const gin::Parameter&, float v) { - return String (v * 5.0f - 1.0f, 1); + return juce::String (v * 5.0f - 1.0f, 1); } -static String filterTextFunction (const Parameter&, float v) +static juce::String filterTextFunction (const gin::Parameter&, float v) { switch (int (v)) { @@ -82,12 +82,12 @@ static String filterTextFunction (const Parameter&, float v) } } -static String freqTextFunction (const Parameter&, float v) +static juce::String freqTextFunction (const gin::Parameter&, float v) { - return String (int (getMidiNoteInHertz (v))); + return juce::String (int (gin::getMidiNoteInHertz (v))); } -static String glideModeTextFunction (const Parameter&, float v) +static juce::String glideModeTextFunction (const gin::Parameter&, float v) { switch (int (v)) { @@ -100,33 +100,13 @@ static String glideModeTextFunction (const Parameter&, float v) } } -//============================================================================== -void WavetableAudioProcessor::WTParams::setup (WavetableAudioProcessor& p, int idx) -{ - String id = "wt" + String (idx + 1); - String nm = "WT" + String (idx + 1) + " "; - - enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f); - table = p.addExtParam (id + "table", nm + "Table", "Table", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0f, 0.0f); - voices = p.addIntParam (id + "unison", nm + "Unison", "Unison", "", { 1.0, 8.0, 1.0, 1.0 }, 1.0, 0.0f); - voicesTrns = p.addExtParam (id + "unisontrns", nm + "Unison Trns", "LTrans", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f); - tune = p.addExtParam (id + "tune", nm + "Tune", "Tune", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f); - finetune = p.addExtParam (id + "finetune", nm + "Fine Tune", "Fine", "ct", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f); - level = p.addExtParam (id + "level", nm + "Level", "Level", "db", { -100.0, 0.0, 1.0, 4.0 }, 0.0, 0.0f); - detune = p.addExtParam (id + "detune", nm + "Detune", "Detune", "", { 0.0, 0.5, 0.0, 1.0 }, 0.0, 0.0f); - spread = p.addExtParam (id + "spread", nm + "Spread", "Spread", "%", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f); - pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f); - - level->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; -} - //============================================================================== void WavetableAudioProcessor::OSCParams::setup (WavetableAudioProcessor& p, int idx) { - String id = "osc" + String (idx + 1); - String nm = "OSC" + String (idx + 1) + " "; + juce::String id = "osc" + juce::String (idx + 1); + juce::String nm = "OSC" + juce::String (idx + 1) + " "; - enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f); + enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f); wave = p.addIntParam (id + "wave", nm + "Wave", "Wave", "", { 1.0, 7.0, 1.0, 1.0 }, 1.0, 0.0f, waveTextFunction); voices = p.addIntParam (id + "unison", nm + "Unison", "Unison", "", { 1.0, 8.0, 1.0, 1.0 }, 1.0, 0.0f); voicesTrns = p.addExtParam (id + "unisontrns", nm + "Unison Trns", "LTrans", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f); @@ -138,16 +118,16 @@ void WavetableAudioProcessor::OSCParams::setup (WavetableAudioProcessor& p, int spread = p.addExtParam (id + "spread", nm + "Spread", "Spread", "%", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f); pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f); - level->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; + level->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; } //============================================================================== void WavetableAudioProcessor::FilterParams::setup (WavetableAudioProcessor& p, int idx) { - String id = "flt" + String (idx + 1); - String nm = "FLT" + String (idx + 1) + " "; + juce::String id = "flt" + juce::String (idx + 1); + juce::String nm = "FLT" + juce::String (idx + 1) + " "; - float maxFreq = float (getMidiNoteFromHertz (20000.0)); + float maxFreq = float (gin::getMidiNoteFromHertz (20000.0)); enable = p.addIntParam (id + "enable", nm + "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f); type = p.addIntParam (id + "type", nm + "Type", "Type", "", { 0.0, 7.0, 1.0, 1.0 }, 0.0, 0.0f, filterTextFunction); @@ -169,8 +149,8 @@ void WavetableAudioProcessor::FilterParams::setup (WavetableAudioProcessor& p, i //============================================================================== void WavetableAudioProcessor::EnvParams::setup (WavetableAudioProcessor& p, int idx) { - String id = "env" + String (idx + 1); - String nm = "ENV" + String (idx + 1) + " "; + juce::String id = "env" + juce::String (idx + 1); + juce::String nm = "ENV" + juce::String (idx + 1) + " "; enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction); attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f); @@ -184,10 +164,10 @@ void WavetableAudioProcessor::EnvParams::setup (WavetableAudioProcessor& p, int //============================================================================== void WavetableAudioProcessor::LFOParams::setup (WavetableAudioProcessor& p, int idx) { - String id = "lfo" + String (idx + 1); - String nm = "LFO" + String (idx + 1) + " "; + juce::String id = "lfo" + juce::String (idx + 1); + juce::String nm = "LFO" + juce::String (idx + 1) + " "; - auto notes = NoteDuration::getNoteDurations(); + auto notes = gin::NoteDuration::getNoteDurations(); enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction); sync = p.addIntParam (id + "sync", nm + "Sync", "Sync", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction); @@ -204,10 +184,10 @@ void WavetableAudioProcessor::LFOParams::setup (WavetableAudioProcessor& p, int //============================================================================== void WavetableAudioProcessor::StepLFOParams::setup (WavetableAudioProcessor& p) { - String id = "slfo"; - String nm = "Step LFO"; + juce::String id = "slfo"; + juce::String nm = "Step LFO"; - auto notes = NoteDuration::getNoteDurations(); + auto notes = gin::NoteDuration::getNoteDurations(); enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction); beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 13.0, 0.0f, durationTextFunction); @@ -215,7 +195,7 @@ void WavetableAudioProcessor::StepLFOParams::setup (WavetableAudioProcessor& p) for (int i = 0; i < 32; i++) { - auto num = String (i + 1); + auto num = juce::String (i + 1); level[i] = p.addIntParam (id + "step" + num, nm + "Step " + num, "", "", { -1.0, 1.0, 0.0, 1.0f }, 0.0f, 0.0f); } } @@ -223,20 +203,20 @@ void WavetableAudioProcessor::StepLFOParams::setup (WavetableAudioProcessor& p) //============================================================================== void WavetableAudioProcessor::GateParams::setup (WavetableAudioProcessor& p) { - String id = "gate"; - String nm = "Gate"; + juce::String id = "gate"; + juce::String nm = "Gate"; - auto notes = NoteDuration::getNoteDurations(); + auto notes = gin::NoteDuration::getNoteDurations(); enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction); beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 7.0, 0.0f, durationTextFunction); length = p.addIntParam (id + "length", nm + "Length", "Length", "", { 2.0, 32.0, 1.0, 1.0f }, 8.0f, 0.0f); - attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f); - release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f); + attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 1.0, 0.0, 0.2f }, 0.1f, 0.0f); + release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 1.0, 0.0, 0.2f }, 0.1f, 0.0f); for (int i = 0; i < 32; i++) { - auto num = String (i + 1); + auto num = juce::String (i + 1); l[i] = p.addIntParam (id + "l" + num, nm + "L " + num, "", "", { 0.0, 1.0, 1.0, 1.0f }, (i % 2 == 0 || i % 5 == 0) ? 1.0f : 0.0f, 0.0f); r[i] = p.addIntParam (id + "r" + num, nm + "R " + num, "", "", { 0.0, 1.0, 1.0, 1.0f }, (i % 2 == 0 || i % 5 == 0) ? 1.0f : 0.0f, 0.0f); } @@ -266,7 +246,7 @@ void WavetableAudioProcessor::GlobalParams::setup (WavetableAudioProcessor& p) voices = p.addIntParam ("voices", "Voices", "", "", { 2.0, 40.0, 1.0, 1.0 }, 40.0f, 0.0f); mpe = p.addIntParam ("mpe", "MPE", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction); - level->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; + level->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; } //============================================================================== @@ -296,11 +276,11 @@ void WavetableAudioProcessor::DistortionParams::setup (WavetableAudioProcessor& //============================================================================== void WavetableAudioProcessor::EQParams::setup (WavetableAudioProcessor& p) { - float maxFreq = float (getMidiNoteFromHertz (20000.0)); - float d1 = float (getMidiNoteFromHertz (80.0)); - float d2 = float (getMidiNoteFromHertz (3000.0)); - float d3 = float (getMidiNoteFromHertz (5000.0)); - float d4 = float (getMidiNoteFromHertz (17000.0)); + float maxFreq = float (gin::getMidiNoteFromHertz (20000.0)); + float d1 = float (gin::getMidiNoteFromHertz (80.0)); + float d2 = float (gin::getMidiNoteFromHertz (3000.0)); + float d3 = float (gin::getMidiNoteFromHertz (5000.0)); + float d4 = float (gin::getMidiNoteFromHertz (17000.0)); enable = p.addIntParam ("eqEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction); @@ -320,15 +300,15 @@ void WavetableAudioProcessor::EQParams::setup (WavetableAudioProcessor& p) hiQ = p.addExtParam ("eqHiQ", "Hi Q", "Q", "", { 0.025f, 40.0f, 0.0, 0.2f }, 1.0f, 0.0f); hiGain = p.addExtParam ("eqHiGain", "Hi Gain", "Gain", "dB", { -20.0f, 20.0f, 0.0, 1.0 }, 0.0f, 0.0f); - loFreq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); }; - mid1Freq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); }; - mid2Freq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); }; - hiFreq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); }; + loFreq->conversionFunction = [] (float in) { return gin::getMidiNoteInHertz (in); }; + mid1Freq->conversionFunction = [] (float in) { return gin::getMidiNoteInHertz (in); }; + mid2Freq->conversionFunction = [] (float in) { return gin::getMidiNoteInHertz (in); }; + hiFreq->conversionFunction = [] (float in) { return gin::getMidiNoteInHertz (in); }; - loGain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; - mid1Gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; - mid2Gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; - hiGain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; + loGain->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; + mid1Gain->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; + mid2Gain->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; + hiGain->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; } //============================================================================== @@ -344,7 +324,7 @@ void WavetableAudioProcessor::CompressorParams::setup (WavetableAudioProcessor& attack->conversionFunction = [] (float in) { return in / 1000.0f; }; release->conversionFunction = [] (float in) { return in / 1000.0f; }; - gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; + gain->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; } //============================================================================== @@ -352,7 +332,7 @@ void WavetableAudioProcessor::DelayParams::setup (WavetableAudioProcessor& p) { enable = p.addIntParam ("dlEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction); - float mxd = float (NoteDuration::getNoteDurations().size()) - 1.0f; + float mxd = float (gin::NoteDuration::getNoteDurations().size()) - 1.0f; sync = p.addExtParam ("dlSync", "Sync", "", "", { 0.0f, 1.0f, 1.0f, 1.0f}, 0.0f, 0.0f, enableTextFunction); time = p.addExtParam ("dlTime", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 0.3f}, 1.0f, 0.0f); @@ -361,10 +341,10 @@ void WavetableAudioProcessor::DelayParams::setup (WavetableAudioProcessor& p) cf = p.addExtParam ("dlCf", "CF", "", "dB", {-100.0f, 0.0f, 0.0f, 5.0f}, -100.0f, 0.1f); mix = p.addExtParam ("dlMix", "Mix", "", "%", { 0.0f, 100.0f, 0.0f, 1.0f}, 0.5f, 0.1f); - delay = p.addIntParam ("dlDelay", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 1.0f}, 1.0f, {0.2f, SmoothingType::eased}); + delay = p.addIntParam ("dlDelay", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 1.0f}, 1.0f, {0.2f, gin::SmoothingType::eased}); - fb->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; - cf->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; + fb->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; + cf->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; mix->conversionFunction = [] (float in) { return in / 100.0f; }; } @@ -392,30 +372,27 @@ void WavetableAudioProcessor::LimiterParams::setup (WavetableAudioProcessor& p) attack->conversionFunction = [] (float in) { return in / 1000.0f; }; release->conversionFunction = [] (float in) { return in / 1000.0f; }; - gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); }; + gain->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); }; } //============================================================================== WavetableAudioProcessor::WavetableAudioProcessor() { - formatManager->registerBasicFormats(); + lf = std::make_unique(); enableLegacyMode(); setVoiceStealingEnabled (true); - for (int i = 0; i < numElementsInArray (wtParams); i++) - wtParams[i].setup (*this, i); - - for (int i = 0; i < numElementsInArray (oscParams); i++) + for (int i = 0; i < juce::numElementsInArray (oscParams); i++) oscParams[i].setup (*this, i); - for (int i = 0; i < numElementsInArray (filterParams); i++) + for (int i = 0; i < juce::numElementsInArray (filterParams); i++) filterParams[i].setup (*this, i); - for (int i = 0; i < numElementsInArray (envParams); i++) + for (int i = 0; i < juce::numElementsInArray (envParams); i++) envParams[i].setup (*this, i); - for (int i = 0; i < numElementsInArray (lfoParams); i++) + for (int i = 0; i < juce::numElementsInArray (lfoParams); i++) lfoParams[i].setup (*this, i); stepLfoParams.setup (*this); @@ -443,9 +420,6 @@ WavetableAudioProcessor::WavetableAudioProcessor() } setupModMatrix(); - - MemoryBlock mb (BinaryData::WINDOW_S_WAV, BinaryData::WINDOW_S_WAVSize); - updateWavetable (0, mb, 256); } WavetableAudioProcessor::~WavetableAudioProcessor() @@ -476,27 +450,27 @@ void WavetableAudioProcessor::setupModMatrix() for (int i = 0; i <= 119; i++) { - String name = MidiMessage::getControllerName (i); + juce::String name = juce::MidiMessage::getControllerName (i); if (name.isEmpty()) - modSrcCC.add (modMatrix.addMonoModSource (String::formatted ("cc%d", i), String::formatted ("CC %d", i), false)); + modSrcCC.add (modMatrix.addMonoModSource (juce::String::formatted ("cc%d", i), juce::String::formatted ("CC %d", i), false)); else - modSrcCC.add (modMatrix.addMonoModSource (String::formatted ("cc%d", i), String::formatted ("CC %d ", i) + name, false)); + modSrcCC.add (modMatrix.addMonoModSource (juce::String::formatted ("cc%d", i), juce::String::formatted ("CC %d ", i) + name, false)); } for (int i = 0; i < Cfg::numLFOs; i++) - modSrcMonoLFO.add (modMatrix.addMonoModSource (String::formatted ("mlfo%d", i + 1), String::formatted ("LFO %d (Mono)", i + 1), true)); + modSrcMonoLFO.add (modMatrix.addMonoModSource (juce::String::formatted ("mlfo%d", i + 1), juce::String::formatted ("LFO %d (Mono)", i + 1), true)); for (int i = 0; i < Cfg::numLFOs; i++) - modSrcLFO.add (modMatrix.addPolyModSource (String::formatted ("lfo%d", i + 1), String::formatted ("LFO %d", i + 1), true)); + modSrcLFO.add (modMatrix.addPolyModSource (juce::String::formatted ("lfo%d", i + 1), juce::String::formatted ("LFO %d", i + 1), true)); modSrcMonoStep = modMatrix.addMonoModSource ("mstep", "Step LFO (Mono)", true); modSrcStep = modMatrix.addPolyModSource ("step", "Step LFO", true); for (int i = 0; i < Cfg::numFilters; i++) - modSrcFilter.add (modMatrix.addPolyModSource (String::formatted ("fenv%d", i + 1), String::formatted ("Filter Envelope %d", i + 1), false)); + modSrcFilter.add (modMatrix.addPolyModSource (juce::String::formatted ("fenv%d", i + 1), juce::String::formatted ("Filter Envelope %d", i + 1), false)); for (int i = 0; i < Cfg::numENVs; i++) - modSrcEnv.add (modMatrix.addPolyModSource (String::formatted ("env%d", i + 1), String::formatted ("Envelope %d", i + 1), false)); + modSrcEnv.add (modMatrix.addPolyModSource (juce::String::formatted ("env%d", i + 1), juce::String::formatted ("Envelope %d", i + 1), false)); auto firstMonoParam = globalParams.mono; bool polyParam = true; @@ -565,9 +539,9 @@ void WavetableAudioProcessor::releaseResources() { } -void WavetableAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midi) +void WavetableAudioProcessor::processBlock (juce::AudioBuffer& buffer, juce::MidiBuffer& midi) { - ScopedNoDenormals noDenormals; + juce::ScopedNoDenormals noDenormals; startBlock(); setMPE (globalParams.mpe->isOn()); @@ -594,8 +568,8 @@ void WavetableAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuff renderNextBlock (buffer, midi, pos, thisBlock); - auto slice = sliceBuffer (buffer, pos, thisBlock); - applyEffects (slice); + auto bufferSlice = gin::sliceBuffer (buffer, pos, thisBlock); + applyEffects (bufferSlice); modMatrix.finishBlock (thisBlock); @@ -609,9 +583,9 @@ void WavetableAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuff endBlock (buffer.getNumSamples()); } -Array WavetableAudioProcessor::getLiveFilterCutoff (int i) +juce::Array WavetableAudioProcessor::getLiveFilterCutoff (int i) { - Array values; + juce::Array values; for (auto v : voices) { @@ -624,7 +598,7 @@ Array WavetableAudioProcessor::getLiveFilterCutoff (int i) return values; } -void WavetableAudioProcessor::applyEffects (AudioSampleBuffer& buffer) +void WavetableAudioProcessor::applyEffects (juce::AudioSampleBuffer& buffer) { // Apply gate if (gateParams.enable->isOn()) @@ -669,15 +643,15 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) { if (lfoParams[i].enable->isOn()) { - LFO::Parameters params; + gin::LFO::Parameters params; float freq = 0; if (lfoParams[i].sync->getProcValue() > 0.0f) - freq = 1.0f / NoteDuration::getNoteDurations()[size_t (lfoParams[i].beat->getProcValue())].toSeconds (playhead); + freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (lfoParams[i].beat->getProcValue())].toSeconds (playhead); else freq = modMatrix.getValue (lfoParams[i].rate); - params.waveShape = (LFO::WaveShape) int (lfoParams[i].wave->getProcValue()); + params.waveShape = (gin::LFO::WaveShape) int (lfoParams[i].wave->getProcValue()); params.frequency = freq; params.phase = modMatrix.getValue (lfoParams[i].phase); params.offset = modMatrix.getValue (lfoParams[i].offset); @@ -699,7 +673,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) // Update Mono Step LFO if (stepLfoParams.enable->isOn()) { - float freq = 1.0f / NoteDuration::getNoteDurations()[size_t (stepLfoParams.beat->getProcValue())].toSeconds (playhead); + float freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (stepLfoParams.beat->getProcValue())].toSeconds (playhead); modStepLFO.setFreq (freq); @@ -720,7 +694,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) // Update Gate if (gateParams.enable->isOn()) { - float freq = 1.0f / NoteDuration::getNoteDurations()[size_t (gateParams.beat->getProcValue())].toSeconds (playhead); + float freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (gateParams.beat->getProcValue())].toSeconds (playhead); int n = int (gateParams.length->getProcValue()); @@ -757,22 +731,22 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) // Update EQ if (eqParams.enable->isOn()) { - eq.setParams (0, EQ::lowshelf, + eq.setParams (0, gin::EQ::lowshelf, modMatrix.getValue (eqParams.loFreq), modMatrix.getValue (eqParams.loQ), modMatrix.getValue (eqParams.loGain)); - eq.setParams (1, EQ::peak, + eq.setParams (1, gin::EQ::peak, modMatrix.getValue (eqParams.mid1Freq), modMatrix.getValue (eqParams.mid1Q), modMatrix.getValue (eqParams.mid1Gain)); - eq.setParams (2, EQ::peak, + eq.setParams (2, gin::EQ::peak, modMatrix.getValue (eqParams.mid2Freq), modMatrix.getValue (eqParams.mid2Q), modMatrix.getValue (eqParams.mid2Gain)); - eq.setParams (3, EQ::highshelf, + eq.setParams (3, gin::EQ::highshelf, modMatrix.getValue (eqParams.hiFreq), modMatrix.getValue (eqParams.hiQ), modMatrix.getValue (eqParams.hiGain)); @@ -784,6 +758,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) compressor.setInputGain (1.0f); compressor.setOutputGain (modMatrix.getValue (compressorParams.gain)); compressor.setParams (modMatrix.getValue (compressorParams.attack), + 0, modMatrix.getValue (compressorParams.release), modMatrix.getValue (compressorParams.threshold), modMatrix.getValue (compressorParams.ratio), @@ -795,7 +770,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) { if (delayParams.sync->isOn()) { - auto& duration = NoteDuration::getNoteDurations()[(size_t)delayParams.beat->getUserValueInt()]; + auto& duration = gin::NoteDuration::getNoteDurations()[(size_t)delayParams.beat->getUserValueInt()]; delayParams.delay->setUserValue (duration.toSeconds (getPlayHead())); } else @@ -813,10 +788,10 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) // Update Reverb if (reverbParams.enable->isOn()) { - Reverb::Parameters p; + juce::Reverb::Parameters p; auto mix = modMatrix.getValue (reverbParams.mix); - WetDryMix wetDry (mix); + gin::WetDryMix wetDry (mix); p.damping = modMatrix.getValue (reverbParams.damping); p.freezeMode = modMatrix.getValue (reverbParams.freezeMode); @@ -834,6 +809,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) limiter.setInputGain (1.0f); limiter.setOutputGain (modMatrix.getValue (limiterParams.gain)); limiter.setParams (modMatrix.getValue (limiterParams.attack), + 0, modMatrix.getValue (limiterParams.release), modMatrix.getValue (limiterParams.threshold), 100, 6); @@ -843,7 +819,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) outputGain.setGain (modMatrix.getValue (globalParams.level)); } -void WavetableAudioProcessor::handleMidiEvent (const MidiMessage& m) +void WavetableAudioProcessor::handleMidiEvent (const juce::MidiMessage& m) { MPESynthesiser::handleMidiEvent (m); @@ -856,36 +832,20 @@ void WavetableAudioProcessor::handleController ([[maybe_unused]] int ch, int num modMatrix.setMonoValue (modSrcCC[num], val / 127.0f); } -void WavetableAudioProcessor::updateWavetable (int idx, MemoryBlock& src, int tableSize) -{ - if (auto reader = std::unique_ptr (formatManager->createReaderFor (std::make_unique (src, false)))) - { - AudioSampleBuffer buffer; - buffer.setSize (1, int (reader->lengthInSamples)); - reader->read (&buffer, 0, int (reader->lengthInSamples), 0, true, false); - - loadWavetables (waveTables[idx], buffer, reader->sampleRate, tableSize); - - for (auto v : voices) - if (auto wtv = dynamic_cast(v)) - wtv->setWavetable (idx, waveTables[idx]); - } -} - //============================================================================== bool WavetableAudioProcessor::hasEditor() const { return true; } -AudioProcessorEditor* WavetableAudioProcessor::createEditor() +juce::AudioProcessorEditor* WavetableAudioProcessor::createEditor() { return new WavetableAudioProcessorEditor (*this); } //============================================================================== // This creates new instances of the plugin.. -AudioProcessor* JUCE_CALLTYPE createPluginFilter() +juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() { return new WavetableAudioProcessor(); } diff --git a/plugin/Source/PluginProcessor.h b/plugin/Source/PluginProcessor.h index c2b5778..5161c85 100644 --- a/plugin/Source/PluginProcessor.h +++ b/plugin/Source/PluginProcessor.h @@ -6,7 +6,7 @@ //============================================================================== class WavetableAudioProcessor : public gin::Processor, - public gin::Synthesiser + public gin::Synthesiser { public: //============================================================================== @@ -21,10 +21,10 @@ public: void prepareToPlay (double sampleRate, int samplesPerBlock) override; void releaseResources() override; - void processBlock (AudioBuffer&, MidiBuffer&) override; + void processBlock (juce::AudioBuffer&, juce::MidiBuffer&) override; //============================================================================== - AudioProcessorEditor* createEditor() override; + juce::AudioProcessorEditor* createEditor() override; bool hasEditor() const override; void updateParams (int blockSize); @@ -33,25 +33,12 @@ public: gin::BandLimitedLookupTables bandLimitedLookupTables; //============================================================================== - void handleMidiEvent (const MidiMessage& m) override; + void handleMidiEvent (const juce::MidiMessage& m) override; void handleController (int ch, int num, int val) override; //============================================================================== - Array getLiveFilterCutoff (int idx); + juce::Array getLiveFilterCutoff (int idx); - void applyEffects (AudioSampleBuffer& buffer); - - // WT Params - struct WTParams - { - WTParams() = default; - - gin::Parameter::Ptr enable, table, voices, voicesTrns, tune, finetune, - level, detune, spread, pan; - - void setup (WavetableAudioProcessor& p, int idx); - - JUCE_DECLARE_NON_COPYABLE (WTParams) - }; + void applyEffects (juce::AudioSampleBuffer& buffer); // Voice Params struct OSCParams @@ -230,19 +217,14 @@ public: JUCE_DECLARE_NON_COPYABLE (LimiterParams) }; - //============================================================================== - void updateWavetable (int idx, MemoryBlock& src, int tableSize); - //============================================================================== gin::ModSrcId modSrcPressure, modSrcTimbre, modScrPitchBend, modSrcNote, modSrcVelocity, modSrcStep, modSrcMonoStep; - Array modSrcCC, modSrcMonoLFO, modSrcLFO, modSrcFilter, modSrcEnv; + juce::Array modSrcCC, modSrcMonoLFO, modSrcLFO, modSrcFilter, modSrcEnv; //============================================================================== - SharedResourcePointer formatManager; - WTParams wtParams[Cfg::numWTs]; OSCParams oscParams[Cfg::numOSCs]; FilterParams filterParams[Cfg::numFilters]; EnvParams envParams[Cfg::numENVs]; @@ -269,7 +251,7 @@ public: gin::Dynamics compressor; gin::Dynamics limiter; gin::EQ eq {4}; - Reverb reverb; + juce::Reverb reverb; gin::GainProcessor outputGain; gin::AudioFifo fifo { 2, 44100 }; @@ -279,9 +261,7 @@ public: gin::LFO modLFOs[Cfg::numLFOs]; gin::StepLFO modStepLFO; - AudioPlayHead* playhead = nullptr; - - OwnedArray waveTables[Cfg::numWTs]; + juce::AudioPlayHead* playhead = nullptr; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavetableAudioProcessor) diff --git a/plugin/Source/WavetableVoice.cpp b/plugin/Source/WavetableVoice.cpp index 4509358..99fd7a7 100644 --- a/plugin/Source/WavetableVoice.cpp +++ b/plugin/Source/WavetableVoice.cpp @@ -1,10 +1,8 @@ #include "WavetableVoice.h" #include "PluginProcessor.h" -using namespace gin; - //============================================================================== -WavetableVoice::WavetableVoice (WavetableAudioProcessor& p, BandLimitedLookupTables& bllt) +WavetableVoice::WavetableVoice (WavetableAudioProcessor& p, gin::BandLimitedLookupTables& bllt) : proc (p) , bandLimitedLookupTables (bllt) { @@ -12,11 +10,6 @@ WavetableVoice::WavetableVoice (WavetableAudioProcessor& p, BandLimitedLookupTab f.setNumChannels (2); } -void WavetableVoice::setWavetable (int idx, OwnedArray& table) -{ - wtOscillators[idx].setWavetable (table); -} - void WavetableVoice::noteStarted() { fastKill = false; @@ -38,7 +31,7 @@ void WavetableVoice::noteStarted() proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.initialTimbre.asUnsignedFloat()); proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat()); - ScopedValueSetter svs (disableSmoothing, true); + juce::ScopedValueSetter svs (disableSmoothing, true); for (auto& f : filters) f.reset(); @@ -56,10 +49,7 @@ void WavetableVoice::noteStarted() snapParams(); updateParams (0); snapParams(); - - for (auto& osc : wtOscillators) - osc.noteOn(); - + for (auto& osc : oscillators) osc.noteOn(); @@ -99,9 +89,6 @@ void WavetableVoice::noteRetriggered() updateParams (0); - for (auto& osc : wtOscillators) - osc.noteOn(); - for (auto& osc : oscillators) osc.noteOn(); @@ -148,9 +135,6 @@ void WavetableVoice::setCurrentSampleRate (double newRate) { MPESynthesiserVoice::setCurrentSampleRate (newRate); - for (auto& osc : wtOscillators) - osc.setSampleRate (newRate); - for (auto& osc : oscillators) osc.setSampleRate (newRate); @@ -168,34 +152,30 @@ void WavetableVoice::setCurrentSampleRate (double newRate) adsr.setSampleRate (newRate); } -void WavetableVoice::renderNextBlock (AudioBuffer& outputBuffer, int startSample, int numSamples) +void WavetableVoice::renderNextBlock (juce::AudioBuffer& outputBuffer, int startSample, int numSamples) { updateParams (numSamples); // Run OSC - ScratchBuffer buffer (2, numSamples); - - for (int i = 0; i < Cfg::numWTs; i++) - if (proc.wtParams[i].enable->isOn()) - wtOscillators[i].processAdding (currentMidiNotes[i], wtParams[i], buffer); + gin::ScratchBuffer buffer (2, numSamples); for (int i = 0; i < Cfg::numOSCs; i++) if (proc.oscParams[i].enable->isOn()) - oscillators[i].processAdding (currentMidiNotes[Cfg::numWTs + i], oscParams[i], buffer); + oscillators[i].processAdding (currentMidiNotes[i], oscParams[i], buffer); // Apply velocity float velocity = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat(); - buffer.applyGain (velocityToGain (velocity, ampKeyTrack)); + buffer.applyGain (gin::velocityToGain (velocity, ampKeyTrack)); // Apply filters - for (int i = 0; i < numElementsInArray (filters); i++) + for (int i = 0; i < juce::numElementsInArray (filters); i++) if (proc.filterParams[i].enable->isOn()) filters[i].process (buffer); // Run ADSR adsr.processMultiplying (buffer); - if (adsr.getState() == AnalogADSR::State::idle) + if (adsr.getState() == gin::AnalogADSR::State::idle) { clearCurrentNote(); stopVoice(); @@ -214,34 +194,16 @@ void WavetableVoice::updateParams (int blockSize) proc.modMatrix.setPolyValue (*this, proc.modSrcNote, note.initialNote / 127.0f); - for (int i = 0; i < Cfg::numWTs; i++) - { - if (! proc.wtParams[i].enable->isOn()) continue; - - currentMidiNotes[i] = noteSmoother.getCurrentValue() * 127.0f; - if (glideInfo.glissando) currentMidiNotes[i] = roundToInt (currentMidiNotes[i]); - currentMidiNotes[i] += float (note.totalPitchbendInSemitones); - currentMidiNotes[i] += getValue (proc.wtParams[i].tune) + getValue (proc.wtParams[i].finetune) / 100.0f; - - wtParams[i].voices = int (proc.wtParams[i].voices->getProcValue()); - wtParams[i].vcTrns = int (proc.wtParams[i].voicesTrns->getProcValue()); - wtParams[i].pw = getValue (proc.wtParams[i].table); - wtParams[i].pan = getValue (proc.wtParams[i].pan); - wtParams[i].spread = getValue (proc.wtParams[i].spread) / 100.0f; - wtParams[i].detune = getValue (proc.wtParams[i].detune); - wtParams[i].gain = getValue (proc.wtParams[i].level); - } - for (int i = 0; i < Cfg::numOSCs; i++) { if (! proc.oscParams[i].enable->isOn()) continue; - currentMidiNotes[Cfg::numWTs + i] = noteSmoother.getCurrentValue() * 127.0f; - if (glideInfo.glissando) currentMidiNotes[Cfg::numWTs + i] = roundToInt (currentMidiNotes[Cfg::numWTs + i]); - currentMidiNotes[Cfg::numWTs + i] += float (note.totalPitchbendInSemitones); - currentMidiNotes[Cfg::numWTs + i] += getValue (proc.oscParams[i].tune) + getValue (proc.oscParams[i].finetune) / 100.0f; + currentMidiNotes[i] = noteSmoother.getCurrentValue() * 127.0f; + if (glideInfo.glissando) currentMidiNotes[i] = (float) juce::roundToInt (currentMidiNotes[i]); + currentMidiNotes[i] += float (note.totalPitchbendInSemitones); + currentMidiNotes[i] += getValue (proc.oscParams[i].tune) + getValue (proc.oscParams[i].finetune) / 100.0f; - oscParams[i].wave = (Wave) int (proc.oscParams[i].wave->getProcValue()); + oscParams[i].wave = (gin::Wave) int (proc.oscParams[i].wave->getProcValue()); oscParams[i].voices = int (proc.oscParams[i].voices->getProcValue()); oscParams[i].vcTrns = int (proc.oscParams[i].voicesTrns->getProcValue()); oscParams[i].pw = getValue (proc.oscParams[i].pulsewidth) / 100.0f; @@ -268,7 +230,7 @@ void WavetableVoice::updateParams (int blockSize) filterADSRs[i].process (blockSize); - float filterWidth = float (getMidiNoteFromHertz (20000.0)); + float filterWidth = float (gin::getMidiNoteFromHertz (20000.0)); float filterEnv = filterADSRs[i].getOutput(); float filterSens = getValue (proc.filterParams[i].velocityTracking); filterSens = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat() * filterSens + 1.0f - filterSens; @@ -277,45 +239,45 @@ void WavetableVoice::updateParams (int blockSize) n += (currentlyPlayingNote.initialNote - 60) * getValue (proc.filterParams[i].keyTracking); n += filterEnv * filterSens * getValue (proc.filterParams[i].amount) * filterWidth; - float f = getMidiNoteInHertz (n); + float f = gin::getMidiNoteInHertz (n); float maxFreq = std::min (20000.0f, float (getSampleRate() / 2)); - f = jlimit (4.0f, maxFreq, f); + f = juce::jlimit (4.0f, maxFreq, f); - float q = Q / (1.0f - (getValue (proc.filterParams[i].resonance) / 100.0f) * 0.99f); + float q = gin::Q / (1.0f - (getValue (proc.filterParams[i].resonance) / 100.0f) * 0.99f); switch (int (proc.filterParams[i].type->getProcValue())) { case 0: - filters[i].setType (Filter::lowpass); - filters[i].setSlope (Filter::db12); + filters[i].setType (gin::Filter::lowpass); + filters[i].setSlope (gin::Filter::db12); break; case 1: - filters[i].setType (Filter::lowpass); - filters[i].setSlope (Filter::db24); + filters[i].setType (gin::Filter::lowpass); + filters[i].setSlope (gin::Filter::db24); break; case 2: - filters[i].setType (Filter::highpass); - filters[i].setSlope (Filter::db12); + filters[i].setType (gin::Filter::highpass); + filters[i].setSlope (gin::Filter::db12); break; case 3: - filters[i].setType (Filter::highpass); - filters[i].setSlope (Filter::db24); + filters[i].setType (gin::Filter::highpass); + filters[i].setSlope (gin::Filter::db24); break; case 4: - filters[i].setType (Filter::bandpass); - filters[i].setSlope (Filter::db12); + filters[i].setType (gin::Filter::bandpass); + filters[i].setSlope (gin::Filter::db12); break; case 5: - filters[i].setType (Filter::bandpass); - filters[i].setSlope (Filter::db24); + filters[i].setType (gin::Filter::bandpass); + filters[i].setSlope (gin::Filter::db24); break; case 6: - filters[i].setType (Filter::notch); - filters[i].setSlope (Filter::db12); + filters[i].setType (gin::Filter::notch); + filters[i].setSlope (gin::Filter::db12); break; case 7: - filters[i].setType (Filter::notch); - filters[i].setSlope (Filter::db24); + filters[i].setType (gin::Filter::notch); + filters[i].setSlope (gin::Filter::db24); break; } @@ -347,15 +309,15 @@ void WavetableVoice::updateParams (int blockSize) { if (proc.lfoParams[i].enable->isOn()) { - LFO::Parameters params; + gin::LFO::Parameters params; float freq = 0; if (proc.lfoParams[i].sync->getProcValue() > 0.0f) - freq = 1.0f / NoteDuration::getNoteDurations()[size_t (proc.lfoParams[i].beat->getProcValue())].toSeconds (proc.playhead); + freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (proc.lfoParams[i].beat->getProcValue())].toSeconds (proc.playhead); else freq = getValue (proc.lfoParams[i].rate); - params.waveShape = (LFO::WaveShape) int (proc.lfoParams[i].wave->getProcValue()); + params.waveShape = (gin::LFO::WaveShape) int (proc.lfoParams[i].wave->getProcValue()); params.frequency = freq; params.phase = getValue (proc.lfoParams[i].phase); params.offset = getValue (proc.lfoParams[i].offset); @@ -377,7 +339,7 @@ void WavetableVoice::updateParams (int blockSize) // Update Step LFO if (proc.stepLfoParams.enable->isOn()) { - float freq = 1.0f / NoteDuration::getNoteDurations()[size_t (proc.stepLfoParams.beat->getProcValue())].toSeconds (proc.playhead); + float freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (proc.stepLfoParams.beat->getProcValue())].toSeconds (proc.playhead); modStepLFO.setFreq (freq); @@ -411,5 +373,5 @@ bool WavetableVoice::isVoiceActive() float WavetableVoice::getFilterCutoffNormalized (int idx) { float freq = filters[idx].getFrequency(); - return proc.filterParams[idx].frequency->getUserRange().convertTo0to1 (getMidiNoteFromHertz (freq)); + return proc.filterParams[idx].frequency->getUserRange().convertTo0to1 (gin::getMidiNoteFromHertz (freq)); } diff --git a/plugin/Source/WavetableVoice.h b/plugin/Source/WavetableVoice.h index ae73949..6101193 100644 --- a/plugin/Source/WavetableVoice.h +++ b/plugin/Source/WavetableVoice.h @@ -7,13 +7,11 @@ class WavetableAudioProcessor; //============================================================================== class WavetableVoice : public gin::SynthesiserVoice, - public gin::ModVoice + public gin::ModVoice { public: WavetableVoice (WavetableAudioProcessor& p, gin::BandLimitedLookupTables& bandLimitedLookupTables); - - void setWavetable (int idx, OwnedArray& table); - + void noteStarted() override; void noteRetriggered() override; void noteStopped (bool allowTailOff) override; @@ -25,7 +23,7 @@ public: void setCurrentSampleRate (double newRate) override; - void renderNextBlock (AudioBuffer& outputBuffer, int startSample, int numSamples) override; + void renderNextBlock (juce::AudioBuffer& outputBuffer, int startSample, int numSamples) override; bool isVoiceActive() override; @@ -37,10 +35,12 @@ private: WavetableAudioProcessor& proc; gin::BandLimitedLookupTables& bandLimitedLookupTables; - gin::WTVoicedStereoOscillator wtOscillators[Cfg::numWTs]; gin::BLLTVoicedStereoOscillator oscillators[Cfg::numOSCs] = { bandLimitedLookupTables, + bandLimitedLookupTables, + bandLimitedLookupTables, + bandLimitedLookupTables, }; gin::Filter filters[Cfg::numFilters]; @@ -52,9 +52,7 @@ private: gin::AnalogADSR adsr; - float currentMidiNotes[Cfg::numWTs + Cfg::numOSCs]; - - gin::WTVoicedStereoOscillator::Params wtParams[Cfg::numWTs]; + float currentMidiNotes[Cfg::numOSCs]; gin::BLLTVoicedStereoOscillator::Params oscParams[Cfg::numOSCs]; gin::EasedValueSmoother noteSmoother; diff --git a/plugin/Wavetable.jucer b/plugin/Wavetable.jucer deleted file mode 100644 index 7e2b7c7..0000000 --- a/plugin/Wavetable.jucer +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -