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.

319 lines
11KB

  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/llvm-dsp.h>
  22. #include <faust/dsp/libfaust.h>
  23. #include <faust/gui/DecoratorUI.h>
  24. #include <faust/gui/ValueConverter.h>
  25. #include <iostream>
  26. #include <memory>
  27. using namespace std;
  28. #define kBufferSize 64
  29. extern rack::Plugin* pluginInstance;
  30. // UI handler for switches, knobs and leds
  31. struct RackUI : public GenericUI
  32. {
  33. typedef function<void(ProcessBlock* block)> updateFunction;
  34. vector<ConverterZoneControl*> fConverters;
  35. vector<updateFunction> fUpdateFunIn;
  36. vector<updateFunction> fUpdateFunOut;
  37. // For checkbox handling
  38. struct CheckBox { float fLast = 0.0f; };
  39. map <FAUSTFLOAT*, CheckBox> fCheckBoxes;
  40. string fKey, fValue, fScale;
  41. int getIndex(const string& value)
  42. {
  43. try {
  44. int index = stoi(value);
  45. if (index >= 0 && index <= NUM_ROWS) {
  46. return index;
  47. } else {
  48. cerr << "ERROR : incorrect '" << index << "' value !\n";
  49. return -1;
  50. }
  51. } catch (invalid_argument& e) {
  52. return -1;
  53. }
  54. }
  55. RackUI():fScale("lin")
  56. {}
  57. virtual ~RackUI()
  58. {
  59. for (auto& it : fConverters) delete it;
  60. }
  61. void addButton(const char* label, FAUSTFLOAT* zone)
  62. {
  63. int index = getIndex(fValue);
  64. if (fKey == "switch" && (index != -1)) {
  65. fUpdateFunIn.push_back([=] (ProcessBlock* block) { *zone = block->switches[index-1]; });
  66. }
  67. }
  68. void addCheckButton(const char* label, FAUSTFLOAT* zone)
  69. {
  70. int index = getIndex(fValue);
  71. if (fKey == "switch" && (index != -1)) {
  72. // Add a checkbox
  73. fCheckBoxes[zone] = CheckBox();
  74. // Update function
  75. fUpdateFunIn.push_back([=] (ProcessBlock* block)
  76. {
  77. float state = block->switches[index-1];
  78. // Detect upfront
  79. if (state == 1.0 && (state != fCheckBoxes[zone].fLast)) {
  80. // Switch button state
  81. *zone = !*zone;
  82. // And set the color
  83. block->switchLights[index-1][0] = *zone;
  84. block->switchLights[index-1][1] = *zone;
  85. block->switchLights[index-1][2] = *zone;
  86. }
  87. // Keep previous button state
  88. fCheckBoxes[zone].fLast = state;
  89. });
  90. }
  91. }
  92. void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  93. {
  94. addNumEntry(label, zone, init, min, max, step);
  95. }
  96. void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  97. {
  98. addNumEntry(label, zone, init, min, max, step);
  99. }
  100. void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
  101. {
  102. int index = getIndex(fValue);
  103. if (fKey == "knob" && (index != -1)) {
  104. ConverterZoneControl* converter;
  105. if (fScale == "log") {
  106. converter = new ConverterZoneControl(zone, new LogValueConverter(0., 1., min, max));
  107. } else if (fScale == "exp") {
  108. converter = new ConverterZoneControl(zone, new ExpValueConverter(0., 1., min, max));
  109. } else {
  110. converter = new ConverterZoneControl(zone, new LinearValueConverter(0., 1., min, max));
  111. }
  112. fUpdateFunIn.push_back([=] (ProcessBlock* block) { converter->update(block->knobs[index-1]); });
  113. fConverters.push_back(converter);
  114. }
  115. }
  116. void addBarGraph(FAUSTFLOAT* zone)
  117. {
  118. int index = getIndex(fValue);
  119. if ((fKey == "light_red") && (index != -1)) {
  120. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][0] = *zone; });
  121. } else if ((fKey == "light_green") && (index != -1)) {
  122. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][1] = *zone; });
  123. } else if ((fKey == "light_blue") && (index != -1)) {
  124. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->lights[index-1][2] = *zone; });
  125. } else if ((fKey == "switchlight_red") && (index != -1)) {
  126. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][0] = *zone; });
  127. } else if ((fKey == "switchlight_green") && (index != -1)) {
  128. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][1] = *zone; });
  129. } else if ((fKey == "switchlight_blue") && (index != -1)) {
  130. fUpdateFunOut.push_back([=] (ProcessBlock* block) { block->switchLights[index-1][2] = *zone; });
  131. }
  132. }
  133. void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
  134. {
  135. addBarGraph(zone);
  136. }
  137. void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
  138. {
  139. addBarGraph(zone);
  140. }
  141. void declare(FAUSTFLOAT* zone, const char* key, const char* val)
  142. {
  143. static vector<string> keys = {"switch", "knob", "light_red", "light_green", "light_blue", "switchlight_red", "switchlight_green", "switchlight_blue"};
  144. if (find(keys.begin(), keys.end(), key) != keys.end()) {
  145. fKey = key;
  146. fValue = val;
  147. } else if (string(key) == "scale") {
  148. fScale = val;
  149. }
  150. }
  151. };
  152. // Faust engine using libfaust/LLVM
  153. class FaustEngine : public ScriptEngine {
  154. public:
  155. FaustEngine():
  156. fDSPFactory(nullptr),
  157. fDSP(nullptr),
  158. fInputs(nullptr),
  159. fOutputs(nullptr),
  160. fDSPLibraries(rack::asset::plugin(pluginInstance, "faust_libraries"))
  161. {}
  162. ~FaustEngine()
  163. {
  164. delete [] fInputs;
  165. delete [] fOutputs;
  166. delete fDSP;
  167. deleteDSPFactory(fDSPFactory);
  168. }
  169. string getEngineName() override
  170. {
  171. return "Faust";
  172. }
  173. int run(const string& path, const string& script) override
  174. {
  175. #if defined ARCH_LIN
  176. string temp_cache = "/var/tmp/VCV_" + generateSHA1(script);
  177. #elif defined ARCH_MAC
  178. string temp_cache = "/private/var/tmp/VCV_" + generateSHA1(script);
  179. #elif defined ARCH_WIN
  180. char buf[MAX_PATH+1] = {0};
  181. GetTempPath(sizeof(buf), buf);
  182. string temp_cache = string(buf) + "/VCV_" + generateSHA1(script);
  183. #endif
  184. string error_msg;
  185. // Try to load the machine code cache
  186. fDSPFactory = readDSPFactoryFromMachineFile(temp_cache, "", error_msg);
  187. if (!fDSPFactory) {
  188. // Otherwise recompile the DSP
  189. int argc = 0;
  190. const char* argv[8];
  191. argv[argc++] = "-I";
  192. argv[argc++] = fDSPLibraries.c_str();
  193. argv[argc] = nullptr; // NULL terminated argv
  194. fDSPFactory = createDSPFactoryFromString("FaustDSP", script, argc, argv, "", error_msg, -1);
  195. if (!fDSPFactory) {
  196. display("ERROR : cannot create factory !");
  197. WARN("Faust Prototype : %s", error_msg.c_str());
  198. return -1;
  199. } else {
  200. // And save the cache
  201. display("Compiling factory finished");
  202. writeDSPFactoryToMachineFile(fDSPFactory, temp_cache, "");
  203. }
  204. }
  205. // Create DSP
  206. fDSP = fDSPFactory->createDSPInstance();
  207. if (!fDSP) {
  208. display("ERROR: cannot create instance !");
  209. return -1;
  210. } else {
  211. display("Created DSP");
  212. }
  213. // Prepare inputs/outputs
  214. if (fDSP->getNumInputs() > NUM_ROWS) {
  215. display("ERROR: DSP has " + to_string(fDSP->getNumInputs()) + " inputs !");
  216. return -1;
  217. }
  218. if (fDSP->getNumOutputs() > NUM_ROWS) {
  219. display("ERROR: DSP has " + to_string(fDSP->getNumInputs()) + " outputs !");
  220. return -1;
  221. }
  222. // Setup UI
  223. fDSP->buildUserInterface(&fRackUI);
  224. setFrameDivider(1);
  225. setBufferSize(kBufferSize);
  226. // Prepare buffers for process
  227. ProcessBlock* block = getProcessBlock();
  228. fInputs = new FAUSTFLOAT*[fDSP->getNumInputs()];
  229. for (int chan = 0; chan < fDSP->getNumInputs(); chan++) {
  230. fInputs[chan] = block->inputs[chan];
  231. }
  232. fOutputs = new FAUSTFLOAT*[fDSP->getNumOutputs()];
  233. for (int chan = 0; chan < fDSP->getNumOutputs(); chan++) {
  234. fOutputs[chan] = block->outputs[chan];
  235. }
  236. // Init DSP with default SR
  237. fDSP->init(44100);
  238. return 0;
  239. }
  240. int process() override
  241. {
  242. ProcessBlock* block = getProcessBlock();
  243. // Possibly update SR
  244. if (block->sampleRate != fDSP->getSampleRate()) {
  245. fDSP->init(block->sampleRate);
  246. }
  247. // Update inputs controllers
  248. for (auto& it : fRackUI.fUpdateFunIn) it(block);
  249. // Compute samples
  250. fDSP->compute(block->bufferSize, fInputs, fOutputs);
  251. // Update output controllers
  252. for (auto& it : fRackUI.fUpdateFunOut) it(block);
  253. return 0;
  254. }
  255. private:
  256. llvm_dsp_factory* fDSPFactory;
  257. llvm_dsp* fDSP;
  258. FAUSTFLOAT** fInputs;
  259. FAUSTFLOAT** fOutputs;
  260. RackUI fRackUI;
  261. string fDSPLibraries;
  262. };
  263. __attribute__((constructor(1000)))
  264. static void constructor() {
  265. addScriptEngine<FaustEngine>("dsp");
  266. }