You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
12KB

  1. /************************************************************************
  2. FAUST Architecture File
  3. Copyright (C) 2020 GRAME, Centre National de Creation Musicale
  4. ---------------------------------------------------------------------
  5. This Architecture section is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 3 of
  8. the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; If not, see <http://www.gnu.org/licenses/>.
  15. EXCEPTION : As a special exception, you may create a larger work
  16. that contains this FAUST architecture section and distribute
  17. that work under terms of your choice, so long as this FAUST
  18. architecture section is not modified.
  19. ************************************************************************/
  20. #include "ScriptEngine.hpp"
  21. #include <faust/dsp/libfaust.h>
  22. #include <faust/gui/DecoratorUI.h>
  23. #include <faust/gui/ValueConverter.h>
  24. #include <iostream>
  25. #include <memory>
  26. #include <map>
  27. #include <vector>
  28. #include <algorithm>
  29. #include <functional>
  30. using namespace std;
  31. #define kBufferSize 64
  32. #ifdef INTERP
  33. #include <faust/dsp/interpreter-dsp.h>
  34. #else
  35. #include <faust/dsp/llvm-dsp.h>
  36. #endif
  37. extern rack::Plugin* pluginInstance;
  38. // UI handler for switches, knobs and lights
  39. struct RackUI : public GenericUI
  40. {
  41. typedef function<void(ProcessBlock* block)> updateFunction;
  42. vector<ConverterZoneControl*> fConverters;
  43. vector<updateFunction> fUpdateFunIn;
  44. vector<updateFunction> fUpdateFunOut;
  45. // For checkbox handling
  46. struct CheckBox { float fLastButton = 0.0f; };
  47. map <FAUSTFLOAT*, CheckBox> fCheckBoxes;
  48. string fKey, fValue, fScale;
  49. int getIndex(const string& value)
  50. {
  51. try {
  52. int index = stoi(value);
  53. if (index >= 0 && index <= NUM_ROWS) {
  54. return index;
  55. } else {
  56. cerr << "ERROR : incorrect '" << index << "' value !\n";
  57. return -1;
  58. }
  59. } catch (invalid_argument& e) {
  60. return -1;
  61. }
  62. }
  63. RackUI():fScale("lin")
  64. {}
  65. virtual ~RackUI()
  66. {
  67. for (auto& it : fConverters) delete it;
  68. }
  69. void addButton(const char* label, FAUSTFLOAT* zone)
  70. {
  71. int index = getIndex(fValue);
  72. if (fKey == "switch" && (index != -1)) {
  73. fUpdateFunIn.push_back([=] (ProcessBlock* block)
  74. {
  75. *zone = block->switches[index-1];
  76. // And set the color to red when ON
  77. block->switchLights[index-1][0] = *zone;
  78. });
  79. }
  80. }
  81. void addCheckButton(const char* label, FAUSTFLOAT* zone)
  82. {
  83. int index = getIndex(fValue);
  84. if (fKey == "switch" && (index != -1)) {
  85. // Add a checkbox
  86. fCheckBoxes[zone] = CheckBox();
  87. // Update function
  88. fUpdateFunIn.push_back([=] (ProcessBlock* block)
  89. {
  90. float button = block->switches[index-1];
  91. // Detect upfront
  92. if (button == 1.0 && (button != fCheckBoxes[zone].fLastButton)) {
  93. // Switch button state
  94. *zone = !*zone;
  95. // And set the color to white when ON
  96. block->switchLights[index-1][0] = *zone;
  97. block->switchLights[index-1][1] = *zone;
  98. block->switchLights[index-1][2] = *zone;
  99. }
  100. // Keep previous button state
  101. fCheckBoxes[zone].fLastButton = button;
  102. });
  103. }
  104. }
  105. void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  106. {
  107. addNumEntry(label, zone, init, min, max, step);
  108. }
  109. void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  110. {
  111. addNumEntry(label, zone, init, min, max, step);
  112. }
  113. void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  114. {
  115. int index = getIndex(fValue);
  116. if (fKey == "knob" && (index != -1)) {
  117. ConverterZoneControl* converter;
  118. if (fScale == "log") {
  119. converter = new ConverterZoneControl(zone, new LogValueConverter(0., 1., min, max));
  120. } else if (fScale == "exp") {
  121. converter = new ConverterZoneControl(zone, new ExpValueConverter(0., 1., min, max));
  122. } else {
  123. converter = new ConverterZoneControl(zone, new LinearValueConverter(0., 1., min, max));
  124. }
  125. fUpdateFunIn.push_back([=] (ProcessBlock* block) { converter->update(block->knobs[index-1]); });
  126. fConverters.push_back(converter);
  127. }
  128. fScale = "lin";
  129. }
  130. void addBarGraph(FAUSTFLOAT* zone)
  131. {
  132. int index = getIndex(fValue);
  133. if ((fKey == "light_red") && (index != -1)) {
  134. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][0] = *zone; });
  135. } else if ((fKey == "light_green") && (index != -1)) {
  136. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][1] = *zone; });
  137. } else if ((fKey == "light_blue") && (index != -1)) {
  138. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][2] = *zone; });
  139. } else if ((fKey == "switchlight_red") && (index != -1)) {
  140. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][0] = *zone; });
  141. } else if ((fKey == "switchlight_green") && (index != -1)) {
  142. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][1] = *zone; });
  143. } else if ((fKey == "switchlight_blue") && (index != -1)) {
  144. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][2] = *zone; });
  145. }
  146. }
  147. void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
  148. {
  149. addBarGraph(zone);
  150. }
  151. void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
  152. {
  153. addBarGraph(zone);
  154. }
  155. void addSoundfile(const char* label, const char* soundpath, Soundfile** sf_zone)
  156. {
  157. WARN("Faust Prototype : 'soundfile' primitive not yet supported", "");
  158. }
  159. void declare(FAUSTFLOAT* zone, const char* key, const char* val)
  160. {
  161. static vector<string> keys = {"switch", "knob", "light_red", "light_green", "light_blue", "switchlight_red", "switchlight_green", "switchlight_blue"};
  162. if (find(keys.begin(), keys.end(), key) != keys.end()) {
  163. fKey = key;
  164. fValue = val;
  165. } else if (string(key) == "scale") {
  166. fScale = val;
  167. }
  168. }
  169. };
  170. // Faust engine using libfaust/LLVM
  171. class FaustEngine : public ScriptEngine {
  172. public:
  173. FaustEngine():
  174. fDSPFactory(nullptr),
  175. fDSP(nullptr),
  176. fInputs(nullptr),
  177. fOutputs(nullptr),
  178. fDSPLibraries(rack::asset::plugin(pluginInstance, "res/faust"))
  179. {}
  180. ~FaustEngine()
  181. {
  182. delete [] fInputs;
  183. delete [] fOutputs;
  184. delete fDSP;
  185. #ifdef INTERP
  186. deleteInterpreterDSPFactory(static_cast<interpreter_dsp_factory*>(fDSPFactory));
  187. #else
  188. deleteDSPFactory(static_cast<llvm_dsp_factory*>(fDSPFactory));
  189. #endif
  190. }
  191. string getEngineName() override
  192. {
  193. return "Faust";
  194. }
  195. int run(const string& path, const string& script) override
  196. {
  197. #if defined ARCH_LIN
  198. string temp_cache = "/var/tmp/VCV_" + generateSHA1(script);
  199. #elif defined ARCH_MAC
  200. string temp_cache = "/private/var/tmp/VCV_" + generateSHA1(script);
  201. #elif defined ARCH_WIN
  202. char buf[MAX_PATH+1] = {0};
  203. GetTempPath(sizeof(buf), buf);
  204. string temp_cache = string(buf) + "/VCV_" + generateSHA1(script);
  205. #endif
  206. string error_msg;
  207. // Try to load the machine code cache
  208. #ifdef INTERP
  209. fDSPFactory = readInterpreterDSPFactoryFromBitcodeFile(temp_cache, error_msg);
  210. #else
  211. fDSPFactory = readDSPFactoryFromMachineFile(temp_cache, "", error_msg);
  212. #endif
  213. if (!fDSPFactory) {
  214. // Otherwise recompile the DSP
  215. int argc = 0;
  216. const char* argv[8];
  217. argv[argc++] = "-I";
  218. argv[argc++] = fDSPLibraries.c_str();
  219. argv[argc] = nullptr; // NULL terminated argv
  220. #ifdef INTERP
  221. fDSPFactory = createInterpreterDSPFactoryFromString("FaustDSP", script, argc, argv, error_msg);
  222. #else
  223. fDSPFactory = createDSPFactoryFromString("FaustDSP", script, argc, argv, "", error_msg, -1);
  224. #endif
  225. if (!fDSPFactory) {
  226. display("ERROR : cannot create factory !");
  227. WARN("Faust Prototype : %s", error_msg.c_str());
  228. return -1;
  229. } else {
  230. // And save the cache
  231. display("Compiling factory finished");
  232. #ifdef INTERP
  233. writeInterpreterDSPFactoryToBitcodeFile(static_cast<interpreter_dsp_factory*>(fDSPFactory), temp_cache);
  234. #else
  235. writeDSPFactoryToMachineFile(static_cast<llvm_dsp_factory*>(fDSPFactory), temp_cache, "");
  236. #endif
  237. }
  238. }
  239. // Create DSP
  240. fDSP = fDSPFactory->createDSPInstance();
  241. if (!fDSP) {
  242. display("ERROR: cannot create instance !");
  243. return -1;
  244. } else {
  245. display("Created DSP");
  246. }
  247. // Check inputs/outputs
  248. if (fDSP->getNumInputs() > NUM_ROWS) {
  249. display("ERROR: DSP has " + to_string(fDSP->getNumInputs()) + " inputs !");
  250. return -1;
  251. }
  252. if (fDSP->getNumOutputs() > NUM_ROWS) {
  253. display("ERROR: DSP has " + to_string(fDSP->getNumInputs()) + " outputs !");
  254. return -1;
  255. }
  256. // Prepare buffers for process
  257. ProcessBlock* block = getProcessBlock();
  258. fInputs = new FAUSTFLOAT*[fDSP->getNumInputs()];
  259. for (int chan = 0; chan < fDSP->getNumInputs(); chan++) {
  260. fInputs[chan] = block->inputs[chan];
  261. }
  262. fOutputs = new FAUSTFLOAT*[fDSP->getNumOutputs()];
  263. for (int chan = 0; chan < fDSP->getNumOutputs(); chan++) {
  264. fOutputs[chan] = block->outputs[chan];
  265. }
  266. // Setup UI
  267. fDSP->buildUserInterface(&fRackUI);
  268. setFrameDivider(1);
  269. setBufferSize(kBufferSize);
  270. // Init DSP with default SR
  271. fDSP->init(44100);
  272. return 0;
  273. }
  274. int process() override
  275. {
  276. ProcessBlock* block = getProcessBlock();
  277. // Possibly update SR
  278. if (block->sampleRate != fDSP->getSampleRate()) {
  279. fDSP->init(block->sampleRate);
  280. }
  281. // Update inputs controllers
  282. for (auto& it : fRackUI.fUpdateFunIn) it(block);
  283. // Compute samples
  284. fDSP->compute(block->bufferSize, fInputs, fOutputs);
  285. // Update output controllers
  286. for (auto& it : fRackUI.fUpdateFunOut) it(block);
  287. return 0;
  288. }
  289. private:
  290. dsp_factory* fDSPFactory;
  291. dsp* fDSP;
  292. FAUSTFLOAT** fInputs;
  293. FAUSTFLOAT** fOutputs;
  294. RackUI fRackUI;
  295. string fDSPLibraries;
  296. };
  297. __attribute__((constructor(1000)))
  298. static void constructor() {
  299. addScriptEngine<FaustEngine>("dsp");
  300. }