From c5105693a236e2f89ec5262ae0ef91009c958e53 Mon Sep 17 00:00:00 2001 From: Tim Date: Sun, 26 Oct 2025 00:49:50 +0100 Subject: [PATCH] Fixes to the UI --- Examples/MPEIntroductionTutorial.zip | Bin 0 -> 7685 bytes .../JuceLibraryCode/JuceHeader.h | 52 ++ .../JuceLibraryCode/ReadMe.txt | 12 + .../include_juce_audio_basics.cpp | 8 + .../include_juce_audio_basics.mm | 8 + .../include_juce_audio_devices.cpp | 8 + .../include_juce_audio_devices.mm | 8 + .../include_juce_audio_formats.cpp | 8 + .../include_juce_audio_formats.mm | 8 + .../include_juce_audio_processors.cpp | 8 + .../include_juce_audio_processors.mm | 8 + .../include_juce_audio_processors_ara.cpp | 8 + ...include_juce_audio_processors_lv2_libs.cpp | 8 + .../include_juce_audio_utils.cpp | 8 + .../include_juce_audio_utils.mm | 8 + .../JuceLibraryCode/include_juce_core.cpp | 8 + .../JuceLibraryCode/include_juce_core.mm | 8 + .../include_juce_core_CompilationTime.cpp | 8 + .../include_juce_data_structures.cpp | 8 + .../include_juce_data_structures.mm | 8 + .../JuceLibraryCode/include_juce_events.cpp | 8 + .../JuceLibraryCode/include_juce_events.mm | 8 + .../JuceLibraryCode/include_juce_graphics.cpp | 8 + .../JuceLibraryCode/include_juce_graphics.mm | 8 + .../include_juce_graphics_Harfbuzz.cpp | 8 + .../include_juce_graphics_Sheenbidi.c | 8 + .../include_juce_gui_basics.cpp | 8 + .../include_juce_gui_basics.mm | 8 + .../include_juce_gui_extra.cpp | 8 + .../JuceLibraryCode/include_juce_gui_extra.mm | 8 + .../MPEIntroductionTutorial.jucer | 49 ++ .../Source/MPEIntroductionTutorial.h | 536 ++++++++++++++++++ .../MPEIntroductionTutorial/Source/Main.cpp | 96 ++++ Examples/WavetableSynthTutorial.zip | Bin 0 -> 12508 bytes .../JuceLibraryCode/JuceHeader.h | 52 ++ .../JuceLibraryCode/ReadMe.txt | 12 + .../include_juce_audio_basics.cpp | 8 + .../include_juce_audio_basics.mm | 8 + .../include_juce_audio_devices.cpp | 8 + .../include_juce_audio_devices.mm | 8 + .../include_juce_audio_formats.cpp | 8 + .../include_juce_audio_formats.mm | 8 + .../include_juce_audio_processors.cpp | 8 + .../include_juce_audio_processors.mm | 8 + .../include_juce_audio_processors_ara.cpp | 8 + ...include_juce_audio_processors_lv2_libs.cpp | 8 + .../include_juce_audio_utils.cpp | 8 + .../include_juce_audio_utils.mm | 8 + .../JuceLibraryCode/include_juce_core.cpp | 8 + .../JuceLibraryCode/include_juce_core.mm | 8 + .../include_juce_core_CompilationTime.cpp | 8 + .../include_juce_data_structures.cpp | 8 + .../include_juce_data_structures.mm | 8 + .../JuceLibraryCode/include_juce_events.cpp | 8 + .../JuceLibraryCode/include_juce_events.mm | 8 + .../JuceLibraryCode/include_juce_graphics.cpp | 8 + .../JuceLibraryCode/include_juce_graphics.mm | 8 + .../include_juce_graphics_Harfbuzz.cpp | 8 + .../include_juce_graphics_Sheenbidi.c | 8 + .../include_juce_gui_basics.cpp | 8 + .../include_juce_gui_basics.mm | 8 + .../include_juce_gui_extra.cpp | 8 + .../JuceLibraryCode/include_juce_gui_extra.mm | 8 + .../WavetableSynthTutorial/Source/Main.cpp | 96 ++++ .../Source/WavetableSynthTutorial_01.h | 163 ++++++ .../Source/WavetableSynthTutorial_02.h | 196 +++++++ .../Source/WavetableSynthTutorial_03.h | 198 +++++++ .../Source/WavetableSynthTutorial_04.h | 208 +++++++ .../WavetableSynthTutorial.jucer | 87 +++ JuceLibraryCode/JuceHeader.h | 4 +- JuceLibraryCode/JucePluginDefines.h | 10 +- NeuralSynth.jucer | 24 +- Source/PluginEditor.cpp | 70 ++- Source/PluginEditor.h | 7 +- Source/PluginProcessor.cpp | 4 +- build.sh | 4 + 76 files changed, 2280 insertions(+), 32 deletions(-) create mode 100644 Examples/MPEIntroductionTutorial.zip create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/JuceHeader.h create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/ReadMe.txt create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.mm create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.cpp create mode 100644 Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.mm create mode 100644 Examples/MPEIntroductionTutorial/MPEIntroductionTutorial.jucer create mode 100644 Examples/MPEIntroductionTutorial/Source/MPEIntroductionTutorial.h create mode 100644 Examples/MPEIntroductionTutorial/Source/Main.cpp create mode 100644 Examples/WavetableSynthTutorial.zip create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/JuceHeader.h create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/ReadMe.txt create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.mm create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.cpp create mode 100644 Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.mm create mode 100644 Examples/WavetableSynthTutorial/Source/Main.cpp create mode 100644 Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_01.h create mode 100644 Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_02.h create mode 100644 Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_03.h create mode 100644 Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_04.h create mode 100644 Examples/WavetableSynthTutorial/WavetableSynthTutorial.jucer create mode 100755 build.sh diff --git a/Examples/MPEIntroductionTutorial.zip b/Examples/MPEIntroductionTutorial.zip new file mode 100644 index 0000000000000000000000000000000000000000..f6ce5b001b1747b90dff0d42921e6c1a6131fc60 GIT binary patch literal 7685 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION + /** If you've hit this error then the version of the Projucer that was used to generate this project is + older than the version of the JUCE modules being included. To fix this error, re-save your project + using the latest version of the Projucer or, if you aren't using the Projucer to manage your project, + remove the JUCE_PROJUCER_VERSION define. + */ + #error "This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error." +#endif + +#if ! DONT_SET_USING_JUCE_NAMESPACE + // If your code uses a lot of JUCE classes, then this will obviously save you + // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. + using namespace juce; +#endif + +#if ! JUCE_DONT_DECLARE_PROJECTINFO +namespace ProjectInfo +{ + const char* const projectName = "MPEIntroductionTutorial"; + const char* const companyName = "JUCE"; + const char* const versionString = "1.0.0"; + const int versionNumber = 0x10000; +} +#endif diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/ReadMe.txt b/Examples/MPEIntroductionTutorial/JuceLibraryCode/ReadMe.txt new file mode 100644 index 0000000..1e6784f --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/ReadMe.txt @@ -0,0 +1,12 @@ + + Important Note!! + ================ + +The purpose of this folder is to contain files that are auto-generated by the Projucer, +and ALL files in this folder will be mercilessly DELETED and completely re-written whenever +the Projucer saves your project. + +Therefore, it's a bad idea to make any manual changes to the files in here, or to +put any of your own files in here if you don't want to lose them. (Of course you may choose +to add the folder's contents to your version-control system so that you can re-merge your own +modifications after the Projucer has saved its changes). diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.cpp new file mode 100644 index 0000000..4070844 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.mm new file mode 100644 index 0000000..0c09914 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_basics.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.cpp new file mode 100644 index 0000000..c9c2d11 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.mm new file mode 100644 index 0000000..77e69b1 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_devices.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.cpp new file mode 100644 index 0000000..78e74f7 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.mm new file mode 100644 index 0000000..0adf319 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_formats.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.cpp new file mode 100644 index 0000000..0dbc0b6 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.mm new file mode 100644 index 0000000..dac7f37 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp new file mode 100644 index 0000000..1651fc5 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp new file mode 100644 index 0000000..1151b5a --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.cpp new file mode 100644 index 0000000..f31e8b6 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.mm new file mode 100644 index 0000000..4dfd5b4 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_audio_utils.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.cpp new file mode 100644 index 0000000..6f55178 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.mm new file mode 100644 index 0000000..db83b69 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp new file mode 100644 index 0000000..789042d --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.cpp new file mode 100644 index 0000000..f53f241 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.mm new file mode 100644 index 0000000..db212c9 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_data_structures.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.cpp new file mode 100644 index 0000000..33a3a69 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.mm new file mode 100644 index 0000000..6ad0eda --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_events.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.cpp new file mode 100644 index 0000000..12f6750 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.mm new file mode 100644 index 0000000..ab22eb4 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp new file mode 100644 index 0000000..419cf23 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c new file mode 100644 index 0000000..df5eb4b --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.cpp new file mode 100644 index 0000000..80a5878 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.mm new file mode 100644 index 0000000..708837c --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_basics.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.cpp b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.cpp new file mode 100644 index 0000000..ea050e5 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.mm b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.mm new file mode 100644 index 0000000..9bb3fea --- /dev/null +++ b/Examples/MPEIntroductionTutorial/JuceLibraryCode/include_juce_gui_extra.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/MPEIntroductionTutorial/MPEIntroductionTutorial.jucer b/Examples/MPEIntroductionTutorial/MPEIntroductionTutorial.jucer new file mode 100644 index 0000000..72c46c6 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/MPEIntroductionTutorial.jucer @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/MPEIntroductionTutorial/Source/MPEIntroductionTutorial.h b/Examples/MPEIntroductionTutorial/Source/MPEIntroductionTutorial.h new file mode 100644 index 0000000..167dba8 --- /dev/null +++ b/Examples/MPEIntroductionTutorial/Source/MPEIntroductionTutorial.h @@ -0,0 +1,536 @@ +/* + ============================================================================== + + This file is part of the JUCE tutorials. + Copyright (c) 2020 - Raw Material Software Limited + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, + WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR + PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +/******************************************************************************* + The block below describes the properties of this PIP. A PIP is a short snippet + of code that can be read by the Projucer and used to generate a JUCE project. + + BEGIN_JUCE_PIP_METADATA + + name: MPEIntroductionTutorial + version: 1.0.0 + vendor: JUCE + website: http://juce.com + description: Synthesiser using MPE specifications. + + dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, + juce_audio_processors, juce_audio_utils, juce_core, + juce_data_structures, juce_events, juce_graphics, + juce_gui_basics, juce_gui_extra + exporters: xcode_mac, vs2019, linux_make + + type: Component + mainClass: MainComponent + + useLocalCopy: 1 + + END_JUCE_PIP_METADATA + +*******************************************************************************/ + + +#pragma once + +//============================================================================== +class NoteComponent : public juce::Component +{ +public: + NoteComponent (const juce::MPENote& n) + : note (n), colour (juce::Colours::white) + {} + + //============================================================================== + void update (const juce::MPENote& newNote, juce::Point newCentre) + { + note = newNote; + centre = newCentre; + + setBounds (getSquareAroundCentre (juce::jmax (getNoteOnRadius(), getNoteOffRadius(), getPressureRadius())) + .getUnion (getTextRectangle()) + .getSmallestIntegerContainer() + .expanded (3)); + + repaint(); + } + + //============================================================================== + void paint (juce::Graphics& g) override + { + if (note.keyState == juce::MPENote::keyDown || note.keyState == juce::MPENote::keyDownAndSustained) + drawPressedNoteCircle (g, colour); + else if (note.keyState == juce::MPENote::sustained) + drawSustainedNoteCircle (g, colour); + else + return; + + drawNoteLabel (g, colour); + } + + //============================================================================== + juce::MPENote note; + juce::Colour colour; + juce::Point centre; + +private: + //============================================================================== + void drawPressedNoteCircle (juce::Graphics& g, juce::Colour zoneColour) + { + g.setColour (zoneColour.withAlpha (0.3f)); + g.fillEllipse (translateToLocalBounds (getSquareAroundCentre (getNoteOnRadius()))); + g.setColour (zoneColour); + g.drawEllipse (translateToLocalBounds (getSquareAroundCentre (getPressureRadius())), 2.0f); + } + + //============================================================================== + void drawSustainedNoteCircle (juce::Graphics& g, juce::Colour zoneColour) + { + g.setColour (zoneColour); + juce::Path circle, dashedCircle; + circle.addEllipse (translateToLocalBounds (getSquareAroundCentre (getNoteOffRadius()))); + const float dashLengths[] = { 3.0f, 3.0f }; + juce::PathStrokeType (2.0, juce::PathStrokeType::mitered).createDashedStroke (dashedCircle, circle, dashLengths, 2); + g.fillPath (dashedCircle); + } + + //============================================================================== + void drawNoteLabel (juce::Graphics& g, juce::Colour) + { + auto textBounds = translateToLocalBounds (getTextRectangle()).getSmallestIntegerContainer(); + g.drawText ("+", textBounds, juce::Justification::centred); + g.drawText (juce::MidiMessage::getMidiNoteName (note.initialNote, true, true, 3), textBounds, juce::Justification::centredBottom); + g.setFont (juce::Font (22.0f, juce::Font::bold)); + g.drawText (juce::String (note.midiChannel), textBounds, juce::Justification::centredTop); + } + + //============================================================================== + juce::Rectangle getSquareAroundCentre (float radius) const noexcept + { + return juce::Rectangle (radius * 2.0f, radius * 2.0f).withCentre (centre); + } + + juce::Rectangle translateToLocalBounds (juce::Rectangle r) const noexcept + { + return r - getPosition().toFloat(); + } + + juce::Rectangle getTextRectangle() const noexcept + { + return juce::Rectangle (30.0f, 50.0f).withCentre (centre); + } + + float getNoteOnRadius() const noexcept { return note.noteOnVelocity.asUnsignedFloat() * maxNoteRadius; } + float getNoteOffRadius() const noexcept { return note.noteOffVelocity.asUnsignedFloat() * maxNoteRadius; } + float getPressureRadius() const noexcept { return note.pressure.asUnsignedFloat() * maxNoteRadius; } + + static constexpr auto maxNoteRadius = 100.0f; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NoteComponent) +}; + +//============================================================================== +class Visualiser : public juce::Component, + public juce::MPEInstrument::Listener, + private juce::AsyncUpdater +{ +public: + //============================================================================== + Visualiser() {} + + //============================================================================== + void paint (juce::Graphics& g) override + { + g.fillAll (juce::Colours::black); + + auto noteDistance = float (getWidth()) / 128; + + for (auto i = 0; i < 128; ++i) + { + auto x = noteDistance * (float) i; + auto noteHeight = int (juce::MidiMessage::isMidiNoteBlack (i) ? 0.7 * getHeight() : getHeight()); + g.setColour (juce::MidiMessage::isMidiNoteBlack (i) ? juce::Colours::white : juce::Colours::grey); + g.drawLine (x, 0.0f, x, (float) noteHeight); + + if (i > 0 && i % 12 == 0) + { + g.setColour (juce::Colours::grey); + auto octaveNumber = (i / 12) - 2; + g.drawText ("C" + juce::String (octaveNumber), (int) x - 15, getHeight() - 30, 30, 30, juce::Justification::centredBottom); + } + } + } + + //============================================================================== + void noteAdded (juce::MPENote newNote) override + { + const juce::ScopedLock sl (lock); + activeNotes.add (newNote); + triggerAsyncUpdate(); + } + + void notePressureChanged (juce::MPENote note) override { noteChanged (note); } + void notePitchbendChanged (juce::MPENote note) override { noteChanged (note); } + void noteTimbreChanged (juce::MPENote note) override { noteChanged (note); } + void noteKeyStateChanged (juce::MPENote note) override { noteChanged (note); } + + void noteChanged (juce::MPENote changedNote) + { + const juce::ScopedLock sl (lock); + + for (auto& note : activeNotes) + if (note.noteID == changedNote.noteID) + note = changedNote; + + triggerAsyncUpdate(); + } + + void noteReleased (juce::MPENote finishedNote) override + { + const juce::ScopedLock sl (lock); + + for (auto i = activeNotes.size(); --i >= 0;) + if (activeNotes.getReference(i).noteID == finishedNote.noteID) + activeNotes.remove (i); + + triggerAsyncUpdate(); + } + + +private: + //============================================================================== + const juce::MPENote* findActiveNote (int noteID) const noexcept + { + for (auto& note : activeNotes) + if (note.noteID == noteID) + return ¬e; + + return nullptr; + } + + NoteComponent* findNoteComponent (int noteID) const noexcept + { + for (auto& noteComp : noteComponents) + if (noteComp->note.noteID == noteID) + return noteComp; + + return nullptr; + } + + //============================================================================== + void handleAsyncUpdate() override + { + const juce::ScopedLock sl (lock); + + for (auto i = noteComponents.size(); --i >= 0;) + if (findActiveNote (noteComponents.getUnchecked(i)->note.noteID) == nullptr) + noteComponents.remove (i); + + for (auto& note : activeNotes) + if (findNoteComponent (note.noteID) == nullptr) + addAndMakeVisible (noteComponents.add (new NoteComponent (note))); + + for (auto& noteComp : noteComponents) + if (auto* noteInfo = findActiveNote (noteComp->note.noteID)) + noteComp->update (*noteInfo, getCentrePositionForNote (*noteInfo)); + } + + //============================================================================== + juce::Point getCentrePositionForNote (juce::MPENote note) const + { + auto n = float (note.initialNote) + float (note.totalPitchbendInSemitones); + auto x = (float) getWidth() * n / 128; + auto y = (float) getHeight() * (1 - note.timbre.asUnsignedFloat()); + + return { x, y }; + } + + //============================================================================== + juce::OwnedArray noteComponents; + juce::CriticalSection lock; + juce::Array activeNotes; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Visualiser) +}; + +//============================================================================== +class MPEDemoSynthVoice : public juce::MPESynthesiserVoice +{ +public: + //============================================================================== + MPEDemoSynthVoice() {} + + //============================================================================== + void noteStarted() override + { + jassert (currentlyPlayingNote.isValid()); + jassert (currentlyPlayingNote.keyState == juce::MPENote::keyDown + || currentlyPlayingNote.keyState == juce::MPENote::keyDownAndSustained); + + // get data from the current MPENote + level .setTargetValue (currentlyPlayingNote.pressure.asUnsignedFloat()); + frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz()); + timbre .setTargetValue (currentlyPlayingNote.timbre.asUnsignedFloat()); + + phase = 0.0; + auto cyclesPerSample = frequency.getNextValue() / currentSampleRate; + phaseDelta = 2.0 * juce::MathConstants::pi * cyclesPerSample; + + tailOff = 0.0; + } + + void noteStopped (bool allowTailOff) override + { + jassert (currentlyPlayingNote.keyState == juce::MPENote::off); + + if (allowTailOff) + { + // start a tail-off by setting this flag. The render callback will pick up on + // this and do a fade out, calling clearCurrentNote() when it's finished. + + if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the + // stopNote method could be called more than once. + tailOff = 1.0; + } + else + { + // we're being told to stop playing immediately, so reset everything.. + clearCurrentNote(); + phaseDelta = 0.0; + } + } + + void notePressureChanged() override + { + level.setTargetValue (currentlyPlayingNote.pressure.asUnsignedFloat()); + } + + void notePitchbendChanged() override + { + frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz()); + } + + void noteTimbreChanged() override + { + timbre.setTargetValue (currentlyPlayingNote.timbre.asUnsignedFloat()); + } + + void noteKeyStateChanged() override {} + + void setCurrentSampleRate (double newRate) override + { + if (! juce::approximatelyEqual (currentSampleRate, newRate)) + { + noteStopped (false); + currentSampleRate = newRate; + + level .reset (currentSampleRate, smoothingLengthInSeconds); + timbre .reset (currentSampleRate, smoothingLengthInSeconds); + frequency.reset (currentSampleRate, smoothingLengthInSeconds); + } + } + + //============================================================================== + void renderNextBlock (juce::AudioBuffer& outputBuffer, + int startSample, + int numSamples) override + { + if (phaseDelta != 0.0) + { + if (tailOff > 0.0) + { + while (--numSamples >= 0) + { + auto currentSample = getNextSample() * (float) tailOff; + + for (auto i = outputBuffer.getNumChannels(); --i >= 0;) + outputBuffer.addSample (i, startSample, currentSample); + + ++startSample; + + tailOff *= 0.99; + + if (tailOff <= 0.005) + { + clearCurrentNote(); + + phaseDelta = 0.0; + break; + } + } + } + else + { + while (--numSamples >= 0) + { + auto currentSample = getNextSample(); + + for (auto i = outputBuffer.getNumChannels(); --i >= 0;) + outputBuffer.addSample (i, startSample, currentSample); + + ++startSample; + } + } + } + } + +private: + //============================================================================== + float getNextSample() noexcept + { + auto levelDb = (level.getNextValue() - 1.0) * maxLevelDb; + auto amplitude = std::pow (10.0f, 0.05f * levelDb) * maxLevel; + + // timbre is used to blend between a sine and a square. + auto f1 = std::sin (phase); + auto f2 = std::copysign (1.0, f1); + auto a2 = timbre.getNextValue(); + auto a1 = 1.0 - a2; + + auto nextSample = float (amplitude * ((a1 * f1) + (a2 * f2))); + + auto cyclesPerSample = frequency.getNextValue() / currentSampleRate; + phaseDelta = 2.0 * juce::MathConstants::pi * cyclesPerSample; + phase = std::fmod (phase + phaseDelta, 2.0 * juce::MathConstants::pi); + + return nextSample; + } + + //============================================================================== + juce::SmoothedValue level, timbre, frequency; + + double phase = 0.0; + double phaseDelta = 0.0; + double tailOff = 0.0; + + // some useful constants + static constexpr auto maxLevel = 0.05; + static constexpr auto maxLevelDb = 31.0; + static constexpr auto smoothingLengthInSeconds = 0.01; +}; + +//============================================================================== +class MainComponent : public juce::Component, + private juce::AudioIODeviceCallback, // [1] + private juce::MidiInputCallback // [2] +{ +public: + //============================================================================== + MainComponent() + : audioSetupComp (audioDeviceManager, 0, 0, 0, 256, + true, // showMidiInputOptions must be true + true, true, false) + { + audioDeviceManager.initialise (0, 2, nullptr, true, {}, nullptr); + audioDeviceManager.addMidiInputDeviceCallback ({}, this); // [6] + audioDeviceManager.addAudioCallback (this); + + addAndMakeVisible (audioSetupComp); + addAndMakeVisible (visualiserViewport); + + visualiserViewport.setScrollBarsShown (false, true); + visualiserViewport.setViewedComponent (&visualiserComp, false); + visualiserViewport.setViewPositionProportionately (0.5, 0.0); + + visualiserInstrument.addListener (&visualiserComp); + + for (auto i = 0; i < 15; ++i) + synth.addVoice (new MPEDemoSynthVoice()); + + synth.enableLegacyMode (24); + synth.setVoiceStealingEnabled (false); + + visualiserInstrument.enableLegacyMode (24); + + setSize (650, 560); + } + + ~MainComponent() override + { + audioDeviceManager.removeMidiInputDeviceCallback ({}, this); + audioDeviceManager.removeAudioCallback (this); + } + + //============================================================================== + void resized() override + { + auto visualiserCompWidth = 2800; + auto visualiserCompHeight = 300; + + auto r = getLocalBounds(); + + visualiserViewport.setBounds (r.removeFromBottom (visualiserCompHeight)); + visualiserComp.setBounds ({ visualiserCompWidth, + visualiserViewport.getHeight() - visualiserViewport.getScrollBarThickness() }); + + audioSetupComp.setBounds (r); + } + + //============================================================================== + void audioDeviceIOCallbackWithContext (const float* const* /*inputChannelData*/, + int /*numInputChannels*/, + float* const* outputChannelData, + int numOutputChannels, + int numSamples, + const juce::AudioIODeviceCallbackContext& /*context*/) override + { + // make buffer + juce::AudioBuffer buffer (outputChannelData, numOutputChannels, numSamples); + + // clear it to silence + buffer.clear(); + + juce::MidiBuffer incomingMidi; + + // get the MIDI messages for this audio block + midiCollector.removeNextBlockOfMessages (incomingMidi, numSamples); + + // synthesise the block + synth.renderNextBlock (buffer, incomingMidi, 0, numSamples); + } + + void audioDeviceAboutToStart (juce::AudioIODevice* device) override + { + auto sampleRate = device->getCurrentSampleRate(); + midiCollector.reset (sampleRate); + synth.setCurrentPlaybackSampleRate (sampleRate); + } + + void audioDeviceStopped() override {} + +private: + //============================================================================== + void handleIncomingMidiMessage (juce::MidiInput* /*source*/, + const juce::MidiMessage& message) override + { + visualiserInstrument.processNextMidiEvent (message); + midiCollector.addMessageToQueue (message); + } + + //============================================================================== + juce::AudioDeviceManager audioDeviceManager; // [3] + juce::AudioDeviceSelectorComponent audioSetupComp; // [4] + + Visualiser visualiserComp; + juce::Viewport visualiserViewport; + + juce::MPEInstrument visualiserInstrument; + juce::MPESynthesiser synth; + juce::MidiMessageCollector midiCollector; // [5] + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent) +}; diff --git a/Examples/MPEIntroductionTutorial/Source/Main.cpp b/Examples/MPEIntroductionTutorial/Source/Main.cpp new file mode 100644 index 0000000..12364ba --- /dev/null +++ b/Examples/MPEIntroductionTutorial/Source/Main.cpp @@ -0,0 +1,96 @@ +/* + ============================================================================== + + This file contains the startup code for a PIP. + + ============================================================================== +*/ + +#include +#include "MPEIntroductionTutorial.h" + +class Application : public juce::JUCEApplication +{ +public: + //============================================================================== + Application() = default; + + const juce::String getApplicationName() override { return "MPEIntroductionTutorial"; } + const juce::String getApplicationVersion() override { return "1.0.0"; } + + void initialise (const juce::String&) override + { + mainWindow.reset (new MainWindow ("MPEIntroductionTutorial", std::make_unique(), *this)); + } + + void shutdown() override { mainWindow = nullptr; } + +private: + class MainWindow : public juce::DocumentWindow + { + public: + MainWindow (const juce::String& name, std::unique_ptr c, JUCEApplication& a) + : DocumentWindow (name, juce::Desktop::getInstance().getDefaultLookAndFeel() + .findColour (ResizableWindow::backgroundColourId), + juce::DocumentWindow::allButtons), + app (a) + { + setUsingNativeTitleBar (true); + + #if JUCE_ANDROID || JUCE_IOS + setContentOwned (new SafeAreaComponent { std::move (c) }, true); + setFullScreen (true); + #else + setContentOwned (c.release(), true); + setResizable (true, false); + setResizeLimits (300, 250, 10000, 10000); + centreWithSize (getWidth(), getHeight()); + #endif + + setVisible (true); + } + + void closeButtonPressed() override + { + app.systemRequestedQuit(); + } + + #if JUCE_ANDROID || JUCE_IOS + class SafeAreaComponent : public juce::Component + { + public: + explicit SafeAreaComponent (std::unique_ptr c) + : content (std::move (c)) + { + addAndMakeVisible (*content); + } + + void resized() override + { + if (const auto* d = Desktop::getInstance().getDisplays().getDisplayForRect (getLocalBounds())) + content->setBounds (d->safeAreaInsets.subtractedFrom (getLocalBounds())); + } + + private: + std::unique_ptr content; + }; + + void parentSizeChanged() override + { + if (auto* c = getContentComponent()) + c->resized(); + } + #endif + + private: + JUCEApplication& app; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + }; + + std::unique_ptr mainWindow; +}; + +//============================================================================== +START_JUCE_APPLICATION (Application) diff --git a/Examples/WavetableSynthTutorial.zip b/Examples/WavetableSynthTutorial.zip new file mode 100644 index 0000000000000000000000000000000000000000..bf8ae670e29bb4d92135d3eb633450b3bf2f51ed GIT binary patch literal 12508 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�sDcutp9CO5;9#?5h(?tPL?)%Rfak~+}Q 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 literal 0 HcmV?d00001 diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/JuceHeader.h b/Examples/WavetableSynthTutorial/JuceLibraryCode/JuceHeader.h new file mode 100644 index 0000000..0b4b794 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/JuceHeader.h @@ -0,0 +1,52 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + + This is the header file that your files should include in order to get all the + JUCE library headers. You should avoid including the JUCE headers directly in + your own source files, because that wouldn't pick up the correct configuration + options for your app. + +*/ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION + /** If you've hit this error then the version of the Projucer that was used to generate this project is + older than the version of the JUCE modules being included. To fix this error, re-save your project + using the latest version of the Projucer or, if you aren't using the Projucer to manage your project, + remove the JUCE_PROJUCER_VERSION define. + */ + #error "This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error." +#endif + +#if ! DONT_SET_USING_JUCE_NAMESPACE + // If your code uses a lot of JUCE classes, then this will obviously save you + // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. + using namespace juce; +#endif + +#if ! JUCE_DONT_DECLARE_PROJECTINFO +namespace ProjectInfo +{ + const char* const projectName = "WavetableSynthTutorial"; + const char* const companyName = "JUCE"; + const char* const versionString = "1.0.0"; + const int versionNumber = 0x10000; +} +#endif diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/ReadMe.txt b/Examples/WavetableSynthTutorial/JuceLibraryCode/ReadMe.txt new file mode 100644 index 0000000..1e6784f --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/ReadMe.txt @@ -0,0 +1,12 @@ + + Important Note!! + ================ + +The purpose of this folder is to contain files that are auto-generated by the Projucer, +and ALL files in this folder will be mercilessly DELETED and completely re-written whenever +the Projucer saves your project. + +Therefore, it's a bad idea to make any manual changes to the files in here, or to +put any of your own files in here if you don't want to lose them. (Of course you may choose +to add the folder's contents to your version-control system so that you can re-merge your own +modifications after the Projucer has saved its changes). diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.cpp new file mode 100644 index 0000000..4070844 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.mm new file mode 100644 index 0000000..0c09914 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_basics.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.cpp new file mode 100644 index 0000000..c9c2d11 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.mm new file mode 100644 index 0000000..77e69b1 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_devices.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.cpp new file mode 100644 index 0000000..78e74f7 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.mm new file mode 100644 index 0000000..0adf319 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_formats.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.cpp new file mode 100644 index 0000000..0dbc0b6 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.mm new file mode 100644 index 0000000..dac7f37 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp new file mode 100644 index 0000000..1651fc5 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_ara.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp new file mode 100644 index 0000000..1151b5a --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.cpp new file mode 100644 index 0000000..f31e8b6 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.mm new file mode 100644 index 0000000..4dfd5b4 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_audio_utils.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.cpp new file mode 100644 index 0000000..6f55178 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.mm new file mode 100644 index 0000000..db83b69 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp new file mode 100644 index 0000000..789042d --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_core_CompilationTime.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.cpp new file mode 100644 index 0000000..f53f241 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.mm new file mode 100644 index 0000000..db212c9 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_data_structures.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.cpp new file mode 100644 index 0000000..33a3a69 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.mm new file mode 100644 index 0000000..6ad0eda --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_events.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.cpp new file mode 100644 index 0000000..12f6750 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.mm new file mode 100644 index 0000000..ab22eb4 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp new file mode 100644 index 0000000..419cf23 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Harfbuzz.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c new file mode 100644 index 0000000..df5eb4b --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_graphics_Sheenbidi.c @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.cpp new file mode 100644 index 0000000..80a5878 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.mm new file mode 100644 index 0000000..708837c --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_basics.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.cpp b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.cpp new file mode 100644 index 0000000..ea050e5 --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.cpp @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.mm b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.mm new file mode 100644 index 0000000..9bb3fea --- /dev/null +++ b/Examples/WavetableSynthTutorial/JuceLibraryCode/include_juce_gui_extra.mm @@ -0,0 +1,8 @@ +/* + + IMPORTANT! This file is auto-generated each time you save your + project - if you alter its contents, your changes may be overwritten! + +*/ + +#include diff --git a/Examples/WavetableSynthTutorial/Source/Main.cpp b/Examples/WavetableSynthTutorial/Source/Main.cpp new file mode 100644 index 0000000..08c890c --- /dev/null +++ b/Examples/WavetableSynthTutorial/Source/Main.cpp @@ -0,0 +1,96 @@ +/* + ============================================================================== + + This file contains the startup code for a PIP. + + ============================================================================== +*/ + +#include +#include "WavetableSynthTutorial_01.h" + +class Application : public juce::JUCEApplication +{ +public: + //============================================================================== + Application() = default; + + const juce::String getApplicationName() override { return "WavetableSynthTutorial"; } + const juce::String getApplicationVersion() override { return "1.0.0"; } + + void initialise (const juce::String&) override + { + mainWindow.reset (new MainWindow ("WavetableSynthTutorial", std::make_unique(), *this)); + } + + void shutdown() override { mainWindow = nullptr; } + +private: + class MainWindow : public juce::DocumentWindow + { + public: + MainWindow (const juce::String& name, std::unique_ptr c, JUCEApplication& a) + : DocumentWindow (name, juce::Desktop::getInstance().getDefaultLookAndFeel() + .findColour (ResizableWindow::backgroundColourId), + juce::DocumentWindow::allButtons), + app (a) + { + setUsingNativeTitleBar (true); + + #if JUCE_ANDROID || JUCE_IOS + setContentOwned (new SafeAreaComponent { std::move (c) }, true); + setFullScreen (true); + #else + setContentOwned (c.release(), true); + setResizable (true, false); + setResizeLimits (300, 250, 10000, 10000); + centreWithSize (getWidth(), getHeight()); + #endif + + setVisible (true); + } + + void closeButtonPressed() override + { + app.systemRequestedQuit(); + } + + #if JUCE_ANDROID || JUCE_IOS + class SafeAreaComponent : public juce::Component + { + public: + explicit SafeAreaComponent (std::unique_ptr c) + : content (std::move (c)) + { + addAndMakeVisible (*content); + } + + void resized() override + { + if (const auto* d = Desktop::getInstance().getDisplays().getDisplayForRect (getLocalBounds())) + content->setBounds (d->safeAreaInsets.subtractedFrom (getLocalBounds())); + } + + private: + std::unique_ptr content; + }; + + void parentSizeChanged() override + { + if (auto* c = getContentComponent()) + c->resized(); + } + #endif + + private: + JUCEApplication& app; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + }; + + std::unique_ptr mainWindow; +}; + +//============================================================================== +START_JUCE_APPLICATION (Application) diff --git a/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_01.h b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_01.h new file mode 100644 index 0000000..ff24303 --- /dev/null +++ b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_01.h @@ -0,0 +1,163 @@ +/* + ============================================================================== + + This file is part of the JUCE tutorials. + Copyright (c) 2020 - Raw Material Software Limited + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, + WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR + PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +/******************************************************************************* + The block below describes the properties of this PIP. A PIP is a short snippet + of code that can be read by the Projucer and used to generate a JUCE project. + + BEGIN_JUCE_PIP_METADATA + + name: WavetableSynthTutorial + version: 1.0.0 + vendor: JUCE + website: http://juce.com + description: Wavetable synthesiser. + + dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, + juce_audio_processors, juce_audio_utils, juce_core, + juce_data_structures, juce_events, juce_graphics, + juce_gui_basics, juce_gui_extra + exporters: xcode_mac, vs2019, linux_make + + type: Component + mainClass: MainContentComponent + + useLocalCopy: 1 + + END_JUCE_PIP_METADATA + +*******************************************************************************/ + + +#pragma once + +//============================================================================== +class SineOscillator +{ +public: + SineOscillator() {} + + void setFrequency (float frequency, float sampleRate) + { + auto cyclesPerSample = frequency / sampleRate; + angleDelta = cyclesPerSample * juce::MathConstants::twoPi; + } + + forcedinline void updateAngle() noexcept + { + currentAngle += angleDelta; + if (currentAngle >= juce::MathConstants::twoPi) + currentAngle -= juce::MathConstants::twoPi; + } + + forcedinline float getNextSample() noexcept + { + auto currentSample = std::sin (currentAngle); + updateAngle(); + return currentSample; + } + +private: + float currentAngle = 0.0f, angleDelta = 0.0f; +}; + +//============================================================================== +class MainContentComponent : public juce::AudioAppComponent, + public juce::Timer +{ +public: + MainContentComponent() + { + cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification); + cpuUsageText.setJustificationType (juce::Justification::right); + addAndMakeVisible (cpuUsageLabel); + addAndMakeVisible (cpuUsageText); + + setSize (400, 200); + setAudioChannels (0, 2); // no inputs, two outputs + startTimer (50); + } + + ~MainContentComponent() override + { + shutdownAudio(); + } + + void resized() override + { + cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20); + cpuUsageText .setBounds (10, 10, getWidth() - 20, 20); + } + + void timerCallback() override + { + auto cpu = deviceManager.getCpuUsage() * 100; + cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification); + } + + void prepareToPlay (int, double sampleRate) override + { + auto numberOfOscillators = 200; // [1] + + for (auto i = 0; i < numberOfOscillators; ++i) + { + auto* oscillator = new SineOscillator(); // [2] + + auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0; // [3] + auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0); // [4] + + oscillator->setFrequency ((float) frequency, (float) sampleRate); // [5] + oscillators.add (oscillator); + } + + level = 0.25f / (float) numberOfOscillators; // [6] + } + + void releaseResources() override {} + + void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override + { + auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); // [7] + auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample); + + bufferToFill.clearActiveBufferRegion(); + + for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex) + { + auto* oscillator = oscillators.getUnchecked (oscillatorIndex); // [8] + + for (auto sample = 0; sample < bufferToFill.numSamples; ++sample) + { + auto levelSample = oscillator->getNextSample() * level; // [9] + + leftBuffer[sample] += levelSample; // [10] + rightBuffer[sample] += levelSample; + } + } + } + +private: + juce::Label cpuUsageLabel; + juce::Label cpuUsageText; + + float level = 0.0f; + juce::OwnedArray oscillators; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) +}; diff --git a/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_02.h b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_02.h new file mode 100644 index 0000000..0a538da --- /dev/null +++ b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_02.h @@ -0,0 +1,196 @@ +/* + ============================================================================== + + This file is part of the JUCE tutorials. + Copyright (c) 2020 - Raw Material Software Limited + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, + WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR + PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +/******************************************************************************* + The block below describes the properties of this PIP. A PIP is a short snippet + of code that can be read by the Projucer and used to generate a JUCE project. + + BEGIN_JUCE_PIP_METADATA + + name: WavetableSynthTutorial + version: 2.0.0 + vendor: JUCE + website: http://juce.com + description: Wavetable synthesiser. + + dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, + juce_audio_processors, juce_audio_utils, juce_core, + juce_data_structures, juce_events, juce_graphics, + juce_gui_basics, juce_gui_extra + exporters: xcode_mac, vs2019, linux_make + + type: Component + mainClass: MainContentComponent + + useLocalCopy: 1 + + END_JUCE_PIP_METADATA + +*******************************************************************************/ + + +#pragma once + +//============================================================================== +class WavetableOscillator +{ +public: + WavetableOscillator (const juce::AudioSampleBuffer& wavetableToUse) + : wavetable (wavetableToUse) + { + jassert (wavetable.getNumChannels() == 1); + } + + void setFrequency (float frequency, float sampleRate) + { + auto tableSizeOverSampleRate = (float) wavetable.getNumSamples() / sampleRate; + tableDelta = frequency * tableSizeOverSampleRate; + } + + forcedinline float getNextSample() noexcept + { + auto tableSize = (unsigned int) wavetable.getNumSamples(); + + auto index0 = (unsigned int) currentIndex; // [6] + auto index1 = index0 == (tableSize - 1) ? (unsigned int) 0 : index0 + 1; + + auto frac = currentIndex - (float) index0; // [7] + + auto* table = wavetable.getReadPointer (0); // [8] + auto value0 = table[index0]; + auto value1 = table[index1]; + + auto currentSample = value0 + frac * (value1 - value0); // [9] + + if ((currentIndex += tableDelta) > (float) tableSize) // [10] + currentIndex -= (float) tableSize; + + return currentSample; + } + +private: + const juce::AudioSampleBuffer& wavetable; + float currentIndex = 0.0f, tableDelta = 0.0f; +}; + +//============================================================================== +class MainContentComponent : public juce::AudioAppComponent, + public juce::Timer +{ +public: + MainContentComponent() + { + cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification); + cpuUsageText.setJustificationType (juce::Justification::right); + addAndMakeVisible (cpuUsageLabel); + addAndMakeVisible (cpuUsageText); + + createWavetable(); + + setSize (400, 200); + setAudioChannels (0, 2); // no inputs, two outputs + startTimer (50); + } + + ~MainContentComponent() override + { + shutdownAudio(); + } + + void resized() override + { + cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20); + cpuUsageText .setBounds (10, 10, getWidth() - 20, 20); + } + + void timerCallback() override + { + auto cpu = deviceManager.getCpuUsage() * 100; + cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification); + } + + void createWavetable() + { + sineTable.setSize (1, (int) tableSize); + auto* samples = sineTable.getWritePointer (0); // [3] + + auto angleDelta = juce::MathConstants::twoPi / (double) (tableSize - 1); // [4] + auto currentAngle = 0.0; + + for (unsigned int i = 0; i < tableSize; ++i) + { + auto sample = std::sin (currentAngle); // [5] + samples[i] = (float) sample; + currentAngle += angleDelta; + } + } + + void prepareToPlay (int, double sampleRate) override + { + auto numberOfOscillators = 200; + + for (auto i = 0; i < numberOfOscillators; ++i) + { + auto* oscillator = new WavetableOscillator (sineTable); + + auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0; + auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0); + + oscillator->setFrequency ((float) frequency, (float) sampleRate); + oscillators.add (oscillator); + } + + level = 0.25f / (float) numberOfOscillators; + } + + void releaseResources() override {} + + void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override + { + auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); + auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample); + + bufferToFill.clearActiveBufferRegion(); + + for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex) + { + auto* oscillator = oscillators.getUnchecked (oscillatorIndex); + + for (auto sample = 0; sample < bufferToFill.numSamples; ++sample) + { + auto levelSample = oscillator->getNextSample() * level; + + leftBuffer[sample] += levelSample; + rightBuffer[sample] += levelSample; + } + } + } + +private: + juce::Label cpuUsageLabel; + juce::Label cpuUsageText; + + const unsigned int tableSize = 1 << 7; // [2] + float level = 0.0f; + + juce::AudioSampleBuffer sineTable; // [1] + juce::OwnedArray oscillators; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) +}; diff --git a/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_03.h b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_03.h new file mode 100644 index 0000000..f6de098 --- /dev/null +++ b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_03.h @@ -0,0 +1,198 @@ +/* + ============================================================================== + + This file is part of the JUCE tutorials. + Copyright (c) 2020 - Raw Material Software Limited + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, + WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR + PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +/******************************************************************************* + The block below describes the properties of this PIP. A PIP is a short snippet + of code that can be read by the Projucer and used to generate a JUCE project. + + BEGIN_JUCE_PIP_METADATA + + name: WavetableSynthTutorial + version: 3.0.0 + vendor: JUCE + website: http://juce.com + description: Wavetable synthesiser. + + dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, + juce_audio_processors, juce_audio_utils, juce_core, + juce_data_structures, juce_events, juce_graphics, + juce_gui_basics, juce_gui_extra + exporters: xcode_mac, vs2019, linux_make + + type: Component + mainClass: MainContentComponent + + useLocalCopy: 1 + + END_JUCE_PIP_METADATA + +*******************************************************************************/ + + +#pragma once + +//============================================================================== +class WavetableOscillator +{ +public: + WavetableOscillator (const juce::AudioSampleBuffer& wavetableToUse) + : wavetable (wavetableToUse), + tableSize (wavetable.getNumSamples() - 1) + { + jassert (wavetable.getNumChannels() == 1); + } + + void setFrequency (float frequency, float sampleRate) + { + auto tableSizeOverSampleRate = (float) tableSize / sampleRate; + tableDelta = frequency * tableSizeOverSampleRate; + } + + forcedinline float getNextSample() noexcept + { + auto index0 = (unsigned int) currentIndex; + auto index1 = index0 + 1; + + auto frac = currentIndex - (float) index0; + + auto* table = wavetable.getReadPointer (0); + auto value0 = table[index0]; + auto value1 = table[index1]; + + auto currentSample = value0 + frac * (value1 - value0); + + if ((currentIndex += tableDelta) > (float) tableSize) + currentIndex -= (float) tableSize; + + return currentSample; + } + +private: + const juce::AudioSampleBuffer& wavetable; + const int tableSize; + float currentIndex = 0.0f, tableDelta = 0.0f; +}; + +//============================================================================== +class MainContentComponent : public juce::AudioAppComponent, + public juce::Timer +{ +public: + MainContentComponent() + { + cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification); + cpuUsageText.setJustificationType (juce::Justification::right); + addAndMakeVisible (cpuUsageLabel); + addAndMakeVisible (cpuUsageText); + + createWavetable(); + + setSize (400, 200); + setAudioChannels (0, 2); // no inputs, two outputs + startTimer (50); + } + + ~MainContentComponent() override + { + shutdownAudio(); + } + + void resized() override + { + cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20); + cpuUsageText .setBounds (10, 10, getWidth() - 20, 20); + } + + void timerCallback() override + { + auto cpu = deviceManager.getCpuUsage() * 100; + cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification); + } + + void createWavetable() + { + sineTable.setSize (1, (int) tableSize + 1); + auto* samples = sineTable.getWritePointer (0); + + auto angleDelta = juce::MathConstants::twoPi / (double) (tableSize - 1); + auto currentAngle = 0.0; + + for (unsigned int i = 0; i < tableSize; ++i) + { + auto sample = std::sin (currentAngle); + samples[i] = (float) sample; + currentAngle += angleDelta; + } + + samples[tableSize] = samples[0]; + } + + void prepareToPlay (int, double sampleRate) override + { + auto numberOfOscillators = 200; + + for (auto i = 0; i < numberOfOscillators; ++i) + { + auto* oscillator = new WavetableOscillator (sineTable); + + auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0; + auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0); + + oscillator->setFrequency ((float) frequency, (float) sampleRate); + oscillators.add (oscillator); + } + + level = 0.25f / (float) numberOfOscillators; + } + + void releaseResources() override {} + + void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override + { + auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); + auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample); + + bufferToFill.clearActiveBufferRegion(); + + for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex) + { + auto* oscillator = oscillators.getUnchecked (oscillatorIndex); + + for (auto sample = 0; sample < bufferToFill.numSamples; ++sample) + { + auto levelSample = oscillator->getNextSample() * level; + + leftBuffer[sample] += levelSample; + rightBuffer[sample] += levelSample; + } + } + } + +private: + juce::Label cpuUsageLabel; + juce::Label cpuUsageText; + + const unsigned int tableSize = 1 << 7; + float level = 0.0f; + + juce::AudioSampleBuffer sineTable; + juce::OwnedArray oscillators; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) +}; diff --git a/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_04.h b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_04.h new file mode 100644 index 0000000..9a7b12c --- /dev/null +++ b/Examples/WavetableSynthTutorial/Source/WavetableSynthTutorial_04.h @@ -0,0 +1,208 @@ +/* + ============================================================================== + + This file is part of the JUCE tutorials. + Copyright (c) 2020 - Raw Material Software Limited + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, + WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR + PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +/******************************************************************************* + The block below describes the properties of this PIP. A PIP is a short snippet + of code that can be read by the Projucer and used to generate a JUCE project. + + BEGIN_JUCE_PIP_METADATA + + name: WavetableSynthTutorial + version: 4.0.0 + vendor: JUCE + website: http://juce.com + description: Wavetable synthesiser. + + dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, + juce_audio_processors, juce_audio_utils, juce_core, + juce_data_structures, juce_events, juce_graphics, + juce_gui_basics, juce_gui_extra + exporters: xcode_mac, vs2019, linux_make + + type: Component + mainClass: MainContentComponent + + useLocalCopy: 1 + + END_JUCE_PIP_METADATA + +*******************************************************************************/ + + +#pragma once + +//============================================================================== +class WavetableOscillator +{ +public: + WavetableOscillator (const juce::AudioSampleBuffer& wavetableToUse) + : wavetable (wavetableToUse), + tableSize (wavetable.getNumSamples() - 1) + { + jassert (wavetable.getNumChannels() == 1); + } + + void setFrequency (float frequency, float sampleRate) + { + auto tableSizeOverSampleRate = (float) tableSize / sampleRate; + tableDelta = frequency * tableSizeOverSampleRate; + } + + forcedinline float getNextSample() noexcept + { + auto index0 = (unsigned int) currentIndex; + auto index1 = index0 + 1; + + auto frac = currentIndex - (float) index0; + + auto* table = wavetable.getReadPointer (0); + auto value0 = table[index0]; + auto value1 = table[index1]; + + auto currentSample = value0 + frac * (value1 - value0); + + if ((currentIndex += tableDelta) > (float) tableSize) + currentIndex -= (float) tableSize; + + return currentSample; + } + +private: + const juce::AudioSampleBuffer& wavetable; + const int tableSize; + float currentIndex = 0.0f, tableDelta = 0.0f; +}; + +//============================================================================== +class MainContentComponent : public juce::AudioAppComponent, + public juce::Timer +{ +public: + MainContentComponent() + { + cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification); + cpuUsageText.setJustificationType (juce::Justification::right); + addAndMakeVisible (cpuUsageLabel); + addAndMakeVisible (cpuUsageText); + + createWavetable(); + + setSize (400, 200); + setAudioChannels (0, 2); // no inputs, two outputs + startTimer (50); + } + + ~MainContentComponent() override + { + shutdownAudio(); + } + + void resized() override + { + cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20); + cpuUsageText .setBounds (10, 10, getWidth() - 20, 20); + } + + void timerCallback() override + { + auto cpu = deviceManager.getCpuUsage() * 100; + cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification); + } + + void createWavetable() + { + sineTable.setSize (1, (int) tableSize + 1); + sineTable.clear(); + + auto* samples = sineTable.getWritePointer (0); + + int harmonics[] = { 1, 3, 5, 6, 7, 9, 13, 15 }; + float harmonicWeights[] = { 0.5f, 0.1f, 0.05f, 0.125f, 0.09f, 0.005f, 0.002f, 0.001f }; // [1] + + jassert (juce::numElementsInArray (harmonics) == juce::numElementsInArray (harmonicWeights)); + + for (auto harmonic = 0; harmonic < juce::numElementsInArray (harmonics); ++harmonic) + { + auto angleDelta = juce::MathConstants::twoPi / (double) (tableSize - 1) * harmonics[harmonic]; // [2] + auto currentAngle = 0.0; + + for (unsigned int i = 0; i < tableSize; ++i) + { + auto sample = std::sin (currentAngle); + samples[i] += (float) sample * harmonicWeights[harmonic]; // [3] + currentAngle += angleDelta; + } + } + + samples[tableSize] = samples[0]; + } + + void prepareToPlay (int, double sampleRate) override + { + auto numberOfOscillators = 10; + + for (auto i = 0; i < numberOfOscillators; ++i) + { + auto* oscillator = new WavetableOscillator (sineTable); + + auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0; + auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0); + + oscillator->setFrequency ((float) frequency, (float) sampleRate); + oscillators.add (oscillator); + } + + level = 0.25f / (float) numberOfOscillators; + } + + void releaseResources() override {} + + void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override + { + auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); + auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample); + + bufferToFill.clearActiveBufferRegion(); + + for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex) + { + auto* oscillator = oscillators.getUnchecked (oscillatorIndex); + + for (auto sample = 0; sample < bufferToFill.numSamples; ++sample) + { + auto levelSample = oscillator->getNextSample() * level; + + leftBuffer[sample] += levelSample; + rightBuffer[sample] += levelSample; + } + } + } + +private: + juce::Label cpuUsageLabel; + juce::Label cpuUsageText; + + const unsigned int tableSize = 1 << 7; + float level = 0.0f; + + juce::AudioSampleBuffer sineTable; + juce::OwnedArray oscillators; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) +}; diff --git a/Examples/WavetableSynthTutorial/WavetableSynthTutorial.jucer b/Examples/WavetableSynthTutorial/WavetableSynthTutorial.jucer new file mode 100644 index 0000000..b950cc2 --- /dev/null +++ b/Examples/WavetableSynthTutorial/WavetableSynthTutorial.jucer @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index 9eccfa7..9320c75 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -43,7 +43,7 @@ namespace ProjectInfo { const char* const projectName = "NeuralSynth"; const char* const companyName = "Samedi Dimanche"; - const char* const versionString = "0.0.1"; - const int versionNumber = 0x1; + const char* const versionString = "1.0.0"; + const int versionNumber = 0x10000; } #endif diff --git a/JuceLibraryCode/JucePluginDefines.h b/JuceLibraryCode/JucePluginDefines.h index 6c0fa21..83b6b02 100644 --- a/JuceLibraryCode/JucePluginDefines.h +++ b/JuceLibraryCode/JucePluginDefines.h @@ -17,7 +17,7 @@ #define JucePlugin_Build_VST3 1 #endif #ifndef JucePlugin_Build_AU - #define JucePlugin_Build_AU 1 + #define JucePlugin_Build_AU 0 #endif #ifndef JucePlugin_Build_AUv3 #define JucePlugin_Build_AUv3 0 @@ -26,7 +26,7 @@ #define JucePlugin_Build_AAX 0 #endif #ifndef JucePlugin_Build_Standalone - #define JucePlugin_Build_Standalone 1 + #define JucePlugin_Build_Standalone 0 #endif #ifndef JucePlugin_Build_Unity #define JucePlugin_Build_Unity 0 @@ -77,13 +77,13 @@ #define JucePlugin_EditorRequiresKeyboardFocus 0 #endif #ifndef JucePlugin_Version - #define JucePlugin_Version 0.0.1 + #define JucePlugin_Version 1.0.0 #endif #ifndef JucePlugin_VersionCode - #define JucePlugin_VersionCode 0x1 + #define JucePlugin_VersionCode 0x10000 #endif #ifndef JucePlugin_VersionString - #define JucePlugin_VersionString "0.0.1" + #define JucePlugin_VersionString "1.0.0" #endif #ifndef JucePlugin_VSTUniqueID #define JucePlugin_VSTUniqueID JucePlugin_PluginCode diff --git a/NeuralSynth.jucer b/NeuralSynth.jucer index f0cdfd3..f5d28d9 100644 --- a/NeuralSynth.jucer +++ b/NeuralSynth.jucer @@ -4,7 +4,8 @@ addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" companyWebsite="www.samedidimanche.com" bundleIdentifier="com.samedidimanche.NeuralSynth" pluginManufacturer="Samedi Dimanche" companyName="Samedi Dimanche" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn" - pluginVSTNumMidiInputs="1" pluginChannelConfigs="{0, 2}" version="0.0.1"> + pluginVSTNumMidiInputs="1" pluginChannelConfigs="{0, 2}" version="1.0.0" + pluginFormats="buildVST3"> @@ -70,5 +71,26 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 664eddd..b14276c 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -8,12 +8,21 @@ NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAudioProcessor& p) : AudioProcessorEditor (&p), audioProcessor (p), - mainScopeComponent(audioProcessor.getAudioBufferQueue()) + mainScopeComponent(audioProcessor.getAudioBufferQueue()), + keyboardComponent(keyboardState, juce::MidiKeyboardComponent::horizontalKeyboard) { auto& tree = audioProcessor.parameters; addAndMakeVisible(mainScopeComponent); + keyboardState.addListener(this); + keyboardComponent.setMidiChannel(1); + keyboardComponent.setScrollButtonsVisible(true); + keyboardComponent.setKeyWidth(36.0f); + keyboardComponent.setAvailableRange(36, 96); + keyboardComponent.setVelocity(1.0f, true); + addAndMakeVisible(keyboardComponent); + presetMenuButton.setButtonText("Preset"); presetMenuButton.onClick = [this] { showPresetMenu(); }; addAndMakeVisible(presetMenuButton); @@ -185,6 +194,7 @@ NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAud NeuralSynthAudioProcessorEditor::~NeuralSynthAudioProcessorEditor() { stopTimer(); + keyboardState.removeListener(this); } //============================================================================== @@ -199,7 +209,7 @@ void NeuralSynthAudioProcessorEditor::resized() auto outer = getLocalBounds().reduced(16); // --- carve out sidebar for MASTER (right side) -------------------------- - const int sidebarWidth = 160; // tweak if you want it wider/narrower + const int sidebarWidth = 100; // tweak if you want it wider/narrower auto gridArea = outer; auto sidebar = gridArea.removeFromRight(sidebarWidth).reduced(8); @@ -217,17 +227,18 @@ void NeuralSynthAudioProcessorEditor::resized() juce::Grid grid; grid.templateRows = { - juce::Grid::TrackInfo(juce::Grid::Fr(22)), // scope band - juce::Grid::TrackInfo(juce::Grid::Fr(39)), // row 1 - juce::Grid::TrackInfo(juce::Grid::Fr(78)) // row 2 (wider to absorb layer panel) + juce::Grid::TrackInfo(juce::Grid::Fr(10)), // scope band + juce::Grid::TrackInfo(juce::Grid::Fr(35)), // row 1 + juce::Grid::TrackInfo(juce::Grid::Fr(35)), // row 2 + juce::Grid::TrackInfo(juce::Grid::Fr(20)) // keyboard }; grid.templateColumns = { - juce::Grid::TrackInfo(juce::Grid::Fr(1)), - juce::Grid::TrackInfo(juce::Grid::Fr(1)), - juce::Grid::TrackInfo(juce::Grid::Fr(1)), - juce::Grid::TrackInfo(juce::Grid::Fr(1)), - juce::Grid::TrackInfo(juce::Grid::Fr(1)) + juce::Grid::TrackInfo(juce::Grid::Fr(20)), + juce::Grid::TrackInfo(juce::Grid::Fr(20)), + juce::Grid::TrackInfo(juce::Grid::Fr(20)), + juce::Grid::TrackInfo(juce::Grid::Fr(20)), + juce::Grid::TrackInfo(juce::Grid::Fr(20)) }; grid.rowGap = juce::Grid::Px(0); @@ -236,7 +247,7 @@ void NeuralSynthAudioProcessorEditor::resized() grid.items.clear(); // Row 1 (scope row) - grid.items.add(juce::GridItem(mainScopeComponent).withArea(1, 1, 1, 5)); + grid.items.add(juce::GridItem(mainScopeComponent).withArea({}, juce::GridItem::Span(4))); // Put preset button at the top-right cell of the scope row grid.items.add(juce::GridItem(presetMenuButton) .withArea(1, 5) @@ -244,22 +255,39 @@ void NeuralSynthAudioProcessorEditor::resized() .withAlignSelf(juce::GridItem::AlignSelf::start)); // Row 2 (top row of panels): Amp Env, Chorus, Delay, Reverb, EQ - grid.items.add(juce::GridItem(*adsrComponent ).withArea(2, 1)); - grid.items.add(juce::GridItem(*chorusComponent ).withArea(2, 2)); - grid.items.add(juce::GridItem(*delayComponent ).withArea(2, 3)); - grid.items.add(juce::GridItem(*reverbComponent ).withArea(2, 4)); - grid.items.add(juce::GridItem(*eqComponent ).withArea(2, 5)); + grid.items.add(juce::GridItem(*adsrComponent )); + grid.items.add(juce::GridItem(*chorusComponent )); + grid.items.add(juce::GridItem(*delayComponent )); + grid.items.add(juce::GridItem(*reverbComponent )); + grid.items.add(juce::GridItem(*eqComponent )); // Row 3 (bottom row of panels): Flanger, Distortion, Filter, Filter Env, Wavetable - grid.items.add(juce::GridItem(*flangerComponent ).withArea(3, 1)); - grid.items.add(juce::GridItem(*distortionComponent).withArea(3, 2)); - grid.items.add(juce::GridItem(*filterComponent ).withArea(3, 3)); - grid.items.add(juce::GridItem(*filterEnvComponent ).withArea(3, 4)); - grid.items.add(juce::GridItem(*wtComponent ).withArea(3, 5)); + grid.items.add(juce::GridItem(*flangerComponent )); + grid.items.add(juce::GridItem(*distortionComponent)); + grid.items.add(juce::GridItem(*filterComponent )); + grid.items.add(juce::GridItem(*filterEnvComponent )); + grid.items.add(juce::GridItem(*wtComponent )); + + // Row 4: MIDI keyboard spans entire width + grid.items.add(juce::GridItem(keyboardComponent).withArea({}, juce::GridItem::Span(5))); grid.performLayout(gridArea); } +void NeuralSynthAudioProcessorEditor::handleNoteOn(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) +{ + auto message = juce::MidiMessage::noteOn(midiChannel, midiNoteNumber, velocity); + message.setTimeStamp(juce::Time::getMillisecondCounterHiRes() * 0.001); + audioProcessor.midiMessageCollector.addMessageToQueue(message); +} + +void NeuralSynthAudioProcessorEditor::handleNoteOff(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) +{ + auto message = juce::MidiMessage::noteOff(midiChannel, midiNoteNumber, velocity); + message.setTimeStamp(juce::Time::getMillisecondCounterHiRes() * 0.001); + audioProcessor.midiMessageCollector.addMessageToQueue(message); +} + void NeuralSynthAudioProcessorEditor::timerCallback() { const int current = audioProcessor.getCurrentPresetIndex(); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 8ec6691..07043d5 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -368,7 +368,8 @@ public: //============================== Editor ======================================= class NeuralSynthAudioProcessorEditor : public juce::AudioProcessorEditor, - private juce::Timer + private juce::Timer, + private juce::MidiKeyboardStateListener { public: NeuralSynthAudioProcessorEditor (NeuralSynthAudioProcessor&); @@ -377,6 +378,8 @@ public: void paint (juce::Graphics&) override; void resized() override; void timerCallback() override; + void handleNoteOn(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override; + void handleNoteOff(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override; private: NeuralSynthAudioProcessor& audioProcessor; @@ -408,6 +411,8 @@ private: std::unique_ptr gainAttachment; ScopeComponent mainScopeComponent; + juce::MidiKeyboardState keyboardState; + juce::MidiKeyboardComponent keyboardComponent; juce::ComboBox layerSelector; bool controllingLayerB { false }; }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index f051d70..4aa424d 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -210,14 +210,14 @@ void NeuralSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPer void NeuralSynthAudioProcessor::releaseResources() {} -bool NeuralSynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const +/*bool NeuralSynthAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const { if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) return false; return true; -} + }*/ void NeuralSynthAudioProcessor::processBlock(juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midiMessages) { diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..79b3986 --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +pushd Builds/LinuxMakefile/ +make clean +make +popd