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.

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