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.

680 lines
19KB

  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. int xml_k = 0;
  25. bool verbose = false;
  26. const char *XMLwrapper_whitespace_callback(mxml_node_t *node, int where)
  27. {
  28. const char *name = node->value.element.name;
  29. if((where == MXML_WS_BEFORE_OPEN) && (!strcmp(name, "?xml")))
  30. return NULL;
  31. if((where == MXML_WS_BEFORE_CLOSE) && (!strcmp(name, "string")))
  32. return NULL;
  33. if((where == MXML_WS_BEFORE_OPEN) || (where == MXML_WS_BEFORE_CLOSE))
  34. /* const char *tmp=node->value.element.name;
  35. if (tmp!=NULL) {
  36. if ((strstr(tmp,"par")!=tmp)&&(strstr(tmp,"string")!=tmp)) {
  37. printf("%s ",tmp);
  38. if (where==MXML_WS_BEFORE_OPEN) xml_k++;
  39. if (where==MXML_WS_BEFORE_CLOSE) xml_k--;
  40. if (xml_k>=STACKSIZE) xml_k=STACKSIZE-1;
  41. if (xml_k<0) xml_k=0;
  42. printf("%d\n",xml_k);
  43. printf("\n");
  44. };
  45. };
  46. int i=0;
  47. for (i=1;i<xml_k;i++) tabs[i]='\t';
  48. tabs[0]='\n';tabs[i+1]='\0';
  49. if (where==MXML_WS_BEFORE_OPEN) return(tabs);
  50. else return("\n");
  51. */
  52. return "\n";
  53. ;
  54. return 0;
  55. }
  56. //temporary const overload of mxmlFindElement
  57. const mxml_node_t *mxmlFindElement(const mxml_node_t *node,
  58. const mxml_node_t *top,
  59. const char *name,
  60. const char *attr,
  61. const char *value,
  62. int descend)
  63. {
  64. return const_cast<const mxml_node_t *>(mxmlFindElement(
  65. const_cast<mxml_node_t *>(node),
  66. const_cast<mxml_node_t *>(top),
  67. name, attr, value, descend));
  68. }
  69. //temporary const overload of mxmlElementGetAttr
  70. const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name)
  71. {
  72. return mxmlElementGetAttr(const_cast<mxml_node_t *>(node), name);
  73. }
  74. XMLwrapper::XMLwrapper()
  75. {
  76. minimal = true;
  77. node = tree = mxmlNewElement(MXML_NO_PARENT,
  78. "?xml version=\"1.0f\" encoding=\"UTF-8\"?");
  79. /* for mxml 2.1f (and older)
  80. tree=mxmlNewElement(MXML_NO_PARENT,"?xml");
  81. mxmlElementSetAttr(tree,"version","1.0f");
  82. mxmlElementSetAttr(tree,"encoding","UTF-8");
  83. */
  84. mxml_node_t *doctype = mxmlNewElement(tree, "!DOCTYPE");
  85. mxmlElementSetAttr(doctype, "ZynAddSubFX-data", NULL);
  86. node = root = addparams("ZynAddSubFX-data", 4,
  87. "version-major", stringFrom<int>(
  88. version.get_major()).c_str(),
  89. "version-minor", stringFrom<int>(
  90. version.get_minor()).c_str(),
  91. "version-revision",
  92. stringFrom<int>(version.get_revision()).c_str(),
  93. "ZynAddSubFX-author", "Nasca Octavian Paul");
  94. //make the empty branch that will contain the information parameters
  95. info = addparams("INFORMATION", 0);
  96. //save zynaddsubfx specifications
  97. beginbranch("BASE_PARAMETERS");
  98. addpar("max_midi_parts", NUM_MIDI_PARTS);
  99. addpar("max_kit_items_per_instrument", NUM_KIT_ITEMS);
  100. addpar("max_system_effects", NUM_SYS_EFX);
  101. addpar("max_insertion_effects", NUM_INS_EFX);
  102. addpar("max_instrument_effects", NUM_PART_EFX);
  103. addpar("max_addsynth_voices", NUM_VOICES);
  104. endbranch();
  105. }
  106. void
  107. XMLwrapper::cleanup(void)
  108. {
  109. if(tree)
  110. mxmlDelete(tree);
  111. /* make sure freed memory is not referenced */
  112. tree = 0;
  113. node = 0;
  114. root = 0;
  115. }
  116. XMLwrapper::~XMLwrapper()
  117. {
  118. cleanup();
  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. cleanup();
  257. const char *xmldata = doloadfile(filename);
  258. if(xmldata == NULL)
  259. return -1; //the file could not be loaded or uncompressed
  260. root = tree = mxmlLoadString(NULL, trimLeadingWhite(
  261. xmldata), MXML_OPAQUE_CALLBACK);
  262. delete[] xmldata;
  263. if(tree == NULL)
  264. return -2; //this is not XML
  265. node = root = mxmlFindElement(tree,
  266. tree,
  267. "ZynAddSubFX-data",
  268. NULL,
  269. NULL,
  270. MXML_DESCEND);
  271. if(root == NULL)
  272. return -3; //the XML doesnt embbed zynaddsubfx data
  273. //fetch version information
  274. _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
  275. _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
  276. _fileversion.set_revision(
  277. stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
  278. if(verbose)
  279. cout << "loadXMLfile() version: " << _fileversion << endl;
  280. return 0;
  281. }
  282. char *XMLwrapper::doloadfile(const string &filename) const
  283. {
  284. char *xmldata = NULL;
  285. gzFile gzfile = gzopen(filename.c_str(), "rb");
  286. if(gzfile != NULL) { //The possibly compressed file opened
  287. stringstream strBuf; //reading stream
  288. const int bufSize = 500; //fetch size
  289. char fetchBuf[bufSize + 1]; //fetch buffer
  290. int read = 0; //chars read in last fetch
  291. fetchBuf[bufSize] = 0; //force null termination
  292. while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize)))
  293. strBuf << fetchBuf;
  294. fetchBuf[read] = 0; //Truncate last partial read
  295. strBuf << fetchBuf;
  296. gzclose(gzfile);
  297. //Place data in output format
  298. string tmp = strBuf.str();
  299. xmldata = new char[tmp.size() + 1];
  300. strncpy(xmldata, tmp.c_str(), tmp.size() + 1);
  301. }
  302. return xmldata;
  303. }
  304. bool XMLwrapper::putXMLdata(const char *xmldata)
  305. {
  306. cleanup();
  307. if(xmldata == NULL)
  308. return false;
  309. root = tree = mxmlLoadString(NULL, trimLeadingWhite(
  310. xmldata), MXML_OPAQUE_CALLBACK);
  311. if(tree == NULL)
  312. return false;
  313. node = root = mxmlFindElement(tree,
  314. tree,
  315. "ZynAddSubFX-data",
  316. NULL,
  317. NULL,
  318. MXML_DESCEND);
  319. if(root == NULL)
  320. return false;
  321. return true;
  322. }
  323. int XMLwrapper::enterbranch(const string &name)
  324. {
  325. if(verbose)
  326. cout << "enterbranch() " << name << endl;
  327. mxml_node_t *tmp = mxmlFindElement(node, node,
  328. name.c_str(), NULL, NULL,
  329. MXML_DESCEND_FIRST);
  330. if(tmp == NULL)
  331. return 0;
  332. node = tmp;
  333. return 1;
  334. }
  335. int XMLwrapper::enterbranch(const string &name, int id)
  336. {
  337. if(verbose)
  338. cout << "enterbranch(" << id << ") " << name << endl;
  339. mxml_node_t *tmp = mxmlFindElement(node, node,
  340. name.c_str(), "id", stringFrom<int>(
  341. id).c_str(), MXML_DESCEND_FIRST);
  342. if(tmp == NULL)
  343. return 0;
  344. node = tmp;
  345. return 1;
  346. }
  347. void XMLwrapper::exitbranch()
  348. {
  349. if(verbose)
  350. cout << "exitbranch()" << node << "-" << node->value.element.name
  351. << " To "
  352. << node->parent << "-" << node->parent->value.element.name << endl;
  353. node = node->parent;
  354. }
  355. int XMLwrapper::getbranchid(int min, int max) const
  356. {
  357. int id = stringTo<int>(mxmlElementGetAttr(node, "id"));
  358. if((min == 0) && (max == 0))
  359. return id;
  360. if(id < min)
  361. id = min;
  362. else
  363. if(id > max)
  364. id = max;
  365. return id;
  366. }
  367. int XMLwrapper::getpar(const string &name, int defaultpar, int min,
  368. int max) const
  369. {
  370. const mxml_node_t *tmp = mxmlFindElement(node,
  371. node,
  372. "par",
  373. "name",
  374. name.c_str(),
  375. MXML_DESCEND_FIRST);
  376. if(tmp == NULL)
  377. return defaultpar;
  378. const char *strval = mxmlElementGetAttr(tmp, "value");
  379. if(strval == NULL)
  380. return defaultpar;
  381. int val = stringTo<int>(strval);
  382. if(val < min)
  383. val = min;
  384. else
  385. if(val > max)
  386. val = max;
  387. return val;
  388. }
  389. int XMLwrapper::getpar127(const string &name, int defaultpar) const
  390. {
  391. return getpar(name, defaultpar, 0, 127);
  392. }
  393. int XMLwrapper::getparbool(const string &name, int defaultpar) const
  394. {
  395. const mxml_node_t *tmp = mxmlFindElement(node,
  396. node,
  397. "par_bool",
  398. "name",
  399. name.c_str(),
  400. MXML_DESCEND_FIRST);
  401. if(tmp == NULL)
  402. return defaultpar;
  403. const char *strval = mxmlElementGetAttr(tmp, "value");
  404. if(strval == NULL)
  405. return defaultpar;
  406. if((strval[0] == 'Y') || (strval[0] == 'y'))
  407. return 1;
  408. else
  409. return 0;
  410. }
  411. void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const
  412. {
  413. ZERO(par, maxstrlen);
  414. const mxml_node_t *tmp = mxmlFindElement(node,
  415. node,
  416. "string",
  417. "name",
  418. name.c_str(),
  419. MXML_DESCEND_FIRST);
  420. if(tmp == NULL)
  421. return;
  422. if(tmp->child == NULL)
  423. return;
  424. if(tmp->child->type == MXML_OPAQUE) {
  425. snprintf(par, maxstrlen, "%s", tmp->child->value.element.name);
  426. return;
  427. }
  428. if((tmp->child->type == MXML_TEXT)
  429. && (tmp->child->value.text.string != NULL)) {
  430. snprintf(par, maxstrlen, "%s", tmp->child->value.text.string);
  431. return;
  432. }
  433. }
  434. string XMLwrapper::getparstr(const string &name,
  435. const std::string &defaultpar) const
  436. {
  437. const mxml_node_t *tmp = mxmlFindElement(node,
  438. node,
  439. "string",
  440. "name",
  441. name.c_str(),
  442. MXML_DESCEND_FIRST);
  443. if((tmp == NULL) || (tmp->child == NULL))
  444. return defaultpar;
  445. if((tmp->child->type == MXML_OPAQUE)
  446. && (tmp->child->value.element.name != NULL))
  447. return tmp->child->value.element.name;
  448. if((tmp->child->type == MXML_TEXT)
  449. && (tmp->child->value.text.string != NULL))
  450. return tmp->child->value.text.string;
  451. return defaultpar;
  452. }
  453. float XMLwrapper::getparreal(const char *name, float defaultpar) const
  454. {
  455. const mxml_node_t *tmp = mxmlFindElement(node,
  456. node,
  457. "par_real",
  458. "name",
  459. name,
  460. MXML_DESCEND_FIRST);
  461. if(tmp == NULL)
  462. return defaultpar;
  463. const char *strval = mxmlElementGetAttr(tmp, "exact_value");
  464. if (strval != NULL) {
  465. union { float out; uint32_t in; } convert;
  466. sscanf(strval+2, "%x", &convert.in);
  467. return convert.out;
  468. }
  469. strval = mxmlElementGetAttr(tmp, "value");
  470. if(strval == NULL)
  471. return defaultpar;
  472. return stringTo<float>(strval);
  473. }
  474. float XMLwrapper::getparreal(const char *name,
  475. float defaultpar,
  476. float min,
  477. float max) const
  478. {
  479. float result = getparreal(name, defaultpar);
  480. if(result < min)
  481. result = min;
  482. else
  483. if(result > max)
  484. result = max;
  485. return result;
  486. }
  487. /** Private members **/
  488. mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
  489. ...) const
  490. {
  491. /**@todo make this function send out a good error message if something goes
  492. * wrong**/
  493. mxml_node_t *element = mxmlNewElement(node, name);
  494. if(params) {
  495. va_list variableList;
  496. va_start(variableList, params);
  497. const char *ParamName;
  498. const char *ParamValue;
  499. while(params--) {
  500. ParamName = va_arg(variableList, const char *);
  501. ParamValue = va_arg(variableList, const char *);
  502. if(verbose)
  503. cout << "addparams()[" << params << "]=" << name << " "
  504. << ParamName << "=\"" << ParamValue << "\"" << endl;
  505. mxmlElementSetAttr(element, ParamName, ParamValue);
  506. }
  507. va_end(variableList);
  508. }
  509. return element;
  510. }
  511. XmlNode::XmlNode(std::string name_)
  512. :name(name_)
  513. {}
  514. std::string &XmlNode::operator[](std::string name)
  515. {
  516. //fetch an existing one
  517. for(auto &a:attrs)
  518. if(a.name == name)
  519. return a.value;
  520. //create a new one
  521. attrs.push_back({name, ""});
  522. return attrs[attrs.size()-1].value;
  523. }
  524. bool XmlNode::has(std::string name_)
  525. {
  526. //fetch an existing one
  527. for(auto &a:attrs)
  528. if(a.name == name_)
  529. return true;
  530. return false;
  531. }
  532. void XMLwrapper::add(const XmlNode &node_)
  533. {
  534. mxml_node_t *element = mxmlNewElement(node, node_.name.c_str());
  535. for(auto attr:node_.attrs)
  536. mxmlElementSetAttr(element, attr.name.c_str(),
  537. attr.value.c_str());
  538. }
  539. std::vector<XmlNode> XMLwrapper::getBranch(void) const
  540. {
  541. std::vector<XmlNode> res;
  542. mxml_node_t *current = node->child;
  543. while(current) {
  544. if(current->type == MXML_ELEMENT) {
  545. auto elm = current->value.element;
  546. XmlNode n(elm.name);
  547. for(int i=0; i<elm.num_attrs; ++i) {
  548. auto &attr = elm.attrs[i];
  549. n[attr.name] = attr.value;
  550. }
  551. res.push_back(n);
  552. }
  553. current = mxmlWalkNext(current, node, MXML_NO_DESCEND);
  554. }
  555. return res;
  556. }