diff --git a/.gitignore b/.gitignore index b88197b..30d19ad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /plugin.dylib /plugin.dll .DS_Store +/faust_libraries diff --git a/Makefile b/Makefile index 75a1373..dcc67a0 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ FLAGS += -Idep/include CFLAGS += CXXFLAGS += -LDFLAGS += +LDFLAGS += SOURCES += src/Prototype.cpp DISTRIBUTABLES += res examples @@ -13,12 +13,12 @@ DISTRIBUTABLES += $(wildcard LICENSE*) include $(RACK_DIR)/arch.mk DUKTAPE ?= 0 -QUICKJS ?= 0 -LUAJIT ?= 0 +QUICKJS ?= 1 +LUAJIT ?= 1 PYTHON ?= 0 SUPERCOLLIDER ?= 0 -VULT ?= 0 -LIBPD ?= 0 +VULT ?= 1 +LIBPD ?= 1 FAUST ?= 1 # Vult depends on both LuaJIT and QuickJS @@ -223,7 +223,7 @@ FLAGS += -Idep/include/libpd -DHAVE_LIBDL ifdef ARCH_WIN # the PD_INTERNAL leaves the function declarations for libpd unchanged # not specifying that flag would enable the "EXTERN __declspec(dllexport) extern" macro - # which throughs a linker error. I guess this macro should only be used for the windows + # which throughs a linker error. I guess this macro should only be used for the windows # specific .dll dynamic linking format. # The corresponding #define resides in "m_pd.h" inside pure data sources FLAGS += -DPD_INTERNAL @@ -250,15 +250,13 @@ endif # Faust ifeq ($(FAUST), 1) -libfaust := dep/faust/build/lib/libfaust.a +libfaust := dep/lib/libfaust.a SOURCES += src/FaustEngine.cpp OBJECTS += $(libfaust) DEPS += $(libfaust) -FLAGS += -Idep/faust/architecture FLAGS += -DINTERP -LDFLAGS += dep/faust/build/lib/libfaust.a -# Test using LLVM +# Test using LLVM #LDFLAGS += -L/usr/local/lib -lfaust # Test using MIR @@ -266,10 +264,11 @@ LDFLAGS += dep/faust/build/lib/libfaust.a $(libfaust): cd dep && git clone "https://github.com/grame-cncm/faust.git" --recursive - cd dep/faust && git checkout 1dfc452a8250f3123b5100edf8c882e1cea407a1 && cp -rf libraries/* ../../res/faust - cd dep/faust && make -C build cmake BACKENDS=interp.cmake TARGETS=interp.cmake && make -C build + cd dep/faust && git checkout 1dfc452a8250f3123b5100edf8c882e1cea407a1 + cd dep/faust/build && make cmake BACKENDS=interp.cmake TARGETS=interp.cmake + cd dep/faust/build && make install PREFIX="$(DEP_PATH)" + cp -rf dep/faust/libraries/* faust_libraries/ endif include $(RACK_DIR)/plugin.mk - diff --git a/res/faust/rack.lib b/faust_libraries/rack.lib similarity index 100% rename from res/faust/rack.lib rename to faust_libraries/rack.lib diff --git a/src/FaustEngine.cpp b/src/FaustEngine.cpp index 52f1027..3305571 100644 --- a/src/FaustEngine.cpp +++ b/src/FaustEngine.cpp @@ -6,15 +6,15 @@ and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + 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 . - + EXCEPTION : As a special exception, you may create a larger work that contains this FAUST architecture section and distribute that work under terms of your choice, so long as this FAUST @@ -23,10 +23,6 @@ #include "ScriptEngine.hpp" -#include -#include -#include - #include #include #include @@ -34,326 +30,344 @@ #include #include -using namespace std; +#pragma GCC diagnostic push +#ifndef __clang__ + #pragma GCC diagnostic ignored "-Wsuggest-override" +#endif +#include +#include +#include #define kBufferSize 64 #ifdef INTERP -#include + #include #else -#include + #include #endif +#pragma GCC diagnostic pop extern rack::Plugin* pluginInstance; // UI handler for switches, knobs and lights -struct RackUI : public GenericUI -{ - typedef function updateFunction; - - vector fConverters; - vector fUpdateFunIn; - vector fUpdateFunOut; - - // For checkbox handling - struct CheckBox { float fLastButton = 0.0f; }; - map fCheckBoxes; - - string fKey, fValue, fScale; - - int getIndex(const string& value) - { - try { - int index = stoi(value); - if (index >= 0 && index <= NUM_ROWS) { - return index; - } else { - cerr << "ERROR : incorrect '" << index << "' value !\n"; - return -1; - } - } catch (invalid_argument& e) { - return -1; - } - } - - RackUI():fScale("lin") - {} - - virtual ~RackUI() - { - for (auto& it : fConverters) delete it; - } - - void addButton(const char* label, FAUSTFLOAT* zone) - { - int index = getIndex(fValue); - if (fKey == "switch" && (index != -1)) { - fUpdateFunIn.push_back([=] (ProcessBlock* block) - { - *zone = block->switches[index-1]; - - // And set the color to red when ON - block->switchLights[index-1][0] = *zone; - }); - } - fKey = fValue = ""; - } - - void addCheckButton(const char* label, FAUSTFLOAT* zone) - { - int index = getIndex(fValue); - if (fKey == "switch" && (index != -1)) { - // Add a checkbox - fCheckBoxes[zone] = CheckBox(); - // Update function - fUpdateFunIn.push_back([=] (ProcessBlock* block) - { - float button = block->switches[index-1]; - // Detect upfront - if (button == 1.0 && (button != fCheckBoxes[zone].fLastButton)) { - // Switch button state - *zone = !*zone; - // And set the color to white when ON - block->switchLights[index-1][0] = *zone; - block->switchLights[index-1][1] = *zone; - block->switchLights[index-1][2] = *zone; - } - // Keep previous button state - fCheckBoxes[zone].fLastButton = button; - }); - } - fKey = fValue = ""; - } - - void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) - { - addNumEntry(label, zone, init, min, max, step); - } - - void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) - { - addNumEntry(label, zone, init, min, max, step); - } - - void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) - { - int index = getIndex(fValue); - if (fKey == "knob" && (index != -1)) { - ConverterZoneControl* converter; - if (fScale == "log") { - converter = new ConverterZoneControl(zone, new LogValueConverter(0., 1., min, max)); - } else if (fScale == "exp") { - converter = new ConverterZoneControl(zone, new ExpValueConverter(0., 1., min, max)); - } else { - converter = new ConverterZoneControl(zone, new LinearValueConverter(0., 1., min, max)); - } - fUpdateFunIn.push_back([=] (ProcessBlock* block) { converter->update(block->knobs[index-1]); }); - fConverters.push_back(converter); - } - fScale = "lin"; - fKey = fValue = ""; - } - - void addBarGraph(FAUSTFLOAT* zone) - { - int index = getIndex(fValue); - if ((fKey == "light_red") && (index != -1)) { - fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][0] = *zone; }); - } else if ((fKey == "light_green") && (index != -1)) { - fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][1] = *zone; }); - } else if ((fKey == "light_blue") && (index != -1)) { - fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][2] = *zone; }); - } else if ((fKey == "switchlight_red") && (index != -1)) { - fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][0] = *zone; }); - } else if ((fKey == "switchlight_green") && (index != -1)) { - fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][1] = *zone; }); - } else if ((fKey == "switchlight_blue") && (index != -1)) { - fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][2] = *zone; }); - } - fKey = fValue = ""; - } - - void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) - { - addBarGraph(zone); - } - - void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) - { - addBarGraph(zone); - } - - void addSoundfile(const char* label, const char* soundpath, Soundfile** sf_zone) - { - WARN("Faust Prototype : 'soundfile' primitive not yet supported", ""); - } - - void declare(FAUSTFLOAT* zone, const char* key, const char* val) - { - static vector keys = {"switch", "knob", "light_red", "light_green", "light_blue", "switchlight_red", "switchlight_green", "switchlight_blue"}; - if (find(keys.begin(), keys.end(), key) != keys.end()) { - fKey = key; - fValue = val; - } else if (string(key) == "scale") { - fScale = val; - } - } - +struct RackUI : public GenericUI { + typedef std::function updateFunction; + + std::vector fConverters; + std::vector fUpdateFunIn; + std::vector fUpdateFunOut; + + // For checkbox handling + struct CheckBox { + float fLastButton = 0.0f; + }; + std::map fCheckBoxes; + + std::string fKey, fValue, fScale; + + int getIndex(const std::string& value) { + try { + int index = stoi(value); + if (index >= 0 && index <= NUM_ROWS) { + return index; + } + else { + WARN("ERROR : incorrect '%d' value", index); + return -1; + } + } + catch (std::invalid_argument& e) { + return -1; + } + } + + RackUI(): fScale("lin") + {} + + virtual ~RackUI() { + for (auto& it : fConverters) + delete it; + } + + void addButton(const char* label, FAUSTFLOAT* zone) override { + int index = getIndex(fValue); + if (fKey == "switch" && (index != -1)) { + fUpdateFunIn.push_back([ = ](ProcessBlock * block) { + *zone = block->switches[index - 1]; + + // And set the color to red when ON + block->switchLights[index - 1][0] = *zone; + }); + } + fKey = fValue = ""; + } + + void addCheckButton(const char* label, FAUSTFLOAT* zone) override { + int index = getIndex(fValue); + if (fKey == "switch" && (index != -1)) { + // Add a checkbox + fCheckBoxes[zone] = CheckBox(); + // Update function + fUpdateFunIn.push_back([ = ](ProcessBlock * block) { + float button = block->switches[index - 1]; + // Detect upfront + if (button == 1.0 && (button != fCheckBoxes[zone].fLastButton)) { + // Switch button state + *zone = !*zone; + // And set the color to white when ON + block->switchLights[index - 1][0] = *zone; + block->switchLights[index - 1][1] = *zone; + block->switchLights[index - 1][2] = *zone; + } + // Keep previous button state + fCheckBoxes[zone].fLastButton = button; + }); + } + fKey = fValue = ""; + } + + void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override { + addNumEntry(label, zone, init, min, max, step); + } + + void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override { + addNumEntry(label, zone, init, min, max, step); + } + + void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override { + int index = getIndex(fValue); + if (fKey == "knob" && (index != -1)) { + ConverterZoneControl* converter; + if (fScale == "log") { + converter = new ConverterZoneControl(zone, new LogValueConverter(0., 1., min, max)); + } + else if (fScale == "exp") { + converter = new ConverterZoneControl(zone, new ExpValueConverter(0., 1., min, max)); + } + else { + converter = new ConverterZoneControl(zone, new LinearValueConverter(0., 1., min, max)); + } + fUpdateFunIn.push_back([ = ](ProcessBlock * block) { + converter->update(block->knobs[index - 1]); + }); + fConverters.push_back(converter); + } + fScale = "lin"; + fKey = fValue = ""; + } + + void addBarGraph(FAUSTFLOAT* zone) { + int index = getIndex(fValue); + if ((fKey == "light_red") && (index != -1)) { + fUpdateFunOut.push_back([ = ](ProcessBlock * block) { + block->lights[index - 1][0] = *zone; + }); + } + else if ((fKey == "light_green") && (index != -1)) { + fUpdateFunOut.push_back([ = ](ProcessBlock * block) { + block->lights[index - 1][1] = *zone; + }); + } + else if ((fKey == "light_blue") && (index != -1)) { + fUpdateFunOut.push_back([ = ](ProcessBlock * block) { + block->lights[index - 1][2] = *zone; + }); + } + else if ((fKey == "switchlight_red") && (index != -1)) { + fUpdateFunOut.push_back([ = ](ProcessBlock * block) { + block->switchLights[index - 1][0] = *zone; + }); + } + else if ((fKey == "switchlight_green") && (index != -1)) { + fUpdateFunOut.push_back([ = ](ProcessBlock * block) { + block->switchLights[index - 1][1] = *zone; + }); + } + else if ((fKey == "switchlight_blue") && (index != -1)) { + fUpdateFunOut.push_back([ = ](ProcessBlock * block) { + block->switchLights[index - 1][2] = *zone; + }); + } + fKey = fValue = ""; + } + + void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override { + addBarGraph(zone); + } + + void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override { + addBarGraph(zone); + } + + void addSoundfile(const char* label, const char* soundpath, Soundfile** sf_zone) override { + WARN("Faust Prototype : 'soundfile' primitive not yet supported", ""); + } + + void declare(FAUSTFLOAT* zone, const char* key, const char* val) override { + static std::vector keys = {"switch", "knob", "light_red", "light_green", "light_blue", "switchlight_red", "switchlight_green", "switchlight_blue"}; + if (find(keys.begin(), keys.end(), key) != keys.end()) { + fKey = key; + fValue = val; + } + else if (std::string(key) == "scale") { + fScale = val; + } + } }; // Faust engine using libfaust/LLVM class FaustEngine : public ScriptEngine { - - public: - - FaustEngine(): - fDSPFactory(nullptr), - fDSP(nullptr), - fInputs(nullptr), - fOutputs(nullptr), - fDSPLibraries(rack::asset::plugin(pluginInstance, "res/faust")) - {} - - ~FaustEngine() - { - delete [] fInputs; - delete [] fOutputs; - delete fDSP; - #ifdef INTERP - deleteInterpreterDSPFactory(static_cast(fDSPFactory)); - #else - deleteDSPFactory(static_cast(fDSPFactory)); - #endif - } - - string getEngineName() override - { - return "Faust"; - } - - int run(const string& path, const string& script) override - { - #if defined ARCH_LIN - string temp_cache = "/var/tmp/VCV_" + generateSHA1(script); - #elif defined ARCH_MAC - string temp_cache = "/private/var/tmp/VCV_" + generateSHA1(script); - #elif defined ARCH_WIN - char buf[MAX_PATH+1] = {0}; - GetTempPath(sizeof(buf), buf); - string temp_cache = string(buf) + "/VCV_" + generateSHA1(script); - #endif - string error_msg; - - // Try to load the machine code cache - #ifdef INTERP - fDSPFactory = readInterpreterDSPFactoryFromBitcodeFile(temp_cache, error_msg); - #else - fDSPFactory = readDSPFactoryFromMachineFile(temp_cache, "", error_msg); - #endif - - if (!fDSPFactory) { - // Otherwise recompile the DSP - int argc = 0; - const char* argv[8]; - argv[argc++] = "-I"; - argv[argc++] = fDSPLibraries.c_str(); - argv[argc] = nullptr; // NULL terminated argv - - #ifdef INTERP - fDSPFactory = createInterpreterDSPFactoryFromString("FaustDSP", script, argc, argv, error_msg); - #else - fDSPFactory = createDSPFactoryFromString("FaustDSP", script, argc, argv, "", error_msg, -1); - #endif - if (!fDSPFactory) { - display("ERROR : cannot create factory !"); - WARN("Faust Prototype : %s", error_msg.c_str()); - return -1; - } else { - // And save the cache - display("Compiling factory finished"); - #ifdef INTERP - writeInterpreterDSPFactoryToBitcodeFile(static_cast(fDSPFactory), temp_cache); - #else - writeDSPFactoryToMachineFile(static_cast(fDSPFactory), temp_cache, ""); - #endif - } - } - - // Create DSP - fDSP = fDSPFactory->createDSPInstance(); - if (!fDSP) { - display("ERROR: cannot create instance !"); - return -1; - } else { - display("Created DSP"); - } - - // Check inputs/outputs - if (fDSP->getNumInputs() > NUM_ROWS) { - display("ERROR: DSP has " + to_string(fDSP->getNumInputs()) + " inputs !"); - return -1; - } - - if (fDSP->getNumOutputs() > NUM_ROWS) { - display("ERROR: DSP has " + to_string(fDSP->getNumInputs()) + " outputs !"); - return -1; - } - - // Prepare buffers for process - ProcessBlock* block = getProcessBlock(); - - fInputs = new FAUSTFLOAT*[fDSP->getNumInputs()]; - for (int chan = 0; chan < fDSP->getNumInputs(); chan++) { - fInputs[chan] = block->inputs[chan]; - } - - fOutputs = new FAUSTFLOAT*[fDSP->getNumOutputs()]; - for (int chan = 0; chan < fDSP->getNumOutputs(); chan++) { - fOutputs[chan] = block->outputs[chan]; - } - - // Setup UI - fDSP->buildUserInterface(&fRackUI); - - setFrameDivider(1); - setBufferSize(kBufferSize); - - // Init DSP with default SR - fDSP->init(44100); - return 0; - } - - int process() override - { - ProcessBlock* block = getProcessBlock(); - - // Possibly update SR - if (block->sampleRate != fDSP->getSampleRate()) { - fDSP->init(block->sampleRate); - } - - // Update inputs controllers - for (auto& it : fRackUI.fUpdateFunIn) it(block); - - // Compute samples - fDSP->compute(block->bufferSize, fInputs, fOutputs); - - // Update output controllers - for (auto& it : fRackUI.fUpdateFunOut) it(block); - - return 0; - } - - private: - dsp_factory* fDSPFactory; - dsp* fDSP; - FAUSTFLOAT** fInputs; - FAUSTFLOAT** fOutputs; - RackUI fRackUI; - string fDSPLibraries; + +public: + + FaustEngine(): + fDSPFactory(nullptr), + fDSP(nullptr), + fInputs(nullptr), + fOutputs(nullptr), + fDSPLibraries(rack::asset::plugin(pluginInstance, "faust_libraries")) + {} + + ~FaustEngine() { + delete [] fInputs; + delete [] fOutputs; + delete fDSP; +#ifdef INTERP + deleteInterpreterDSPFactory(static_cast(fDSPFactory)); +#else + deleteDSPFactory(static_cast(fDSPFactory)); +#endif + } + + std::string getEngineName() override { + return "Faust"; + } + + int run(const std::string& path, const std::string& script) override { +#if defined ARCH_LIN + std::string temp_cache = "/var/tmp/VCV_" + generateSHA1(script); +#elif defined ARCH_MAC + std::string temp_cache = "/private/var/tmp/VCV_" + generateSHA1(script); +#elif defined ARCH_WIN + char buf[MAX_PATH + 1] = {0}; + GetTempPath(sizeof(buf), buf); + std::string temp_cache = std::string(buf) + "/VCV_" + generateSHA1(script); +#endif + std::string error_msg; + + // Try to load the machine code cache +#ifdef INTERP + fDSPFactory = readInterpreterDSPFactoryFromBitcodeFile(temp_cache, error_msg); +#else + fDSPFactory = readDSPFactoryFromMachineFile(temp_cache, "", error_msg); +#endif + + if (!fDSPFactory) { + // Otherwise recompile the DSP + int argc = 0; + const char* argv[8]; + argv[argc++] = "-I"; + argv[argc++] = fDSPLibraries.c_str(); + argv[argc] = nullptr; // NULL terminated argv + +#ifdef INTERP + fDSPFactory = createInterpreterDSPFactoryFromString("FaustDSP", script, argc, argv, error_msg); +#else + fDSPFactory = createDSPFactoryFromString("FaustDSP", script, argc, argv, "", error_msg, -1); +#endif + if (!fDSPFactory) { + display("ERROR : cannot create factory !"); + WARN("Faust Prototype : %s", error_msg.c_str()); + return -1; + } + else { + // And save the cache + display("Compiling factory finished"); +#ifdef INTERP + writeInterpreterDSPFactoryToBitcodeFile(static_cast(fDSPFactory), temp_cache); +#else + writeDSPFactoryToMachineFile(static_cast(fDSPFactory), temp_cache, ""); +#endif + } + } + + // Create DSP + fDSP = fDSPFactory->createDSPInstance(); + if (!fDSP) { + display("ERROR: cannot create instance !"); + return -1; + } + else { + display("Created DSP"); + } + + // Check inputs/outputs + if (fDSP->getNumInputs() > NUM_ROWS) { + display("ERROR: DSP has " + std::to_string(fDSP->getNumInputs()) + " inputs !"); + return -1; + } + + if (fDSP->getNumOutputs() > NUM_ROWS) { + display("ERROR: DSP has " + std::to_string(fDSP->getNumInputs()) + " outputs !"); + return -1; + } + + // Prepare buffers for process + ProcessBlock* block = getProcessBlock(); + + fInputs = new FAUSTFLOAT*[fDSP->getNumInputs()]; + for (int chan = 0; chan < fDSP->getNumInputs(); chan++) { + fInputs[chan] = block->inputs[chan]; + } + + fOutputs = new FAUSTFLOAT*[fDSP->getNumOutputs()]; + for (int chan = 0; chan < fDSP->getNumOutputs(); chan++) { + fOutputs[chan] = block->outputs[chan]; + } + + // Setup UI + fDSP->buildUserInterface(&fRackUI); + + setFrameDivider(1); + setBufferSize(kBufferSize); + + // Init DSP with default SR + fDSP->init(44100); + return 0; + } + + int process() override { + ProcessBlock* block = getProcessBlock(); + + // Possibly update SR + if (block->sampleRate != fDSP->getSampleRate()) { + fDSP->init(block->sampleRate); + } + + // Update inputs controllers + for (auto& it : fRackUI.fUpdateFunIn) + it(block); + + // Compute samples + fDSP->compute(block->bufferSize, fInputs, fOutputs); + + // Update output controllers + for (auto& it : fRackUI.fUpdateFunOut) + it(block); + + return 0; + } + +private: + dsp_factory* fDSPFactory; + dsp* fDSP; + FAUSTFLOAT** fInputs; + FAUSTFLOAT** fOutputs; + RackUI fRackUI; + std::string fDSPLibraries; }; __attribute__((constructor(1000))) static void constructor() { - addScriptEngine("dsp"); + addScriptEngine("dsp"); }