/************************************************************************ FAUST Architecture File Copyright (C) 2020 GRAME, Centre National de Creation Musicale --------------------------------------------------------------------- This Architecture section 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; 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 architecture section is not modified. ************************************************************************/ #include "ScriptEngine.hpp" #include #include #include #include #include #include using namespace std; #define kBufferSize 64 extern rack::Plugin* pluginInstance; // UI handler for switches, knobs and leds struct RackUI : public GenericUI { typedef function updateFunction; vector fConverters; vector fUpdateFunIn; vector fUpdateFunOut; // For checkbox handling struct CheckBox { float fLast = 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]; }); } } 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 state = block->switches[index-1]; // Detect upfront if (state == 1.0 && (state != fCheckBoxes[zone].fLast)) { // Switch button state *zone = !*zone; // And set the color block->switchLights[index-1][0] = *zone; block->switchLights[index-1][1] = *zone; block->switchLights[index-1][2] = *zone; } // Keep previous button state fCheckBoxes[zone].fLast = state; }); } } 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); } } 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; }); } } 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 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; } } }; // Faust engine using libfaust/LLVM class FaustEngine : public ScriptEngine { public: FaustEngine(): fDSPFactory(nullptr), fDSP(nullptr), fInputs(nullptr), fOutputs(nullptr), fDSPLibraries(rack::asset::plugin(pluginInstance, "faust_libraries")) {} ~FaustEngine() { delete [] fInputs; delete [] fOutputs; delete fDSP; deleteDSPFactory(fDSPFactory); } 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 fDSPFactory = readDSPFactoryFromMachineFile(temp_cache, "", error_msg); 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 fDSPFactory = createDSPFactoryFromString("FaustDSP", script, argc, argv, "", error_msg, -1); 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"); writeDSPFactoryToMachineFile(fDSPFactory, temp_cache, ""); } } // Create DSP fDSP = fDSPFactory->createDSPInstance(); if (!fDSP) { display("ERROR: cannot create instance !"); return -1; } else { display("Created DSP"); } // Prepare 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; } // Setup UI fDSP->buildUserInterface(&fRackUI); setFrameDivider(1); setBufferSize(kBufferSize); // 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]; } // 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: llvm_dsp_factory* fDSPFactory; llvm_dsp* fDSP; FAUSTFLOAT** fInputs; FAUSTFLOAT** fOutputs; RackUI fRackUI; string fDSPLibraries; }; __attribute__((constructor(1000))) static void constructor() { addScriptEngine("dsp"); }