Skip to content

Commit

Permalink
Headless Surge working
Browse files Browse the repository at this point in the history
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 surge-synthesizer#499.
  • Loading branch information
baconpaul committed Feb 4, 2019
1 parent 186cd3e commit 73c8c4f
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions build-osx.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -181,6 +182,7 @@ run_all_builds()

run_build "vst3"
run_build "au"
run_build "headless"
}

run_install_local()
Expand Down Expand Up @@ -239,6 +241,7 @@ run_clean_builds()

run_clean "vst3"
run_clean "au"
run_clean "headless"
}

run_clean_all()
Expand Down Expand Up @@ -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
;;
Expand Down
68 changes: 68 additions & 0 deletions premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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



6 changes: 6 additions & 0 deletions src/common/SurgeSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <windows.h>
#include <process.h>
#endif

#if TARGET_AUDIOUNIT
#include "aulayer.h"
#include "vstgui/plugin-bindings/plugguieditor.h"
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/common/SurgeSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions src/headless/DisplayInfoHeadless.cpp
Original file line number Diff line number Diff line change
@@ -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));
}

}
}
14 changes: 14 additions & 0 deletions src/headless/HeadlessPluginLayerProxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <iostream>
#include <iomanip>

class HeadlessPluginLayerProxy
{
public:
void updateDisplay()
{
std::cerr << "HeadlessPluginLayerProxy::updateDisplay" << std::endl;
}

};
14 changes: 14 additions & 0 deletions src/headless/LinkFixesHeadless.cpp
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions src/headless/UserInteractionsHeadless.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "UserInteractions.h"
#include <iostream>
#include <iomanip>

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

68 changes: 68 additions & 0 deletions src/headless/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <iostream>
#include <iomanip>

#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<SurgeSynthesizer> 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; i<blockCount; ++i )
{
surge->process();

for (int sm=0;sm<BLOCK_SIZE;++sm)
{
float avgOut = 0;
for (int oi=0; oi<surge->getNumOutputs(); ++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 ++;

}
}
}

0 comments on commit 73c8c4f

Please sign in to comment.