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.

1146 lines
31KB

  1. #include "../ports.h"
  2. #include <ostream>
  3. #include <cassert>
  4. #include <climits>
  5. #include <cstring>
  6. #include <string>
  7. using namespace rtosc;
  8. static inline void scat(char *dest, const char *src)
  9. {
  10. while(*dest) dest++;
  11. if(*dest) dest++;
  12. while(*src && *src!=':') *dest++ = *src++;
  13. *dest = 0;
  14. }
  15. RtData::RtData(void)
  16. :loc(NULL), loc_size(0), obj(NULL), matches(0), message(NULL)
  17. {}
  18. void RtData::replyArray(const char *path, const char *args,
  19. rtosc_arg_t *vals)
  20. {
  21. (void) path;
  22. (void) args;
  23. (void) vals;
  24. }
  25. void RtData::reply(const char *path, const char *args, ...)
  26. {
  27. va_list va;
  28. va_start(va,args);
  29. char buffer[1024];
  30. rtosc_vmessage(buffer,1024,path,args,va);
  31. reply(buffer);
  32. va_end(va);
  33. };
  34. void RtData::reply(const char *msg)
  35. {(void)msg;};
  36. void RtData::chain(const char *path, const char *args, ...)
  37. {
  38. (void) path;
  39. (void) args;
  40. }
  41. void RtData::chain(const char *msg)
  42. {
  43. (void) msg;
  44. };
  45. void RtData::chainArray(const char *path, const char *args,
  46. rtosc_arg_t *vals)
  47. {
  48. (void) path;
  49. (void) args;
  50. (void) vals;
  51. };
  52. void RtData::broadcast(const char *path, const char *args, ...)
  53. {
  54. va_list va;
  55. va_start(va,args);
  56. char buffer[1024];
  57. rtosc_vmessage(buffer,1024,path,args,va);
  58. broadcast(buffer);
  59. va_end(va);
  60. }
  61. void RtData::broadcast(const char *msg)
  62. {reply(msg);};
  63. void RtData::broadcastArray(const char *path, const char *args,
  64. rtosc_arg_t *vals)
  65. {
  66. (void) path;
  67. (void) args;
  68. (void) vals;
  69. }
  70. void RtData::forward(const char *rational)
  71. {
  72. (void) rational;
  73. }
  74. void metaiterator_advance(const char *&title, const char *&value)
  75. {
  76. if(!title || !*title) {
  77. value = NULL;
  78. return;
  79. }
  80. //Try to find "\0=" after title string
  81. value = title;
  82. while(*value)
  83. ++value;
  84. if(*++value != '=')
  85. value = NULL;
  86. else
  87. value++;
  88. }
  89. Port::MetaIterator::MetaIterator(const char *str)
  90. :title(str), value(NULL)
  91. {
  92. metaiterator_advance(title, value);
  93. }
  94. Port::MetaIterator& Port::MetaIterator::operator++(void)
  95. {
  96. if(!title || !*title) {
  97. title = NULL;
  98. return *this;
  99. }
  100. //search for next parameter start
  101. //aka "\0:" unless "\0\0" is seen
  102. char prev = 0;
  103. while(prev || (*title && *title != ':'))
  104. prev = *title++;
  105. if(!*title)
  106. title = NULL;
  107. else
  108. ++title;
  109. metaiterator_advance(title, value);
  110. return *this;
  111. }
  112. Port::MetaContainer::MetaContainer(const char *str_)
  113. :str_ptr(str_)
  114. {}
  115. Port::MetaIterator Port::MetaContainer::begin(void) const
  116. {
  117. if(str_ptr && *str_ptr == ':')
  118. return Port::MetaIterator(str_ptr+1);
  119. else
  120. return Port::MetaIterator(str_ptr);
  121. }
  122. Port::MetaIterator Port::MetaContainer::end(void) const
  123. {
  124. return MetaIterator(NULL);
  125. }
  126. Port::MetaIterator Port::MetaContainer::find(const char *str) const
  127. {
  128. for(const auto x : *this)
  129. if(!strcmp(x.title, str))
  130. return x;
  131. return NULL;
  132. }
  133. size_t Port::MetaContainer::length(void) const
  134. {
  135. if(!str_ptr || !*str_ptr)
  136. return 0;
  137. char prev = 0;
  138. const char *itr = str_ptr;
  139. while(prev || *itr)
  140. prev = *itr++;
  141. return 2+(itr-str_ptr);
  142. }
  143. const char *Port::MetaContainer::operator[](const char *str) const
  144. {
  145. for(const auto x : *this)
  146. if(!strcmp(x.title, str))
  147. return x.value;
  148. return NULL;
  149. }
  150. //Match the arg string or fail
  151. inline bool arg_matcher(const char *pattern, const char *args)
  152. {
  153. //match anything if now arg restriction is present (ie the ':')
  154. if(*pattern++ != ':')
  155. return true;
  156. const char *arg_str = args;
  157. bool arg_match = *pattern || *pattern == *arg_str;
  158. while(*pattern && *pattern != ':')
  159. arg_match &= (*pattern++==*arg_str++);
  160. if(*pattern==':') {
  161. if(arg_match && !*arg_str)
  162. return true;
  163. else
  164. return arg_matcher(pattern, args); //retry
  165. }
  166. return arg_match;
  167. }
  168. inline bool scmp(const char *a, const char *b)
  169. {
  170. while(*a && *a == *b) a++, b++;
  171. return a[0] == b[0];
  172. }
  173. typedef std::vector<std::string> words_t;
  174. typedef std::vector<std::string> svec_t;
  175. typedef std::vector<const char *> cvec_t;
  176. typedef std::vector<int> ivec_t;
  177. typedef std::vector<int> tuple_t;
  178. typedef std::vector<tuple_t> tvec_t;
  179. namespace rtosc{
  180. class Port_Matcher
  181. {
  182. public:
  183. bool *enump;
  184. svec_t fixed;
  185. cvec_t arg_spec;
  186. ivec_t pos;
  187. ivec_t assoc;
  188. ivec_t remap;
  189. bool rtosc_match_args(const char *pattern, const char *msg)
  190. {
  191. //match anything if now arg restriction is present
  192. //(ie the ':')
  193. if(*pattern++ != ':')
  194. return true;
  195. const char *arg_str = rtosc_argument_string(msg);
  196. bool arg_match = *pattern || *pattern == *arg_str;
  197. while(*pattern && *pattern != ':')
  198. arg_match &= (*pattern++==*arg_str++);
  199. if(*pattern==':') {
  200. if(arg_match && !*arg_str)
  201. return true;
  202. else
  203. return rtosc_match_args(pattern, msg); //retry
  204. }
  205. return arg_match;
  206. }
  207. bool hard_match(int i, const char *msg)
  208. {
  209. if(strncmp(msg, fixed[i].c_str(), fixed[i].length()))
  210. return false;
  211. if(arg_spec[i])
  212. return rtosc_match_args(arg_spec[i], msg);
  213. else
  214. return true;
  215. }
  216. };
  217. }
  218. tvec_t do_hash(const words_t &strs, const ivec_t &pos)
  219. {
  220. tvec_t tvec;
  221. for(auto &s:strs) {
  222. tuple_t tuple;
  223. tuple.push_back(s.length());
  224. for(const auto &p:pos)
  225. if(p < (int)s.size())
  226. tuple.push_back(s[p]);
  227. tvec.push_back(std::move(tuple));
  228. }
  229. return tvec;
  230. }
  231. template<class T>
  232. int count_dups(std::vector<T> &t)
  233. {
  234. int dups = 0;
  235. int N = t.size();
  236. bool mark[t.size()];
  237. memset(mark, 0, N);
  238. for(int i=0; i<N; ++i) {
  239. if(mark[i])
  240. continue;
  241. for(int j=i+1; j<N; ++j) {
  242. if(t[i] == t[j]) {
  243. dups++;
  244. mark[j] = true;
  245. }
  246. }
  247. }
  248. return dups;
  249. }
  250. template<class T, class Z>
  251. bool has(T &t, Z&z)
  252. {
  253. for(auto tt:t)
  254. if(tt==z)
  255. return true;
  256. return false;
  257. }
  258. static int int_max(int a, int b) { return a<b?b:a;}
  259. static ivec_t find_pos(words_t &strs)
  260. {
  261. ivec_t pos;
  262. int current_dups = strs.size();
  263. int N = 0;
  264. for(auto w:strs)
  265. N = int_max(N,w.length());
  266. int pos_best = -1;
  267. int pos_best_val = INT_MAX;
  268. while(true)
  269. {
  270. for(int i=0; i<N; ++i) {
  271. ivec_t npos = pos;
  272. if(has(pos, i))
  273. continue;
  274. npos.push_back(i);
  275. auto hashed = do_hash(strs, npos);
  276. int d = count_dups(hashed);
  277. if(d < pos_best_val) {
  278. pos_best_val = d;
  279. pos_best = i;
  280. }
  281. }
  282. if(pos_best_val >= current_dups)
  283. break;
  284. current_dups = pos_best_val;
  285. pos.push_back(pos_best);
  286. }
  287. auto hashed = do_hash(strs, pos);
  288. int d = count_dups(hashed);
  289. //printf("Total Dups: %d\n", d);
  290. if(d != 0)
  291. pos.clear();
  292. return pos;
  293. }
  294. static ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc)
  295. {
  296. ivec_t ivec;
  297. ivec.reserve(strs.size());
  298. for(auto &s:strs) {
  299. int t = s.length();
  300. for(auto p:pos)
  301. if(p < (int)s.size())
  302. t += assoc[s[p]];
  303. ivec.push_back(t);
  304. }
  305. return ivec;
  306. }
  307. static ivec_t find_assoc(const words_t &strs, const ivec_t &pos)
  308. {
  309. ivec_t assoc;
  310. int current_dups = strs.size();
  311. int N = 127;
  312. std::vector<char> useful_chars;
  313. for(auto w:strs)
  314. for(auto c:w)
  315. if(!has(useful_chars, c))
  316. useful_chars.push_back(c);
  317. for(int i=0; i<N; ++i)
  318. assoc.push_back(0);
  319. int assoc_best = -1;
  320. int assoc_best_val = INT_MAX;
  321. for(int k=0; k<4; ++k)
  322. {
  323. for(int i:useful_chars) {
  324. assoc_best_val = INT_MAX;
  325. for(int j=0; j<100; ++j) {
  326. //printf(".");
  327. assoc[i] = j;
  328. auto hashed = do_hash(strs, pos, assoc);
  329. //for(int i=0; i<hashed.size(); ++i)
  330. // printf("%d ", hashed[i]);
  331. //printf("\n");
  332. int d = count_dups(hashed);
  333. //printf("dup %d\n",d);
  334. if(d < assoc_best_val) {
  335. assoc_best_val = d;
  336. assoc_best = j;
  337. }
  338. }
  339. assoc[i] = assoc_best;
  340. }
  341. if(assoc_best_val >= current_dups)
  342. break;
  343. current_dups = assoc_best_val;
  344. }
  345. auto hashed = do_hash(strs, pos, assoc);
  346. //int d = count_dups(hashed);
  347. //printf("Total Dups Assoc: %d\n", d);
  348. return assoc;
  349. }
  350. static ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc)
  351. {
  352. ivec_t remap;
  353. auto hashed = do_hash(strs, pos, assoc);
  354. //for(int i=0; i<strs.size(); ++i)
  355. // printf("%d) '%s'\n", hashed[i], strs[i].c_str());
  356. int N = 0;
  357. for(auto h:hashed)
  358. N = int_max(N,h+1);
  359. for(int i=0; i<N; ++i)
  360. remap.push_back(0);
  361. for(int i=0; i<(int)hashed.size(); ++i)
  362. remap[hashed[i]] = i;
  363. return remap;
  364. }
  365. static void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm)
  366. {
  367. if(str.empty())
  368. return;
  369. pm.pos = find_pos(str);
  370. if(pm.pos.empty()) {
  371. fprintf(stderr, "rtosc: Failed to generate minimal hash\n");
  372. return;
  373. }
  374. pm.assoc = find_assoc(str, pm.pos);
  375. pm.remap = find_remap(str, pm.pos, pm.assoc);
  376. }
  377. static void generate_minimal_hash(Ports &p, Port_Matcher &pm)
  378. {
  379. svec_t keys;
  380. cvec_t args;
  381. bool enump = false;
  382. for(unsigned i=0; i<p.ports.size(); ++i)
  383. if(strchr(p.ports[i].name, '#'))
  384. enump = true;
  385. if(enump)
  386. return;
  387. for(unsigned i=0; i<p.ports.size(); ++i)
  388. {
  389. std::string tmp = p.ports[i].name;
  390. const char *arg = NULL;
  391. int idx = tmp.find(':');
  392. if(idx > 0) {
  393. arg = p.ports[i].name+idx;
  394. tmp = tmp.substr(0,idx);
  395. }
  396. keys.push_back(tmp);
  397. args.push_back(arg);
  398. }
  399. pm.fixed = keys;
  400. pm.arg_spec = args;
  401. generate_minimal_hash(keys, pm);
  402. }
  403. Ports::Ports(std::initializer_list<Port> l)
  404. :ports(l), impl(NULL)
  405. {
  406. refreshMagic();
  407. }
  408. Ports::~Ports()
  409. {
  410. delete []impl->enump;
  411. delete impl;
  412. }
  413. #if !defined(__GNUC__)
  414. #define __builtin_expect(a,b) a
  415. #endif
  416. void Ports::dispatch(const char *m, rtosc::RtData &d, bool base_dispatch) const
  417. {
  418. void *obj = d.obj;
  419. //handle the first dispatch layer
  420. if(base_dispatch) {
  421. d.matches = 0;
  422. d.message = m;
  423. if(m && *m == '/')
  424. m++;
  425. if(d.loc)
  426. d.loc[0] = 0;
  427. }
  428. //simple case
  429. if(!d.loc || !d.loc_size) {
  430. for(const Port &port: ports) {
  431. if(rtosc_match(port.name,m))
  432. d.port = &port, port.cb(m,d), d.obj = obj;
  433. }
  434. } else {
  435. //TODO this function is certainly buggy at the moment, some tests
  436. //are needed to make it clean
  437. //XXX buffer_size is not properly handled yet
  438. if(__builtin_expect(d.loc[0] == 0, 0)) {
  439. memset(d.loc, 0, d.loc_size);
  440. d.loc[0] = '/';
  441. }
  442. char *old_end = d.loc;
  443. while(*old_end) ++old_end;
  444. if(impl->pos.empty()) { //No perfect minimal hash function
  445. for(unsigned i=0; i<elms; ++i) {
  446. const Port &port = ports[i];
  447. if(!rtosc_match(port.name, m))
  448. continue;
  449. if(!port.ports)
  450. d.matches++;
  451. //Append the path
  452. if(strchr(port.name,'#')) {
  453. const char *msg = m;
  454. char *pos = old_end;
  455. while(*msg && *msg != '/')
  456. *pos++ = *msg++;
  457. if(strchr(port.name, '/'))
  458. *pos++ = '/';
  459. *pos = '\0';
  460. } else
  461. scat(d.loc, port.name);
  462. d.port = &port;
  463. //Apply callback
  464. port.cb(m,d), d.obj = obj;
  465. //Remove the rest of the path
  466. char *tmp = old_end;
  467. while(*tmp) *tmp++=0;
  468. }
  469. } else {
  470. //Define string to be hashed
  471. unsigned len=0;
  472. const char *tmp = m;
  473. while(*tmp && *tmp != '/')
  474. tmp++;
  475. if(*tmp == '/')
  476. tmp++;
  477. len = tmp-m;
  478. //Compute the hash
  479. int t = len;
  480. for(auto p:impl->pos)
  481. if(p < (int)len)
  482. t += impl->assoc[m[p]];
  483. if(t >= (int)impl->remap.size() && !default_handler)
  484. return;
  485. else if(t >= (int)impl->remap.size() && default_handler) {
  486. d.matches++;
  487. default_handler(m,d), d.obj = obj;
  488. return;
  489. }
  490. int port_num = impl->remap[t];
  491. //Verify the chosen port is correct
  492. if(__builtin_expect(impl->hard_match(port_num, m), 1)) {
  493. const Port &port = ports[impl->remap[t]];
  494. if(!port.ports)
  495. d.matches++;
  496. //Append the path
  497. if(impl->enump[port_num]) {
  498. const char *msg = m;
  499. char *pos = old_end;
  500. while(*msg && *msg != '/')
  501. *pos++ = *msg++;
  502. if(strchr(port.name, '/'))
  503. *pos++ = '/';
  504. *pos = '\0';
  505. } else
  506. memcpy(old_end, impl->fixed[port_num].c_str(),
  507. impl->fixed[port_num].length()+1);
  508. d.port = &port;
  509. //Apply callback
  510. port.cb(m,d), d.obj = obj;
  511. //Remove the rest of the path
  512. old_end[0] = '\0';
  513. } else if(default_handler) {
  514. d.matches++;
  515. default_handler(m,d), d.obj = obj;
  516. }
  517. }
  518. }
  519. }
  520. const Port *Ports::operator[](const char *name) const
  521. {
  522. for(const Port &port:ports) {
  523. const char *_needle = name,
  524. *_haystack = port.name;
  525. while(*_needle && *_needle==*_haystack)_needle++,_haystack++;
  526. if(*_needle == 0 && (*_haystack == ':' || *_haystack == '\0')) {
  527. return &port;
  528. }
  529. }
  530. return NULL;
  531. }
  532. static msg_t snip(msg_t m)
  533. {
  534. while(*m && *m != '/') ++m;
  535. return m+1;
  536. }
  537. const Port *Ports::apropos(const char *path) const
  538. {
  539. if(path && path[0] == '/')
  540. ++path;
  541. for(const Port &port: ports)
  542. if(strchr(port.name,'/') && rtosc_match_path(port.name,path))
  543. return (strchr(path,'/')[1]==0) ? &port :
  544. port.ports->apropos(snip(path));
  545. //This is the lowest level, now find the best port
  546. for(const Port &port: ports)
  547. if(*path && (strstr(port.name, path)==port.name ||
  548. rtosc_match_path(port.name, path)))
  549. return &port;
  550. return NULL;
  551. }
  552. static bool parent_path_p(char *read, char *start)
  553. {
  554. if(read-start<2)
  555. return false;
  556. return read[0]=='.' && read[-1]=='.' && read[-2]=='/';
  557. }
  558. static void read_path(char *&r, char *start)
  559. {
  560. while(1)
  561. {
  562. if(r<start)
  563. break;
  564. bool doBreak = *r=='/';
  565. r--;
  566. if(doBreak)
  567. break;
  568. }
  569. }
  570. static void move_path(char *&r, char *&w, char *start)
  571. {
  572. while(1)
  573. {
  574. if(r<start)
  575. break;
  576. bool doBreak = *r=='/';
  577. *w-- = *r--;
  578. if(doBreak)
  579. break;
  580. }
  581. }
  582. char *Ports::collapsePath(char *p)
  583. {
  584. //obtain the pointer to the last non-null char
  585. char *p_end = p;
  586. while(*p_end) p_end++;
  587. p_end--;
  588. //number of subpaths to consume
  589. int consuming = 0;
  590. char *write_pos = p_end;
  591. char *read_pos = p_end;
  592. while(read_pos >= p) {
  593. //per path chunk either
  594. //(1) find a parent ref and inc consuming
  595. //(2) find a normal ref and consume
  596. //(3) find a normal ref and write through
  597. bool ppath = parent_path_p(read_pos, p);
  598. if(ppath) {
  599. read_path(read_pos, p);
  600. consuming++;
  601. } else if(consuming) {
  602. read_path(read_pos, p);
  603. consuming--;
  604. } else
  605. move_path(read_pos, write_pos, p);
  606. }
  607. //return last written location, not next to write
  608. return write_pos+1;
  609. };
  610. void Ports::refreshMagic()
  611. {
  612. delete impl;
  613. impl = new Port_Matcher;
  614. generate_minimal_hash(*this, *impl);
  615. impl->enump = new bool[ports.size()];
  616. for(int i=0; i<(int)ports.size(); ++i)
  617. impl->enump[i] = strchr(ports[i].name, '#');
  618. elms = ports.size();
  619. }
  620. ClonePorts::ClonePorts(const Ports &ports_,
  621. std::initializer_list<ClonePort> c)
  622. :Ports({})
  623. {
  624. for(auto &to_clone:c) {
  625. const Port *clone_port = NULL;
  626. for(auto &p:ports_.ports)
  627. if(!strcmp(p.name, to_clone.name))
  628. clone_port = &p;
  629. if(!clone_port && strcmp("*", to_clone.name)) {
  630. fprintf(stderr, "Cannot find a clone port for '%s'\n",to_clone.name);
  631. assert(false);
  632. }
  633. if(clone_port) {
  634. ports.push_back({clone_port->name, clone_port->metadata,
  635. clone_port->ports, to_clone.cb});
  636. } else {
  637. default_handler = to_clone.cb;
  638. }
  639. }
  640. refreshMagic();
  641. }
  642. MergePorts::MergePorts(std::initializer_list<const rtosc::Ports*> c)
  643. :Ports({})
  644. {
  645. //XXX TODO remove duplicates in some sane and documented way
  646. //e.g. repeated ports override and remove older ones
  647. for(auto *to_clone:c) {
  648. assert(to_clone);
  649. for(auto &p:to_clone->ports) {
  650. bool already_there = false;
  651. for(auto &pp:ports)
  652. if(!strcmp(pp.name, p.name))
  653. already_there = true;
  654. if(!already_there)
  655. ports.push_back(p);
  656. }
  657. }
  658. refreshMagic();
  659. }
  660. void rtosc::walk_ports(const Ports *base,
  661. char *name_buffer,
  662. size_t buffer_size,
  663. void *data,
  664. port_walker_t walker)
  665. {
  666. //only walk valid ports
  667. if(!base)
  668. return;
  669. assert(name_buffer);
  670. //XXX buffer_size is not properly handled yet
  671. if(name_buffer[0] == 0)
  672. name_buffer[0] = '/';
  673. char *old_end = name_buffer;
  674. while(*old_end) ++old_end;
  675. for(const Port &p: *base) {
  676. if(strchr(p.name, '/')) {//it is another tree
  677. if(strchr(p.name,'#')) {
  678. const char *name = p.name;
  679. char *pos = old_end;
  680. while(*name != '#') *pos++ = *name++;
  681. const unsigned max = atoi(name+1);
  682. for(unsigned i=0; i<max; ++i)
  683. {
  684. sprintf(pos,"%d",i);
  685. //Ensure the result is a path
  686. if(strrchr(name_buffer, '/')[1] != '/')
  687. strcat(name_buffer, "/");
  688. //Recurse
  689. rtosc::walk_ports(p.ports, name_buffer, buffer_size,
  690. data, walker);
  691. }
  692. } else {
  693. //Append the path
  694. scat(name_buffer, p.name);
  695. //Recurse
  696. rtosc::walk_ports(p.ports, name_buffer, buffer_size,
  697. data, walker);
  698. }
  699. } else {
  700. if(strchr(p.name,'#')) {
  701. const char *name = p.name;
  702. char *pos = old_end;
  703. while(*name != '#') *pos++ = *name++;
  704. const unsigned max = atoi(name+1);
  705. for(unsigned i=0; i<max; ++i)
  706. {
  707. sprintf(pos,"%d",i);
  708. //Apply walker function
  709. walker(&p, name_buffer, data);
  710. }
  711. } else {
  712. //Append the path
  713. scat(name_buffer, p.name);
  714. //Apply walker function
  715. walker(&p, name_buffer, data);
  716. }
  717. }
  718. //Remove the rest of the path
  719. char *tmp = old_end;
  720. while(*tmp) *tmp++=0;
  721. }
  722. }
  723. void walk_ports2(const rtosc::Ports *base,
  724. char *name_buffer,
  725. size_t buffer_size,
  726. void *data,
  727. rtosc::port_walker_t walker)
  728. {
  729. if(!base)
  730. return;
  731. assert(name_buffer);
  732. //XXX buffer_size is not properly handled yet
  733. if(name_buffer[0] == 0)
  734. name_buffer[0] = '/';
  735. char *old_end = name_buffer;
  736. while(*old_end) ++old_end;
  737. for(const rtosc::Port &p: *base) {
  738. if(strchr(p.name, '/')) {//it is another tree
  739. if(strchr(p.name,'#')) {
  740. const char *name = p.name;
  741. char *pos = old_end;
  742. while(*name != '#') *pos++ = *name++;
  743. const unsigned max = atoi(name+1);
  744. //for(unsigned i=0; i<max; ++i)
  745. {
  746. sprintf(pos,"[0,%d]",max-1);
  747. //Ensure the result is a path
  748. if(strrchr(name_buffer, '/')[1] != '/')
  749. strcat(name_buffer, "/");
  750. //Recurse
  751. walk_ports2(p.ports, name_buffer, buffer_size,
  752. data, walker);
  753. }
  754. } else {
  755. //Append the path
  756. scat(name_buffer, p.name);
  757. //Recurse
  758. walk_ports2(p.ports, name_buffer, buffer_size,
  759. data, walker);
  760. }
  761. } else {
  762. if(strchr(p.name,'#')) {
  763. const char *name = p.name;
  764. char *pos = old_end;
  765. while(*name != '#') *pos++ = *name++;
  766. const unsigned max = atoi(name+1);
  767. //for(unsigned i=0; i<max; ++i)
  768. {
  769. sprintf(pos,"[0,%d]",max-1);
  770. //Apply walker function
  771. walker(&p, name_buffer, data);
  772. }
  773. } else {
  774. //Append the path
  775. scat(name_buffer, p.name);
  776. //Apply walker function
  777. walker(&p, name_buffer, data);
  778. }
  779. }
  780. //Remove the rest of the path
  781. char *tmp = old_end;
  782. while(*tmp) *tmp++=0;
  783. }
  784. }
  785. static void units(std::ostream &o, const char *u)
  786. {
  787. if(!u)
  788. return;
  789. o << " units=\"" << u << "\"";
  790. }
  791. using std::ostream;
  792. using std::string;
  793. static int enum_min(Port::MetaContainer meta)
  794. {
  795. int min = 0;
  796. for(auto m:meta)
  797. if(strstr(m.title, "map "))
  798. min = atoi(m.title+4);
  799. for(auto m:meta)
  800. if(strstr(m.title, "map "))
  801. min = min>atoi(m.title+4) ? atoi(m.title+4) : min;
  802. return min;
  803. }
  804. static int enum_max(Port::MetaContainer meta)
  805. {
  806. int max = 0;
  807. for(auto m:meta)
  808. if(strstr(m.title, "map "))
  809. max = atoi(m.title+4);
  810. for(auto m:meta)
  811. if(strstr(m.title, "map "))
  812. max = max<atoi(m.title+4) ? atoi(m.title+4) : max;
  813. return max;
  814. }
  815. static ostream &add_options(ostream &o, Port::MetaContainer meta)
  816. {
  817. string sym_names = "xyzabcdefghijklmnopqrstuvw";
  818. int sym_idx = 0;
  819. bool has_options = false;
  820. for(auto m:meta)
  821. if(strstr(m.title, "map "))
  822. has_options = true;
  823. for(auto m:meta)
  824. if(strcmp(m.title, "documentation") &&
  825. strcmp(m.title, "parameter") &&
  826. strcmp(m.title, "max") &&
  827. strcmp(m.title, "min"))
  828. printf("m.title = <%s>\n", m.title);
  829. if(!has_options)
  830. return o;
  831. o << " <hints>\n";
  832. for(auto m:meta) {
  833. if(strstr(m.title, "map ")) {
  834. o << " <point symbol=\"" << sym_names[sym_idx++] << "\" value=\"";
  835. o << m.title+4 << "\">" << m.value << "</point>\n";
  836. }
  837. }
  838. o << " </hints>\n";
  839. return o;
  840. }
  841. static ostream &dump_t_f_port(ostream &o, string name, string doc)
  842. {
  843. o << " <message_in pattern=\"" << name << "\" typetag=\"T\">\n";
  844. o << " <desc>Enable " << doc << "</desc>\n";
  845. o << " <param_T symbol=\"x\"/>\n";
  846. o << " </message_in>\n";
  847. o << " <message_in pattern=\"" << name << "\" typetag=\"F\">\n";
  848. o << " <desc>Disable " << doc << "</desc>\n";
  849. o << " <param_F symbol=\"x\"/>\n";
  850. o << " </message_in>\n";
  851. o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
  852. o << " <desc>Get state of " << doc << "</desc>\n";
  853. o << " </message_in>\n";
  854. o << " <message_out pattern=\"" << name << "\" typetag=\"T\">\n";
  855. o << " <desc>Value of " << doc << "</desc>\n";
  856. o << " <param_T symbol=\"x\"/>";
  857. o << " </message_out>\n";
  858. o << " <message_out pattern=\"" << name << "\" typetag=\"F\">\n";
  859. o << " <desc>Value of " << doc << "</desc>\n";
  860. o << " <param_F symbol=\"x\"/>";
  861. o << " </message_out>\n";
  862. return o;
  863. }
  864. static ostream &dump_any_port(ostream &o, string name, string doc)
  865. {
  866. o << " <message_in pattern=\"" << name << "\" typetag=\"*\">\n";
  867. o << " <desc>" << doc << "</desc>\n";
  868. o << " </message_in>\n";
  869. return o;
  870. }
  871. static ostream &dump_generic_port(ostream &o, string name, string doc, string type)
  872. {
  873. const char *t = type.c_str();
  874. string arg_names = "xyzabcdefghijklmnopqrstuvw";
  875. //start out with argument separator
  876. if(*t++ != ':')
  877. return o;
  878. //now real arguments (assume [] don't exist)
  879. string args;
  880. while(*t && *t != ':')
  881. args += *t++;
  882. o << " <message_in pattern=\"" << name << "\" typetag=\"" << args << "\">\n";
  883. o << " <desc>" << doc << "</desc>\n";
  884. assert(args.length()<arg_names.length());
  885. for(unsigned i=0; i<args.length(); ++i)
  886. o << " <param_" << args[i] << " symbol=\"" << arg_names[i] << "\"/>\n";
  887. o << " </message_in>\n";
  888. if(*t == ':')
  889. return dump_generic_port(o, name, doc, t);
  890. else
  891. return o;
  892. }
  893. void dump_ports_cb(const rtosc::Port *p, const char *name, void *v)
  894. {
  895. std::ostream &o = *(std::ostream*)v;
  896. auto meta = p->meta();
  897. const char *args = strchr(p->name, ':');
  898. auto mparameter = meta.find("parameter");
  899. auto mdoc = meta.find("documentation");
  900. string doc;
  901. if(mdoc != p->meta().end())
  902. doc = mdoc.value;
  903. if(meta.find("internal") != meta.end()) {
  904. doc += "[INTERNAL]";
  905. }
  906. if(mparameter != p->meta().end()) {
  907. char type = 0;
  908. if(args) {
  909. if(strchr(args, 'f'))
  910. type = 'f';
  911. else if(strchr(args, 'i'))
  912. type = 'i';
  913. else if(strchr(args, 'c'))
  914. type = 'c';
  915. else if(strchr(args, 'T'))
  916. type = 't';
  917. else if(strchr(args, 's'))
  918. type = 's';
  919. }
  920. if(!type) {
  921. fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", name);
  922. fprintf(stderr, " args = <%s>\n", args);
  923. return;
  924. }
  925. if(type == 't') {
  926. dump_t_f_port(o, name, doc);
  927. return;
  928. }
  929. o << " <message_in pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
  930. o << " <desc>Set Value of " << doc << "</desc>\n";
  931. if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') {
  932. o << " <param_" << type << " symbol=\"x\"";
  933. units(o, meta["unit"]);
  934. o << ">\n";
  935. o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
  936. o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
  937. o << " </param_" << type << ">";
  938. } else if(meta.find("enumerated") != meta.end()) {
  939. o << " <param_" << type << " symbol=\"x\">\n";
  940. o << " <range_min_max min=\"" << enum_min(meta) << "\" max=\"";
  941. o << enum_max(meta) << "\">\n";
  942. add_options(o, meta);
  943. o << " </range_min_max>\n";
  944. o << " </param_" << type << ">\n";
  945. } else {
  946. o << " <param_" << type << " symbol=\"x\"";
  947. units(o, meta["unit"]);
  948. o << "/>\n";
  949. }
  950. o << " </message_in>\n";
  951. o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n";
  952. o << " <desc>Get Value of " << doc << "</desc>\n";
  953. o << " </message_in>\n";
  954. o << " <message_out pattern=\"" << name << "\" typetag=\"" << type << "\">\n";
  955. o << " <desc>Value of " << doc << "</desc>\n";
  956. if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') {
  957. o << " <param_" << type << " symbol=\"x\"";
  958. units(o, meta["unit"]);
  959. o << ">\n";
  960. o << " <range_min_max " << (type == 'f' ? "lmin=\"[\" lmax=\"]\"" : "");
  961. o << " min=\"" << meta["min"] << "\" max=\"" << meta["max"] << "\"/>\n";
  962. o << " </param_" << type << ">\n";
  963. } else if(meta.find("enumerated") != meta.end()) {
  964. o << " <param_" << type << " symbol=\"x\">\n";
  965. o << " <range_min_max min=\"" << enum_min(meta) << "\" max=\"";
  966. o << enum_max(meta) << "\">\n";
  967. add_options(o, meta);
  968. o << " </range_min_max>\n";
  969. o << " </param_" << type << ">\n";
  970. } else {
  971. o << " <param_" << type << " symbol=\"x\"";
  972. units(o, meta["unit"]);
  973. o << "/>\n";
  974. }
  975. o << " </message_out>\n";
  976. } else if(mdoc != meta.end() && (!args || args == std::string(""))) {
  977. dump_any_port(o, name, doc);
  978. } else if(mdoc != meta.end() && args) {
  979. dump_generic_port(o, name, doc, args);
  980. } else if(mdoc != meta.end()) {
  981. fprintf(stderr, "Skipping \"%s\"\n", name);
  982. if(args) {
  983. fprintf(stderr, " type = %s\n", args);
  984. }
  985. } else
  986. fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name);
  987. }
  988. std::ostream &rtosc::operator<<(std::ostream &o, rtosc::OscDocFormatter &formatter)
  989. {
  990. o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  991. o << "<osc_unit format_version=\"1.0\">\n";
  992. o << " <meta>\n";
  993. o << " <name>" << formatter.prog_name << "</name>\n";
  994. o << " <uri>" << formatter.uri << "</uri>\n";
  995. o << " <doc_origin>" << formatter.doc_origin << "</doc_origin>\n";
  996. o << " <author><firstname>" << formatter.author_first;
  997. o << "</firstname><lastname>" << formatter.author_last << "</lastname></author>\n";
  998. o << " </meta>\n";
  999. char buffer[1024];
  1000. memset(buffer, 0, sizeof(buffer));
  1001. walk_ports2(formatter.p, buffer, 1024, &o, dump_ports_cb);
  1002. o << "</osc_unit>\n";
  1003. return o;
  1004. }