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

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