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.

XMLwrapper.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. XMLwrapper.cpp - XML wrapper
  4. Copyright (C) 2003-2005 Nasca Octavian Paul
  5. Copyright (C) 2009-2009 Mark McCurry
  6. Author: Nasca Octavian Paul
  7. Mark McCurry
  8. This program is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU General Public License
  10. as published by the Free Software Foundation; either version 2
  11. of the License, or (at your option) any later version.
  12. */
  13. #include "XMLwrapper.h"
  14. #include <cstring>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <cstdarg>
  18. #include <zlib.h>
  19. #include <iostream>
  20. #include <sstream>
  21. #include "globals.h"
  22. #include "Util.h"
  23. using namespace std;
  24. namespace zyncarla {
  25. int xml_k = 0;
  26. bool verbose = false;
  27. const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where)
  28. {
  29. const char *name = node->value.element.name;
  30. if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml")))
  31. return NULL;
  32. if((where == MXML_WS_BEFORE_CLOSE) && (!strcmp(name, "string")))
  33. return NULL;
  34. if((where == MXML_WS_BEFORE_OPEN) || (where == MXML_WS_BEFORE_CLOSE))
  35. /* const char *tmp=node->value.element.name;
  36. if (tmp!=NULL) {
  37. if ((strstr(tmp,"par")!=tmp)&&(strstr(tmp,"string")!=tmp)) {
  38. printf("%s ",tmp);
  39. if (where==MXML_WS_BEFORE_OPEN) xml_k++;
  40. if (where==MXML_WS_BEFORE_CLOSE) xml_k--;
  41. if (xml_k>=STACKSIZE) xml_k=STACKSIZE-1;
  42. if (xml_k<0) xml_k=0;
  43. printf("%d\n",xml_k);
  44. printf("\n");
  45. };
  46. };
  47. int i=0;
  48. for (i=1;i<xml_k;i++) tabs[i]='\t';
  49. tabs[0]='\n';tabs[i+1]='\0';
  50. if (where==MXML_WS_BEFORE_OPEN) return(tabs);
  51. else return("\n");
  52. */
  53. return "\n";
  54. ;
  55. return 0;
  56. }
  57. //temporary const overload of mxmlFindElement
  58. const mxml_node_t *mxmlFindElement(const mxml_node_t *node,
  59. const mxml_node_t *top,
  60. const char *name,
  61. const char *attr,
  62. const char *value,
  63. int descend)
  64. {
  65. return const_cast<const mxml_node_t *>(mxmlFindElement(
  66. const_cast<mxml_node_t *>(node),
  67. const_cast<mxml_node_t *>(top),
  68. name, attr, value, descend));
  69. }
  70. //temporary const overload of mxmlElementGetAttr
  71. const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name)
  72. {
  73. return mxmlElementGetAttr(const_cast<mxml_node_t *>(node), name);
  74. }
  75. XMLwrapper::XMLwrapper()
  76. {
  77. minimal = true;
  78. node = tree = mxmlNewElement(MXML_NO_PARENT,
  79. "?xml version=\"1.0f\" encoding=\"UTF-8\"?");
  80. /* for mxml 2.1f (and older)
  81. tree=mxmlNewElement(MXML_NO_PARENT,"?xml");
  82. mxmlElementSetAttr(tree,"version","1.0f");
  83. mxmlElementSetAttr(tree,"encoding","UTF-8");
  84. */
  85. mxml_node_t *doctype = mxmlNewElement(tree, "!DOCTYPE");
  86. mxmlElementSetAttr(doctype, "ZynAddSubFX-data", NULL);
  87. node = root = addparams("ZynAddSubFX-data", 4,
  88. "version-major", stringFrom<int>(
  89. version.get_major()).c_str(),
  90. "version-minor", stringFrom<int>(
  91. version.get_minor()).c_str(),
  92. "version-revision",
  93. stringFrom<int>(version.get_revision()).c_str(),
  94. "ZynAddSubFX-author", "Nasca Octavian Paul");
  95. //make the empty branch that will contain the information parameters
  96. info = addparams("INFORMATION", 0);
  97. //save zynaddsubfx specifications
  98. beginbranch("BASE_PARAMETERS");
  99. addpar("max_midi_parts", NUM_MIDI_PARTS);
  100. addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS);
  101. addpar("max_system_effects", NUM_SYS_EFX);
  102. addpar("max_insertion_effects", NUM_INS_EFX);
  103. addpar("max_instrument_effects", NUM_PART_EFX);
  104. addpar("max_addsynth_voices", NUM_VOICES);
  105. endbranch();
  106. }
  107. void
  108. XMLwrapper::cleanup(void)
  109. {
  110. if(tree)
  111. mxmlDelete(tree);
  112. /* make sure freed memory is not referenced */
  113. tree = 0;
  114. node = 0;
  115. root = 0;
  116. }
  117. XMLwrapper::~XMLwrapper()
  118. {
  119. cleanup();
  120. }
  121. void XMLwrapper::setPadSynth(bool enabled)
  122. {
  123. /**@bug this might create multiple nodes when only one is needed*/
  124. mxml_node_t *oldnode = node;
  125. node = info;
  126. //Info storing
  127. addparbool("PADsynth_used", enabled);
  128. node = oldnode;
  129. }
  130. bool XMLwrapper::hasPadSynth() const
  131. {
  132. /**Right now this has a copied implementation of setparbool, so this should
  133. * be reworked as XMLwrapper evolves*/
  134. mxml_node_t *tmp = mxmlFindElement(tree,
  135. tree,
  136. "INFORMATION",
  137. NULL,
  138. NULL,
  139. MXML_DESCEND);
  140. mxml_node_t *parameter = mxmlFindElement(tmp,
  141. tmp,
  142. "par_bool",
  143. "name",
  144. "PADsynth_used",
  145. MXML_DESCEND_FIRST);
  146. if(parameter == NULL) //no information availiable
  147. return false;
  148. const char *strval = mxmlElementGetAttr(parameter, "value");
  149. if(strval == NULL) //no information available
  150. return false;
  151. if((strval[0] == 'Y') || (strval[0] == 'y'))
  152. return true;
  153. else
  154. return false;
  155. }
  156. /* SAVE XML members */
  157. int XMLwrapper::saveXMLfile(const string &filename, int compression) const
  158. {
  159. char *xmldata = getXMLdata();
  160. if(xmldata == NULL)
  161. return -2;
  162. int result = dosavefile(filename.c_str(), compression, xmldata);
  163. free(xmldata);
  164. return result;
  165. }
  166. char *XMLwrapper::getXMLdata() const
  167. {
  168. xml_k = 0;
  169. char *xmldata = mxmlSaveAllocString(tree, XMLwrapper_whitespace_callback);
  170. return xmldata;
  171. }
  172. int XMLwrapper::dosavefile(const char *filename,
  173. int compression,
  174. const char *xmldata) const
  175. {
  176. if(compression == 0) {
  177. FILE *file;
  178. file = fopen(filename, "w");
  179. if(file == NULL)
  180. return -1;
  181. fputs(xmldata, file);
  182. fclose(file);
  183. }
  184. else {
  185. if(compression > 9)
  186. compression = 9;
  187. if(compression < 1)
  188. compression = 1;
  189. char options[10];
  190. snprintf(options, 10, "wb%d", compression);
  191. gzFile gzfile;
  192. gzfile = gzopen(filename, options);
  193. if(gzfile == NULL)
  194. return -1;
  195. gzputs(gzfile, xmldata);
  196. gzclose(gzfile);
  197. }
  198. return 0;
  199. }
  200. void XMLwrapper::addpar(const string &name, int val)
  201. {
  202. addparams("par", 2, "name", name.c_str(), "value", stringFrom<int>(
  203. val).c_str());
  204. }
  205. void XMLwrapper::addparreal(const string &name, float val)
  206. {
  207. union { float in; uint32_t out; } convert;
  208. char buf[11];
  209. convert.in = val;
  210. sprintf(buf, "0x%.8X", convert.out);
  211. addparams("par_real", 3, "name", name.c_str(), "value",
  212. stringFrom<float>(val).c_str(), "exact_value", buf);
  213. }
  214. void XMLwrapper::addparbool(const string &name, int val)
  215. {
  216. if(val != 0)
  217. addparams("par_bool", 2, "name", name.c_str(), "value", "yes");
  218. else
  219. addparams("par_bool", 2, "name", name.c_str(), "value", "no");
  220. }
  221. void XMLwrapper::addparstr(const string &name, const string &val)
  222. {
  223. mxml_node_t *element = mxmlNewElement(node, "string");
  224. mxmlElementSetAttr(element, "name", name.c_str());
  225. mxmlNewText(element, 0, val.c_str());
  226. }
  227. void XMLwrapper::beginbranch(const string &name)
  228. {
  229. if(verbose)
  230. cout << "beginbranch()" << name << endl;
  231. node = addparams(name.c_str(), 0);
  232. }
  233. void XMLwrapper::beginbranch(const string &name, int id)
  234. {
  235. if(verbose)
  236. cout << "beginbranch(" << id << ")" << name << endl;
  237. node = addparams(name.c_str(), 1, "id", stringFrom<int>(id).c_str());
  238. }
  239. void XMLwrapper::endbranch()
  240. {
  241. if(verbose)
  242. cout << "endbranch()" << node << "-" << node->value.element.name
  243. << " To "
  244. << node->parent << "-" << node->parent->value.element.name << endl;
  245. node = node->parent;
  246. }
  247. //workaround for memory leak
  248. const char *trimLeadingWhite(const char *c)
  249. {
  250. while(isspace(*c))
  251. ++c;
  252. return c;
  253. }
  254. /* LOAD XML members */
  255. int XMLwrapper::loadXMLfile(const string &filename)
  256. {
  257. cleanup();
  258. const char *xmldata = doloadfile(filename);
  259. if(xmldata == NULL)
  260. return -1; //the file could not be loaded or uncompressed
  261. root = tree = mxmlLoadString(NULL, trimLeadingWhite(
  262. xmldata), MXML_OPAQUE_CALLBACK);
  263. delete[] xmldata;
  264. if(tree == NULL)
  265. return -2; //this is not XML
  266. node = root = mxmlFindElement(tree,
  267. tree,
  268. "ZynAddSubFX-data",
  269. NULL,
  270. NULL,
  271. MXML_DESCEND);
  272. if(root == NULL)
  273. return -3; //the XML doesnt embbed zynaddsubfx data
  274. //fetch version information
  275. _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
  276. _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
  277. _fileversion.set_revision(
  278. stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
  279. if(verbose)
  280. cout << "loadXMLfile() version: " << _fileversion << endl;
  281. return 0;
  282. }
  283. char *XMLwrapper::doloadfile(const string &filename) const
  284. {
  285. char *xmldata = NULL;
  286. gzFile gzfile = gzopen(filename.c_str(), "rb");
  287. if(gzfile != NULL) { //The possibly compressed file opened
  288. stringstream strBuf; //reading stream
  289. const int bufSize = 500; //fetch size
  290. char fetchBuf[bufSize + 1]; //fetch buffer
  291. int read = 0; //chars read in last fetch
  292. fetchBuf[bufSize] = 0; //force null termination
  293. while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize)))
  294. strBuf << fetchBuf;
  295. fetchBuf[read] = 0; //Truncate last partial read
  296. strBuf << fetchBuf;
  297. gzclose(gzfile);
  298. //Place data in output format
  299. string tmp = strBuf.str();
  300. xmldata = new char[tmp.size() + 1];
  301. strncpy(xmldata, tmp.c_str(), tmp.size() + 1);
  302. }
  303. return xmldata;
  304. }
  305. bool XMLwrapper::putXMLdata(const char *xmldata)
  306. {
  307. cleanup();
  308. if(xmldata == NULL)
  309. return false;
  310. root = tree = mxmlLoadString(NULL, trimLeadingWhite(
  311. xmldata), MXML_OPAQUE_CALLBACK);
  312. if(tree == NULL)
  313. return false;
  314. node = root = mxmlFindElement(tree,
  315. tree,
  316. "ZynAddSubFX-data",
  317. NULL,
  318. NULL,
  319. MXML_DESCEND);
  320. if(root == NULL)
  321. return false;
  322. //fetch version information
  323. _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
  324. _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
  325. _fileversion.set_revision(
  326. stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
  327. return true;
  328. }
  329. int XMLwrapper::enterbranch(const string &name)
  330. {
  331. if(verbose)
  332. cout << "enterbranch() " << name << endl;
  333. mxml_node_t *tmp = mxmlFindElement(node, node,
  334. name.c_str(), NULL, NULL,
  335. MXML_DESCEND_FIRST);
  336. if(tmp == NULL)
  337. return 0;
  338. node = tmp;
  339. return 1;
  340. }
  341. int XMLwrapper::enterbranch(const string &name, int id)
  342. {
  343. if(verbose)
  344. cout << "enterbranch(" << id << ") " << name << endl;
  345. mxml_node_t *tmp = mxmlFindElement(node, node,
  346. name.c_str(), "id", stringFrom<int>(
  347. id).c_str(), MXML_DESCEND_FIRST);
  348. if(tmp == NULL)
  349. return 0;
  350. node = tmp;
  351. return 1;
  352. }
  353. void XMLwrapper::exitbranch()
  354. {
  355. if(verbose)
  356. cout << "exitbranch()" << node << "-" << node->value.element.name
  357. << " To "
  358. << node->parent << "-" << node->parent->value.element.name << endl;
  359. node = node->parent;
  360. }
  361. int XMLwrapper::getbranchid(int min, int max) const
  362. {
  363. int id = stringTo<int>(mxmlElementGetAttr(node, "id"));
  364. if((min == 0) && (max == 0))
  365. return id;
  366. if(id < min)
  367. id = min;
  368. else
  369. if(id > max)
  370. id = max;
  371. return id;
  372. }
  373. int XMLwrapper::getpar(const string &name, int defaultpar, int min,
  374. int max) const
  375. {
  376. const mxml_node_t *tmp = mxmlFindElement(node,
  377. node,
  378. "par",
  379. "name",
  380. name.c_str(),
  381. MXML_DESCEND_FIRST);
  382. if(tmp == NULL)
  383. return defaultpar;
  384. const char *strval = mxmlElementGetAttr(tmp, "value");
  385. if(strval == NULL)
  386. return defaultpar;
  387. int val = stringTo<int>(strval);
  388. if(val < min)
  389. val = min;
  390. else
  391. if(val > max)
  392. val = max;
  393. return val;
  394. }
  395. int XMLwrapper::getpar127(const string &name, int defaultpar) const
  396. {
  397. return getpar(name, defaultpar, 0, 127);
  398. }
  399. int XMLwrapper::getparbool(const string &name, int defaultpar) const
  400. {
  401. const mxml_node_t *tmp = mxmlFindElement(node,
  402. node,
  403. "par_bool",
  404. "name",
  405. name.c_str(),
  406. MXML_DESCEND_FIRST);
  407. if(tmp == NULL)
  408. return defaultpar;
  409. const char *strval = mxmlElementGetAttr(tmp, "value");
  410. if(strval == NULL)
  411. return defaultpar;
  412. if((strval[0] == 'Y') || (strval[0] == 'y'))
  413. return 1;
  414. else
  415. return 0;
  416. }
  417. void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const
  418. {
  419. ZERO(par, maxstrlen);
  420. const mxml_node_t *tmp = mxmlFindElement(node,
  421. node,
  422. "string",
  423. "name",
  424. name.c_str(),
  425. MXML_DESCEND_FIRST);
  426. if(tmp == NULL)
  427. return;
  428. if(tmp->child == NULL)
  429. return;
  430. if(tmp->child->type == MXML_OPAQUE) {
  431. snprintf(par, maxstrlen, "%s", tmp->child->value.element.name);
  432. return;
  433. }
  434. if((tmp->child->type == MXML_TEXT)
  435. && (tmp->child->value.text.string != NULL)) {
  436. snprintf(par, maxstrlen, "%s", tmp->child->value.text.string);
  437. return;
  438. }
  439. }
  440. string XMLwrapper::getparstr(const string &name,
  441. const std::string &defaultpar) const
  442. {
  443. const mxml_node_t *tmp = mxmlFindElement(node,
  444. node,
  445. "string",
  446. "name",
  447. name.c_str(),
  448. MXML_DESCEND_FIRST);
  449. if((tmp == NULL) || (tmp->child == NULL))
  450. return defaultpar;
  451. if((tmp->child->type == MXML_OPAQUE)
  452. && (tmp->child->value.element.name != NULL))
  453. return tmp->child->value.element.name;
  454. if((tmp->child->type == MXML_TEXT)
  455. && (tmp->child->value.text.string != NULL))
  456. return tmp->child->value.text.string;
  457. return defaultpar;
  458. }
  459. float XMLwrapper::getparreal(const char *name, float defaultpar) const
  460. {
  461. const mxml_node_t *tmp = mxmlFindElement(node,
  462. node,
  463. "par_real",
  464. "name",
  465. name,
  466. MXML_DESCEND_FIRST);
  467. if(tmp == NULL)
  468. return defaultpar;
  469. const char *strval = mxmlElementGetAttr(tmp, "exact_value");
  470. if (strval != NULL) {
  471. union { float out; uint32_t in; } convert;
  472. sscanf(strval+2, "%x", &convert.in);
  473. return convert.out;
  474. }
  475. strval = mxmlElementGetAttr(tmp, "value");
  476. if(strval == NULL)
  477. return defaultpar;
  478. return stringTo<float>(strval);
  479. }
  480. float XMLwrapper::getparreal(const char *name,
  481. float defaultpar,
  482. float min,
  483. float max) const
  484. {
  485. float result = getparreal(name, defaultpar);
  486. if(result < min)
  487. result = min;
  488. else
  489. if(result > max)
  490. result = max;
  491. return result;
  492. }
  493. /** Private members **/
  494. mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
  495. ...) const
  496. {
  497. /**@todo make this function send out a good error message if something goes
  498. * wrong**/
  499. mxml_node_t *element = mxmlNewElement(node, name);
  500. if(params) {
  501. va_list variableList;
  502. va_start(variableList, params);
  503. const char *ParamName;
  504. const char *ParamValue;
  505. while(params--) {
  506. ParamName = va_arg(variableList, const char *);
  507. ParamValue = va_arg(variableList, const char *);
  508. if(verbose)
  509. cout << "addparams()[" << params << "]=" << name << " "
  510. << ParamName << "=\"" << ParamValue << "\"" << endl;
  511. mxmlElementSetAttr(element, ParamName, ParamValue);
  512. }
  513. va_end(variableList);
  514. }
  515. return element;
  516. }
  517. XmlNode::XmlNode(std::string name_)
  518. :name(name_)
  519. {}
  520. std::string &XmlNode::operator[](std::string name)
  521. {
  522. //fetch an existing one
  523. for(auto &a:attrs)
  524. if(a.name == name)
  525. return a.value;
  526. //create a new one
  527. attrs.push_back({name, ""});
  528. return attrs[attrs.size()-1].value;
  529. }
  530. bool XmlNode::has(std::string name_)
  531. {
  532. //fetch an existing one
  533. for(auto &a:attrs)
  534. if(a.name == name_)
  535. return true;
  536. return false;
  537. }
  538. void XMLwrapper::add(const XmlNode &node_)
  539. {
  540. mxml_node_t *element = mxmlNewElement(node, node_.name.c_str());
  541. for(auto attr:node_.attrs)
  542. mxmlElementSetAttr(element, attr.name.c_str(),
  543. attr.value.c_str());
  544. }
  545. std::vector<XmlNode> XMLwrapper::getBranch(void) const
  546. {
  547. std::vector<XmlNode> res;
  548. mxml_node_t *current = node->child;
  549. while(current) {
  550. if(current->type == MXML_ELEMENT) {
  551. auto elm = current->value.element;
  552. XmlNode n(elm.name);
  553. for(int i=0; i<elm.num_attrs; ++i) {
  554. auto &attr = elm.attrs[i];
  555. n[attr.name] = attr.value;
  556. }
  557. res.push_back(n);
  558. }
  559. current = mxmlWalkNext(current, node, MXML_NO_DESCEND);
  560. }
  561. return res;
  562. }
  563. }