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.

357 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. //#define MIR
  33. #ifdef MIR
  34. #include <faust/dsp/interpreter-dsp.h>
  35. #else
  36. #include <faust/dsp/llvm-dsp.h>
  37. #endif
  38. extern rack::Plugin* pluginInstance;
  39. // UI handler for switches, knobs and lights
  40. struct RackUI : public GenericUI
  41. {
  42. typedef function<void(ProcessBlock* block)> updateFunction;
  43. vector<ConverterZoneControl*> fConverters;
  44. vector<updateFunction> fUpdateFunIn;
  45. vector<updateFunction> fUpdateFunOut;
  46. // For checkbox handling
  47. struct CheckBox { float fLastButton = 0.0f; };
  48. map <FAUSTFLOAT*, CheckBox> fCheckBoxes;
  49. string fKey, fValue, fScale;
  50. int getIndex(const string& value)
  51. {
  52. try {
  53. int index = stoi(value);
  54. if (index >= 0 && index <= NUM_ROWS) {
  55. return index;
  56. } else {
  57. cerr << "ERROR : incorrect '" << index << "' value !\n";
  58. return -1;
  59. }
  60. } catch (invalid_argument& e) {
  61. return -1;
  62. }
  63. }
  64. RackUI():fScale("lin")
  65. {}
  66. virtual ~RackUI()
  67. {
  68. for (auto& it : fConverters) delete it;
  69. }
  70. void addButton(const char* label, FAUSTFLOAT* zone)
  71. {
  72. int index = getIndex(fValue);
  73. if (fKey == "switch" && (index != -1)) {
  74. fUpdateFunIn.push_back([=] (ProcessBlock* block)
  75. {
  76. *zone = block->switches[index-1];
  77. // And set the color to red when ON
  78. block->switchLights[index-1][0] = *zone;
  79. });
  80. }
  81. }
  82. void addCheckButton(const char* label, FAUSTFLOAT* zone)
  83. {
  84. int index = getIndex(fValue);
  85. if (fKey == "switch" && (index != -1)) {
  86. // Add a checkbox
  87. fCheckBoxes[zone] = CheckBox();
  88. // Update function
  89. fUpdateFunIn.push_back([=] (ProcessBlock* block)
  90. {
  91. float button = block->switches[index-1];
  92. // Detect upfront
  93. if (button == 1.0 && (button != fCheckBoxes[zone].fLastButton)) {
  94. // Switch button state
  95. *zone = !*zone;
  96. // And set the color to white when ON
  97. block->switchLights[index-1][0] = *zone;
  98. block->switchLights[index-1][1] = *zone;
  99. block->switchLights[index-1][2] = *zone;
  100. }
  101. // Keep previous button state
  102. fCheckBoxes[zone].fLastButton = button;
  103. });
  104. }
  105. }
  106. void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  107. {
  108. addNumEntry(label, zone, init, min, max, step);
  109. }
  110. void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  111. {
  112. addNumEntry(label, zone, init, min, max, step);
  113. }
  114. void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  115. {
  116. int index = getIndex(fValue);
  117. if (fKey == "knob" && (index != -1)) {
  118. ConverterZoneControl* converter;
  119. if (fScale == "log") {
  120. converter = new ConverterZoneControl(zone, new LogValueConverter(0., 1., min, max));
  121. } else if (fScale == "exp") {
  122. converter = new ConverterZoneControl(zone, new ExpValueConverter(0., 1., min, max));
  123. } else {
  124. converter = new ConverterZoneControl(zone, new LinearValueConverter(0., 1., min, max));
  125. }
  126. fUpdateFunIn.push_back([=] (ProcessBlock* block) { converter->update(block->knobs[index-1]); });
  127. fConverters.push_back(converter);
  128. }
  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, "faust_libraries"))
  179. {}
  180. ~FaustEngine()
  181. {
  182. delete [] fInputs;
  183. delete [] fOutputs;
  184. delete fDSP;
  185. #ifdef MIR
  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 MIR
  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 MIR
  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 MIR
  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. }