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.

305 lines
7.6KB

  1. #include "ScriptEngine.hpp"
  2. #include "z_libpd.h"
  3. #include "util/z_print_util.h"
  4. using namespace rack;
  5. #define BUFFERSIZE MAX_BUFFER_SIZE * NUM_ROWS
  6. // there is no multi-instance support for receiving messages from libpd
  7. // for now, received values for the prototype gui will be stored in global variables
  8. float g_lights[NUM_ROWS][3] = {};
  9. float g_switchLights[NUM_ROWS][3] = {};
  10. std::string g_utility[2] = {};
  11. bool g_display_is_valid = false;
  12. std::vector<std::string> split (const std::string &s, char delim) {
  13. std::vector<std::string> result;
  14. std::stringstream ss (s);
  15. std::string item;
  16. while (getline (ss, item, delim)) {
  17. result.push_back (item);
  18. }
  19. return result;
  20. }
  21. struct LibPDEngine : ScriptEngine {
  22. ~LibPDEngine() {
  23. libpd_free_instance(_lpd);
  24. }
  25. void sendInitialStates(const ProcessBlock* block);
  26. static void receiveLights(const char *s);
  27. bool knobChanged(const float* knobs, int idx);
  28. bool switchChanged(const bool* knobs, int idx);
  29. void sendKnob(const int idx, const float value);
  30. void sendSwitch(const int idx, const bool value);
  31. t_pdinstance *_lpd;
  32. int _pd_block_size = 64;
  33. int _sampleRate = 0;
  34. int _ticks = 0;
  35. bool _init = true;
  36. float _old_knobs[NUM_ROWS] = {};
  37. bool _old_switches[NUM_ROWS] = {};
  38. float _output[BUFFERSIZE] = {};
  39. float _input[BUFFERSIZE] = {};// = (float*)malloc(1024*2*sizeof(float));
  40. const static std::map<std::string, int> _light_map;
  41. const static std::map<std::string, int> _switchLight_map;
  42. const static std::map<std::string, int> _utility_map;
  43. std::string getEngineName() override {
  44. return "Pure Data";
  45. }
  46. int run(const std::string& path, const std::string& script) override {
  47. ProcessBlock* block = getProcessBlock();
  48. _sampleRate = block->sampleRate;
  49. setBufferSize(_pd_block_size);
  50. setFrameDivider(1);
  51. libpd_init();
  52. _lpd = libpd_new_instance();
  53. libpd_set_printhook((t_libpd_printhook)libpd_print_concatenator);
  54. libpd_set_concatenated_printhook( receiveLights );
  55. if(libpd_num_instances()>2)
  56. {
  57. display("Sorry, multi instance support in libpd is under development!");
  58. return -1;
  59. }
  60. //display(std::to_string(libpd_num_instances()));
  61. libpd_init_audio(NUM_ROWS, NUM_ROWS, _sampleRate);
  62. // compute audio [; pd dsp 1(
  63. libpd_start_message(1); // one enstry in list
  64. libpd_add_float(1.0f);
  65. libpd_finish_message("pd", "dsp");
  66. std::string name = string::filename(path);
  67. std::string dir = string::directory(path);
  68. libpd_openfile(name.c_str(), dir.c_str());
  69. sendInitialStates(block);
  70. return 0;
  71. }
  72. int process() override {
  73. // block
  74. ProcessBlock* block = getProcessBlock();
  75. // get samples prototype
  76. int rows = NUM_ROWS;
  77. for (int s = 0; s < _pd_block_size; s++) {
  78. for (int r = 0; r < rows; r++) {
  79. _input[s*rows+r] = block->inputs[r][s];
  80. }
  81. }
  82. libpd_set_instance(_lpd);
  83. // knobs
  84. for (int i=0; i<NUM_ROWS; i++){
  85. if( knobChanged(block->knobs, i) ){
  86. sendKnob(i, block->knobs[i]);
  87. }
  88. }
  89. // lights
  90. for(int i=0; i<NUM_ROWS; i++){
  91. block->lights[i][0] = g_lights[i][0];
  92. block->lights[i][1] = g_lights[i][1];
  93. block->lights[i][2] = g_lights[i][2];
  94. }
  95. // switch lights
  96. for(int i=0; i<NUM_ROWS; i++){
  97. block->switchLights[i][0] = g_switchLights[i][0];
  98. block->switchLights[i][1] = g_switchLights[i][1];
  99. block->switchLights[i][2] = g_switchLights[i][2];
  100. }
  101. // switches
  102. for(int i=0; i<NUM_ROWS; i++){
  103. if( switchChanged(block->switches, i) ){
  104. sendSwitch(i, block->switches[i]);
  105. }
  106. }
  107. // display
  108. if(g_display_is_valid){
  109. display(g_utility[1]);
  110. g_display_is_valid = false;
  111. }
  112. // process samples in libpd
  113. _ticks = 1;
  114. libpd_process_float(_ticks, _input, _output);
  115. //return samples to prototype
  116. for (int s = 0; s < _pd_block_size; s++) {
  117. for (int r = 0; r < rows; r++) {
  118. block->outputs[r][s] = _output[s*rows+r];
  119. }
  120. }
  121. return 0;
  122. }
  123. };
  124. __attribute__((constructor(1000)))
  125. static void constructor() {
  126. addScriptEngine<LibPDEngine>("pd");
  127. }
  128. void LibPDEngine::receiveLights(const char *s) {
  129. std::string str = std::string(s);
  130. std::vector<std::string> atoms = split (str, ' ');
  131. if(atoms[0]=="toVCV:"){
  132. // parse lights list
  133. bool light_is_valid = true;
  134. int light_idx = -1;
  135. try {
  136. light_idx = _light_map.at(atoms[1]); // map::at throws an out-of-range
  137. }
  138. catch (const std::out_of_range& oor) {
  139. light_is_valid = false;
  140. //display("Warning:"+atoms[1]+" not found!");
  141. }
  142. //std::cout << v[1] << ", " << g_led_map[v[1]] << std::endl;
  143. if(light_is_valid && atoms.size()==5){
  144. g_lights[light_idx][0] = stof(atoms[2]); // red
  145. g_lights[light_idx][1] = stof(atoms[3]); // green
  146. g_lights[light_idx][2] = stof(atoms[4]); // blue
  147. }
  148. else {
  149. // error
  150. }
  151. // parse switch lights list
  152. bool switchLight_is_valid = true;
  153. int switchLight_idx = -1;
  154. try {
  155. switchLight_idx = _switchLight_map.at(atoms[1]); // map::at throws an out-of-range
  156. }
  157. catch (const std::out_of_range& oor) {
  158. switchLight_is_valid = false;
  159. //display("Warning:"+atoms[1]+" not found!");
  160. }
  161. //std::cout << v[1] << ", " << g_led_map[v[1]] << std::endl;
  162. if(switchLight_is_valid && atoms.size()==5){
  163. g_switchLights[switchLight_idx][0] = stof(atoms[2]); // red
  164. g_switchLights[switchLight_idx][1] = stof(atoms[3]); // green
  165. g_switchLights[switchLight_idx][2] = stof(atoms[4]); // blue
  166. }
  167. else {
  168. // error
  169. }
  170. // parse switch lights list
  171. bool utility_is_valid = true;
  172. int utility_idx = -1;
  173. try {
  174. utility_idx = _utility_map.at(atoms[1]); // map::at throws an out-of-range
  175. }
  176. catch (const std::out_of_range& oor) {
  177. utility_is_valid = false;
  178. //g_display_is_valid = true;
  179. //display("Warning:"+atoms[1]+" not found!");
  180. }
  181. //std::cout << v[1] << ", " << g_led_map[v[1]] << std::endl;
  182. if(utility_is_valid && atoms.size()>=3){
  183. g_utility[0] = atoms[1]; // display
  184. g_utility[1] = {""};
  185. for(int i=0; i<atoms.size()-2; i++){
  186. g_utility[1] += " " +atoms[i+2]; // concatenate message
  187. }
  188. g_display_is_valid = true;
  189. }
  190. else {
  191. // error
  192. }
  193. }
  194. else {
  195. // print out on command line
  196. std::cout << std::string(s) << std::endl;
  197. }
  198. }
  199. bool LibPDEngine::knobChanged(const float* knobs, int i){
  200. bool knob_changed = false;
  201. if (_old_knobs[i] != knobs[i]){
  202. knob_changed = true;
  203. _old_knobs[i] = knobs[i];
  204. }
  205. return knob_changed;
  206. }
  207. bool LibPDEngine::switchChanged(const bool* switches, int i){
  208. bool switch_changed = false;
  209. if (_old_switches[i] != switches[i]){
  210. switch_changed = true;
  211. _old_switches[i] = switches[i];
  212. }
  213. return switch_changed;
  214. }
  215. const std::map<std::string, int> LibPDEngine::_light_map{
  216. { "L1", 0 },
  217. { "L2", 1 },
  218. { "L3", 2 },
  219. { "L4", 3 },
  220. { "L5", 4 },
  221. { "L6", 5 }
  222. };
  223. const std::map<std::string, int> LibPDEngine::_switchLight_map{
  224. { "S1", 0 },
  225. { "S2", 1 },
  226. { "S3", 2 },
  227. { "S4", 3 },
  228. { "S5", 4 },
  229. { "S6", 5 }
  230. };
  231. const std::map<std::string, int> LibPDEngine::_utility_map{
  232. { "display", 0 }
  233. };
  234. void LibPDEngine::sendKnob(const int idx, const float value){
  235. std::string knob = "K"+std::to_string(idx+1);
  236. libpd_start_message(1);
  237. libpd_add_float(value);
  238. libpd_finish_message("fromVCV", knob.c_str());
  239. }
  240. void LibPDEngine::sendSwitch(const int idx, const bool value){
  241. std::string sw = "S"+std::to_string(idx+1);
  242. libpd_start_message(1);
  243. libpd_add_float(value);
  244. libpd_finish_message("fromVCV", sw.c_str());
  245. }
  246. void LibPDEngine::sendInitialStates(const ProcessBlock* block){
  247. // knobs
  248. for (int i=0; i<NUM_ROWS; i++){
  249. sendKnob(i, block->knobs[i]);
  250. sendSwitch(i, block->knobs[i]);
  251. }
  252. std::string version = "pd "+std::to_string(PD_MAJOR_VERSION)+"."+
  253. std::to_string(PD_MINOR_VERSION)+"."
  254. +std::to_string(PD_BUGFIX_VERSION);
  255. display(version);
  256. }