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.

1028 lines
28KB

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