From 3b386e5f0e02af3ac4542c6684dca7bb13bfd8df Mon Sep 17 00:00:00 2001 From: David Braun <2096055+DBraun@users.noreply.github.com> Date: Thu, 1 Jun 2023 00:06:06 -0700 Subject: [PATCH] Multiprocessing example (#169) --- Builds/.gitignore | 3 +- Builds/LinuxMakefile/Makefile | 4 +- .../DawDreamer.xcodeproj/project.pbxproj | 12 +- .../DawDreamer_DynamicLibrary.vcxproj | 8 +- .../DawDreamer_DynamicLibrary.vcxproj.user | 4 + Builds/VisualStudio2022/resources.rc | 6 +- DawDreamer.jucer | 2 +- JuceLibraryCode/JuceHeader.h | 4 +- README.md | 1 + Source/PluginProcessor.cpp | 31 +-- examples/multiprocessing_plugins/.gitignore | 1 + examples/multiprocessing_plugins/README.md | 22 +++ examples/multiprocessing_plugins/main.py | 177 ++++++++++++++++++ tests/dawdreamer_utils.py | 4 +- tests/test_libfaust_box.py | 9 +- tests/test_multithread.py | 171 +++++++++++++++++ 16 files changed, 404 insertions(+), 55 deletions(-) create mode 100644 Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.user create mode 100644 examples/multiprocessing_plugins/.gitignore create mode 100644 examples/multiprocessing_plugins/README.md create mode 100644 examples/multiprocessing_plugins/main.py create mode 100644 tests/test_multithread.py diff --git a/Builds/.gitignore b/Builds/.gitignore index 067c13d2..4c1fde2c 100644 --- a/Builds/.gitignore +++ b/Builds/.gitignore @@ -2,4 +2,5 @@ VisualStudio*/.vs VisualStudio*/x64 LinuxMakefile/* MacOSX/build/* -MacOSX/RecentFiles* \ No newline at end of file +MacOSX/RecentFiles* +PythonApplication*/*.pyproj \ No newline at end of file diff --git a/Builds/LinuxMakefile/Makefile b/Builds/LinuxMakefile/Makefile index 0803d5d9..705c7dec 100644 --- a/Builds/LinuxMakefile/Makefile +++ b/Builds/LinuxMakefile/Makefile @@ -39,7 +39,7 @@ ifeq ($(CONFIG),Debug) TARGET_ARCH := endif - JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.0" "-DJUCE_APP_VERSION_HEX=0x700" $(shell $(PKG_CONFIG) --cflags alsa freetype2) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) + JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.1" "-DJUCE_APP_VERSION_HEX=0x701" $(shell $(PKG_CONFIG) --cflags alsa freetype2) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) JUCE_CPPFLAGS_DYNAMIC_LIBRARY := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0" JUCE_CFLAGS_DYNAMIC_LIBRARY := -fPIC -fvisibility=hidden JUCE_LDFLAGS_DYNAMIC_LIBRARY := -shared @@ -62,7 +62,7 @@ ifeq ($(CONFIG),Release) TARGET_ARCH := endif - JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.0" "-DJUCE_APP_VERSION_HEX=0x700" $(shell $(PKG_CONFIG) --cflags alsa freetype2) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) + JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DPIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==" "-DSAMPLER_SKIP_UI" "-DJUCE_MODAL_LOOPS_PERMITTED" "-DHAVE_LIBSAMPLERATE" "-DUSE_BUILTIN_FFT" "-DUSE_PTHREADS" "-DBUILD_DAWDREAMER_FAUST" "-DBUILD_DAWDREAMER_RUBBERBAND" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=0.7.1" "-DJUCE_APP_VERSION_HEX=0x701" $(shell $(PKG_CONFIG) --cflags alsa freetype2) -pthread -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/LV2_SDK -I../../JuceLibraryCode/modules/juce_audio_processors/format_types/VST3_SDK -I../../thirdparty/JUCE/modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../JuceLibraryCode/modules -I../../thirdparty -I../../thirdparty/pybind11/include -I../../thirdparty/faust/architecture -I../../thirdparty/faust/compiler -I../../thirdparty/faust/compiler/boxes -I../../thirdparty/faust/compiler/documentator -I../../thirdparty/faust/compiler/draw -I../../thirdparty/faust/compiler/draw/device -I../../thirdparty/faust/compiler/draw/schema -I../../thirdparty/faust/compiler/errors -I../../thirdparty/faust/compiler/evaluate -I../../thirdparty/faust/compiler/extended -I../../thirdparty/faust/compiler/generator -I../../thirdparty/faust/compiler/generator/interpreter -I../../thirdparty/faust/compiler/normalize -I../../thirdparty/faust/compiler/parallelize -I../../thirdparty/faust/compiler/parser -I../../thirdparty/faust/compiler/patternmatcher -I../../thirdparty/faust/compiler/propagate -I../../thirdparty/faust/compiler/signals -I../../thirdparty/faust/compiler/tlib -I../../thirdparty/faust/compiler/transform -I../../thirdparty/faust/compiler/utils -I../../thirdparty/libsamplerate/src -I../../thirdparty/libsamplerate/include $(CPPFLAGS) JUCE_CPPFLAGS_DYNAMIC_LIBRARY := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0" JUCE_CFLAGS_DYNAMIC_LIBRARY := -fPIC -fvisibility=hidden JUCE_LDFLAGS_DYNAMIC_LIBRARY := -shared diff --git a/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj b/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj index c2f1669c..e995b877 100644 --- a/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/DawDreamer.xcodeproj/project.pbxproj @@ -619,8 +619,8 @@ "BUILD_DAWDREAMER_FAUST", "BUILD_DAWDREAMER_RUBBERBAND", "JUCER_XCODE_MAC_F6D2F4CF=1", - "JUCE_APP_VERSION=0.7.0", - "JUCE_APP_VERSION_HEX=0x700", + "JUCE_APP_VERSION=0.7.1", + "JUCE_APP_VERSION_HEX=0x701", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", @@ -715,8 +715,8 @@ "BUILD_DAWDREAMER_FAUST", "BUILD_DAWDREAMER_RUBBERBAND", "JUCER_XCODE_MAC_F6D2F4CF=1", - "JUCE_APP_VERSION=0.7.0", - "JUCE_APP_VERSION_HEX=0x700", + "JUCE_APP_VERSION=0.7.1", + "JUCE_APP_VERSION_HEX=0x701", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", @@ -812,8 +812,8 @@ "BUILD_DAWDREAMER_FAUST", "BUILD_DAWDREAMER_RUBBERBAND", "JUCER_XCODE_MAC_F6D2F4CF=1", - "JUCE_APP_VERSION=0.7.0", - "JUCE_APP_VERSION_HEX=0x700", + "JUCE_APP_VERSION=0.7.1", + "JUCE_APP_VERSION_HEX=0x701", "JucePlugin_Build_VST=0", "JucePlugin_Build_VST3=0", "JucePlugin_Build_AU=0", diff --git a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj index b3a00909..20517863 100644 --- a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj +++ b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj @@ -66,7 +66,7 @@ Disabled ProgramDatabase ..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\VST3_SDK;..\..\thirdparty\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\JuceLibraryCode\modules;$(pythonLocation)/include;../../thirdparty;../../thirdparty/pybind11/include;../../thirdparty/faust/architecture;../../thirdparty/faust/compiler;../../thirdparty/faust/compiler/boxes;../../thirdparty/faust/compiler/documentator;../../thirdparty/faust/compiler/draw;../../thirdparty/faust/compiler/draw/device;../../thirdparty/faust/compiler/draw/schema;../../thirdparty/faust/compiler/errors;../../thirdparty/faust/compiler/evaluate;../../thirdparty/faust/compiler/extended;../../thirdparty/faust/compiler/generator;../../thirdparty/faust/compiler/generator/interpreter;../../thirdparty/faust/compiler/normalize;../../thirdparty/faust/compiler/parallelize;../../thirdparty/faust/compiler/parser;../../thirdparty/faust/compiler/patternmatcher;../../thirdparty/faust/compiler/propagate;../../thirdparty/faust/compiler/signals;../../thirdparty/faust/compiler/tlib;../../thirdparty/faust/compiler/transform;../../thirdparty/faust/compiler/utils;../../thirdparty/libsamplerate/src;../../thirdparty/libsamplerate/include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.0;JUCE_APP_VERSION_HEX=0x700;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.1;JUCE_APP_VERSION_HEX=0x701;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -80,7 +80,7 @@ ..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\VST3_SDK;..\..\thirdparty\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\JuceLibraryCode\modules;$(pythonLocation)/include;../../thirdparty;../../thirdparty/pybind11/include;../../thirdparty/faust/architecture;../../thirdparty/faust/compiler;../../thirdparty/faust/compiler/boxes;../../thirdparty/faust/compiler/documentator;../../thirdparty/faust/compiler/draw;../../thirdparty/faust/compiler/draw/device;../../thirdparty/faust/compiler/draw/schema;../../thirdparty/faust/compiler/errors;../../thirdparty/faust/compiler/evaluate;../../thirdparty/faust/compiler/extended;../../thirdparty/faust/compiler/generator;../../thirdparty/faust/compiler/generator/interpreter;../../thirdparty/faust/compiler/normalize;../../thirdparty/faust/compiler/parallelize;../../thirdparty/faust/compiler/parser;../../thirdparty/faust/compiler/patternmatcher;../../thirdparty/faust/compiler/propagate;../../thirdparty/faust/compiler/signals;../../thirdparty/faust/compiler/tlib;../../thirdparty/faust/compiler/transform;../../thirdparty/faust/compiler/utils;../../thirdparty/libsamplerate/src;../../thirdparty/libsamplerate/include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.0;JUCE_APP_VERSION_HEX=0x700;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.1;JUCE_APP_VERSION_HEX=0x701;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) $(OutDir)\dawdreamer.dll @@ -114,7 +114,7 @@ Full ..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\VST3_SDK;..\..\thirdparty\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\JuceLibraryCode\modules;$(pythonLocation)/include;../../thirdparty;../../thirdparty/pybind11/include;../../thirdparty/faust/architecture;../../thirdparty/faust/compiler;../../thirdparty/faust/compiler/boxes;../../thirdparty/faust/compiler/documentator;../../thirdparty/faust/compiler/draw;../../thirdparty/faust/compiler/draw/device;../../thirdparty/faust/compiler/draw/schema;../../thirdparty/faust/compiler/errors;../../thirdparty/faust/compiler/evaluate;../../thirdparty/faust/compiler/extended;../../thirdparty/faust/compiler/generator;../../thirdparty/faust/compiler/generator/interpreter;../../thirdparty/faust/compiler/normalize;../../thirdparty/faust/compiler/parallelize;../../thirdparty/faust/compiler/parser;../../thirdparty/faust/compiler/patternmatcher;../../thirdparty/faust/compiler/propagate;../../thirdparty/faust/compiler/signals;../../thirdparty/faust/compiler/tlib;../../thirdparty/faust/compiler/transform;../../thirdparty/faust/compiler/utils;../../thirdparty/libsamplerate/src;../../thirdparty/libsamplerate/include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;BUILD_DAWDREAMER_FAUST;BUILD_DAWDREAMER_RUBBERBAND;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.0;JUCE_APP_VERSION_HEX=0x700;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;BUILD_DAWDREAMER_FAUST;BUILD_DAWDREAMER_RUBBERBAND;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.1;JUCE_APP_VERSION_HEX=0x701;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) MultiThreadedDLL true NotUsing @@ -128,7 +128,7 @@ ..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\LV2_SDK;..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\VST3_SDK;..\..\thirdparty\JUCE\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\JuceLibraryCode\modules;$(pythonLocation)/include;../../thirdparty;../../thirdparty/pybind11/include;../../thirdparty/faust/architecture;../../thirdparty/faust/compiler;../../thirdparty/faust/compiler/boxes;../../thirdparty/faust/compiler/documentator;../../thirdparty/faust/compiler/draw;../../thirdparty/faust/compiler/draw/device;../../thirdparty/faust/compiler/draw/schema;../../thirdparty/faust/compiler/errors;../../thirdparty/faust/compiler/evaluate;../../thirdparty/faust/compiler/extended;../../thirdparty/faust/compiler/generator;../../thirdparty/faust/compiler/generator/interpreter;../../thirdparty/faust/compiler/normalize;../../thirdparty/faust/compiler/parallelize;../../thirdparty/faust/compiler/parser;../../thirdparty/faust/compiler/patternmatcher;../../thirdparty/faust/compiler/propagate;../../thirdparty/faust/compiler/signals;../../thirdparty/faust/compiler/tlib;../../thirdparty/faust/compiler/transform;../../thirdparty/faust/compiler/utils;../../thirdparty/libsamplerate/src;../../thirdparty/libsamplerate/include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;BUILD_DAWDREAMER_FAUST;BUILD_DAWDREAMER_RUBBERBAND;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.0;JUCE_APP_VERSION_HEX=0x700;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;PIP_JUCE_EXAMPLES_DIRECTORY=QzpcdG9vbHNcSlVDRVxleGFtcGxlcw==;BUILD_DAWDREAMER_FAUST;BUILD_DAWDREAMER_RUBBERBAND;SAMPLER_SKIP_UI;JUCE_MODAL_LOOPS_PERMITTED;_WIN32;__SSE__;__SSE2__;NOMINMAX;HAVE_LIBSAMPLERATE;USE_BUILTIN_FFT;NO_THREADING;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=0.7.1;JUCE_APP_VERSION_HEX=0x701;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;_LIB;%(PreprocessorDefinitions) $(OutDir)\dawdreamer.dll diff --git a/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.user b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.user new file mode 100644 index 00000000..88a55094 --- /dev/null +++ b/Builds/VisualStudio2022/DawDreamer_DynamicLibrary.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Builds/VisualStudio2022/resources.rc b/Builds/VisualStudio2022/resources.rc index ce143af0..d290db04 100644 --- a/Builds/VisualStudio2022/resources.rc +++ b/Builds/VisualStudio2022/resources.rc @@ -9,16 +9,16 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 0,7,0,0 +FILEVERSION 0,7,1,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "FileDescription", "DawDreamer\0" - VALUE "FileVersion", "0.7.0\0" + VALUE "FileVersion", "0.7.1\0" VALUE "ProductName", "DawDreamer\0" - VALUE "ProductVersion", "0.7.0\0" + VALUE "ProductVersion", "0.7.1\0" END END diff --git a/DawDreamer.jucer b/DawDreamer.jucer index 0ba342fb..e2fcd70e 100644 --- a/DawDreamer.jucer +++ b/DawDreamer.jucer @@ -1,6 +1,6 @@ - lock(GLOBAL_PLUGIN_MUTEX); - juce::MessageManager::getInstance(); // to avoid runtime jassert(false) - // thrown by JUCE - } - for (int i = pluginFormatManager.getNumFormats(); --i >= 0;) { pluginList.scanAndAddFile(String(myPluginPath), true, pluginDescriptions, *pluginFormatManager.getFormat(i)); } if (myPlugin.get()) { - std::lock_guard lock(GLOBAL_PLUGIN_MUTEX); myPlugin.get()->releaseResources(); myPlugin.reset(); - GLOBAL_PLUGIN_ACTIVE_COUNT--; - if (GLOBAL_PLUGIN_ACTIVE_COUNT == 0) { - juce::DeletedAtShutdown::deleteAll(); - juce::MessageManager::deleteInstance(); - } } // If there is a problem here first check the preprocessor definitions @@ -101,9 +83,7 @@ bool PluginProcessor::loadPlugin(double sampleRate, int samplesPerBlock) { String errorMessage; - // introduce scope because we'll use a mutex. - { - std::lock_guard lock(GLOBAL_PLUGIN_MUTEX); + myPlugin = pluginFormatManager.createPluginInstance( *pluginDescriptions[0], sampleRate, samplesPerBlock, errorMessage); @@ -112,8 +92,7 @@ bool PluginProcessor::loadPlugin(double sampleRate, int samplesPerBlock) { errorMessage.toStdString()); } // We loaded the plugin. - GLOBAL_PLUGIN_ACTIVE_COUNT++; - } + auto outputs = myPlugin->getTotalNumOutputChannels(); @@ -149,14 +128,8 @@ bool PluginProcessor::loadPlugin(double sampleRate, int samplesPerBlock) { PluginProcessor::~PluginProcessor() { if (myPlugin.get()) { - std::lock_guard lock(GLOBAL_PLUGIN_MUTEX); myPlugin.get()->releaseResources(); myPlugin.reset(); - GLOBAL_PLUGIN_ACTIVE_COUNT--; - if (GLOBAL_PLUGIN_ACTIVE_COUNT == 0) { - juce::DeletedAtShutdown::deleteAll(); - juce::MessageManager::deleteInstance(); - } } myMidiBufferQN.clear(); diff --git a/examples/multiprocessing_plugins/.gitignore b/examples/multiprocessing_plugins/.gitignore new file mode 100644 index 00000000..6caf68af --- /dev/null +++ b/examples/multiprocessing_plugins/.gitignore @@ -0,0 +1 @@ +output \ No newline at end of file diff --git a/examples/multiprocessing_plugins/README.md b/examples/multiprocessing_plugins/README.md new file mode 100644 index 00000000..39008468 --- /dev/null +++ b/examples/multiprocessing_plugins/README.md @@ -0,0 +1,22 @@ +# DawDreamer - Multiprocessing Plugins + +This script demonstrates how to use [multiprocessing](https://docs.python.org/3/library/multiprocessing.html) to efficiently generate one-shots of a synthesizer. The number of workers is by default `multiprocessing.cpu_count()`. Each worker has a persistent RenderEngine which loads a plugin instrument of our choice. Each worker consumes paths of presets from a multiprocessing [Queue](https://docs.python.org/3/library/multiprocessing.html#pipes-and-queues). For each preset, the worker renders out audio for a configurable MIDI pitch range. The output audio path includes the pitch and preset name. + +**Not every plugin is guaranteed to work. Serum has been tested on Windows, and it should work perfectly.** + +Example usage: + +```bash +python main.py --plugin "path/to/Serum_x64.dll" --preset-dir "path/to/serum_fxp_files" +``` + +To see all available parameters: +```bash +python main.py --help +``` + +Improvement ideas: +* The input could be a more nested directory of presets. +* Alternatively, the items in the input queue could be parameter settings rather than preset paths. A multiprocessing *Processor* could add random parameters to the input queue. +* Variations in velocity +* Variations in note duration diff --git a/examples/multiprocessing_plugins/main.py b/examples/multiprocessing_plugins/main.py new file mode 100644 index 00000000..889976f5 --- /dev/null +++ b/examples/multiprocessing_plugins/main.py @@ -0,0 +1,177 @@ + # + # This file is part of the DawDreamer distribution (https://github.com/DBraun/DawDreamer). + # Copyright (c) 2023 David Braun. + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by + # the Free Software Foundation, version 3. + # + # This program is distributed in the hope that it will be useful, but + # WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + # General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program. If not, see . + # + +import logging +import multiprocessing +import time +import traceback +from collections import namedtuple +from glob import glob +from os.path import basename +from os import makedirs +from pathlib import Path + +# extra libraries to install with pip +import dawdreamer as daw +import numpy as np +from scipy.io import wavfile +from tqdm import tqdm + + +Item = namedtuple("Item", "preset_path") + + +class Worker: + + def __init__(self, queue: multiprocessing.Queue, plugin_path: str, + sample_rate=44100, block_size=512, bpm=120, note_duration=2, + render_duration=5, pitch_low=60, pitch_high=72, velocity=100, + output_dir='output'): + self.queue = queue + self.sample_rate = sample_rate + self.block_size = block_size + self.bpm = bpm + self.plugin_path = plugin_path + self.note_duration = note_duration + self.render_duration = render_duration + self.pitch_low, self.pitch_high = pitch_low, pitch_high + self.velocity = velocity + self.output_dir = Path(output_dir) + + def startup(self): + engine = daw.RenderEngine(self.sample_rate, self.block_size) + engine.set_bpm(self.bpm) + + synth = engine.make_plugin_processor("synth", self.plugin_path) + + graph = [(synth, [])] + engine.load_graph(graph) + + self.engine = engine + self.synth = synth + + def process_item(self, item: Item): + preset_path = item.preset_path + self.synth.load_preset(preset_path) + bname = basename(preset_path) + + for pitch in range(self.pitch_low, self.pitch_high+1): + self.synth.add_midi_note(pitch, self.velocity, 0.0, self.note_duration) + self.engine.render(self.render_duration) + self.synth.clear_midi() + audio = self.engine.get_audio() + output_path = self.output_dir / f'{pitch}_{bname}.wav' + wavfile.write(str(output_path), self.sample_rate, audio.transpose()) + + def run(self): + try: + self.startup() + while True: + try: + item = self.queue.get_nowait() + self.process_item(item) + except multiprocessing.queues.Empty: + break + except Exception as e: + return traceback.format_exc() + + +def main(plugin_path, preset_dir, note_duration=2, render_duration=4, + pitch_low=60, pitch_high=60, num_workers=None, output_dir='output', + logging_level='DEBUG'): + + # Create logger + logging.basicConfig() + logger = logging.getLogger('dawdreamer') + logger.setLevel(logging_level.upper()) + + # Glob all the preset file paths, looking shallowly only + preset_paths = list(glob(str(Path(preset_dir) / '*.fxp'))) + + # Get num items so that the progress bar works well + num_items = len(preset_paths) + + # Create a Queue and add items + input_queue = multiprocessing.Manager().Queue() + for preset_path in preset_paths: + input_queue.put(Item(preset_path)) + + # Create a list to hold the worker processes + workers = [] + + # The number of workers to spawn + num_processes = num_workers or multiprocessing.cpu_count() + + # Debug info + logger.info(f'Note duration: {note_duration}') + logger.info(f'Render duration: {render_duration}') + logger.info(f'Using num workers: {num_processes}') + logger.info(f'Pitch low: {pitch_low}') + logger.info(f'Pitch high: {pitch_high}') + logger.info(f'Output directory: {output_dir}') + + makedirs(output_dir, exist_ok=True) + + # Create a multiprocessing Pool + with multiprocessing.Pool(processes=num_processes) as pool: + # Create and start a worker process for each CPU + for i in range(num_processes): + worker = Worker(input_queue, plugin_path, + note_duration=note_duration, render_duration=render_duration, + pitch_low=pitch_low, pitch_high=pitch_high, + output_dir=output_dir) + async_result = pool.apply_async(worker.run) + workers.append(async_result) + + # Use tqdm to track progress. Update the progress bar in each iteration. + pbar = tqdm(total=num_items) + while True: + incomplete_count = sum(1 for w in workers if not w.ready()) + pbar.update(pbar.total - incomplete_count - pbar.n) + if incomplete_count == 0: + break + time.sleep(0.1) + pbar.close() + + # Check for exceptions in the worker processes + for i, worker in enumerate(workers): + exception = worker.get() + if exception is not None: + logger.error(f"Exception in worker {i}:\n{exception}") + + logger.info('All done!') + +if __name__ == "__main__": + # We're using multiprocessing.Pool, so our code MUST be inside __main__. + # See https://docs.python.org/3/library/multiprocessing.html + + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('--plugin', required=True, help="Path to plugin instrument (.dll, .vst3).") + parser.add_argument('--preset-dir', required=True, help="Directory path of plugin presets.") + parser.add_argument('--note-duration', default=2, help="Note duration in seconds.") + parser.add_argument('--pitch-low', default=60, help="Lowest MIDI pitch to be used.") + parser.add_argument('--pitch-high', default=64, help="Highest MIDI pitch to be used.") + parser.add_argument('--render-duration', default=4, help="Render duration in seconds.") + parser.add_argument('--num-workers', default=None, help="Number of workers to use.") + parser.add_argument('--output-dir', default='output', help="Output directory.") + parser.add_argument('--log-level', default='DEBUG', choices=['DEBUG','INFO','WARNING','ERROR','CRITICAL', 'NOTSET'], help="Logger level.") + args = parser.parse_args() + + main(args.plugin, args.preset_dir, args.note_duration, args.render_duration, + args.pitch_low, args.pitch_high, args.num_workers, args.output_dir, + args.log_level) diff --git a/tests/dawdreamer_utils.py b/tests/dawdreamer_utils.py index 4a622fec..a5a77155 100644 --- a/tests/dawdreamer_utils.py +++ b/tests/dawdreamer_utils.py @@ -34,13 +34,13 @@ def make_sine(freq: float, duration: float, sr=SAMPLE_RATE): N = int(duration * sr) # Number of samples return np.sin(np.pi*2.*freq*np.arange(N)/sr) -def load_audio_file(file_path: str, duration=None): +def load_audio_file(file_path: str, duration=None, offset=0): file_path = str(file_path) if USE_LIBROSA: - sig, rate = librosa.load(file_path, duration=duration, mono=False, sr=SAMPLE_RATE) + sig, rate = librosa.load(file_path, duration=duration, mono=False, sr=SAMPLE_RATE, offset=offset) assert(rate == SAMPLE_RATE) else: diff --git a/tests/test_libfaust_box.py b/tests/test_libfaust_box.py index 848d59ac..8ab3891a 100644 --- a/tests/test_libfaust_box.py +++ b/tests/test_libfaust_box.py @@ -12,7 +12,7 @@ from scipy import signal import numpy as np -from dawdreamer.faust import createLibContext, destroyLibContext +from dawdreamer.faust import createLibContext, destroyLibContext, FaustContext BUFFER_SIZE = 1 SAMPLE_RATE = 44100 @@ -27,10 +27,9 @@ def with_lib_context(func): """ def wrapped(*args, **kwargs): - createLibContext() - result = func(*args, **kwargs) - destroyLibContext() - return result + with FaustContext(): + result = func(*args, **kwargs) + return result return wrapped diff --git a/tests/test_multithread.py b/tests/test_multithread.py new file mode 100644 index 00000000..135994e2 --- /dev/null +++ b/tests/test_multithread.py @@ -0,0 +1,171 @@ +from dawdreamer_utils import * + +from dawdreamer.faust.box import boxMul, boxPar, boxWire, boxReal +from dawdreamer.faust import FaustContext + +import numpy as np + +import multiprocessing + +# GLOBAL PARAMETERS +BPM = 160 +DURATION = .25 + +# Static parameters +SAMPLE_RATE = 44100 +BLOCK_SIZE = 512 + +from unittest.mock import patch +from pytest import fixture + +class MockPoolApplyResult: + def __init__(self, func, args): + self._func = func + self._args = args + + def get(self, timeout=0): + return self._func(*self._args) + + +@fixture(autouse=True) +def mock_pool_apply_async(monkeypatch): + monkeypatch.setattr("multiprocessing.pool.Pool.apply_async", + lambda self, func, args=(), kwds={}, callback=None, error_callback=None: + MockPoolApplyResult(func, args)) + + +def instrument(synthPlugin, name): + engine = daw.RenderEngine(SAMPLE_RATE, BLOCK_SIZE) + engine.set_bpm(BPM) + + synth = engine.make_plugin_processor("synth", synthPlugin) + + synth.add_midi_note(60, 100, 0.0, .15) + + graph = [(synth, [])] + engine.load_graph(graph) + + print("Rendering instrument...") + engine.render(DURATION) + print("Finished rendering instrument.") + + output = engine.get_audio() + + assert np.abs(output).mean() > .001 + + return output + +def playback(audio, name): + engine = daw.RenderEngine(SAMPLE_RATE, BLOCK_SIZE) + engine.set_bpm(BPM) + playbackProcessor = engine.make_playback_processor("playback", audio) + + graph = [(playbackProcessor, [])] + engine.load_graph(graph) + + print("Rendering playback...") + engine.render(DURATION) + print("Finished rendering playback.") + + output = engine.get_audio() + + assert np.allclose(audio[:,:output.shape[1]], output) + + return output + +def faust_playback(audio, name): + engine = daw.RenderEngine(SAMPLE_RATE, BLOCK_SIZE) + engine.set_bpm(BPM) + playbackProcessor = engine.make_playback_processor("playback", audio) + faustProcessor = engine.make_faust_processor("faust") + faustProcessor.set_dsp_string("""process = si.bus(2) : sp.stereoize(_*hslider("vol",1,0,1,0.001));""") + assert faustProcessor.compile() + + graph = [(playbackProcessor, []), (faustProcessor, ["playback"])] + engine.load_graph(graph) + + print("Rendering faust playback...") + engine.render(DURATION) + print("Finished rendering faust playback.") + + output = engine.get_audio() + + assert np.allclose(audio[:,:output.shape[1]], output) + + return output + +def faust_boxes(audio, name): + engine = daw.RenderEngine(SAMPLE_RATE, BLOCK_SIZE) + engine.set_bpm(BPM) + playbackProcessor = engine.make_playback_processor("playback", audio) + faustProcessor = engine.make_faust_processor("faust") + + with FaustContext(): + boxGainHalf = boxMul(boxWire(), boxReal(0.5)) + box = boxPar(boxGainHalf, boxGainHalf) + + faustProcessor.compile_box(box) + + graph = [(playbackProcessor, []), (faustProcessor, ["playback"])] + engine.load_graph(graph) + + print("Rendering faust box...") + engine.render(DURATION) + print("Finished rendering faust box.") + + output = engine.get_audio() + + # confirm that the input was multiplied by 0.5 + minSize = min(output.shape[1], audio.shape[1]) + assert np.allclose(audio[:,:minSize]*.5, output[:,:minSize]) + + return output + + +class TestClass: + + @staticmethod + def get_audio(): + return load_audio_file(str(ASSETS / "Music Delta - Disco/drums.wav"), duration=10, offset=0.26) + + @staticmethod + def get_pool(): + return multiprocessing.pool.Pool(processes=multiprocessing.cpu_count()) + + # currently skipping this test (by putting underscore in front) because it fails on some plugins + @pytest.mark.parametrize("plugin_path", ALL_PLUGIN_INSTRUMENTS[0:1]) + def _test_instrument(self, plugin_path): + audio_data = self.get_audio() + with self.get_pool() as pool: + tasks = [pool.apply_async(instrument, (plugin_path, f"test{i}")) for i in range(8)] + # Collect tasks: + outputs = [res.get() for res in tasks] + + def test_playback(self): + audio_data = self.get_audio() + with self.get_pool() as pool: + tasks = [pool.apply_async(playback, (audio_data, f"test{i}")) for i in range(100)] + # Collect tasks: + outputs = [res.get() for res in tasks] + + def test_faust_playback(self): + audio_data = self.get_audio() + with self.get_pool() as pool: + tasks = [pool.apply_async(faust_playback, (audio_data, f"test{i}")) for i in range(100)] + # Collect tasks: + outputs = [res.get() for res in tasks] + + def test_faust_boxes(self): + audio_data = self.get_audio() + with self.get_pool() as pool: + tasks = [pool.apply_async(faust_boxes, (audio_data, f"test{i}")) for i in range(20)] + # Collect tasks: + outputs = [res.get() for res in tasks] + +if __name__ == "__main__": + test_class = TestClass() + test_class._test_instrument(ALL_PLUGIN_INSTRUMENTS[0]) + test_class.test_playback() + test_class.test_faust_playback() + test_class.test_faust_boxes() + print('all done') \ No newline at end of file