From 73c8c4fac125bed4c27c7f47b74b0d3858f35cb2 Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Mon, 4 Feb 2019 17:14:53 -0500 Subject: [PATCH] Headless Surge working The gui is well separable from the patch/parameter/dsp/synth code so a headless mode should have been readily doable at any time. With this commit, it is done. This shows a small example of setting up and running a 2 oscillator detuned saw configuration of surge. On Linux, one VSTGUI symbol is still pulled in for a reason I can't determine, so introduce it in a fixes file which we need to address in a future issue if/as we develop headless. Closes #499. --- .gitignore | 1 + build-osx.sh | 7 +++ premake5.lua | 68 +++++++++++++++++++++++ src/common/SurgeSynthesizer.cpp | 6 ++ src/common/SurgeSynthesizer.h | 3 + src/headless/DisplayInfoHeadless.cpp | 22 ++++++++ src/headless/HeadlessPluginLayerProxy.h | 14 +++++ src/headless/LinkFixesHeadless.cpp | 14 +++++ src/headless/UserInteractionsHeadless.cpp | 37 ++++++++++++ src/headless/main.cpp | 68 +++++++++++++++++++++++ 10 files changed, 240 insertions(+) create mode 100644 src/headless/DisplayInfoHeadless.cpp create mode 100644 src/headless/HeadlessPluginLayerProxy.h create mode 100644 src/headless/LinkFixesHeadless.cpp create mode 100644 src/headless/UserInteractionsHeadless.cpp create mode 100644 src/headless/main.cpp diff --git a/.gitignore b/.gitignore index 7565480e3bc..f1518025853 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ Surge.xcworkspace/ surge-au.xcodeproj/ surge-vst2.xcodeproj/ surge-vst3.xcodeproj/ +surge-headless.xcodeproj/ products/ installer_mac/installer installer_mac/*.dmg diff --git a/build-osx.sh b/build-osx.sh index 94a49683488..dbdf74f1600 100755 --- a/build-osx.sh +++ b/build-osx.sh @@ -38,6 +38,7 @@ Commands are: --build-validate-au Build and install the audio unit then validate it --build-install-vst2 Build and install only the VST2 --build-install-vst3 Build and install only the VST3 + --build-headless Build the headless application --package Creates a .pkg file from current built state in products --clean-and-package Cleans everything; runs all the builds; makes an installer; drops it in products @@ -181,6 +182,7 @@ run_all_builds() run_build "vst3" run_build "au" + run_build "headless" } run_install_local() @@ -239,6 +241,7 @@ run_clean_builds() run_clean "vst3" run_clean "au" + run_clean "headless" } run_clean_all() @@ -320,6 +323,10 @@ case $command in --build-install-vst3) run_build_install_vst3 ;; + --build-headless) + run_premake_if + run_build "headless" + ;; --clean) run_clean_builds ;; diff --git a/premake5.lua b/premake5.lua index 61d4f43ac3f..69777d6366e 100644 --- a/premake5.lua +++ b/premake5.lua @@ -662,3 +662,71 @@ if (os.istarget("linux")) then configuration { "Release" } targetdir "target/app/Release" end + +-- HEADLESS APP + +project "surge-headless" +kind "ConsoleApp" + +defines +{ + "TARGET_HEADLESS=1" +} + +plugincommon() + +files { + "src/headless/main.cpp", + "src/headless/DisplayInfoHeadless.cpp", + "src/headless/UserInteractionsHeadless.cpp", + "src/headless/LinkFixesHeadless.cpp" + } + +excludes { + "src/common/gui/*" +} + +includedirs { + "src/headless" +} + +configuration { "Debug" } +targetdir "target/headless/Debug" +targetsuffix "-Debug" + +configuration { "Release" } +targetdir "target/headless/Release" + +configuration {} + +if (os.istarget("macosx")) then + excludes{ + VSTGUI .. "vstgui_mac.mm", + VSTGUI .. "vstgui_uidescription_mac.mm", + "src/mac/DisplayInfoMac.mm", + "src/mac/UserInteractionsMac.cpp", + } +end + +if (os.istarget("windows")) then + excludes{ + VSTGUI .. "vstgui_win32.cpp", + VSTGUI .. "vstgui_uidescription_win32.cpp", + "src/windows/DisplayInfoWin.cpp", + "src/windows/UserInteractionsWin.cpp", + } +end + +if (os.istarget("linux")) then + excludes { + VSTGUI .. "vstgui.cpp", + VSTGUI .. "lib/platform/linux/**.cpp", + VSTGUI .. "lib/platform/common/genericoptionmenu.cpp", + VSTGUI .. "lib/platform/common/generictextedit.cpp", + "src/linux/DisplayInfoLinux.cpp", + "src/linux/UserInteractionsLinux.cpp", + } +end + + + diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index cbb6bc29466..644c68446a1 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -10,6 +10,7 @@ #include #include #endif + #if TARGET_AUDIOUNIT #include "aulayer.h" #include "vstgui/plugin-bindings/plugguieditor.h" @@ -19,8 +20,11 @@ #elif TARGET_APP #include "PluginLayer.h" #include "vstgui/plugin-bindings/plugguieditor.h" +#elif TARGET_HEADLESS +#include "HeadlessPluginLayerProxy.h" // from src/headless #else #include "Vst2PluginInstance.h" + #if LINUX #include "../linux/linux-aeffguieditor.h" #else @@ -669,6 +673,8 @@ void SurgeSynthesizer::sendParameterAutomation(long index, float value) getParent()->setParameterAutomated(externalparam, value); #elif TARGET_APP getParent()->sendParameterAutomation(externalparam, value); +#elif TARGET_HEADLESS + // NO OP #else getParent()->setParameterAutomated(externalparam, value); #endif diff --git a/src/common/SurgeSynthesizer.h b/src/common/SurgeSynthesizer.h index aef6dc9c33e..bebc79b2d0e 100644 --- a/src/common/SurgeSynthesizer.h +++ b/src/common/SurgeSynthesizer.h @@ -21,6 +21,9 @@ typedef SurgeVst3Processor PluginLayer; #elif TARGET_VST2 class Vst2PluginInstance; using PluginLayer = Vst2PluginInstance; +#elif TARGET_HEADLESS +class HeadlessPluginLayerProxy; +using PluginLayer = HeadlessPluginLayerProxy; #else class PluginLayer; #endif diff --git a/src/headless/DisplayInfoHeadless.cpp b/src/headless/DisplayInfoHeadless.cpp new file mode 100644 index 00000000000..771eda02b67 --- /dev/null +++ b/src/headless/DisplayInfoHeadless.cpp @@ -0,0 +1,22 @@ +#include "DisplayInfo.h" +#include "UserInteractions.h" + +namespace Surge +{ +namespace GUI +{ + +using namespace VSTGUI; + +float getDisplayBackingScaleFactor(CFrame *) +{ + return 1.0; +} + +CRect getScreenDimensions(CFrame *) +{ + return CRect(CPoint(0,0), CPoint(1024,768)); +} + +} +} diff --git a/src/headless/HeadlessPluginLayerProxy.h b/src/headless/HeadlessPluginLayerProxy.h new file mode 100644 index 00000000000..2948aeabd25 --- /dev/null +++ b/src/headless/HeadlessPluginLayerProxy.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +class HeadlessPluginLayerProxy +{ +public: + void updateDisplay() + { + std::cerr << "HeadlessPluginLayerProxy::updateDisplay" << std::endl; + } + +}; diff --git a/src/headless/LinkFixesHeadless.cpp b/src/headless/LinkFixesHeadless.cpp new file mode 100644 index 00000000000..f830f8eaeba --- /dev/null +++ b/src/headless/LinkFixesHeadless.cpp @@ -0,0 +1,14 @@ +/* +** There are a few symbols which on Linux currently get linked +** for a very difficult to determine reason. We should fix this, +** but for the meantime lets add this file to patch the link +** errors +*/ +#if LINUX +namespace VSTGUI +{ +void doAssert(const char*, const char*, const char*) +{ +} +} +#endif diff --git a/src/headless/UserInteractionsHeadless.cpp b/src/headless/UserInteractionsHeadless.cpp new file mode 100644 index 00000000000..c64e34744f7 --- /dev/null +++ b/src/headless/UserInteractionsHeadless.cpp @@ -0,0 +1,37 @@ +#include "UserInteractions.h" +#include +#include + +namespace Surge +{ + namespace UserInteractions + { + void promptError(const Surge::Error &e) + { + promptError(e.getMessage(), e.getTitle()); + } + + void promptError(const std::string &message, + const std::string &title) + { + std::cerr << "Surge Error\n" + << title << "\n" + << message << "\n" << std::flush; + } + + UserInteractions::MessageResult promptOKCancel(const std::string &message, + const std::string &title) + { + std::cerr << "Surge OkCancel\n" + << title << "\n" + << message << "\n" + << "Returning CANCEL" << std::flush; + return UserInteractions::CANCEL; + } + + void openURL(const std::string &url) + { + } + }; +}; + diff --git a/src/headless/main.cpp b/src/headless/main.cpp new file mode 100644 index 00000000000..be2d8916bcc --- /dev/null +++ b/src/headless/main.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "SurgeSynthesizer.h" + +#include "HeadlessPluginLayerProxy.h" + +int main(int argc, char** argv) +{ + std::cout << "Surge Headless Mode" << std::endl; + + HeadlessPluginLayerProxy *parent = new HeadlessPluginLayerProxy(); + std::unique_ptr surge(new SurgeSynthesizer(parent)); + surge->setSamplerate(44100); + + /* + ** Change a parameter in the scene. Do this by traversing the + ** graph in the current patch (which is in surge->storage). + ** + ** Clearly a more fulsome headless API would provide wrappers around + ** this for common activities. This sets up a pair of detuned saw waves + ** both active. + */ + surge->storage.getPatch().scene[0].osc[0].pitch.set_value_f01(4); + surge->storage.getPatch().scene[0].mute_o2.set_value_f01(0,true); + surge->storage.getPatch().scene[0].osc[1].pitch.set_value_f01(1); + + /* + ** Play a note. channel, note, velocity, detune + */ + surge->playNote((char)0, (char)60, (char)100, 0); + + /* + ** Strip off some processing first to avoid the attach transient + */ + for(auto i=0; i<20; ++i) surge->process(); + + /* + ** Then run the sampler + */ + int blockCount = 30; + int overSample = 8; + float overS = 0; + int sampleCount = 0; + for(auto i=0; iprocess(); + + for (int sm=0;smgetNumOutputs(); ++oi) + { + avgOut += surge->output[oi][sm]; + } + + overS += avgOut; + if (((sampleCount-1)%overSample) == 0) + { + overS /= overSample; + int gWidth = (int)((overS + 1)*30); + std::cout << "Sample: " << std::setw( 15 ) << overS << std::setw(gWidth) << "X" << std::endl;; + } + sampleCount ++; + + } + } +}