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 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  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 = 4;
  84. version.Revision = 3;
  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) const
  157. {
  158. char *xmldata = getXMLdata();
  159. if(xmldata == NULL)
  160. return -2;
  161. int compression = config.cfg.GzipCompression;
  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. addparams("par_real", 2, "name", name.c_str(), "value",
  208. stringFrom<float>(val).c_str());
  209. }
  210. void XMLwrapper::addparbool(const string &name, int val)
  211. {
  212. if(val != 0)
  213. addparams("par_bool", 2, "name", name.c_str(), "value", "yes");
  214. else
  215. addparams("par_bool", 2, "name", name.c_str(), "value", "no");
  216. }
  217. void XMLwrapper::addparstr(const string &name, const string &val)
  218. {
  219. mxml_node_t *element = mxmlNewElement(node, "string");
  220. mxmlElementSetAttr(element, "name", name.c_str());
  221. mxmlNewText(element, 0, val.c_str());
  222. }
  223. void XMLwrapper::beginbranch(const string &name)
  224. {
  225. if(verbose)
  226. cout << "beginbranch()" << name << endl;
  227. node = addparams(name.c_str(), 0);
  228. }
  229. void XMLwrapper::beginbranch(const string &name, int id)
  230. {
  231. if(verbose)
  232. cout << "beginbranch(" << id << ")" << name << endl;
  233. node = addparams(name.c_str(), 1, "id", stringFrom<int>(id).c_str());
  234. }
  235. void XMLwrapper::endbranch()
  236. {
  237. if(verbose)
  238. cout << "endbranch()" << node << "-" << node->value.element.name
  239. << " To "
  240. << node->parent << "-" << node->parent->value.element.name << endl;
  241. node = node->parent;
  242. }
  243. //workaround for memory leak
  244. const char *trimLeadingWhite(const char *c)
  245. {
  246. while(isspace(*c))
  247. ++c;
  248. return c;
  249. }
  250. /* LOAD XML members */
  251. int XMLwrapper::loadXMLfile(const string &filename)
  252. {
  253. if(tree != NULL)
  254. mxmlDelete(tree);
  255. tree = NULL;
  256. const char *xmldata = doloadfile(filename.c_str());
  257. if(xmldata == NULL)
  258. return -1; //the file could not be loaded or uncompressed
  259. root = tree = mxmlLoadString(NULL, trimLeadingWhite(
  260. xmldata), MXML_OPAQUE_CALLBACK);
  261. delete[] xmldata;
  262. if(tree == NULL)
  263. return -2; //this is not XML
  264. node = root = mxmlFindElement(tree,
  265. tree,
  266. "ZynAddSubFX-data",
  267. NULL,
  268. NULL,
  269. MXML_DESCEND);
  270. if(root == NULL)
  271. return -3; //the XML doesnt embbed zynaddsubfx data
  272. //fetch version information
  273. version.Major = stringTo<int>(mxmlElementGetAttr(root, "version-major"));
  274. version.Minor = stringTo<int>(mxmlElementGetAttr(root, "version-minor"));
  275. version.Revision =
  276. stringTo<int>(mxmlElementGetAttr(root, "version-revision"));
  277. if(verbose)
  278. cout << "loadXMLfile() version: " << version.Major << '.'
  279. << version.Minor << '.' << version.Revision << 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. if(tree != NULL)
  307. mxmlDelete(tree);
  308. tree = NULL;
  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. return true;
  324. }
  325. int XMLwrapper::enterbranch(const string &name)
  326. {
  327. if(verbose)
  328. cout << "enterbranch() " << name << endl;
  329. mxml_node_t *tmp = mxmlFindElement(node, node,
  330. name.c_str(), NULL, NULL,
  331. MXML_DESCEND_FIRST);
  332. if(tmp == NULL)
  333. return 0;
  334. node = tmp;
  335. return 1;
  336. }
  337. int XMLwrapper::enterbranch(const string &name, int id)
  338. {
  339. if(verbose)
  340. cout << "enterbranch(" << id << ") " << name << endl;
  341. mxml_node_t *tmp = mxmlFindElement(node, node,
  342. name.c_str(), "id", stringFrom<int>(
  343. id).c_str(), MXML_DESCEND_FIRST);
  344. if(tmp == NULL)
  345. return 0;
  346. node = tmp;
  347. return 1;
  348. }
  349. void XMLwrapper::exitbranch()
  350. {
  351. if(verbose)
  352. cout << "exitbranch()" << node << "-" << node->value.element.name
  353. << " To "
  354. << node->parent << "-" << node->parent->value.element.name << endl;
  355. node = node->parent;
  356. }
  357. int XMLwrapper::getbranchid(int min, int max) const
  358. {
  359. int id = stringTo<int>(mxmlElementGetAttr(node, "id"));
  360. if((min == 0) && (max == 0))
  361. return id;
  362. if(id < min)
  363. id = min;
  364. else
  365. if(id > max)
  366. id = max;
  367. return id;
  368. }
  369. int XMLwrapper::getpar(const string &name, int defaultpar, int min,
  370. int max) const
  371. {
  372. const mxml_node_t *tmp = mxmlFindElement(node,
  373. node,
  374. "par",
  375. "name",
  376. name.c_str(),
  377. MXML_DESCEND_FIRST);
  378. if(tmp == NULL)
  379. return defaultpar;
  380. const char *strval = mxmlElementGetAttr(tmp, "value");
  381. if(strval == NULL)
  382. return defaultpar;
  383. int val = stringTo<int>(strval);
  384. if(val < min)
  385. val = min;
  386. else
  387. if(val > max)
  388. val = max;
  389. return val;
  390. }
  391. int XMLwrapper::getpar127(const string &name, int defaultpar) const
  392. {
  393. return getpar(name, defaultpar, 0, 127);
  394. }
  395. int XMLwrapper::getparbool(const string &name, int defaultpar) const
  396. {
  397. const mxml_node_t *tmp = mxmlFindElement(node,
  398. node,
  399. "par_bool",
  400. "name",
  401. name.c_str(),
  402. MXML_DESCEND_FIRST);
  403. if(tmp == NULL)
  404. return defaultpar;
  405. const char *strval = mxmlElementGetAttr(tmp, "value");
  406. if(strval == NULL)
  407. return defaultpar;
  408. if((strval[0] == 'Y') || (strval[0] == 'y'))
  409. return 1;
  410. else
  411. return 0;
  412. }
  413. void XMLwrapper::getparstr(const string &name, char *par, int maxstrlen) const
  414. {
  415. ZERO(par, maxstrlen);
  416. const mxml_node_t *tmp = mxmlFindElement(node,
  417. node,
  418. "string",
  419. "name",
  420. name.c_str(),
  421. MXML_DESCEND_FIRST);
  422. if(tmp == NULL)
  423. return;
  424. if(tmp->child == NULL)
  425. return;
  426. if(tmp->child->type == MXML_OPAQUE) {
  427. snprintf(par, maxstrlen, "%s", tmp->child->value.element.name);
  428. return;
  429. }
  430. if((tmp->child->type == MXML_TEXT)
  431. && (tmp->child->value.text.string != NULL)) {
  432. snprintf(par, maxstrlen, "%s", tmp->child->value.text.string);
  433. return;
  434. }
  435. }
  436. string XMLwrapper::getparstr(const string &name,
  437. const std::string &defaultpar) const
  438. {
  439. const mxml_node_t *tmp = mxmlFindElement(node,
  440. node,
  441. "string",
  442. "name",
  443. name.c_str(),
  444. MXML_DESCEND_FIRST);
  445. if((tmp == NULL) || (tmp->child == NULL))
  446. return defaultpar;
  447. if((tmp->child->type == MXML_OPAQUE)
  448. && (tmp->child->value.element.name != NULL))
  449. return tmp->child->value.element.name;
  450. if((tmp->child->type == MXML_TEXT)
  451. && (tmp->child->value.text.string != NULL))
  452. return tmp->child->value.text.string;
  453. return defaultpar;
  454. }
  455. float XMLwrapper::getparreal(const char *name, float defaultpar) const
  456. {
  457. const mxml_node_t *tmp = mxmlFindElement(node,
  458. node,
  459. "par_real",
  460. "name",
  461. name,
  462. MXML_DESCEND_FIRST);
  463. if(tmp == NULL)
  464. return defaultpar;
  465. const char *strval = mxmlElementGetAttr(tmp, "value");
  466. if(strval == NULL)
  467. return defaultpar;
  468. return stringTo<float>(strval);
  469. }
  470. float XMLwrapper::getparreal(const char *name,
  471. float defaultpar,
  472. float min,
  473. float max) const
  474. {
  475. float result = getparreal(name, defaultpar);
  476. if(result < min)
  477. result = min;
  478. else
  479. if(result > max)
  480. result = max;
  481. return result;
  482. }
  483. /** Private members **/
  484. mxml_node_t *XMLwrapper::addparams(const char *name, unsigned int params,
  485. ...) const
  486. {
  487. /**@todo make this function send out a good error message if something goes
  488. * wrong**/
  489. mxml_node_t *element = mxmlNewElement(node, name);
  490. if(params) {
  491. va_list variableList;
  492. va_start(variableList, params);
  493. const char *ParamName;
  494. const char *ParamValue;
  495. while(params--) {
  496. ParamName = va_arg(variableList, const char *);
  497. ParamValue = va_arg(variableList, const char *);
  498. if(verbose)
  499. cout << "addparams()[" << params << "]=" << name << " "
  500. << ParamName << "=\"" << ParamValue << "\"" << endl;
  501. mxmlElementSetAttr(element, ParamName, ParamValue);
  502. }
  503. }
  504. return element;
  505. }