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.

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