External plugins for 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.

702 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. 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 = mxmlGetElement(node);
  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=mxmlGetElement(node);
  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. auto parent = mxmlGetParent(node);
  242. if(verbose)
  243. cout << "endbranch()" << node << "-" << mxmlGetElement(node)
  244. << " To "
  245. << parent << "-" << mxmlGetElement(parent) << endl;
  246. node = parent;
  247. }
  248. //workaround for memory leak
  249. const char *trimLeadingWhite(const char *c)
  250. {
  251. while(isspace(*c))
  252. ++c;
  253. return c;
  254. }
  255. /* LOAD XML members */
  256. int XMLwrapper::loadXMLfile(const string &filename)
  257. {
  258. cleanup();
  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. _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
  277. _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
  278. _fileversion.set_revision(
  279. stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
  280. if(verbose)
  281. cout << "loadXMLfile() version: " << _fileversion << endl;
  282. return 0;
  283. }
  284. char *XMLwrapper::doloadfile(const string &filename) const
  285. {
  286. char *xmldata = NULL;
  287. gzFile gzfile = gzopen(filename.c_str(), "rb");
  288. if(gzfile != NULL) { //The possibly compressed file opened
  289. stringstream strBuf; //reading stream
  290. const int bufSize = 500; //fetch size
  291. char fetchBuf[bufSize + 1]; //fetch buffer
  292. int read = 0; //chars read in last fetch
  293. fetchBuf[bufSize] = 0; //force null termination
  294. while(bufSize == (read = gzread(gzfile, fetchBuf, bufSize)))
  295. strBuf << fetchBuf;
  296. fetchBuf[read] = 0; //Truncate last partial read
  297. strBuf << fetchBuf;
  298. gzclose(gzfile);
  299. //Place data in output format
  300. string tmp = strBuf.str();
  301. xmldata = new char[tmp.size() + 1];
  302. strncpy(xmldata, tmp.c_str(), tmp.size() + 1);
  303. }
  304. return xmldata;
  305. }
  306. bool XMLwrapper::putXMLdata(const char *xmldata)
  307. {
  308. cleanup();
  309. if(xmldata == NULL)
  310. return false;
  311. root = tree = mxmlLoadString(NULL, trimLeadingWhite(
  312. xmldata), MXML_OPAQUE_CALLBACK);
  313. if(tree == NULL)
  314. return false;
  315. node = root = mxmlFindElement(tree,
  316. tree,
  317. "ZynAddSubFX-data",
  318. NULL,
  319. NULL,
  320. MXML_DESCEND);
  321. if(root == NULL)
  322. return false;
  323. //fetch version information
  324. _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major")));
  325. _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor")));
  326. _fileversion.set_revision(
  327. stringTo<int>(mxmlElementGetAttr(root, "version-revision")));
  328. return true;
  329. }
  330. int XMLwrapper::enterbranch(const string &name)
  331. {
  332. if(verbose)
  333. cout << "enterbranch() " << name << endl;
  334. mxml_node_t *tmp = mxmlFindElement(node, node,
  335. name.c_str(), NULL, NULL,
  336. MXML_DESCEND_FIRST);
  337. if(tmp == NULL)
  338. return 0;
  339. node = tmp;
  340. return 1;
  341. }
  342. int XMLwrapper::enterbranch(const string &name, int id)
  343. {
  344. if(verbose)
  345. cout << "enterbranch(" << id << ") " << name << endl;
  346. mxml_node_t *tmp = mxmlFindElement(node, node,
  347. name.c_str(), "id", stringFrom<int>(
  348. id).c_str(), MXML_DESCEND_FIRST);
  349. if(tmp == NULL)
  350. return 0;
  351. node = tmp;
  352. return 1;
  353. }
  354. void XMLwrapper::exitbranch()
  355. {
  356. auto parent = mxmlGetParent(node);
  357. if(verbose)
  358. cout << "exitbranch()" << node << "-" << mxmlGetElement(node)
  359. << " To "
  360. << parent << "-" << mxmlGetElement(parent) << endl;
  361. node = parent;
  362. }
  363. int XMLwrapper::getbranchid(int min, int max) const
  364. {
  365. int id = stringTo<int>(mxmlElementGetAttr(node, "id"));
  366. if((min == 0) && (max == 0))
  367. return id;
  368. if(id < min)
  369. id = min;
  370. else
  371. if(id > max)
  372. id = max;
  373. return id;
  374. }
  375. int XMLwrapper::getpar(const string &name, int defaultpar, int min,
  376. int max) const
  377. {
  378. const mxml_node_t *tmp = mxmlFindElement(node,
  379. node,
  380. "par",
  381. "name",
  382. name.c_str(),
  383. MXML_DESCEND_FIRST);
  384. if(tmp == NULL)
  385. return defaultpar;
  386. const char *strval = mxmlElementGetAttr(tmp, "value");
  387. if(strval == NULL)
  388. return defaultpar;
  389. int val = stringTo<int>(strval);
  390. if(val < min)
  391. val = min;
  392. else
  393. if(val > max)
  394. val = max;
  395. return val;
  396. }
  397. int XMLwrapper::getpar127(const string &name, int defaultpar) const
  398. {
  399. return getpar(name, defaultpar, 0, 127);
  400. }
  401. int XMLwrapper::getparbool(const string &name, int defaultpar) const
  402. {
  403. const mxml_node_t *tmp = mxmlFindElement(node,
  404. node,
  405. "par_bool",
  406. "name",
  407. name.c_str(),
  408. MXML_DESCEND_FIRST);
  409. if(tmp == NULL)
  410. return defaultpar;
  411. const char *strval = mxmlElementGetAttr(tmp, "value");
  412. if(strval == NULL)
  413. return defaultpar;
  414. if((strval[0] == 'Y') || (strval[0] == 'y'))
  415. return 1;
  416. else
  417. return 0;
  418. }
  419. void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const
  420. {
  421. ZERO(par, maxstrlen);
  422. mxml_node_t *tmp = mxmlFindElement(node,
  423. node,
  424. "string",
  425. "name",
  426. name.c_str(),
  427. MXML_DESCEND_FIRST);
  428. if(tmp == NULL)
  429. return;
  430. mxml_node_t *child = mxmlGetFirstChild(tmp);
  431. if(child == NULL)
  432. return;
  433. if(mxmlGetType(child) == MXML_OPAQUE) {
  434. snprintf(par, maxstrlen, "%s", mxmlGetElement(child));
  435. return;
  436. }
  437. if(mxmlGetType(child) == MXML_TEXT) {
  438. const char *text = mxmlGetText(child, NULL);
  439. if (text != NULL) {
  440. snprintf(par, maxstrlen, "%s", text);
  441. return;
  442. }
  443. }
  444. }
  445. string XMLwrapper::getparstr(const string &name,
  446. const std::string &defaultpar) const
  447. {
  448. mxml_node_t *tmp = mxmlFindElement(node,
  449. node,
  450. "string",
  451. "name",
  452. name.c_str(),
  453. MXML_DESCEND_FIRST);
  454. if(tmp == NULL)
  455. return defaultpar;
  456. mxml_node_t *child = mxmlGetFirstChild(tmp);
  457. if(child == NULL)
  458. return defaultpar;
  459. if(mxmlGetType(child) == MXML_OPAQUE) {
  460. const char *name = mxmlGetElement(child);
  461. if(name != NULL)
  462. return name;
  463. }
  464. if(mxmlGetType(child) == MXML_TEXT) {
  465. const char *value = mxmlGetText(child, NULL);
  466. if(value != NULL)
  467. return value;
  468. }
  469. return defaultpar;
  470. }
  471. float XMLwrapper::getparreal(const char *name, float defaultpar) const
  472. {
  473. const mxml_node_t *tmp = mxmlFindElement(node,
  474. node,
  475. "par_real",
  476. "name",
  477. name,
  478. MXML_DESCEND_FIRST);
  479. if(tmp == NULL)
  480. return defaultpar;
  481. const char *strval = mxmlElementGetAttr(tmp, "exact_value");
  482. if (strval != NULL) {
  483. union { float out; uint32_t in; } convert;
  484. sscanf(strval+2, "%x", &convert.in);
  485. return convert.out;
  486. }
  487. strval = mxmlElementGetAttr(tmp, "value");
  488. if(strval == NULL)
  489. return defaultpar;
  490. return stringTo<float>(strval);
  491. }
  492. float XMLwrapper::getparreal(const char *name,
  493. float defaultpar,
  494. float min,
  495. float max) const
  496. {
  497. float result = getparreal(name, defaultpar);
  498. if(result < min)
  499. result = min;
  500. else
  501. if(result > max)
  502. result = max;
  503. return result;
  504. }
  505. /** Private members **/
  506. mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
  507. ...) const
  508. {
  509. /**@todo make this function send out a good error message if something goes
  510. * wrong**/
  511. mxml_node_t *element = mxmlNewElement(node, name);
  512. if(params) {
  513. va_list variableList;
  514. va_start(variableList, params);
  515. const char *ParamName;
  516. const char *ParamValue;
  517. while(params--) {
  518. ParamName = va_arg(variableList, const char *);
  519. ParamValue = va_arg(variableList, const char *);
  520. if(verbose)
  521. cout << "addparams()[" << params << "]=" << name << " "
  522. << ParamName << "=\"" << ParamValue << "\"" << endl;
  523. mxmlElementSetAttr(element, ParamName, ParamValue);
  524. }
  525. va_end(variableList);
  526. }
  527. return element;
  528. }
  529. XmlNode::XmlNode(std::string name_)
  530. :name(name_)
  531. {}
  532. std::string &XmlNode::operator[](std::string name)
  533. {
  534. //fetch an existing one
  535. for(auto &a:attrs)
  536. if(a.name == name)
  537. return a.value;
  538. //create a new one
  539. attrs.push_back({name, ""});
  540. return attrs[attrs.size()-1].value;
  541. }
  542. bool XmlNode::has(std::string name_)
  543. {
  544. //fetch an existing one
  545. for(auto &a:attrs)
  546. if(a.name == name_)
  547. return true;
  548. return false;
  549. }
  550. void XMLwrapper::add(const XmlNode &node_)
  551. {
  552. mxml_node_t *element = mxmlNewElement(node, node_.name.c_str());
  553. for(auto attr:node_.attrs)
  554. mxmlElementSetAttr(element, attr.name.c_str(),
  555. attr.value.c_str());
  556. }
  557. std::vector<XmlNode> XMLwrapper::getBranch(void) const
  558. {
  559. std::vector<XmlNode> res;
  560. mxml_node_t *current = mxmlGetFirstChild(node);
  561. while(current) {
  562. if(mxmlGetType(current) == MXML_ELEMENT) {
  563. XmlNode n(mxmlGetElement(current));
  564. for(int i=0; i<mxmlElementGetAttrCount(current); ++i) {
  565. const char *name;
  566. const char *value = mxmlElementGetAttrByIndex(current, i, &name);
  567. n[name] = value;
  568. }
  569. res.push_back(n);
  570. }
  571. current = mxmlWalkNext(current, node, MXML_NO_DESCEND);
  572. }
  573. return res;
  574. }
  575. }