Audio plugin host https://kx.studio/carla
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.

2029 lines
60KB

  1. #include "../ports.h"
  2. #include "../rtosc.h"
  3. #include "../pretty-format.h"
  4. #include <cinttypes>
  5. #include <ostream>
  6. #include <cassert>
  7. #include <limits>
  8. #include <cstring>
  9. #include <string>
  10. /* Compatibility with non-clang compilers */
  11. #ifndef __has_feature
  12. # define __has_feature(x) 0
  13. #endif
  14. #ifndef __has_extension
  15. # define __has_extension __has_feature
  16. #endif
  17. /* Check for C++11 support */
  18. #if defined(HAVE_CPP11_SUPPORT)
  19. # if HAVE_CPP11_SUPPORT
  20. # define DISTRHO_PROPER_CPP11_SUPPORT
  21. # endif
  22. #elif __cplusplus >= 201103L || (defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) || __has_extension(cxx_noexcept)
  23. # define DISTRHO_PROPER_CPP11_SUPPORT
  24. # if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) < 407 && ! defined(__clang__)) || (defined(__clang__) && ! __has_extension(cxx_override_control))
  25. # define override // gcc4.7+ only
  26. # define final // gcc4.7+ only
  27. # endif
  28. #endif
  29. using namespace rtosc;
  30. static inline void scat(char *dest, const char *src)
  31. {
  32. while(*dest) dest++;
  33. while(*src && *src!=':') *dest++ = *src++;
  34. *dest = 0;
  35. }
  36. RtData::RtData(void)
  37. :loc(NULL), loc_size(0), obj(NULL), matches(0), message(NULL)
  38. {
  39. for(int i=0; i<(int)(sizeof(idx)/sizeof(int)); ++i)
  40. idx[i] = 0;
  41. }
  42. void RtData::push_index(int ind)
  43. {
  44. for(int i=1; i<(int)(sizeof(idx)/sizeof(int)); ++i)
  45. idx[i] = idx[i-1];
  46. idx[0] = ind;
  47. }
  48. void RtData::pop_index(void)
  49. {
  50. int n = sizeof(idx)/sizeof(int);
  51. for(int i=n-2; i >= 0; --i)
  52. idx[i] = idx[i+1];
  53. idx[n-1] = 0;
  54. }
  55. void RtData::replyArray(const char *path, const char *args,
  56. rtosc_arg_t *vals)
  57. {
  58. (void) path;
  59. (void) args;
  60. (void) vals;
  61. }
  62. void RtData::reply(const char *path, const char *args, ...)
  63. {
  64. va_list va;
  65. va_start(va,args);
  66. char buffer[1024];
  67. rtosc_vmessage(buffer,1024,path,args,va);
  68. reply(buffer);
  69. va_end(va);
  70. }
  71. void RtData::reply(const char *msg)
  72. {(void)msg;}
  73. void RtData::chain(const char *path, const char *args, ...)
  74. {
  75. (void) path;
  76. (void) args;
  77. }
  78. void RtData::chain(const char *msg)
  79. {
  80. (void) msg;
  81. }
  82. void RtData::chainArray(const char *path, const char *args,
  83. rtosc_arg_t *vals)
  84. {
  85. (void) path;
  86. (void) args;
  87. (void) vals;
  88. };
  89. void RtData::broadcast(const char *path, const char *args, ...)
  90. {
  91. va_list va;
  92. va_start(va,args);
  93. char buffer[1024];
  94. rtosc_vmessage(buffer,1024,path,args,va);
  95. broadcast(buffer);
  96. va_end(va);
  97. }
  98. void RtData::broadcast(const char *msg)
  99. {reply(msg);};
  100. void RtData::broadcastArray(const char *path, const char *args,
  101. rtosc_arg_t *vals)
  102. {
  103. (void) path;
  104. (void) args;
  105. (void) vals;
  106. }
  107. void RtData::forward(const char *rational)
  108. {
  109. (void) rational;
  110. }
  111. void metaiterator_advance(const char *&title, const char *&value)
  112. {
  113. if(!title || !*title) {
  114. value = NULL;
  115. return;
  116. }
  117. //Try to find "\0=" after title string
  118. value = title;
  119. while(*value)
  120. ++value;
  121. if(*++value != '=')
  122. value = NULL;
  123. else
  124. value++;
  125. }
  126. Port::MetaIterator::MetaIterator(const char *str)
  127. :title(str), value(NULL)
  128. {
  129. metaiterator_advance(title, value);
  130. }
  131. Port::MetaIterator& Port::MetaIterator::operator++(void)
  132. {
  133. if(!title || !*title) {
  134. title = NULL;
  135. return *this;
  136. }
  137. //search for next parameter start
  138. //aka "\0:" unless "\0\0" is seen
  139. char prev = 0;
  140. while(prev || (*title && *title != ':'))
  141. prev = *title++;
  142. if(!*title)
  143. title = NULL;
  144. else
  145. ++title;
  146. metaiterator_advance(title, value);
  147. return *this;
  148. }
  149. Port::MetaIterator::operator bool(void) const
  150. {
  151. return title;
  152. }
  153. Port::MetaContainer::MetaContainer(const char *str_)
  154. :str_ptr(str_)
  155. {}
  156. Port::MetaIterator Port::MetaContainer::begin(void) const
  157. {
  158. if(str_ptr && *str_ptr == ':')
  159. return Port::MetaIterator(str_ptr+1);
  160. else
  161. return Port::MetaIterator(str_ptr);
  162. }
  163. Port::MetaIterator Port::MetaContainer::end(void) const
  164. {
  165. return MetaIterator(NULL);
  166. }
  167. Port::MetaIterator Port::MetaContainer::find(const char *str) const
  168. {
  169. for(const auto x : *this)
  170. if(!strcmp(x.title, str))
  171. return x;
  172. return NULL;
  173. }
  174. size_t Port::MetaContainer::length(void) const
  175. {
  176. if(!str_ptr || !*str_ptr)
  177. return 0;
  178. char prev = 0;
  179. const char *itr = str_ptr;
  180. while(prev || *itr)
  181. prev = *itr++;
  182. return 2+(itr-str_ptr);
  183. }
  184. const char *Port::MetaContainer::operator[](const char *str) const
  185. {
  186. for(const auto x : *this)
  187. if(!strcmp(x.title, str))
  188. return x.value;
  189. return NULL;
  190. }
  191. //Match the arg string or fail
  192. inline bool arg_matcher(const char *pattern, const char *args)
  193. {
  194. //match anything if now arg restriction is present (ie the ':')
  195. if(*pattern++ != ':')
  196. return true;
  197. const char *arg_str = args;
  198. bool arg_match = *pattern || *pattern == *arg_str;
  199. while(*pattern && *pattern != ':')
  200. arg_match &= (*pattern++==*arg_str++);
  201. if(*pattern==':') {
  202. if(arg_match && !*arg_str)
  203. return true;
  204. else
  205. return arg_matcher(pattern, args); //retry
  206. }
  207. return arg_match;
  208. }
  209. inline bool scmp(const char *a, const char *b)
  210. {
  211. while(*a && *a == *b) a++, b++;
  212. return a[0] == b[0];
  213. }
  214. typedef std::vector<std::string> words_t;
  215. typedef std::vector<std::string> svec_t;
  216. typedef std::vector<const char *> cvec_t;
  217. typedef std::vector<int> ivec_t;
  218. typedef std::vector<int> tuple_t;
  219. typedef std::vector<tuple_t> tvec_t;
  220. namespace rtosc{
  221. class Port_Matcher
  222. {
  223. public:
  224. bool *enump;
  225. svec_t fixed;
  226. cvec_t arg_spec;
  227. ivec_t pos;
  228. ivec_t assoc;
  229. ivec_t remap;
  230. bool rtosc_match_args(const char *pattern, const char *msg)
  231. {
  232. //match anything if no arg restriction is present
  233. //(ie the ':')
  234. if(*pattern++ != ':')
  235. return true;
  236. const char *arg_str = rtosc_argument_string(msg);
  237. bool arg_match = *pattern || *pattern == *arg_str;
  238. while(*pattern && *pattern != ':')
  239. arg_match &= (*pattern++==*arg_str++);
  240. if(*pattern==':') {
  241. if(arg_match && !*arg_str)
  242. return true;
  243. else
  244. return rtosc_match_args(pattern, msg); //retry
  245. }
  246. return arg_match;
  247. }
  248. bool hard_match(int i, const char *msg)
  249. {
  250. if(strncmp(msg, fixed[i].c_str(), fixed[i].length()))
  251. return false;
  252. if(arg_spec[i])
  253. return rtosc_match_args(arg_spec[i], msg);
  254. else
  255. return true;
  256. }
  257. };
  258. }
  259. tvec_t do_hash(const words_t &strs, const ivec_t &pos)
  260. {
  261. tvec_t tvec;
  262. for(auto &s:strs) {
  263. tuple_t tuple;
  264. tuple.push_back(s.length());
  265. for(const auto &p:pos)
  266. if(p < (int)s.size())
  267. tuple.push_back(s[p]);
  268. tvec.push_back(std::move(tuple));
  269. }
  270. return tvec;
  271. }
  272. template<class T>
  273. int count_dups(std::vector<T> &t)
  274. {
  275. int dups = 0;
  276. int N = t.size();
  277. bool mark[t.size()];
  278. memset(mark, 0, N);
  279. for(int i=0; i<N; ++i) {
  280. if(mark[i])
  281. continue;
  282. for(int j=i+1; j<N; ++j) {
  283. if(t[i] == t[j]) {
  284. dups++;
  285. mark[j] = true;
  286. }
  287. }
  288. }
  289. return dups;
  290. }
  291. template<class T, class Z>
  292. bool has(T &t, Z&z)
  293. {
  294. for(auto tt:t)
  295. if(tt==z)
  296. return true;
  297. return false;
  298. }
  299. static int int_max(int a, int b) { return a<b?b:a;}
  300. static ivec_t find_pos(words_t &strs)
  301. {
  302. ivec_t pos;
  303. int current_dups = strs.size();
  304. int N = 0;
  305. for(auto w:strs)
  306. N = int_max(N,w.length());
  307. int pos_best = -1;
  308. int pos_best_val = std::numeric_limits<int>::max();
  309. while(true)
  310. {
  311. for(int i=0; i<N; ++i) {
  312. ivec_t npos = pos;
  313. if(has(pos, i))
  314. continue;
  315. npos.push_back(i);
  316. auto hashed = do_hash(strs, npos);
  317. int d = count_dups(hashed);
  318. if(d < pos_best_val) {
  319. pos_best_val = d;
  320. pos_best = i;
  321. }
  322. }
  323. if(pos_best_val >= current_dups)
  324. break;
  325. current_dups = pos_best_val;
  326. pos.push_back(pos_best);
  327. }
  328. auto hashed = do_hash(strs, pos);
  329. int d = count_dups(hashed);
  330. //printf("Total Dups: %d\n", d);
  331. if(d != 0)
  332. pos.clear();
  333. return pos;
  334. }
  335. static ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc)
  336. {
  337. ivec_t ivec;
  338. ivec.reserve(strs.size());
  339. for(auto &s:strs) {
  340. int t = s.length();
  341. for(auto p:pos)
  342. if(p < (int)s.size())
  343. t += assoc[s[p]];
  344. ivec.push_back(t);
  345. }
  346. return ivec;
  347. }
  348. static ivec_t find_assoc(const words_t &strs, const ivec_t &pos)
  349. {
  350. ivec_t assoc;
  351. int current_dups = strs.size();
  352. int N = 127;
  353. std::vector<char> useful_chars;
  354. for(auto w:strs)
  355. for(auto c:w)
  356. if(!has(useful_chars, c))
  357. useful_chars.push_back(c);
  358. for(int i=0; i<N; ++i)
  359. assoc.push_back(0);
  360. int assoc_best = -1;
  361. int assoc_best_val = std::numeric_limits<int>::max();;
  362. for(int k=0; k<4; ++k)
  363. {
  364. for(int i:useful_chars) {
  365. assoc_best_val = std::numeric_limits<int>::max();
  366. for(int j=0; j<100; ++j) {
  367. //printf(".");
  368. assoc[i] = j;
  369. auto hashed = do_hash(strs, pos, assoc);
  370. //for(int i=0; i<hashed.size(); ++i)
  371. // printf("%d ", hashed[i]);
  372. //printf("\n");
  373. int d = count_dups(hashed);
  374. //printf("dup %d\n",d);
  375. if(d < assoc_best_val) {
  376. assoc_best_val = d;
  377. assoc_best = j;
  378. }
  379. }
  380. assoc[i] = assoc_best;
  381. }
  382. if(assoc_best_val >= current_dups)
  383. break;
  384. current_dups = assoc_best_val;
  385. }
  386. auto hashed = do_hash(strs, pos, assoc);
  387. //int d = count_dups(hashed);
  388. //printf("Total Dups Assoc: %d\n", d);
  389. return assoc;
  390. }
  391. static ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc)
  392. {
  393. ivec_t remap;
  394. auto hashed = do_hash(strs, pos, assoc);
  395. //for(int i=0; i<strs.size(); ++i)
  396. // printf("%d) '%s'\n", hashed[i], strs[i].c_str());
  397. int N = 0;
  398. for(auto h:hashed)
  399. N = int_max(N,h+1);
  400. for(int i=0; i<N; ++i)
  401. remap.push_back(0);
  402. for(int i=0; i<(int)hashed.size(); ++i)
  403. remap[hashed[i]] = i;
  404. return remap;
  405. }
  406. static void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm)
  407. {
  408. if(str.empty())
  409. return;
  410. pm.pos = find_pos(str);
  411. if(pm.pos.empty()) {
  412. fprintf(stderr, "rtosc: Failed to generate minimal hash\n");
  413. return;
  414. }
  415. pm.assoc = find_assoc(str, pm.pos);
  416. pm.remap = find_remap(str, pm.pos, pm.assoc);
  417. }
  418. static void generate_minimal_hash(Ports &p, Port_Matcher &pm)
  419. {
  420. svec_t keys;
  421. cvec_t args;
  422. bool enump = false;
  423. for(unsigned i=0; i<p.ports.size(); ++i)
  424. if(strchr(p.ports[i].name, '#'))
  425. enump = true;
  426. if(enump)
  427. return;
  428. for(unsigned i=0; i<p.ports.size(); ++i)
  429. {
  430. std::string tmp = p.ports[i].name;
  431. const char *arg = NULL;
  432. int idx = tmp.find(':');
  433. if(idx > 0) {
  434. arg = p.ports[i].name+idx;
  435. tmp = tmp.substr(0,idx);
  436. }
  437. keys.push_back(tmp);
  438. args.push_back(arg);
  439. }
  440. pm.fixed = keys;
  441. pm.arg_spec = args;
  442. generate_minimal_hash(keys, pm);
  443. }
  444. Ports::Ports(std::initializer_list<Port> l)
  445. :ports(l), impl(NULL)
  446. {
  447. refreshMagic();
  448. }
  449. Ports::~Ports()
  450. {
  451. delete []impl->enump;
  452. delete impl;
  453. }
  454. #if !defined(__GNUC__)
  455. #define __builtin_expect(a,b) a
  456. #endif
  457. void Ports::dispatch(const char *m, rtosc::RtData &d, bool base_dispatch) const
  458. {
  459. if(!strcmp(m, "pointer"))
  460. {
  461. // rRecur*Cb have already set d.loc to the pointer we need,
  462. // so we just return
  463. return;
  464. }
  465. void *obj = d.obj;
  466. //handle the first dispatch layer
  467. if(base_dispatch) {
  468. d.matches = 0;
  469. d.message = m;
  470. if(m && *m == '/')
  471. m++;
  472. if(d.loc)
  473. d.loc[0] = 0;
  474. }
  475. //simple case
  476. if(!d.loc || !d.loc_size) {
  477. for(const Port &port: ports) {
  478. if(rtosc_match(port.name,m, NULL))
  479. d.port = &port, port.cb(m,d), d.obj = obj;
  480. }
  481. } else {
  482. //TODO this function is certainly buggy at the moment, some tests
  483. //are needed to make it clean
  484. //XXX buffer_size is not properly handled yet
  485. if(__builtin_expect(d.loc[0] == 0, 0)) {
  486. memset(d.loc, 0, d.loc_size);
  487. d.loc[0] = '/';
  488. }
  489. char *old_end = d.loc;
  490. while(*old_end) ++old_end;
  491. if(impl->pos.empty()) { //No perfect minimal hash function
  492. for(unsigned i=0; i<elms; ++i) {
  493. const Port &port = ports[i];
  494. const char* m_end;
  495. if(!rtosc_match(port.name, m, &m_end))
  496. continue;
  497. if(!port.ports)
  498. d.matches++;
  499. //Append the path
  500. if(strchr(port.name,'#')) {
  501. const char *msg = m;
  502. char *pos = old_end;
  503. while(*msg && msg != m_end)
  504. *pos++ = *msg++;
  505. *pos = '\0';
  506. } else
  507. scat(d.loc, port.name);
  508. d.port = &port;
  509. //Apply callback
  510. port.cb(m,d), d.obj = obj;
  511. //Remove the rest of the path
  512. char *tmp = old_end;
  513. while(*tmp) *tmp++=0;
  514. }
  515. } else {
  516. //Define string to be hashed
  517. unsigned len=0;
  518. const char *tmp = m;
  519. while(*tmp && *tmp != '/')
  520. tmp++;
  521. if(*tmp == '/')
  522. tmp++;
  523. len = tmp-m;
  524. //Compute the hash
  525. int t = len;
  526. for(auto p:impl->pos)
  527. if(p < (int)len)
  528. t += impl->assoc[m[p]];
  529. if(t >= (int)impl->remap.size() && !default_handler)
  530. return;
  531. else if(t >= (int)impl->remap.size() && default_handler) {
  532. d.matches++;
  533. default_handler(m,d), d.obj = obj;
  534. return;
  535. }
  536. int port_num = impl->remap[t];
  537. //Verify the chosen port is correct
  538. if(__builtin_expect(impl->hard_match(port_num, m), 1)) {
  539. const Port &port = ports[impl->remap[t]];
  540. if(!port.ports)
  541. d.matches++;
  542. //Append the path
  543. if(impl->enump[port_num]) {
  544. const char *msg = m;
  545. char *pos = old_end;
  546. while(*msg && *msg != '/')
  547. *pos++ = *msg++;
  548. if(strchr(port.name, '/'))
  549. *pos++ = '/';
  550. *pos = '\0';
  551. } else
  552. memcpy(old_end, impl->fixed[port_num].c_str(),
  553. impl->fixed[port_num].length()+1);
  554. d.port = &port;
  555. //Apply callback
  556. port.cb(m,d), d.obj = obj;
  557. //Remove the rest of the path
  558. old_end[0] = '\0';
  559. } else if(default_handler) {
  560. d.matches++;
  561. default_handler(m,d), d.obj = obj;
  562. }
  563. }
  564. }
  565. }
  566. /*
  567. * Returning values from runtime
  568. */
  569. //! RtData subclass to capture argument values pretty-printed from
  570. //! a runtime object
  571. class CapturePretty : public RtData
  572. {
  573. char* buffer;
  574. std::size_t buffersize;
  575. int cols_used;
  576. void reply(const char *) override { assert(false); }
  577. /* void replyArray(const char*, const char *args,
  578. rtosc_arg_t *vals)
  579. {
  580. size_t cur_idx = 0;
  581. for(const char* ptr = args; *ptr; ++ptr, ++cur_idx)
  582. {
  583. assert(cur_idx < max_args);
  584. arg_vals[cur_idx].type = *ptr;
  585. arg_vals[cur_idx].val = vals[cur_idx];
  586. }
  587. // TODO: refactor code, also with Capture ?
  588. size_t wrt = rtosc_print_arg_vals(arg_vals, cur_idx,
  589. buffer, buffersize, NULL,
  590. cols_used);
  591. assert(wrt);
  592. }*/
  593. void reply(const char *, const char *args, ...) override
  594. {
  595. va_list va;
  596. va_start(va,args);
  597. size_t nargs = strlen(args);
  598. rtosc_arg_val_t arg_vals[nargs];
  599. rtosc_v2argvals(arg_vals, nargs, args, va);
  600. size_t wrt = rtosc_print_arg_vals(arg_vals, nargs,
  601. buffer, buffersize, NULL,
  602. cols_used);
  603. (void) wrt;
  604. va_end(va);
  605. assert(wrt);
  606. }
  607. public:
  608. //! Return the argument values, pretty-printed
  609. const char* value() const { return buffer; }
  610. CapturePretty(char* buffer, std::size_t size, int cols_used) :
  611. buffer(buffer), buffersize(size), cols_used(cols_used) {}
  612. };
  613. /**
  614. * @brief Returns a port's value pretty-printed from a runtime object. The
  615. * port object must not be known.
  616. *
  617. * For the parameters, see the overloaded function
  618. * @return The argument values, pretty-printed
  619. */
  620. static const char* get_value_from_runtime(void* runtime,
  621. const Ports& ports,
  622. size_t loc_size,
  623. char* loc,
  624. char* buffer_with_port,
  625. std::size_t buffersize,
  626. int cols_used)
  627. {
  628. std::size_t addr_len = strlen(buffer_with_port);
  629. // use the port buffer to print the result, but do not overwrite the
  630. // port name
  631. CapturePretty d(buffer_with_port + addr_len, buffersize - addr_len,
  632. cols_used);
  633. d.obj = runtime;
  634. d.loc_size = loc_size;
  635. d.loc = loc;
  636. d.matches = 0;
  637. // does the message at least fit the arguments?
  638. assert(buffersize - addr_len >= 8);
  639. // append type
  640. memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments
  641. buffer_with_port[addr_len + (4-addr_len%4)] = ',';
  642. d.message = buffer_with_port;
  643. // buffer_with_port is a message in this call:
  644. ports.dispatch(buffer_with_port, d, false);
  645. return d.value();
  646. }
  647. //! RtData subclass to capture argument values from a runtime object
  648. class Capture : public RtData
  649. {
  650. size_t max_args;
  651. rtosc_arg_val_t* arg_vals;
  652. int nargs;
  653. void chain(const char *path, const char *args, ...) override
  654. {
  655. nargs = 0;
  656. }
  657. void chain(const char *msg) override
  658. {
  659. nargs = 0;
  660. }
  661. void reply(const char *) override { assert(false); }
  662. void replyArray(const char*, const char *args,
  663. rtosc_arg_t *vals) override
  664. {
  665. size_t cur_idx = 0;
  666. for(const char* ptr = args; *ptr; ++ptr, ++cur_idx)
  667. {
  668. assert(cur_idx < max_args);
  669. arg_vals[cur_idx].type = *ptr;
  670. arg_vals[cur_idx].val = vals[cur_idx];
  671. }
  672. nargs = cur_idx;
  673. }
  674. void reply(const char *, const char *args, ...) override
  675. {
  676. va_list va;
  677. va_start(va,args);
  678. nargs = strlen(args);
  679. assert((size_t)nargs <= max_args);
  680. rtosc_v2argvals(arg_vals, nargs, args, va);
  681. va_end(va);
  682. }
  683. public:
  684. //! Return the number of argument values stored
  685. int size() const { return nargs; }
  686. Capture(std::size_t max_args, rtosc_arg_val_t* arg_vals) :
  687. max_args(max_args), arg_vals(arg_vals), nargs(-1) {}
  688. };
  689. /**
  690. * @brief Returns a port's current value(s)
  691. *
  692. * This function returns the value(s) of a known port object and stores them as
  693. * rtosc_arg_val_t.
  694. * @param runtime The runtime object
  695. * @param port the port where the value shall be retrieved
  696. * @param loc A buffer where dispatch can write down the currently dispatched
  697. * path
  698. * @param loc_size Size of loc
  699. * @param portname_from_base The name of the port, relative to its base
  700. * @param buffer_with_port A buffer which already contains the port.
  701. * This buffer will be modified and must at least have space for 8 more bytes.
  702. * @param buffersize Size of @p buffer_with_port
  703. * @param max_args Maximum capacity of @p arg_vals
  704. * @param arg_vals Argument buffer for returned argument values
  705. * @return The number of argument values stored in @p arg_vals
  706. */
  707. static size_t get_value_from_runtime(void* runtime,
  708. const Port& port,
  709. size_t loc_size,
  710. char* loc,
  711. const char* portname_from_base,
  712. char* buffer_with_port,
  713. std::size_t buffersize,
  714. std::size_t max_args,
  715. rtosc_arg_val_t* arg_vals)
  716. {
  717. strncpy(buffer_with_port, portname_from_base, buffersize);
  718. std::size_t addr_len = strlen(buffer_with_port);
  719. Capture d(max_args, arg_vals);
  720. d.obj = runtime;
  721. d.loc_size = loc_size;
  722. d.loc = loc;
  723. d.port = &port;
  724. d.matches = 0;
  725. assert(*loc);
  726. // does the message at least fit the arguments?
  727. assert(buffersize - addr_len >= 8);
  728. // append type
  729. memset(buffer_with_port + addr_len, 0, 8); // cover string end and arguments
  730. buffer_with_port[addr_len + (4-addr_len%4)] = ',';
  731. // TODO? code duplication
  732. // buffer_with_port is a message in this call:
  733. d.message = buffer_with_port;
  734. port.cb(buffer_with_port, d);
  735. assert(d.size() >= 0);
  736. return d.size();
  737. }
  738. /*
  739. * default values
  740. */
  741. const char* rtosc::get_default_value(const char* port_name, const Ports& ports,
  742. void* runtime, const Port* port_hint,
  743. int32_t idx, int recursive)
  744. {
  745. constexpr std::size_t buffersize = 1024;
  746. char buffer[buffersize];
  747. char loc[buffersize] = "";
  748. assert(recursive >= 0); // forbid recursing twice
  749. char default_annotation[20] = "default";
  750. // if(idx > 0)
  751. // snprintf(default_annotation + 7, 13, "[%" PRId32 "]", idx);
  752. const char* const dependent_annotation = "default depends";
  753. const char* return_value = nullptr;
  754. if(!port_hint)
  755. port_hint = ports.apropos(port_name);
  756. assert(port_hint); // port must be found
  757. const Port::MetaContainer metadata = port_hint->meta();
  758. // Let complex cases depend upon a marker variable
  759. // If the runtime is available the exact preset number can be found
  760. // This generalizes to envelope types nicely if envelopes have a read
  761. // only port which indicates if they're amplitude/frequency/etc
  762. const char* dependent = metadata[dependent_annotation];
  763. if(dependent)
  764. {
  765. char* dependent_port = buffer;
  766. *dependent_port = 0;
  767. assert(strlen(port_name) + strlen(dependent_port) + 5 < buffersize);
  768. strncat(dependent_port, port_name,
  769. buffersize - strlen(dependent_port) - 1);
  770. strncat(dependent_port, "/../",
  771. buffersize - strlen(dependent_port) - 1);
  772. strncat(dependent_port, dependent,
  773. buffersize - strlen(dependent_port) - 1);
  774. dependent_port = Ports::collapsePath(dependent_port);
  775. // TODO: collapsePath bug?
  776. // Relative paths should not start with a slash after collapsing ...
  777. if(*dependent_port == '/')
  778. ++dependent_port;
  779. const char* dependent_value =
  780. runtime
  781. ? get_value_from_runtime(runtime, ports,
  782. buffersize, loc,
  783. dependent_port,
  784. buffersize-1, 0)
  785. : get_default_value(dependent_port, ports,
  786. runtime, NULL, recursive-1);
  787. assert(strlen(dependent_value) < 16); // must be an int
  788. char* default_variant = buffer;
  789. *default_variant = 0;
  790. assert(strlen(default_annotation) + 1 + 16 < buffersize);
  791. strncat(default_variant, default_annotation,
  792. buffersize - strlen(default_variant));
  793. strncat(default_variant, " ", buffersize - strlen(default_variant));
  794. strncat(default_variant, dependent_value,
  795. buffersize - strlen(default_variant));
  796. return_value = metadata[default_variant];
  797. }
  798. // If return_value is NULL, this can have two meanings:
  799. // 1. there was no depedent annotation
  800. // => check for a direct (non-dependent) default value
  801. // (a non existing direct default value is OK)
  802. // 2. there was a dependent annotation, but the dependent value has no
  803. // mapping (mapping for default_variant was NULL)
  804. // => check for the direct default value, which acts as a default
  805. // mapping for all presets; a missing default value indicates an
  806. // error in the metadata
  807. if(!return_value)
  808. {
  809. return_value = metadata[default_annotation];
  810. assert(!dependent || return_value);
  811. }
  812. return return_value;
  813. }
  814. int rtosc::canonicalize_arg_vals(rtosc_arg_val_t* av, size_t n,
  815. const char* port_args,
  816. Port::MetaContainer meta)
  817. {
  818. const char* first = port_args;
  819. int errors_found = 0;
  820. for( ; *first && (*first == ':' || *first == '[' || *first == ']');
  821. ++first) ;
  822. for(size_t i = 0; i < n; ++i, ++first, ++av)
  823. {
  824. for( ; *first && (*first == '[' || *first == ']'); ++first) ;
  825. if(!*first || *first == ':')
  826. {
  827. // (n-i) arguments left, but we have no recipe to convert them
  828. return n-i;
  829. }
  830. if(av->type == 'S' && *first == 'i')
  831. {
  832. int val = enum_key(meta, av->val.s);
  833. if(val == std::numeric_limits<int>::min())
  834. ++errors_found;
  835. else
  836. {
  837. av->type = 'i';
  838. av->val.i = val;
  839. }
  840. }
  841. }
  842. return errors_found;
  843. }
  844. void rtosc::map_arg_vals(rtosc_arg_val_t* av, size_t n,
  845. Port::MetaContainer meta)
  846. {
  847. char mapbuf[20] = "map ";
  848. for(size_t i = 0; i < n; ++i, ++av)
  849. {
  850. if(av->type == 'i')
  851. {
  852. snprintf(mapbuf + 4, 16, "%d", av->val.i);
  853. const char* val = meta[mapbuf];
  854. if(val)
  855. {
  856. av->type = 'S';
  857. av->val.s = val;
  858. }
  859. }
  860. }
  861. }
  862. int rtosc::get_default_value(const char* port_name, const char* port_args,
  863. const Ports& ports,
  864. void* runtime, const Port* port_hint,
  865. int32_t idx,
  866. size_t n, rtosc_arg_val_t* res,
  867. char* strbuf, size_t strbufsize)
  868. {
  869. const char* pretty = get_default_value(port_name, ports, runtime, port_hint,
  870. idx, 0);
  871. int nargs;
  872. if(pretty)
  873. {
  874. nargs = rtosc_count_printed_arg_vals(pretty);
  875. assert(nargs > 0); // parse error => error in the metadata?
  876. assert((size_t)nargs < n);
  877. rtosc_scan_arg_vals(pretty, res, nargs, strbuf, strbufsize);
  878. {
  879. int errs_found = canonicalize_arg_vals(res,
  880. nargs,
  881. port_args,
  882. port_hint->meta());
  883. if(errs_found)
  884. fprintf(stderr, "Could not canonicalize %s\n", pretty);
  885. assert(!errs_found); // error in the metadata?
  886. }
  887. }
  888. else
  889. nargs = -1;
  890. return nargs;
  891. }
  892. std::string rtosc::get_changed_values(const Ports& ports, void* runtime)
  893. {
  894. std::string res;
  895. constexpr std::size_t buffersize = 1024;
  896. char port_buffer[buffersize];
  897. memset(port_buffer, 0, buffersize); // requirement for walk_ports
  898. const size_t max_arg_vals = 256;
  899. auto on_reach_port =
  900. [](const Port* p, const char* port_buffer,
  901. const char* port_from_base, const Ports& base,
  902. void* data, void* runtime)
  903. {
  904. assert(runtime);
  905. const Port::MetaContainer meta = p->meta();
  906. if((p->name[strlen(p->name)-1] != ':' && !strstr(p->name, "::"))
  907. || meta.find("parameter") == meta.end())
  908. {
  909. // runtime information can not be retrieved,
  910. // thus, it can not be compared with the default value
  911. return;
  912. }
  913. char loc[buffersize] = "";
  914. rtosc_arg_val_t arg_vals_default[max_arg_vals];
  915. rtosc_arg_val_t arg_vals_runtime[max_arg_vals];
  916. char buffer_with_port[buffersize];
  917. char cur_value_pretty[buffersize] = " ";
  918. char strbuf[buffersize]; // temporary string buffer for pretty-printing
  919. std::string* res = (std::string*)data;
  920. assert(strlen(port_buffer) + 1 < buffersize);
  921. strncpy(loc, port_buffer, buffersize); // TODO: +-1?
  922. strncpy(buffer_with_port, port_from_base, buffersize);
  923. const char* portargs = strchr(p->name, ':');
  924. if(!portargs)
  925. portargs = p->name + strlen(p->name);
  926. #if 0 // debugging stuff
  927. if(!strncmp(port_buffer, "/part1/Penabled", 5) &&
  928. !strncmp(port_buffer+6, "/Penabled", 9))
  929. {
  930. printf("runtime: %ld\n", (long int)runtime);
  931. }
  932. #endif
  933. // TODO: p->name: duplicate to p
  934. int nargs_default = get_default_value(p->name,
  935. portargs,
  936. base,
  937. runtime,
  938. p,
  939. -1,
  940. max_arg_vals,
  941. arg_vals_default,
  942. strbuf,
  943. buffersize);
  944. size_t nargs_runtime = get_value_from_runtime(runtime,
  945. *p,
  946. buffersize, loc,
  947. port_from_base,
  948. buffer_with_port,
  949. buffersize,
  950. max_arg_vals,
  951. arg_vals_runtime);
  952. if(nargs_default == (int) nargs_runtime)
  953. {
  954. canonicalize_arg_vals(arg_vals_default, nargs_default,
  955. strchr(p->name, ':'), meta);
  956. if(!rtosc_arg_vals_eq(arg_vals_default,
  957. arg_vals_runtime,
  958. nargs_default,
  959. nargs_runtime,
  960. NULL))
  961. {
  962. map_arg_vals(arg_vals_runtime, nargs_runtime, meta);
  963. rtosc_print_arg_vals(arg_vals_runtime, nargs_runtime,
  964. cur_value_pretty + 1, buffersize - 1,
  965. NULL, strlen(port_buffer) + 1);
  966. *res += port_buffer;
  967. *res += cur_value_pretty;
  968. *res += "\n";
  969. }
  970. }
  971. };
  972. walk_ports(&ports, port_buffer, buffersize, &res, on_reach_port,
  973. runtime);
  974. if(res.length()) // remove trailing newline
  975. res.resize(res.length()-1);
  976. return res;
  977. }
  978. void rtosc::savefile_dispatcher_t::operator()(const char* msg)
  979. {
  980. *loc = 0;
  981. RtData d;
  982. d.obj = runtime;
  983. d.loc = loc; // we're always dispatching at the base
  984. d.loc_size = 1024;
  985. ports->dispatch(msg, d, true);
  986. }
  987. int savefile_dispatcher_t::default_response(size_t nargs,
  988. bool first_round,
  989. savefile_dispatcher_t::dependency_t
  990. dependency)
  991. {
  992. // default implementation:
  993. // no dependencies => round 0,
  994. // has dependencies => round 1,
  995. // not specified => both rounds
  996. return (dependency == not_specified
  997. || !(dependency ^ first_round))
  998. ? nargs // argument number is not changed
  999. : (int)discard;
  1000. }
  1001. int savefile_dispatcher_t::on_dispatch(size_t, char *,
  1002. size_t, size_t nargs,
  1003. rtosc_arg_val_t *,
  1004. bool round2,
  1005. dependency_t dependency)
  1006. {
  1007. return default_response(nargs, round2, dependency);
  1008. }
  1009. int rtosc::dispatch_printed_messages(const char* messages,
  1010. const Ports& ports, void* runtime,
  1011. savefile_dispatcher_t* dispatcher)
  1012. {
  1013. constexpr std::size_t buffersize = 1024;
  1014. char portname[buffersize], message[buffersize], strbuf[buffersize];
  1015. int rd, rd_total = 0;
  1016. int nargs;
  1017. int msgs_read = 0;
  1018. savefile_dispatcher_t dummy_dispatcher;
  1019. if(!dispatcher)
  1020. dispatcher = &dummy_dispatcher;
  1021. dispatcher->ports = &ports;
  1022. dispatcher->runtime = runtime;
  1023. // scan all messages twice:
  1024. // * in the second round, only dispatch those with ports that depend on
  1025. // other ports
  1026. // * in the first round, only dispatch all others
  1027. for(int round = 0; round < 2 && msgs_read >= 0; ++round)
  1028. {
  1029. msgs_read = 0;
  1030. rd_total = 0;
  1031. const char* msg_ptr = messages;
  1032. while(*msg_ptr && (msgs_read >= 0))
  1033. {
  1034. nargs = rtosc_count_printed_arg_vals_of_msg(msg_ptr);
  1035. if(nargs >= 0)
  1036. {
  1037. // 16 is usually too much, but it allows the user to add
  1038. // arguments if necessary
  1039. size_t maxargs = 16;
  1040. rtosc_arg_val_t arg_vals[maxargs];
  1041. rd = rtosc_scan_message(msg_ptr, portname, buffersize,
  1042. arg_vals, nargs, strbuf, buffersize);
  1043. rd_total += rd;
  1044. const Port* port = ports.apropos(portname);
  1045. savefile_dispatcher_t::dependency_t dependency =
  1046. (savefile_dispatcher_t::dependency_t)
  1047. (port
  1048. ? !!port->meta()["default depends"]
  1049. : (int)savefile_dispatcher_t::not_specified);
  1050. // let the user modify the message and the args
  1051. // the argument number may have changed, or the user
  1052. // wants to discard the message or abort the savefile loading
  1053. nargs = dispatcher->on_dispatch(buffersize, portname,
  1054. maxargs, nargs, arg_vals,
  1055. round, dependency);
  1056. if(nargs == savefile_dispatcher_t::abort)
  1057. msgs_read = -rd_total-1; // => causes abort
  1058. else
  1059. {
  1060. if(nargs != savefile_dispatcher_t::discard)
  1061. {
  1062. rtosc_arg_t vals[nargs];
  1063. char argstr[nargs+1];
  1064. for(int i = 0; i < nargs; ++i) {
  1065. vals[i] = arg_vals[i].val;
  1066. argstr[i] = arg_vals[i].type;
  1067. }
  1068. argstr[nargs] = 0;
  1069. rtosc_amessage(message, buffersize, portname,
  1070. argstr, vals);
  1071. (*dispatcher)(message);
  1072. }
  1073. }
  1074. msg_ptr += rd;
  1075. ++msgs_read;
  1076. }
  1077. else if(nargs == std::numeric_limits<int>::min())
  1078. {
  1079. // this means the (rest of the) file is whitespace only
  1080. // => don't increase msgs_read
  1081. while(*++msg_ptr) ;
  1082. }
  1083. else {
  1084. // overwrite meaning of msgs_read in order to
  1085. // inform the user where the read error occurred
  1086. msgs_read = -rd_total-1;
  1087. }
  1088. }
  1089. }
  1090. return msgs_read;
  1091. }
  1092. std::string rtosc::save_to_file(const Ports &ports, void *runtime,
  1093. const char *appname, rtosc_version appver)
  1094. {
  1095. std::string res;
  1096. char rtosc_vbuf[12], app_vbuf[12];
  1097. {
  1098. rtosc_version rtoscver = rtosc_current_version();
  1099. rtosc_version_print_to_12byte_str(&rtoscver, rtosc_vbuf);
  1100. rtosc_version_print_to_12byte_str(&appver, app_vbuf);
  1101. }
  1102. res += "% RT OSC v"; res += rtosc_vbuf; res += " savefile\n"
  1103. "% "; res += appname; res += " v"; res += app_vbuf; res += "\n";
  1104. res += get_changed_values(ports, runtime);
  1105. return res;
  1106. }
  1107. int rtosc::load_from_file(const char* file_content,
  1108. const Ports& ports, void* runtime,
  1109. const char* appname,
  1110. rtosc_version appver,
  1111. savefile_dispatcher_t* dispatcher)
  1112. {
  1113. char appbuf[128];
  1114. int bytes_read = 0;
  1115. if(dispatcher)
  1116. {
  1117. dispatcher->app_curver = appver;
  1118. dispatcher->rtosc_curver = rtosc_current_version();
  1119. }
  1120. unsigned vma, vmi, vre;
  1121. int n = 0;
  1122. sscanf(file_content,
  1123. "%% RT OSC v%u.%u.%u savefile%n ", &vma, &vmi, &vre, &n);
  1124. if(n <= 0 || vma > 255 || vmi > 255 || vre > 255)
  1125. return -bytes_read-1;
  1126. if(dispatcher)
  1127. {
  1128. dispatcher->rtosc_filever.major = vma;
  1129. dispatcher->rtosc_filever.minor = vmi;
  1130. dispatcher->rtosc_filever.revision = vre;
  1131. }
  1132. file_content += n;
  1133. bytes_read += n;
  1134. n = 0;
  1135. sscanf(file_content,
  1136. "%% %128s v%u.%u.%u%n ", appbuf, &vma, &vmi, &vre, &n);
  1137. if(n <= 0 || strcmp(appbuf, appname) || vma > 255 || vmi > 255 || vre > 255)
  1138. return -bytes_read-1;
  1139. if(dispatcher)
  1140. {
  1141. dispatcher->app_filever.major = vma;
  1142. dispatcher->app_filever.minor = vmi;
  1143. dispatcher->app_filever.revision = vre;
  1144. }
  1145. file_content += n;
  1146. bytes_read += n;
  1147. n = 0;
  1148. int rval = dispatch_printed_messages(file_content,
  1149. ports, runtime, dispatcher);
  1150. return (rval < 0) ? (rval-bytes_read) : rval;
  1151. }
  1152. /*
  1153. * Miscellaneous
  1154. */
  1155. const Port *Ports::operator[](const char *name) const
  1156. {
  1157. for(const Port &port:ports) {
  1158. const char *_needle = name,
  1159. *_haystack = port.name;
  1160. while(*_needle && *_needle==*_haystack)_needle++,_haystack++;
  1161. if(*_needle == 0 && (*_haystack == ':' || *_haystack == '\0')) {
  1162. return &port;
  1163. }
  1164. }
  1165. return NULL;
  1166. }
  1167. static msg_t snip(msg_t m)
  1168. {
  1169. while(*m && *m != '/') ++m;
  1170. return m+1;
  1171. }
  1172. const Port *Ports::apropos(const char *path) const
  1173. {
  1174. if(path && path[0] == '/')
  1175. ++path;
  1176. for(const Port &port: ports)
  1177. if(strchr(port.name,'/') && rtosc_match_path(port.name,path, NULL))
  1178. return (strchr(path,'/')[1]==0) ? &port :
  1179. port.ports->apropos(snip(path));
  1180. //This is the lowest level, now find the best port
  1181. for(const Port &port: ports)
  1182. if(*path && (strstr(port.name, path)==port.name ||
  1183. rtosc_match_path(port.name, path, NULL)))
  1184. return &port;
  1185. return NULL;
  1186. }
  1187. static bool parent_path_p(char *read, char *start)
  1188. {
  1189. if(read-start<2)
  1190. return false;
  1191. return read[0]=='.' && read[-1]=='.' && read[-2]=='/';
  1192. }
  1193. static void read_path(char *&r, char *start)
  1194. {
  1195. while(1)
  1196. {
  1197. if(r<start)
  1198. break;
  1199. bool doBreak = *r=='/';
  1200. r--;
  1201. if(doBreak)
  1202. break;
  1203. }
  1204. }
  1205. static void move_path(char *&r, char *&w, char *start)
  1206. {
  1207. while(1)
  1208. {
  1209. if(r<start)
  1210. break;
  1211. bool doBreak = *r=='/';
  1212. *w-- = *r--;
  1213. if(doBreak)
  1214. break;
  1215. }
  1216. }
  1217. char *Ports::collapsePath(char *p)
  1218. {
  1219. //obtain the pointer to the last non-null char
  1220. char *p_end = p;
  1221. while(*p_end) p_end++;
  1222. p_end--;
  1223. //number of subpaths to consume
  1224. int consuming = 0;
  1225. char *write_pos = p_end;
  1226. char *read_pos = p_end;
  1227. while(read_pos >= p) {
  1228. //per path chunk either
  1229. //(1) find a parent ref and inc consuming
  1230. //(2) find a normal ref and consume
  1231. //(3) find a normal ref and write through
  1232. bool ppath = parent_path_p(read_pos, p);
  1233. if(ppath) {
  1234. read_path(read_pos, p);
  1235. consuming++;
  1236. } else if(consuming) {
  1237. read_path(read_pos, p);
  1238. consuming--;
  1239. } else
  1240. move_path(read_pos, write_pos, p);
  1241. }
  1242. //return last written location, not next to write
  1243. return write_pos+1;
  1244. };
  1245. void Ports::refreshMagic()
  1246. {
  1247. delete impl;
  1248. impl = new Port_Matcher;
  1249. generate_minimal_hash(*this, *impl);
  1250. impl->enump = new bool[ports.size()];
  1251. for(int i=0; i<(int)ports.size(); ++i)
  1252. impl->enump[i] = strchr(ports[i].name, '#');
  1253. elms = ports.size();
  1254. }
  1255. ClonePorts::ClonePorts(const Ports &ports_,
  1256. std::initializer_list<ClonePort> c)
  1257. :Ports({})
  1258. {
  1259. for(auto &to_clone:c) {
  1260. const Port *clone_port = NULL;
  1261. for(auto &p:ports_.ports)
  1262. if(!strcmp(p.name, to_clone.name))
  1263. clone_port = &p;
  1264. if(!clone_port && strcmp("*", to_clone.name)) {
  1265. fprintf(stderr, "Cannot find a clone port for '%s'\n",to_clone.name);
  1266. assert(false);
  1267. }
  1268. if(clone_port) {
  1269. ports.push_back({clone_port->name, clone_port->metadata,
  1270. clone_port->ports, to_clone.cb});
  1271. } else {
  1272. default_handler = to_clone.cb;
  1273. }
  1274. }
  1275. refreshMagic();
  1276. }
  1277. MergePorts::MergePorts(std::initializer_list<const rtosc::Ports*> c)
  1278. :Ports({})
  1279. {
  1280. //XXX TODO remove duplicates in some sane and documented way
  1281. //e.g. repeated ports override and remove older ones
  1282. for(auto *to_clone:c) {
  1283. assert(to_clone);
  1284. for(auto &p:to_clone->ports) {
  1285. bool already_there = false;
  1286. for(auto &pp:ports)
  1287. if(!strcmp(pp.name, p.name))
  1288. already_there = true;
  1289. if(!already_there)
  1290. ports.push_back(p);
  1291. }
  1292. }
  1293. refreshMagic();
  1294. }
  1295. /**
  1296. * @brief Check if the port @p port is enabled
  1297. * @param port The port to be checked. Usually of type rRecur* or rSelf.
  1298. * @param loc The absolute path of @p port
  1299. * @param loc_size The maximum usable size of @p loc
  1300. * @param ports The Ports object containing @p port
  1301. * @param runtime TODO
  1302. * @return TODO
  1303. */
  1304. bool port_is_enabled(const Port* port, char* loc, size_t loc_size,
  1305. const Ports& base, void *runtime)
  1306. {
  1307. if(port && runtime)
  1308. {
  1309. const char* enable_port = port->meta()["enabled by"];
  1310. if(enable_port)
  1311. {
  1312. /*
  1313. find out which Ports object to dispatch at
  1314. (the current one or its child?)
  1315. */
  1316. const char* n = port->name;
  1317. const char* e = enable_port;
  1318. for( ; *n && (*n == *e) && *n != '/' && *e != '/'; ++n, ++e) ;
  1319. bool subport = (*e == '/' && *n == '/');
  1320. const char* ask_port_str = subport
  1321. ? e+1
  1322. : enable_port;
  1323. const Ports& ask_ports = subport ? *base[port->name]->ports
  1324. : base;
  1325. assert(!strchr(ask_port_str, '/'));
  1326. const Port* ask_port = ask_ports[ask_port_str];
  1327. assert(ask_port);
  1328. rtosc_arg_val_t rval;
  1329. /*
  1330. concatenate the location string
  1331. */
  1332. if(subport)
  1333. strncat(loc, "/../", loc_size - strlen(loc) - 1);
  1334. strncat(loc, enable_port, loc_size - strlen(loc) - 1);
  1335. char* collapsed_loc = Ports::collapsePath(loc);
  1336. loc_size -= (collapsed_loc - loc);
  1337. // TODO: collapse, use .. only in one case
  1338. /*
  1339. receive the "enabled" property
  1340. */
  1341. char buf[loc_size];
  1342. #ifdef NEW_CODE
  1343. strncpy(buf, collapsed_loc, loc_size);
  1344. #else
  1345. // TODO: try to use portname_from_base, since Ports might
  1346. // also be of type a#N/b
  1347. const char* last_slash = strrchr(collapsed_loc, '/');
  1348. strncpy(buf,
  1349. last_slash ? last_slash + 1 : collapsed_loc,
  1350. loc_size);
  1351. #endif
  1352. get_value_from_runtime(runtime, *ask_port,
  1353. loc_size, collapsed_loc, ask_port_str,
  1354. buf, 0, 1, &rval);
  1355. assert(rval.type == 'T' || rval.type == 'F');
  1356. return rval.val.T == 'T';
  1357. }
  1358. else // Port has no "enabled" property, so it is always enabled
  1359. return true;
  1360. }
  1361. else // no runtime provided, so run statically through all subports
  1362. return true;
  1363. }
  1364. // TODO: copy the changes into walk_ports_2
  1365. void rtosc::walk_ports(const Ports *base,
  1366. char *name_buffer,
  1367. size_t buffer_size,
  1368. void *data,
  1369. port_walker_t walker,
  1370. void* runtime)
  1371. {
  1372. auto walk_ports_recurse = [](const Port& p, char* name_buffer,
  1373. size_t buffer_size, const Ports& base,
  1374. void* data, port_walker_t walker,
  1375. void* runtime, const char* old_end)
  1376. {
  1377. // TODO: all/most of these checks must also be done for the
  1378. // first, non-recursive call
  1379. bool enabled = true;
  1380. if(runtime)
  1381. {
  1382. enabled = (p.meta().find("no walk") == p.meta().end());
  1383. if(enabled)
  1384. {
  1385. // get child runtime and check if it's NULL
  1386. RtData r;
  1387. r.obj = runtime;
  1388. r.port = &p;
  1389. char buf[1024];
  1390. strncpy(buf, old_end, 1024);
  1391. strncat(buf, "pointer", 1024 - strlen(buf) - 1);
  1392. assert(1024 - strlen(buf) >= 8);
  1393. strncpy(buf + strlen(buf) + 1, ",", 2);
  1394. p.cb(buf, r);
  1395. runtime = r.obj; // callback has stored the child pointer here
  1396. // if there is runtime information, but the pointer is NULL,
  1397. // the port is not enabled
  1398. enabled = (bool) runtime;
  1399. if(enabled)
  1400. {
  1401. // check if the port is disabled by a switch
  1402. enabled = port_is_enabled(&p, name_buffer, buffer_size,
  1403. base, runtime);
  1404. }
  1405. }
  1406. }
  1407. if(enabled)
  1408. rtosc::walk_ports(p.ports, name_buffer, buffer_size,
  1409. data, walker, runtime);
  1410. };
  1411. //only walk valid ports
  1412. if(!base)
  1413. return;
  1414. assert(name_buffer);
  1415. //XXX buffer_size is not properly handled yet
  1416. if(name_buffer[0] == 0)
  1417. name_buffer[0] = '/';
  1418. char *old_end = name_buffer;
  1419. while(*old_end) ++old_end;
  1420. if(port_is_enabled((*base)["self:"], name_buffer, buffer_size, *base,
  1421. runtime))
  1422. for(const Port &p: *base) {
  1423. //if(strchr(p.name, '/')) {//it is another tree
  1424. if(p.ports) {//it is another tree
  1425. if(strchr(p.name,'#')) {
  1426. const char *name = p.name;
  1427. char *pos = old_end;
  1428. while(*name != '#') *pos++ = *name++;
  1429. const unsigned max = atoi(name+1);
  1430. for(unsigned i=0; i<max; ++i)
  1431. {
  1432. sprintf(pos,"%d",i);
  1433. //Ensure the result is a path
  1434. if(strrchr(name_buffer, '/')[1] != '/')
  1435. strcat(name_buffer, "/");
  1436. //Recurse
  1437. walk_ports_recurse(p, name_buffer, buffer_size,
  1438. *base, data, walker, runtime, old_end);
  1439. }
  1440. } else {
  1441. //Append the path
  1442. const char* old_end = name_buffer + strlen(name_buffer);
  1443. scat(name_buffer, p.name);
  1444. //Recurse
  1445. walk_ports_recurse(p, name_buffer, buffer_size,
  1446. *base, data, walker, runtime, old_end);
  1447. }
  1448. } else {
  1449. if(strchr(p.name,'#')) {
  1450. const char *name = p.name;
  1451. char *pos = old_end;
  1452. while(*name != '#') *pos++ = *name++;
  1453. const unsigned max = atoi(name+1);
  1454. while(isdigit(*++name)) ;
  1455. for(unsigned i=0; i<max; ++i)
  1456. {
  1457. char* pos_after_num = pos + sprintf(pos,"%d",i);
  1458. const char* name2_2 = name;
  1459. // append everything behind the '#' (for cases like a#N/b)
  1460. while(*name2_2 && *name2_2 != ':')
  1461. *pos_after_num++ = *name2_2++;
  1462. //Apply walker function
  1463. walker(&p, name_buffer, old_end, *base, data, runtime);
  1464. }
  1465. } else {
  1466. //Append the path
  1467. scat(name_buffer, p.name);
  1468. //Apply walker function
  1469. walker(&p, name_buffer, old_end, *base, data, runtime);
  1470. }
  1471. }
  1472. //Remove the rest of the path
  1473. char *tmp = old_end;
  1474. while(*tmp) *tmp++=0;
  1475. }
  1476. }
  1477. void walk_ports2(const rtosc::Ports *base,
  1478. char *name_buffer,
  1479. size_t buffer_size,
  1480. void *data,
  1481. rtosc::port_walker_t walker)
  1482. {
  1483. if(!base)
  1484. return;
  1485. assert(name_buffer);
  1486. //XXX buffer_size is not properly handled yet
  1487. if(name_buffer[0] == 0)
  1488. name_buffer[0] = '/';
  1489. char *old_end = name_buffer;
  1490. while(*old_end) ++old_end;
  1491. for(const rtosc::Port &p: *base) {
  1492. if(strchr(p.name, '/')) {//it is another tree
  1493. if(strchr(p.name,'#')) {
  1494. const char *name = p.name;
  1495. char *pos = old_end;
  1496. while(*name != '#') *pos++ = *name++;
  1497. const unsigned max = atoi(name+1);
  1498. //for(unsigned i=0; i<max; ++i)
  1499. {
  1500. sprintf(pos,"[0,%d]",max-1);
  1501. //Ensure the result is a path
  1502. if(strrchr(name_buffer, '/')[1] != '/')
  1503. strcat(name_buffer, "/");
  1504. //Recurse
  1505. walk_ports2(p.ports, name_buffer, buffer_size,
  1506. data, walker);
  1507. }
  1508. } else {
  1509. //Append the path
  1510. scat(name_buffer, p.name);
  1511. //Recurse
  1512. walk_ports2(p.ports, name_buffer, buffer_size,
  1513. data, walker);
  1514. }
  1515. } else {
  1516. if(strchr(p.name,'#')) {
  1517. const char *name = p.name;
  1518. char *pos = old_end;
  1519. while(*name != '#') *pos++ = *name++;
  1520. const unsigned max = atoi(name+1);
  1521. //for(unsigned i=0; i<max; ++i)
  1522. {
  1523. sprintf(pos,"[0,%d]",max-1);
  1524. //Apply walker function
  1525. walker(&p, name_buffer, old_end, *base, data, nullptr);
  1526. }
  1527. } else {
  1528. //Append the path
  1529. scat(name_buffer, p.name);
  1530. //Apply walker function
  1531. walker(&p, name_buffer, old_end, *base, data, nullptr);
  1532. }
  1533. }
  1534. //Remove the rest of the path
  1535. char *tmp = old_end;
  1536. while(*tmp) *tmp++=0;
  1537. }
  1538. }
  1539. static void units(std::ostream &o, const char *u)
  1540. {
  1541. if(!u)
  1542. return;
  1543. o << " units=\"" << u << "\"";
  1544. }
  1545. using std::ostream;
  1546. using std::string;
  1547. static int enum_min(Port::MetaContainer meta)
  1548. {
  1549. int min = 0;
  1550. for(auto m:meta)
  1551. if(strstr(m.title, "map "))
  1552. min = atoi(m.title+4);
  1553. for(auto m:meta)
  1554. if(strstr(m.title, "map "))
  1555. min = min>atoi(m.title+4) ? atoi(m.title+4) : min;
  1556. return min;
  1557. }
  1558. static int enum_max(Port::MetaContainer meta)
  1559. {
  1560. int max = 0;
  1561. for(auto m:meta)
  1562. if(strstr(m.title, "map "))
  1563. max = atoi(m.title+4);
  1564. for(auto m:meta)
  1565. if(strstr(m.title, "map "))
  1566. max = max<atoi(m.title+4) ? atoi(m.title+4) : max;
  1567. return max;
  1568. }
  1569. int rtosc::enum_key(Port::MetaContainer meta, const char* value)
  1570. {
  1571. int result = std::numeric_limits<int>::min();
  1572. for(auto m:meta)
  1573. if(strstr(m.title, "map "))
  1574. if(!strcmp(m.value, value))
  1575. {
  1576. result = atoi(m.title+4);
  1577. break;
  1578. }
  1579. return result;
  1580. }
  1581. static ostream &add_options(ostream &o, Port::MetaContainer meta)
  1582. {
  1583. string sym_names = "xyzabcdefghijklmnopqrstuvw";
  1584. int sym_idx = 0;
  1585. bool has_options = false;
  1586. for(auto m:meta)
  1587. if(strstr(m.title, "map "))
  1588. has_options = true;
  1589. for(auto m:meta)
  1590. if(strcmp(m.title, "documentation") &&
  1591. strcmp(m.title, "parameter") &&
  1592. strcmp(m.title, "max") &&
  1593. strcmp(m.title, "min"))
  1594. printf("m.title = <%s>\n", m.title);
  1595. if(!has_options)
  1596. return o;
  1597. o << " <hints>\n";
  1598. for(auto m:meta) {
  1599. if(strstr(m.title, "map ")) {
  1600. o << " <point symbol=\"" << sym_names[sym_idx++] << "\" value=\"";
  1601. o << m.title+4 << "\">" << m.value << "</point>\n";
  1602. }
  1603. }
  1604. o << " </hints>\n";
  1605. return o;
  1606. }
  1607. static ostream &dump_t_f_port(ostream &o, string name, string doc)
  1608. {
  1609. o << " <message_in pattern=\"" << name << "\" typetag=\"T\">\n";
  1610. o << " <desc>Enable " << doc << "</desc>\n";
  1611. o << " <param_T symbol=\"x\"/>\n";
  1612. o << " </message_in>\n";
  1613. o << " <message_in pattern=\"" << name << "\" typetag=\"F\">\n";
  1614. o << " <desc>Disable " << doc << "</desc>\n";
  1615. o << " <param_F symbol=\"x\"/>\n";
  1616. o << " </message_in>\n";
  1617. o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
  1618. o << " <desc>Get state of " << doc << "</desc>\n";
  1619. o << " </message_in>\n";
  1620. o << " <message_out pattern=\"" << name << "\" typetag=\"T\">\n";
  1621. o << " <desc>Value of " << doc << "</desc>\n";
  1622. o << " <param_T symbol=\"x\"/>";
  1623. o << " </message_out>\n";
  1624. o << " <message_out pattern=\"" << name << "\" typetag=\"F\">\n";
  1625. o << " <desc>Value of " << doc << "</desc>\n";
  1626. o << " <param_F symbol=\"x\"/>";
  1627. o << " </message_out>\n";
  1628. return o;
  1629. }
  1630. static ostream &dump_any_port(ostream &o, string name, string doc)
  1631. {
  1632. o << " <message_in pattern=\"" << name << "\" typetag=\"*\">\n";
  1633. o << " <desc>" << doc << "</desc>\n";
  1634. o << " </message_in>\n";
  1635. return o;
  1636. }
  1637. static ostream &dump_generic_port(ostream &o, string name, string doc, string type)
  1638. {
  1639. const char *t = type.c_str();
  1640. string arg_names = "xyzabcdefghijklmnopqrstuvw";
  1641. //start out with argument separator
  1642. if(*t++ != ':')
  1643. return o;
  1644. //now real arguments (assume [] don't exist)
  1645. string args;
  1646. while(*t && *t != ':')
  1647. args += *t++;
  1648. o << " <message_in pattern=\"" << name << "\" typetag=\"" << args << "\">\n";
  1649. o << " <desc>" << doc << "</desc>\n";
  1650. assert(args.length()<arg_names.length());
  1651. for(unsigned i=0; i<args.length(); ++i)
  1652. o << " <param_" << args[i] << " symbol=\"" << arg_names[i] << "\"/>\n";
  1653. o << " </message_in>\n";
  1654. if(*t == ':')
  1655. return dump_generic_port(o, name, doc, t);
  1656. else
  1657. return o;
  1658. }
  1659. void dump_ports_cb(const rtosc::Port *p, const char *name,const char*,
  1660. const Ports&,void *v, void*)
  1661. {
  1662. std::ostream &o = *(std::ostream*)v;
  1663. auto meta = p->meta();
  1664. const char *args = strchr(p->name, ':');
  1665. auto mparameter = meta.find("parameter");
  1666. auto mdoc = meta.find("documentation");
  1667. string doc;
  1668. if(mdoc != p->meta().end())
  1669. doc = mdoc.value;
  1670. if(meta.find("internal") != meta.end()) {
  1671. doc += "[INTERNAL]";
  1672. }
  1673. if(mparameter != p->meta().end()) {
  1674. char type = 0;
  1675. if(args) {
  1676. if(strchr(args, 'f'))
  1677. type = 'f';
  1678. else if(strchr(args, 'i'))
  1679. type = 'i';
  1680. else if(strchr(args, 'c'))
  1681. type = 'c';
  1682. else if(strchr(args, 'T'))
  1683. type = 't';
  1684. else if(strchr(args, 's'))
  1685. type = 's';
  1686. }
  1687. if(!type) {
  1688. fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", name);
  1689. fprintf(stderr, " args = <%s>\n", args);
  1690. return;
  1691. }
  1692. if(type == 't') {
  1693. dump_t_f_port(o, name, doc);
  1694. return;
  1695. }
  1696. o << " <message_in pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
  1697. o << " <desc>Set Value of " << doc << "</desc>\n";
  1698. if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') {
  1699. o << " <param_" << type << " symbol=\"x\"";
  1700. units(o, meta["unit"]);
  1701. o << ">\n";
  1702. o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
  1703. o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
  1704. o << " </param_" << type << ">";
  1705. } else if(meta.find("enumerated") != meta.end()) {
  1706. o << " <param_" << type << " symbol=\"x\">\n";
  1707. o << " <range_min_max min=\"" << enum_min(meta) << "\" max=\"";
  1708. o << enum_max(meta) << "\">\n";
  1709. add_options(o, meta);
  1710. o << " </range_min_max>\n";
  1711. o << " </param_" << type << ">\n";
  1712. } else {
  1713. o << " <param_" << type << " symbol=\"x\"";
  1714. units(o, meta["unit"]);
  1715. o << "/>\n";
  1716. }
  1717. o << " </message_in>\n";
  1718. o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
  1719. o << " <desc>Get Value of " << doc << "</desc>\n";
  1720. o << " </message_in>\n";
  1721. o << " <message_out pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
  1722. o << " <desc>Value of " << doc << "</desc>\n";
  1723. if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') {
  1724. o << " <param_" << type << " symbol=\"x\"";
  1725. units(o, meta["unit"]);
  1726. o << ">\n";
  1727. o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
  1728. o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
  1729. o << " </param_" << type << ">\n";
  1730. } else if(meta.find("enumerated") != meta.end()) {
  1731. o << " <param_" << type << " symbol=\"x\">\n";
  1732. o << " <range_min_max min=\"" << enum_min(meta) << "\" max=\"";
  1733. o << enum_max(meta) << "\">\n";
  1734. add_options(o, meta);
  1735. o << " </range_min_max>\n";
  1736. o << " </param_" << type << ">\n";
  1737. } else {
  1738. o << " <param_" << type << " symbol=\"x\"";
  1739. units(o, meta["unit"]);
  1740. o << "/>\n";
  1741. }
  1742. o << " </message_out>\n";
  1743. } else if(mdoc != meta.end() && (!args || args == std::string(""))) {
  1744. dump_any_port(o, name, doc);
  1745. } else if(mdoc != meta.end() && args) {
  1746. dump_generic_port(o, name, doc, args);
  1747. } else if(mdoc != meta.end()) {
  1748. fprintf(stderr, "Skipping \"%s\"\n", name);
  1749. if(args) {
  1750. fprintf(stderr, " type = %s\n", args);
  1751. }
  1752. } else
  1753. fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name);
  1754. }
  1755. std::ostream &rtosc::operator<<(std::ostream &o, rtosc::OscDocFormatter &formatter)
  1756. {
  1757. o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  1758. o << "<osc_unit format_version=\"1.0\">\n";
  1759. o << " <meta>\n";
  1760. o << " <name>" << formatter.prog_name << "</name>\n";
  1761. o << " <uri>" << formatter.uri << "</uri>\n";
  1762. o << " <doc_origin>" << formatter.doc_origin << "</doc_origin>\n";
  1763. o << " <author><firstname>" << formatter.author_first;
  1764. o << "</firstname><lastname>" << formatter.author_last << "</lastname></author>\n";
  1765. o << " </meta>\n";
  1766. char buffer[1024];
  1767. memset(buffer, 0, sizeof(buffer));
  1768. walk_ports2(formatter.p, buffer, 1024, &o, dump_ports_cb);
  1769. o << "</osc_unit>\n";
  1770. return o;
  1771. }