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.

695 lines
19KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. Microtonal.cpp - Tuning settings and microtonal capabilities
  4. Copyright (C) 2002-2005 Nasca Octavian Paul
  5. Author: Nasca Octavian Paul
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of version 2 of the GNU General Public License
  8. as published by the Free Software Foundation.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License (version 2 or later) for more details.
  13. You should have received a copy of the GNU General Public License (version 2)
  14. along with this program; if not, write to the Free Software Foundation,
  15. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. */
  17. #include <math.h>
  18. #include <string.h>
  19. #include "Microtonal.h"
  20. #define MAX_LINE_SIZE 80
  21. Microtonal::Microtonal()
  22. {
  23. Pname = new unsigned char[MICROTONAL_MAX_NAME_LEN];
  24. Pcomment = new unsigned char[MICROTONAL_MAX_NAME_LEN];
  25. defaults();
  26. }
  27. void Microtonal::defaults()
  28. {
  29. Pinvertupdown = 0;
  30. Pinvertupdowncenter = 60;
  31. octavesize = 12;
  32. Penabled = 0;
  33. PAnote = 69;
  34. PAfreq = 440.0f;
  35. Pscaleshift = 64;
  36. Pfirstkey = 0;
  37. Plastkey = 127;
  38. Pmiddlenote = 60;
  39. Pmapsize = 12;
  40. Pmappingenabled = 0;
  41. for(int i = 0; i < 128; ++i)
  42. Pmapping[i] = i;
  43. for(int i = 0; i < MAX_OCTAVE_SIZE; ++i) {
  44. octave[i].tuning = tmpoctave[i].tuning = powf(
  45. 2,
  46. (i % octavesize
  47. + 1) / 12.0f);
  48. octave[i].type = tmpoctave[i].type = 1;
  49. octave[i].x1 = tmpoctave[i].x1 = (i % octavesize + 1) * 100;
  50. octave[i].x2 = tmpoctave[i].x2 = 0;
  51. }
  52. octave[11].type = 2;
  53. octave[11].x1 = 2;
  54. octave[11].x2 = 1;
  55. for(int i = 0; i < MICROTONAL_MAX_NAME_LEN; ++i) {
  56. Pname[i] = '\0';
  57. Pcomment[i] = '\0';
  58. }
  59. snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "12tET");
  60. snprintf((char *) Pcomment,
  61. MICROTONAL_MAX_NAME_LEN,
  62. "Equal Temperament 12 notes per octave");
  63. Pglobalfinedetune = 64;
  64. }
  65. Microtonal::~Microtonal()
  66. {
  67. delete [] Pname;
  68. delete [] Pcomment;
  69. }
  70. /*
  71. * Get the size of the octave
  72. */
  73. unsigned char Microtonal::getoctavesize() const
  74. {
  75. if(Penabled != 0)
  76. return octavesize;
  77. else
  78. return 12;
  79. }
  80. /*
  81. * Get the frequency according the note number
  82. */
  83. float Microtonal::getnotefreq(int note, int keyshift) const
  84. {
  85. // in this function will appears many times things like this:
  86. // var=(a+b*100)%b
  87. // I had written this way because if I use var=a%b gives unwanted results when a<0
  88. // This is the same with divisions.
  89. if((Pinvertupdown != 0) && ((Pmappingenabled == 0) || (Penabled == 0)))
  90. note = (int) Pinvertupdowncenter * 2 - note;
  91. //compute global fine detune
  92. float globalfinedetunerap = powf(2.0f,
  93. (Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents
  94. if(Penabled == 0)
  95. return powf(2.0f,
  96. (note - PAnote
  97. + keyshift) / 12.0f) * PAfreq * globalfinedetunerap; //12tET
  98. int scaleshift =
  99. ((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize;
  100. //compute the keyshift
  101. float rap_keyshift = 1.0f;
  102. if(keyshift != 0) {
  103. int kskey = (keyshift + (int)octavesize * 100) % octavesize;
  104. int ksoct = (keyshift + (int)octavesize * 100) / octavesize - 100;
  105. rap_keyshift = (kskey == 0) ? (1.0f) : (octave[kskey - 1].tuning);
  106. rap_keyshift *= powf(octave[octavesize - 1].tuning, ksoct);
  107. }
  108. //if the mapping is enabled
  109. if(Pmappingenabled != 0) {
  110. if((note < Pfirstkey) || (note > Plastkey))
  111. return -1.0f;
  112. //Compute how many mapped keys are from middle note to reference note
  113. //and find out the proportion between the freq. of middle note and "A" note
  114. int tmp = PAnote - Pmiddlenote, minus = 0;
  115. if(tmp < 0) {
  116. tmp = -tmp;
  117. minus = 1;
  118. }
  119. int deltanote = 0;
  120. for(int i = 0; i < tmp; ++i)
  121. if(Pmapping[i % Pmapsize] >= 0)
  122. deltanote++;
  123. float rap_anote_middlenote =
  124. (deltanote ==
  125. 0) ? (1.0f) : (octave[(deltanote - 1) % octavesize].tuning);
  126. if(deltanote != 0)
  127. rap_anote_middlenote *=
  128. powf(octave[octavesize - 1].tuning,
  129. (deltanote - 1) / octavesize);
  130. if(minus != 0)
  131. rap_anote_middlenote = 1.0f / rap_anote_middlenote;
  132. //Convert from note (midi) to degree (note from the tunning)
  133. int degoct =
  134. (note - (int)Pmiddlenote + (int) Pmapsize
  135. * 200) / (int)Pmapsize - 200;
  136. int degkey = (note - Pmiddlenote + (int)Pmapsize * 100) % Pmapsize;
  137. degkey = Pmapping[degkey];
  138. if(degkey < 0)
  139. return -1.0f; //this key is not mapped
  140. //invert the keyboard upside-down if it is asked for
  141. //TODO: do the right way by using Pinvertupdowncenter
  142. if(Pinvertupdown != 0) {
  143. degkey = octavesize - degkey - 1;
  144. degoct = -degoct;
  145. }
  146. //compute the frequency of the note
  147. degkey = degkey + scaleshift;
  148. degoct += degkey / octavesize;
  149. degkey %= octavesize;
  150. float freq = (degkey == 0) ? (1.0f) : octave[degkey - 1].tuning;
  151. freq *= powf(octave[octavesize - 1].tuning, degoct);
  152. freq *= PAfreq / rap_anote_middlenote;
  153. freq *= globalfinedetunerap;
  154. if(scaleshift != 0)
  155. freq /= octave[scaleshift - 1].tuning;
  156. return freq * rap_keyshift;
  157. }
  158. else { //if the mapping is disabled
  159. int nt = note - PAnote + scaleshift;
  160. int ntkey = (nt + (int)octavesize * 100) % octavesize;
  161. int ntoct = (nt - ntkey) / octavesize;
  162. float oct = octave[octavesize - 1].tuning;
  163. float freq =
  164. octave[(ntkey + octavesize - 1) % octavesize].tuning * powf(oct,
  165. ntoct)
  166. * PAfreq;
  167. if(ntkey == 0)
  168. freq /= oct;
  169. if(scaleshift != 0)
  170. freq /= octave[scaleshift - 1].tuning;
  171. // fprintf(stderr,"note=%d freq=%.3f cents=%d\n",note,freq,(int)floor(logf(freq/PAfreq)/logf(2.0f)*1200.0f+0.5f));
  172. freq *= globalfinedetunerap;
  173. return freq * rap_keyshift;
  174. }
  175. }
  176. bool Microtonal::operator==(const Microtonal &micro) const
  177. {
  178. return !(*this != micro);
  179. }
  180. bool Microtonal::operator!=(const Microtonal &micro) const
  181. {
  182. //A simple macro to test equality MiCRotonal EQuals (not the perfect
  183. //approach, but good enough)
  184. #define MCREQ(x) if(x != micro.x) \
  185. return true
  186. //for floats
  187. #define FMCREQ(x) if(!((x < micro.x + 0.0001f) && (x > micro.x - 0.0001f))) \
  188. return true
  189. MCREQ(Pinvertupdown);
  190. MCREQ(Pinvertupdowncenter);
  191. MCREQ(octavesize);
  192. MCREQ(Penabled);
  193. MCREQ(PAnote);
  194. FMCREQ(PAfreq);
  195. MCREQ(Pscaleshift);
  196. MCREQ(Pfirstkey);
  197. MCREQ(Plastkey);
  198. MCREQ(Pmiddlenote);
  199. MCREQ(Pmapsize);
  200. MCREQ(Pmappingenabled);
  201. for(int i = 0; i < 128; ++i)
  202. MCREQ(Pmapping[i]);
  203. for(int i = 0; i < octavesize; ++i) {
  204. FMCREQ(octave[i].tuning);
  205. MCREQ(octave[i].type);
  206. MCREQ(octave[i].x1);
  207. MCREQ(octave[i].x2);
  208. }
  209. if(strcmp((const char *)this->Pname, (const char *)micro.Pname))
  210. return true;
  211. if(strcmp((const char *)this->Pcomment, (const char *)micro.Pcomment))
  212. return true;
  213. MCREQ(Pglobalfinedetune);
  214. return false;
  215. //undefine macros, as they are no longer needed
  216. #undef MCREQ
  217. #undef FMCREQ
  218. }
  219. /*
  220. * Convert a line to tunings; returns -1 if it ok
  221. */
  222. int Microtonal::linetotunings(unsigned int nline, const char *line)
  223. {
  224. int x1 = -1, x2 = -1, type = -1;
  225. float x = -1.0f, tmp, tuning = 1.0f;
  226. if(strstr(line, "/") == NULL) {
  227. if(strstr(line, ".") == NULL) { // M case (M=M/1)
  228. sscanf(line, "%d", &x1);
  229. x2 = 1;
  230. type = 2; //division
  231. }
  232. else { // float number case
  233. sscanf(line, "%f", &x);
  234. if(x < 0.000001f)
  235. return 1;
  236. type = 1; //float type(cents)
  237. }
  238. }
  239. else { // M/N case
  240. sscanf(line, "%d/%d", &x1, &x2);
  241. if((x1 < 0) || (x2 < 0))
  242. return 1;
  243. if(x2 == 0)
  244. x2 = 1;
  245. type = 2; //division
  246. }
  247. if(x1 <= 0)
  248. x1 = 1; //not allow zero frequency sounds (consider 0 as 1)
  249. //convert to float if the number are too big
  250. if((type == 2)
  251. && ((x1 > (128 * 128 * 128 - 1)) || (x2 > (128 * 128 * 128 - 1)))) {
  252. type = 1;
  253. x = ((float) x1) / x2;
  254. }
  255. switch(type) {
  256. case 1:
  257. x1 = (int) floor(x);
  258. tmp = fmod(x, 1.0f);
  259. x2 = (int) (floor(tmp * 1e6));
  260. tuning = powf(2.0f, x / 1200.0f);
  261. break;
  262. case 2:
  263. x = ((float)x1) / x2;
  264. tuning = x;
  265. break;
  266. }
  267. tmpoctave[nline].tuning = tuning;
  268. tmpoctave[nline].type = type;
  269. tmpoctave[nline].x1 = x1;
  270. tmpoctave[nline].x2 = x2;
  271. return -1; //ok
  272. }
  273. /*
  274. * Convert the text to tunnings
  275. */
  276. int Microtonal::texttotunings(const char *text)
  277. {
  278. unsigned int i, k = 0, nl = 0;
  279. char *lin;
  280. lin = new char[MAX_LINE_SIZE + 1];
  281. while(k < strlen(text)) {
  282. for(i = 0; i < MAX_LINE_SIZE; ++i) {
  283. lin[i] = text[k++];
  284. if(lin[i] < 0x20)
  285. break;
  286. }
  287. lin[i] = '\0';
  288. if(strlen(lin) == 0)
  289. continue;
  290. int err = linetotunings(nl, lin);
  291. if(err != -1) {
  292. delete [] lin;
  293. return nl; //Parse error
  294. }
  295. nl++;
  296. }
  297. delete [] lin;
  298. if(nl > MAX_OCTAVE_SIZE)
  299. nl = MAX_OCTAVE_SIZE;
  300. if(nl == 0)
  301. return -2; //the input is empty
  302. octavesize = nl;
  303. for(i = 0; i < octavesize; ++i) {
  304. octave[i].tuning = tmpoctave[i].tuning;
  305. octave[i].type = tmpoctave[i].type;
  306. octave[i].x1 = tmpoctave[i].x1;
  307. octave[i].x2 = tmpoctave[i].x2;
  308. }
  309. return -1; //ok
  310. }
  311. /*
  312. * Convert the text to mapping
  313. */
  314. void Microtonal::texttomapping(const char *text)
  315. {
  316. unsigned int i, k = 0;
  317. char *lin;
  318. lin = new char[MAX_LINE_SIZE + 1];
  319. for(i = 0; i < 128; ++i)
  320. Pmapping[i] = -1;
  321. int tx = 0;
  322. while(k < strlen(text)) {
  323. for(i = 0; i < MAX_LINE_SIZE; ++i) {
  324. lin[i] = text[k++];
  325. if(lin[i] < 0x20)
  326. break;
  327. }
  328. lin[i] = '\0';
  329. if(strlen(lin) == 0)
  330. continue;
  331. int tmp = 0;
  332. if(sscanf(lin, "%d", &tmp) == 0)
  333. tmp = -1;
  334. if(tmp < -1)
  335. tmp = -1;
  336. Pmapping[tx] = tmp;
  337. if((tx++) > 127)
  338. break;
  339. }
  340. delete [] lin;
  341. if(tx == 0)
  342. tx = 1;
  343. Pmapsize = tx;
  344. }
  345. /*
  346. * Convert tunning to text line
  347. */
  348. void Microtonal::tuningtoline(int n, char *line, int maxn)
  349. {
  350. if((n > octavesize) || (n > MAX_OCTAVE_SIZE)) {
  351. line[0] = '\0';
  352. return;
  353. }
  354. if(octave[n].type == 1)
  355. snprintf(line, maxn, "%d.%06d", octave[n].x1, octave[n].x2);
  356. if(octave[n].type == 2)
  357. snprintf(line, maxn, "%d/%d", octave[n].x1, octave[n].x2);
  358. }
  359. int Microtonal::loadline(FILE *file, char *line)
  360. {
  361. do {
  362. if(fgets(line, 500, file) == 0)
  363. return 1;
  364. } while(line[0] == '!');
  365. return 0;
  366. }
  367. /*
  368. * Loads the tunnings from a scl file
  369. */
  370. int Microtonal::loadscl(const char *filename)
  371. {
  372. FILE *file = fopen(filename, "r");
  373. char tmp[500];
  374. fseek(file, 0, SEEK_SET);
  375. //loads the short description
  376. if(loadline(file, &tmp[0]) != 0)
  377. return 2;
  378. for(int i = 0; i < 500; ++i)
  379. if(tmp[i] < 32)
  380. tmp[i] = 0;
  381. snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "%s", tmp);
  382. snprintf((char *) Pcomment, MICROTONAL_MAX_NAME_LEN, "%s", tmp);
  383. //loads the number of the notes
  384. if(loadline(file, &tmp[0]) != 0)
  385. return 2;
  386. int nnotes = MAX_OCTAVE_SIZE;
  387. sscanf(&tmp[0], "%d", &nnotes);
  388. if(nnotes > MAX_OCTAVE_SIZE)
  389. return 2;
  390. //load the tunnings
  391. for(int nline = 0; nline < nnotes; ++nline) {
  392. if(loadline(file, &tmp[0]) != 0)
  393. return 2;
  394. linetotunings(nline, &tmp[0]);
  395. }
  396. fclose(file);
  397. octavesize = nnotes;
  398. for(int i = 0; i < octavesize; ++i) {
  399. octave[i].tuning = tmpoctave[i].tuning;
  400. octave[i].type = tmpoctave[i].type;
  401. octave[i].x1 = tmpoctave[i].x1;
  402. octave[i].x2 = tmpoctave[i].x2;
  403. }
  404. return 0;
  405. }
  406. /*
  407. * Loads the mapping from a kbm file
  408. */
  409. int Microtonal::loadkbm(const char *filename)
  410. {
  411. FILE *file = fopen(filename, "r");
  412. int x;
  413. char tmp[500];
  414. fseek(file, 0, SEEK_SET);
  415. //loads the mapsize
  416. if(loadline(file, &tmp[0]) != 0)
  417. return 2;
  418. if(sscanf(&tmp[0], "%d", &x) == 0)
  419. return 2;
  420. if(x < 1)
  421. x = 0;
  422. if(x > 127)
  423. x = 127; //just in case...
  424. Pmapsize = x;
  425. //loads first MIDI note to retune
  426. if(loadline(file, &tmp[0]) != 0)
  427. return 2;
  428. if(sscanf(&tmp[0], "%d", &x) == 0)
  429. return 2;
  430. if(x < 1)
  431. x = 0;
  432. if(x > 127)
  433. x = 127; //just in case...
  434. Pfirstkey = x;
  435. //loads last MIDI note to retune
  436. if(loadline(file, &tmp[0]) != 0)
  437. return 2;
  438. if(sscanf(&tmp[0], "%d", &x) == 0)
  439. return 2;
  440. if(x < 1)
  441. x = 0;
  442. if(x > 127)
  443. x = 127; //just in case...
  444. Plastkey = x;
  445. //loads last the middle note where scale fro scale degree=0
  446. if(loadline(file, &tmp[0]) != 0)
  447. return 2;
  448. if(sscanf(&tmp[0], "%d", &x) == 0)
  449. return 2;
  450. if(x < 1)
  451. x = 0;
  452. if(x > 127)
  453. x = 127; //just in case...
  454. Pmiddlenote = x;
  455. //loads the reference note
  456. if(loadline(file, &tmp[0]) != 0)
  457. return 2;
  458. if(sscanf(&tmp[0], "%d", &x) == 0)
  459. return 2;
  460. if(x < 1)
  461. x = 0;
  462. if(x > 127)
  463. x = 127; //just in case...
  464. PAnote = x;
  465. //loads the reference freq.
  466. if(loadline(file, &tmp[0]) != 0)
  467. return 2;
  468. float tmpPAfreq = 440.0f;
  469. if(sscanf(&tmp[0], "%f", &tmpPAfreq) == 0)
  470. return 2;
  471. PAfreq = tmpPAfreq;
  472. //the scale degree(which is the octave) is not loaded, it is obtained by the tunnings with getoctavesize() method
  473. if(loadline(file, &tmp[0]) != 0)
  474. return 2;
  475. //load the mappings
  476. if(Pmapsize != 0) {
  477. for(int nline = 0; nline < Pmapsize; ++nline) {
  478. if(loadline(file, &tmp[0]) != 0)
  479. return 2;
  480. if(sscanf(&tmp[0], "%d", &x) == 0)
  481. x = -1;
  482. Pmapping[nline] = x;
  483. }
  484. Pmappingenabled = 1;
  485. }
  486. else {
  487. Pmappingenabled = 0;
  488. Pmapping[0] = 0;
  489. Pmapsize = 1;
  490. }
  491. fclose(file);
  492. return 0;
  493. }
  494. void Microtonal::add2XML(XMLwrapper *xml) const
  495. {
  496. xml->addparstr("name", (char *) Pname);
  497. xml->addparstr("comment", (char *) Pcomment);
  498. xml->addparbool("invert_up_down", Pinvertupdown);
  499. xml->addpar("invert_up_down_center", Pinvertupdowncenter);
  500. xml->addparbool("enabled", Penabled);
  501. xml->addpar("global_fine_detune", Pglobalfinedetune);
  502. xml->addpar("a_note", PAnote);
  503. xml->addparreal("a_freq", PAfreq);
  504. if((Penabled == 0) && (xml->minimal))
  505. return;
  506. xml->beginbranch("SCALE");
  507. xml->addpar("scale_shift", Pscaleshift);
  508. xml->addpar("first_key", Pfirstkey);
  509. xml->addpar("last_key", Plastkey);
  510. xml->addpar("middle_note", Pmiddlenote);
  511. xml->beginbranch("OCTAVE");
  512. xml->addpar("octave_size", octavesize);
  513. for(int i = 0; i < octavesize; ++i) {
  514. xml->beginbranch("DEGREE", i);
  515. if(octave[i].type == 1)
  516. xml->addparreal("cents", octave[i].tuning);
  517. ;
  518. if(octave[i].type == 2) {
  519. xml->addpar("numerator", octave[i].x1);
  520. xml->addpar("denominator", octave[i].x2);
  521. }
  522. xml->endbranch();
  523. }
  524. xml->endbranch();
  525. xml->beginbranch("KEYBOARD_MAPPING");
  526. xml->addpar("map_size", Pmapsize);
  527. xml->addpar("mapping_enabled", Pmappingenabled);
  528. for(int i = 0; i < Pmapsize; ++i) {
  529. xml->beginbranch("KEYMAP", i);
  530. xml->addpar("degree", Pmapping[i]);
  531. xml->endbranch();
  532. }
  533. xml->endbranch();
  534. xml->endbranch();
  535. }
  536. void Microtonal::getfromXML(XMLwrapper *xml)
  537. {
  538. xml->getparstr("name", (char *) Pname, MICROTONAL_MAX_NAME_LEN);
  539. xml->getparstr("comment", (char *) Pcomment, MICROTONAL_MAX_NAME_LEN);
  540. Pinvertupdown = xml->getparbool("invert_up_down", Pinvertupdown);
  541. Pinvertupdowncenter = xml->getpar127("invert_up_down_center",
  542. Pinvertupdowncenter);
  543. Penabled = xml->getparbool("enabled", Penabled);
  544. Pglobalfinedetune = xml->getpar127("global_fine_detune", Pglobalfinedetune);
  545. PAnote = xml->getpar127("a_note", PAnote);
  546. PAfreq = xml->getparreal("a_freq", PAfreq, 1.0f, 10000.0f);
  547. if(xml->enterbranch("SCALE")) {
  548. Pscaleshift = xml->getpar127("scale_shift", Pscaleshift);
  549. Pfirstkey = xml->getpar127("first_key", Pfirstkey);
  550. Plastkey = xml->getpar127("last_key", Plastkey);
  551. Pmiddlenote = xml->getpar127("middle_note", Pmiddlenote);
  552. if(xml->enterbranch("OCTAVE")) {
  553. octavesize = xml->getpar127("octave_size", octavesize);
  554. for(int i = 0; i < octavesize; ++i) {
  555. if(xml->enterbranch("DEGREE", i) == 0)
  556. continue;
  557. octave[i].x2 = 0;
  558. octave[i].tuning = xml->getparreal("cents", octave[i].tuning);
  559. octave[i].x1 = xml->getpar127("numerator", octave[i].x1);
  560. octave[i].x2 = xml->getpar127("denominator", octave[i].x2);
  561. if(octave[i].x2 != 0)
  562. octave[i].type = 2;
  563. else {
  564. octave[i].type = 1;
  565. //populate fields for display
  566. float x = logf(octave[i].tuning) / LOG_2 * 1200.0f;
  567. octave[i].x1 = (int) floor(x);
  568. octave[i].x2 = (int) (floor(fmodf(x, 1.0f) * 1e6));
  569. }
  570. xml->exitbranch();
  571. }
  572. xml->exitbranch();
  573. }
  574. if(xml->enterbranch("KEYBOARD_MAPPING")) {
  575. Pmapsize = xml->getpar127("map_size", Pmapsize);
  576. Pmappingenabled = xml->getpar127("mapping_enabled", Pmappingenabled);
  577. for(int i = 0; i < Pmapsize; ++i) {
  578. if(xml->enterbranch("KEYMAP", i) == 0)
  579. continue;
  580. Pmapping[i] = xml->getpar127("degree", Pmapping[i]);
  581. xml->exitbranch();
  582. }
  583. xml->exitbranch();
  584. }
  585. xml->exitbranch();
  586. }
  587. }
  588. int Microtonal::saveXML(const char *filename) const
  589. {
  590. XMLwrapper *xml = new XMLwrapper();
  591. xml->beginbranch("MICROTONAL");
  592. add2XML(xml);
  593. xml->endbranch();
  594. int result = xml->saveXMLfile(filename);
  595. delete (xml);
  596. return result;
  597. }
  598. int Microtonal::loadXML(const char *filename)
  599. {
  600. XMLwrapper *xml = new XMLwrapper();
  601. if(xml->loadXMLfile(filename) < 0) {
  602. delete (xml);
  603. return -1;
  604. }
  605. if(xml->enterbranch("MICROTONAL") == 0)
  606. return -10;
  607. getfromXML(xml);
  608. xml->exitbranch();
  609. delete (xml);
  610. return 0;
  611. }