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.

976 lines
38KB

  1. /*
  2. * Carla State utils
  3. * Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaStateUtils.hpp"
  18. #include "CarlaBackendUtils.hpp"
  19. #include "CarlaMathUtils.hpp"
  20. #include "CarlaMIDI.h"
  21. #ifdef HAVE_JUCE_LATER
  22. # include "juce_core.h"
  23. using juce::String;
  24. using juce::XmlElement;
  25. #else
  26. # include <QtCore/QString>
  27. # include <QtXml/QDomNode>
  28. #endif
  29. CARLA_BACKEND_START_NAMESPACE
  30. // -----------------------------------------------------------------------
  31. // StateParameter
  32. StateParameter::StateParameter() noexcept
  33. : isInput(true),
  34. index(-1),
  35. name(nullptr),
  36. symbol(nullptr),
  37. value(0.0f),
  38. midiChannel(0),
  39. midiCC(-1) {}
  40. StateParameter::~StateParameter() noexcept
  41. {
  42. if (name != nullptr)
  43. {
  44. delete[] name;
  45. name = nullptr;
  46. }
  47. if (symbol != nullptr)
  48. {
  49. delete[] symbol;
  50. symbol = nullptr;
  51. }
  52. }
  53. // -----------------------------------------------------------------------
  54. // StateCustomData
  55. StateCustomData::StateCustomData() noexcept
  56. : type(nullptr),
  57. key(nullptr),
  58. value(nullptr) {}
  59. StateCustomData::~StateCustomData() noexcept
  60. {
  61. if (type != nullptr)
  62. {
  63. delete[] type;
  64. type = nullptr;
  65. }
  66. if (key != nullptr)
  67. {
  68. delete[] key;
  69. key = nullptr;
  70. }
  71. if (value != nullptr)
  72. {
  73. delete[] value;
  74. value = nullptr;
  75. }
  76. }
  77. // -----------------------------------------------------------------------
  78. // SaveState
  79. SaveState::SaveState() noexcept
  80. : type(nullptr),
  81. name(nullptr),
  82. label(nullptr),
  83. binary(nullptr),
  84. uniqueId(0),
  85. active(false),
  86. dryWet(1.0f),
  87. volume(1.0f),
  88. balanceLeft(-1.0f),
  89. balanceRight(1.0f),
  90. panning(0.0f),
  91. ctrlChannel(-1),
  92. currentProgramIndex(-1),
  93. currentProgramName(nullptr),
  94. currentMidiBank(-1),
  95. currentMidiProgram(-1),
  96. chunk(nullptr) {}
  97. SaveState::~SaveState() noexcept
  98. {
  99. reset();
  100. }
  101. void SaveState::reset() noexcept
  102. {
  103. if (type != nullptr)
  104. {
  105. delete[] type;
  106. type = nullptr;
  107. }
  108. if (name != nullptr)
  109. {
  110. delete[] name;
  111. name = nullptr;
  112. }
  113. if (label != nullptr)
  114. {
  115. delete[] label;
  116. label = nullptr;
  117. }
  118. if (binary != nullptr)
  119. {
  120. delete[] binary;
  121. binary = nullptr;
  122. }
  123. if (currentProgramName != nullptr)
  124. {
  125. delete[] currentProgramName;
  126. currentProgramName = nullptr;
  127. }
  128. if (chunk != nullptr)
  129. {
  130. delete[] chunk;
  131. chunk = nullptr;
  132. }
  133. uniqueId = 0;
  134. active = false;
  135. dryWet = 1.0f;
  136. volume = 1.0f;
  137. balanceLeft = -1.0f;
  138. balanceRight = 1.0f;
  139. panning = 0.0f;
  140. ctrlChannel = -1;
  141. currentProgramIndex = -1;
  142. currentMidiBank = -1;
  143. currentMidiProgram = -1;
  144. for (StateParameterItenerator it = parameters.begin(); it.valid(); it.next())
  145. {
  146. StateParameter* const stateParameter(it.getValue());
  147. delete stateParameter;
  148. }
  149. for (StateCustomDataItenerator it = customData.begin(); it.valid(); it.next())
  150. {
  151. StateCustomData* const stateCustomData(it.getValue());
  152. delete stateCustomData;
  153. }
  154. parameters.clear();
  155. customData.clear();
  156. }
  157. // -----------------------------------------------------------------------
  158. // xmlSafeString
  159. #ifdef HAVE_JUCE_LATER
  160. static String xmlSafeString(const String& string, const bool toXml)
  161. {
  162. String newString(string);
  163. if (toXml)
  164. return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
  165. else
  166. return newString.replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"").replace("&amp;","&");
  167. }
  168. #else
  169. static QString xmlSafeString(const QString& string, const bool toXml)
  170. {
  171. QString newString(string);
  172. if (toXml)
  173. return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
  174. else
  175. return newString.replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"").replace("&amp;","&");
  176. }
  177. #endif
  178. // -----------------------------------------------------------------------
  179. // xmlSafeStringCharDup
  180. #ifdef HAVE_JUCE_LATER
  181. static const char* xmlSafeStringCharDup(const String& string, const bool toXml)
  182. {
  183. return carla_strdup(xmlSafeString(string, toXml).toRawUTF8());
  184. }
  185. #else
  186. static const char* xmlSafeStringCharDup(const QString& string, const bool toXml)
  187. {
  188. return carla_strdup(xmlSafeString(string, toXml).toUtf8().constData());
  189. }
  190. #endif
  191. // -----------------------------------------------------------------------
  192. // fillSaveStateFromXmlNode
  193. #ifdef HAVE_JUCE_LATER
  194. void fillSaveStateFromXmlNode(SaveState& saveState, const XmlElement* const xmlElement)
  195. {
  196. CARLA_SAFE_ASSERT_RETURN(xmlElement != nullptr,);
  197. for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
  198. {
  199. String tagName(elem->getTagName());
  200. // ---------------------------------------------------------------
  201. // Info
  202. if (tagName.equalsIgnoreCase("info"))
  203. {
  204. for (XmlElement* xmlInfo = elem->getFirstChildElement(); xmlInfo != nullptr; xmlInfo = xmlInfo->getNextElement())
  205. {
  206. const String& tag(xmlInfo->getTagName());
  207. const String text(xmlInfo->getAllSubText().trim());
  208. if (tag.equalsIgnoreCase("type"))
  209. saveState.type = xmlSafeStringCharDup(text, false);
  210. else if (tag.equalsIgnoreCase("name"))
  211. saveState.name = xmlSafeStringCharDup(text, false);
  212. else if (tag.equalsIgnoreCase("label") || tag.equalsIgnoreCase("uri"))
  213. saveState.label = xmlSafeStringCharDup(text, false);
  214. else if (tag.equalsIgnoreCase("binary") || tag.equalsIgnoreCase("bundle") || tag.equalsIgnoreCase("filename"))
  215. saveState.binary = xmlSafeStringCharDup(text, false);
  216. else if (tag.equalsIgnoreCase("uniqueid"))
  217. saveState.uniqueId = text.getLargeIntValue();
  218. }
  219. }
  220. // ---------------------------------------------------------------
  221. // Data
  222. else if (tagName.equalsIgnoreCase("data"))
  223. {
  224. for (XmlElement* xmlData = elem->getFirstChildElement(); xmlData != nullptr; xmlData = xmlData->getNextElement())
  225. {
  226. const String& tag(xmlData->getTagName());
  227. const String text(xmlData->getAllSubText().trim());
  228. // -------------------------------------------------------
  229. // Internal Data
  230. if (tag.equalsIgnoreCase("active"))
  231. {
  232. saveState.active = (text.equalsIgnoreCase("yes") || text.equalsIgnoreCase("true"));
  233. }
  234. else if (tag.equalsIgnoreCase("drywet"))
  235. {
  236. saveState.dryWet = carla_fixValue(0.0f, 1.0f, text.getFloatValue());
  237. }
  238. else if (tag.equalsIgnoreCase("volume"))
  239. {
  240. saveState.volume = carla_fixValue(0.0f, 1.27f, text.getFloatValue());
  241. }
  242. else if (tag.equalsIgnoreCase("balanceleft") || tag.equalsIgnoreCase("balance-left"))
  243. {
  244. saveState.balanceLeft = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
  245. }
  246. else if (tag.equalsIgnoreCase("balanceright") || tag.equalsIgnoreCase("balance-right"))
  247. {
  248. saveState.balanceRight = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
  249. }
  250. else if (tag.equalsIgnoreCase("panning"))
  251. {
  252. saveState.panning = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
  253. }
  254. else if (tag.equalsIgnoreCase("controlchannel") || tag.equalsIgnoreCase("control-channel"))
  255. {
  256. const int value(text.getIntValue());
  257. if (value >= 1 && value <= MAX_MIDI_CHANNELS)
  258. saveState.ctrlChannel = static_cast<int8_t>(value-1);
  259. }
  260. // -------------------------------------------------------
  261. // Program (current)
  262. else if (tag.equalsIgnoreCase("currentprogramindex") || tag.equalsIgnoreCase("current-program-index"))
  263. {
  264. const int value(text.getIntValue());
  265. if (value >= 1)
  266. saveState.currentProgramIndex = value-1;
  267. }
  268. else if (tag.equalsIgnoreCase("currentprogramname") || tag.equalsIgnoreCase("current-program-name"))
  269. {
  270. saveState.currentProgramName = xmlSafeStringCharDup(text, false);
  271. }
  272. // -------------------------------------------------------
  273. // Midi Program (current)
  274. else if (tag.equalsIgnoreCase("currentmidibank") || tag.equalsIgnoreCase("current-midi-bank"))
  275. {
  276. const int value(text.getIntValue());
  277. if (value >= 1)
  278. saveState.currentMidiBank = value-1;
  279. }
  280. else if (tag.equalsIgnoreCase("currentmidiprogram") || tag.equalsIgnoreCase("current-midi-program"))
  281. {
  282. const int value(text.getIntValue());
  283. if (value >= 1)
  284. saveState.currentMidiProgram = value-1;
  285. }
  286. // -------------------------------------------------------
  287. // Parameters
  288. else if (tag.equalsIgnoreCase("parameter"))
  289. {
  290. StateParameter* const stateParameter(new StateParameter());
  291. for (XmlElement* xmlSubData = xmlData->getFirstChildElement(); xmlSubData != nullptr; xmlSubData = xmlSubData->getNextElement())
  292. {
  293. const String& pTag(xmlSubData->getTagName());
  294. const String pText(xmlSubData->getAllSubText().trim());
  295. if (pTag.equalsIgnoreCase("index"))
  296. {
  297. const int index(pText.getIntValue());
  298. if (index >= 0)
  299. stateParameter->index = index;
  300. }
  301. else if (pTag.equalsIgnoreCase("name"))
  302. {
  303. stateParameter->name = xmlSafeStringCharDup(pText, false);
  304. }
  305. else if (pTag.equalsIgnoreCase("symbol"))
  306. {
  307. stateParameter->symbol = xmlSafeStringCharDup(pText, false);
  308. }
  309. else if (pTag.equalsIgnoreCase("value"))
  310. {
  311. stateParameter->value = pText.getFloatValue();
  312. }
  313. else if (pTag.equalsIgnoreCase("midichannel") || pTag.equalsIgnoreCase("midi-channel"))
  314. {
  315. const int channel(pText.getIntValue());
  316. if (channel >= 1 && channel <= MAX_MIDI_CHANNELS)
  317. stateParameter->midiChannel = static_cast<uint8_t>(channel-1);
  318. }
  319. else if (pTag.equalsIgnoreCase("midicc") || pTag.equalsIgnoreCase("midi-cc"))
  320. {
  321. const int cc(pText.getIntValue());
  322. if (cc >= 1 && cc < 0x5F)
  323. stateParameter->midiCC = static_cast<int16_t>(cc);
  324. }
  325. }
  326. saveState.parameters.append(stateParameter);
  327. }
  328. // -------------------------------------------------------
  329. // Custom Data
  330. else if (tag.equalsIgnoreCase("customdata") || tag.equalsIgnoreCase("custom-data"))
  331. {
  332. StateCustomData* const stateCustomData(new StateCustomData());
  333. for (XmlElement* xmlSubData = xmlData->getFirstChildElement(); xmlSubData != nullptr; xmlSubData = xmlSubData->getNextElement())
  334. {
  335. const String& cTag(xmlSubData->getTagName());
  336. const String cText(xmlSubData->getAllSubText().trim());
  337. if (cTag.equalsIgnoreCase("type"))
  338. stateCustomData->type = xmlSafeStringCharDup(cText, false);
  339. else if (cTag.equalsIgnoreCase("key"))
  340. stateCustomData->key = xmlSafeStringCharDup(cText, false);
  341. else if (cTag.equalsIgnoreCase("value"))
  342. stateCustomData->value = xmlSafeStringCharDup(cText, false);
  343. }
  344. saveState.customData.append(stateCustomData);
  345. }
  346. // -------------------------------------------------------
  347. // Chunk
  348. else if (tag.equalsIgnoreCase("chunk"))
  349. {
  350. saveState.chunk = xmlSafeStringCharDup(text, false);
  351. }
  352. }
  353. }
  354. }
  355. }
  356. #else
  357. void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode)
  358. {
  359. CARLA_SAFE_ASSERT_RETURN(! xmlNode.isNull(),);
  360. for (QDomNode node = xmlNode.firstChild(); ! node.isNull(); node = node.nextSibling())
  361. {
  362. QString tagName(node.toElement().tagName());
  363. // ---------------------------------------------------------------
  364. // Info
  365. if (tagName.compare("info", Qt::CaseInsensitive) == 0)
  366. {
  367. for (QDomNode xmlInfo = node.toElement().firstChild(); ! xmlInfo.isNull(); xmlInfo = xmlInfo.nextSibling())
  368. {
  369. const QString tag(xmlInfo.toElement().tagName());
  370. const QString text(xmlInfo.toElement().text().trimmed());
  371. if (tag.compare("type", Qt::CaseInsensitive) == 0)
  372. {
  373. saveState.type = xmlSafeStringCharDup(text, false);
  374. }
  375. else if (tag.compare("name", Qt::CaseInsensitive) == 0)
  376. {
  377. saveState.name = xmlSafeStringCharDup(text, false);
  378. }
  379. else if (tag.compare("label", Qt::CaseInsensitive) == 0 || tag.compare("uri", Qt::CaseInsensitive) == 0)
  380. {
  381. saveState.label = xmlSafeStringCharDup(text, false);
  382. }
  383. else if (tag.compare("binary", Qt::CaseInsensitive) == 0 || tag.compare("bundle", Qt::CaseInsensitive) == 0 || tag.compare("filename", Qt::CaseInsensitive) == 0)
  384. {
  385. saveState.binary = xmlSafeStringCharDup(text, false);
  386. }
  387. else if (tag.compare("uniqueid", Qt::CaseInsensitive) == 0)
  388. {
  389. bool ok;
  390. const qlonglong uniqueId(text.toLongLong(&ok));
  391. if (ok) saveState.uniqueId = static_cast<int64_t>(uniqueId);
  392. }
  393. }
  394. }
  395. // ---------------------------------------------------------------
  396. // Data
  397. else if (tagName.compare("data", Qt::CaseInsensitive) == 0)
  398. {
  399. for (QDomNode xmlData = node.toElement().firstChild(); ! xmlData.isNull(); xmlData = xmlData.nextSibling())
  400. {
  401. const QString tag(xmlData.toElement().tagName());
  402. const QString text(xmlData.toElement().text().trimmed());
  403. // -------------------------------------------------------
  404. // Internal Data
  405. if (tag.compare("active", Qt::CaseInsensitive) == 0)
  406. {
  407. saveState.active = (text.compare("yes", Qt::CaseInsensitive) == 0 || text.compare("true", Qt::CaseInsensitive) == 0);
  408. }
  409. else if (tag.compare("drywet", Qt::CaseInsensitive) == 0)
  410. {
  411. bool ok;
  412. const float value(text.toFloat(&ok));
  413. if (ok) saveState.dryWet = carla_fixValue(0.0f, 1.0f, value);
  414. }
  415. else if (tag.compare("volume", Qt::CaseInsensitive) == 0)
  416. {
  417. bool ok;
  418. const float value(text.toFloat(&ok));
  419. if (ok) saveState.volume = carla_fixValue(0.0f, 1.27f, value);
  420. }
  421. else if (tag.compare("balanceleft", Qt::CaseInsensitive) == 0 || tag.compare("balance-left", Qt::CaseInsensitive) == 0)
  422. {
  423. bool ok;
  424. const float value(text.toFloat(&ok));
  425. if (ok) saveState.balanceLeft = carla_fixValue(-1.0f, 1.0f, value);
  426. }
  427. else if (tag.compare("balanceright", Qt::CaseInsensitive) == 0 || tag.compare("balance-right", Qt::CaseInsensitive) == 0)
  428. {
  429. bool ok;
  430. const float value(text.toFloat(&ok));
  431. if (ok) saveState.balanceRight = carla_fixValue(-1.0f, 1.0f, value);
  432. }
  433. else if (tag.compare("panning", Qt::CaseInsensitive) == 0)
  434. {
  435. bool ok;
  436. const float value(text.toFloat(&ok));
  437. if (ok) saveState.panning = carla_fixValue(-1.0f, 1.0f, value);
  438. }
  439. else if (tag.compare("controlchannel", Qt::CaseInsensitive) == 0 || tag.compare("control-channel", Qt::CaseInsensitive) == 0)
  440. {
  441. bool ok;
  442. const short value(text.toShort(&ok));
  443. if (ok && value >= 1 && value <= MAX_MIDI_CHANNELS)
  444. saveState.ctrlChannel = static_cast<int8_t>(value-1);
  445. }
  446. // -------------------------------------------------------
  447. // Program (current)
  448. else if (tag.compare("currentprogramindex", Qt::CaseInsensitive) == 0 || tag.compare("current-program-index", Qt::CaseInsensitive) == 0)
  449. {
  450. bool ok;
  451. const int value(text.toInt(&ok));
  452. if (ok && value >= 1)
  453. saveState.currentProgramIndex = value-1;
  454. }
  455. else if (tag.compare("currentprogramname", Qt::CaseInsensitive) == 0 || tag.compare("current-program-name", Qt::CaseInsensitive) == 0)
  456. {
  457. saveState.currentProgramName = xmlSafeStringCharDup(text, false);
  458. }
  459. // -------------------------------------------------------
  460. // Midi Program (current)
  461. else if (tag.compare("currentmidibank", Qt::CaseInsensitive) == 0 || tag.compare("current-midi-bank", Qt::CaseInsensitive) == 0)
  462. {
  463. bool ok;
  464. const int value(text.toInt(&ok));
  465. if (ok && value >= 1)
  466. saveState.currentMidiBank = value-1;
  467. }
  468. else if (tag.compare("currentmidiprogram", Qt::CaseInsensitive) == 0 || tag.compare("current-midi-program", Qt::CaseInsensitive) == 0)
  469. {
  470. bool ok;
  471. const int value(text.toInt(&ok));
  472. if (ok && value >= 1)
  473. saveState.currentMidiProgram = value-1;
  474. }
  475. // -------------------------------------------------------
  476. // Parameters
  477. else if (tag.compare("parameter", Qt::CaseInsensitive) == 0)
  478. {
  479. StateParameter* const stateParameter(new StateParameter());
  480. for (QDomNode xmlSubData = xmlData.toElement().firstChild(); ! xmlSubData.isNull(); xmlSubData = xmlSubData.nextSibling())
  481. {
  482. const QString pTag(xmlSubData.toElement().tagName());
  483. const QString pText(xmlSubData.toElement().text().trimmed());
  484. if (pTag.compare("index", Qt::CaseInsensitive) == 0)
  485. {
  486. bool ok;
  487. const int index(pText.toInt(&ok));
  488. if (ok && index >= 0) stateParameter->index = index;
  489. }
  490. else if (pTag.compare("name", Qt::CaseInsensitive) == 0)
  491. {
  492. stateParameter->name = xmlSafeStringCharDup(pText, false);
  493. }
  494. else if (pTag.compare("symbol", Qt::CaseInsensitive) == 0)
  495. {
  496. stateParameter->symbol = xmlSafeStringCharDup(pText, false);
  497. }
  498. else if (pTag.compare("value", Qt::CaseInsensitive) == 0)
  499. {
  500. bool ok;
  501. const float value(pText.toFloat(&ok));
  502. if (ok) stateParameter->value = value;
  503. }
  504. else if (pTag.compare("midichannel", Qt::CaseInsensitive) == 0 || pTag.compare("midi-channel", Qt::CaseInsensitive) == 0)
  505. {
  506. bool ok;
  507. const ushort channel(pText.toUShort(&ok));
  508. if (ok && channel >= 1 && channel <= MAX_MIDI_CHANNELS)
  509. stateParameter->midiChannel = static_cast<uint8_t>(channel-1);
  510. }
  511. else if (pTag.compare("midicc", Qt::CaseInsensitive) == 0 || pTag.compare("midi-cc", Qt::CaseInsensitive) == 0)
  512. {
  513. bool ok;
  514. const int cc(pText.toInt(&ok));
  515. if (ok && cc >= 1 && cc < 0x5F)
  516. stateParameter->midiCC = static_cast<int16_t>(cc);
  517. }
  518. }
  519. saveState.parameters.append(stateParameter);
  520. }
  521. // -------------------------------------------------------
  522. // Custom Data
  523. else if (tag.compare("customdata", Qt::CaseInsensitive) == 0 || tag.compare("custom-data", Qt::CaseInsensitive) == 0)
  524. {
  525. StateCustomData* const stateCustomData(new StateCustomData());
  526. for (QDomNode xmlSubData = xmlData.toElement().firstChild(); ! xmlSubData.isNull(); xmlSubData = xmlSubData.nextSibling())
  527. {
  528. const QString cTag(xmlSubData.toElement().tagName());
  529. const QString cText(xmlSubData.toElement().text().trimmed());
  530. if (cTag.compare("type", Qt::CaseInsensitive) == 0)
  531. stateCustomData->type = xmlSafeStringCharDup(cText, false);
  532. else if (cTag.compare("key", Qt::CaseInsensitive) == 0)
  533. stateCustomData->key = xmlSafeStringCharDup(cText, false);
  534. else if (cTag.compare("value", Qt::CaseInsensitive) == 0)
  535. stateCustomData->value = xmlSafeStringCharDup(cText, false);
  536. }
  537. saveState.customData.append(stateCustomData);
  538. }
  539. // -------------------------------------------------------
  540. // Chunk
  541. else if (tag.compare("chunk", Qt::CaseInsensitive) == 0)
  542. {
  543. saveState.chunk = xmlSafeStringCharDup(text, false);
  544. }
  545. }
  546. }
  547. }
  548. }
  549. #endif
  550. // -----------------------------------------------------------------------
  551. // fillXmlStringFromSaveState
  552. #ifdef HAVE_JUCE_LATER
  553. void fillXmlStringFromSaveState(String& content, const SaveState& saveState)
  554. {
  555. {
  556. String info(" <Info>\n");
  557. info << " <Type>" << saveState.type << "</Type>\n";
  558. info << " <Name>" << xmlSafeString(saveState.name, true) << "</Name>\n";
  559. switch (getPluginTypeFromString(saveState.type))
  560. {
  561. case PLUGIN_NONE:
  562. break;
  563. case PLUGIN_INTERNAL:
  564. info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
  565. break;
  566. case PLUGIN_LADSPA:
  567. info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
  568. info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
  569. info << " <UniqueID>" << saveState.uniqueId << "</UniqueID>\n";
  570. break;
  571. case PLUGIN_DSSI:
  572. info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
  573. info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
  574. break;
  575. case PLUGIN_LV2:
  576. info << " <Bundle>" << xmlSafeString(saveState.binary, true) << "</Bundle>\n";
  577. info << " <URI>" << xmlSafeString(saveState.label, true) << "</URI>\n";
  578. break;
  579. case PLUGIN_VST:
  580. info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
  581. info << " <UniqueID>" << saveState.uniqueId << "</UniqueID>\n";
  582. break;
  583. case PLUGIN_VST3:
  584. // TODO?
  585. info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
  586. info << " <UniqueID>" << saveState.uniqueId << "</UniqueID>\n";
  587. break;
  588. case PLUGIN_AU:
  589. // TODO?
  590. info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
  591. info << " <UniqueID>" << saveState.uniqueId << "</UniqueID>\n";
  592. break;
  593. case PLUGIN_JACK:
  594. info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
  595. break;
  596. case PLUGIN_REWIRE:
  597. info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
  598. break;
  599. case PLUGIN_FILE_CSD:
  600. case PLUGIN_FILE_GIG:
  601. case PLUGIN_FILE_SF2:
  602. case PLUGIN_FILE_SFZ:
  603. info << " <Filename>" << xmlSafeString(saveState.binary, true) << "</Filename>\n";
  604. info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
  605. break;
  606. }
  607. info << " </Info>\n\n";
  608. content << info;
  609. }
  610. content << " <Data>\n";
  611. {
  612. String data;
  613. data << " <Active>" << (saveState.active ? "Yes" : "No") << "</Active>\n";
  614. if (saveState.dryWet != 1.0f)
  615. data << " <DryWet>" << saveState.dryWet << "</DryWet>\n";
  616. if (saveState.volume != 1.0f)
  617. data << " <Volume>" << saveState.volume << "</Volume>\n";
  618. if (saveState.balanceLeft != -1.0f)
  619. data << " <Balance-Left>" << saveState.balanceLeft << "</Balance-Left>\n";
  620. if (saveState.balanceRight != 1.0f)
  621. data << " <Balance-Right>" << saveState.balanceRight << "</Balance-Right>\n";
  622. if (saveState.panning != 0.0f)
  623. data << " <Panning>" << saveState.panning << "</Panning>\n";
  624. if (saveState.ctrlChannel < 0)
  625. data << " <ControlChannel>N</ControlChannel>\n";
  626. else
  627. data << " <ControlChannel>" << int(saveState.ctrlChannel+1) << "</ControlChannel>\n";
  628. content << data;
  629. }
  630. for (StateParameterItenerator it = saveState.parameters.begin(); it.valid(); it.next())
  631. {
  632. StateParameter* const stateParameter(it.getValue());
  633. String parameter("\n"" <Parameter>\n");
  634. parameter << " <Index>" << (long)stateParameter->index << "</Index>\n"; // FIXME
  635. parameter << " <Name>" << xmlSafeString(stateParameter->name, true) << "</Name>\n";
  636. if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
  637. parameter << " <Symbol>" << xmlSafeString(stateParameter->symbol, true) << "</Symbol>\n";
  638. if (stateParameter->isInput)
  639. parameter << " <Value>" << stateParameter->value << "</Value>\n";
  640. if (stateParameter->midiCC > 0)
  641. {
  642. parameter << " <MidiCC>" << stateParameter->midiCC << "</MidiCC>\n";
  643. parameter << " <MidiChannel>" << stateParameter->midiChannel+1 << "</MidiChannel>\n";
  644. }
  645. parameter << " </Parameter>\n";
  646. content << parameter;
  647. }
  648. if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr && saveState.currentProgramName[0] != '\0')
  649. {
  650. // ignore 'default' program
  651. if (saveState.currentProgramIndex > 0 || ! String(saveState.currentProgramName).equalsIgnoreCase("default"))
  652. {
  653. String program("\n");
  654. program << " <CurrentProgramIndex>" << saveState.currentProgramIndex+1 << "</CurrentProgramIndex>\n";
  655. program << " <CurrentProgramName>" << xmlSafeString(saveState.currentProgramName, true) << "</CurrentProgramName>\n";
  656. content << program;
  657. }
  658. }
  659. if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0)
  660. {
  661. String midiProgram("\n");
  662. midiProgram << " <CurrentMidiBank>" << saveState.currentMidiBank+1 << "</CurrentMidiBank>\n";
  663. midiProgram << " <CurrentMidiProgram>" << saveState.currentMidiProgram+1 << "</CurrentMidiProgram>\n";
  664. content << midiProgram;
  665. }
  666. for (StateCustomDataItenerator it = saveState.customData.begin(); it.valid(); it.next())
  667. {
  668. StateCustomData* const stateCustomData(it.getValue());
  669. String customData("\n"" <CustomData>\n");
  670. customData << " <Type>" << xmlSafeString(stateCustomData->type, true) << "</Type>\n";
  671. customData << " <Key>" << xmlSafeString(stateCustomData->key, true) << "</Key>\n";
  672. if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
  673. {
  674. customData << " <Value>\n";
  675. customData << xmlSafeString(stateCustomData->value, true);
  676. customData << " </Value>\n";
  677. }
  678. else
  679. {
  680. customData << " <Value>";
  681. customData << xmlSafeString(stateCustomData->value, true);
  682. customData << "</Value>\n";
  683. }
  684. customData << " </CustomData>\n";
  685. content << customData;
  686. }
  687. if (saveState.chunk != nullptr && saveState.chunk[0] != '\0')
  688. {
  689. String chunk("\n"" <Chunk>\n");
  690. chunk << saveState.chunk << "\n </Chunk>\n";
  691. content << chunk;
  692. }
  693. content << " </Data>\n";
  694. }
  695. #else
  696. void fillXmlStringFromSaveState(QString& content, const SaveState& saveState)
  697. {
  698. {
  699. QString info(" <Info>\n");
  700. info += QString(" <Type>%1</Type>\n").arg(saveState.type);
  701. info += QString(" <Name>%1</Name>\n").arg(xmlSafeString(saveState.name, true));
  702. switch (getPluginTypeFromString(saveState.type))
  703. {
  704. case PLUGIN_NONE:
  705. break;
  706. case PLUGIN_INTERNAL:
  707. info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
  708. break;
  709. case PLUGIN_LADSPA:
  710. info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
  711. info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
  712. info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueId);
  713. break;
  714. case PLUGIN_DSSI:
  715. info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
  716. info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
  717. break;
  718. case PLUGIN_LV2:
  719. info += QString(" <Bundle>%1</Bundle>\n").arg(xmlSafeString(saveState.binary, true));
  720. info += QString(" <URI>%1</URI>\n").arg(xmlSafeString(saveState.label, true));
  721. break;
  722. case PLUGIN_VST:
  723. info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
  724. info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueId);
  725. break;
  726. case PLUGIN_VST3:
  727. // TODO?
  728. info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
  729. info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueId);
  730. break;
  731. case PLUGIN_AU:
  732. // TODO?
  733. info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
  734. info += QString(" <UniqueID>%1</UniqueID>\n").arg(saveState.uniqueId);
  735. break;
  736. case PLUGIN_JACK:
  737. info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(saveState.binary, true));
  738. break;
  739. case PLUGIN_REWIRE:
  740. info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
  741. break;
  742. case PLUGIN_FILE_CSD:
  743. case PLUGIN_FILE_GIG:
  744. case PLUGIN_FILE_SF2:
  745. case PLUGIN_FILE_SFZ:
  746. info += QString(" <Filename>%1</Filename>\n").arg(xmlSafeString(saveState.binary, true));
  747. info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(saveState.label, true));
  748. break;
  749. }
  750. info += " </Info>\n\n";
  751. content += info;
  752. }
  753. content += " <Data>\n";
  754. {
  755. QString data;
  756. data += QString(" <Active>%1</Active>\n").arg(saveState.active ? "Yes" : "No");
  757. if (saveState.dryWet != 1.0f)
  758. data += QString(" <DryWet>%1</DryWet>\n").arg(saveState.dryWet, 0, 'g', 7);
  759. if (saveState.volume != 1.0f)
  760. data += QString(" <Volume>%1</Volume>\n").arg(saveState.volume, 0, 'g', 7);
  761. if (saveState.balanceLeft != -1.0f)
  762. data += QString(" <Balance-Left>%1</Balance-Left>\n").arg(saveState.balanceLeft, 0, 'g', 7);
  763. if (saveState.balanceRight != 1.0f)
  764. data += QString(" <Balance-Right>%1</Balance-Right>\n").arg(saveState.balanceRight, 0, 'g', 7);
  765. if (saveState.panning != 0.0f)
  766. data += QString(" <Panning>%1</Panning>\n").arg(saveState.panning, 0, 'g', 7);
  767. if (saveState.ctrlChannel < 0)
  768. data += QString(" <ControlChannel>N</ControlChannel>\n");
  769. else
  770. data += QString(" <ControlChannel>%1</ControlChannel>\n").arg(saveState.ctrlChannel+1);
  771. content += data;
  772. }
  773. for (StateParameterItenerator it = saveState.parameters.begin(); it.valid(); it.next())
  774. {
  775. StateParameter* const stateParameter(it.getValue());
  776. QString parameter("\n"" <Parameter>\n");
  777. parameter += QString(" <Index>%1</Index>\n").arg(stateParameter->index);
  778. parameter += QString(" <Name>%1</Name>\n").arg(xmlSafeString(stateParameter->name, true));
  779. if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
  780. parameter += QString(" <Symbol>%1</Symbol>\n").arg(xmlSafeString(stateParameter->symbol, true));
  781. if (stateParameter->isInput)
  782. parameter += QString(" <Value>%1</Value>\n").arg(stateParameter->value, 0, 'g', 15);
  783. if (stateParameter->midiCC > 0)
  784. {
  785. parameter += QString(" <MidiCC>%1</MidiCC>\n").arg(stateParameter->midiCC);
  786. parameter += QString(" <MidiChannel>%1</MidiChannel>\n").arg(stateParameter->midiChannel+1);
  787. }
  788. parameter += " </Parameter>\n";
  789. content += parameter;
  790. }
  791. if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr && saveState.currentProgramName[0] != '\0')
  792. {
  793. // ignore 'default' program
  794. if (saveState.currentProgramIndex > 0 || QString(saveState.currentProgramName).compare("default", Qt::CaseInsensitive) != 0)
  795. {
  796. QString program("\n");
  797. program += QString(" <CurrentProgramIndex>%1</CurrentProgramIndex>\n").arg(saveState.currentProgramIndex+1);
  798. program += QString(" <CurrentProgramName>%1</CurrentProgramName>\n").arg(xmlSafeString(saveState.currentProgramName, true));
  799. content += program;
  800. }
  801. }
  802. if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0)
  803. {
  804. QString midiProgram("\n");
  805. midiProgram += QString(" <CurrentMidiBank>%1</CurrentMidiBank>\n").arg(saveState.currentMidiBank+1);
  806. midiProgram += QString(" <CurrentMidiProgram>%1</CurrentMidiProgram>\n").arg(saveState.currentMidiProgram+1);
  807. content += midiProgram;
  808. }
  809. for (StateCustomDataItenerator it = saveState.customData.begin(); it.valid(); it.next())
  810. {
  811. StateCustomData* const stateCustomData(it.getValue());
  812. QString customData("\n"" <CustomData>\n");
  813. customData += QString(" <Type>%1</Type>\n").arg(xmlSafeString(stateCustomData->type, true));
  814. customData += QString(" <Key>%1</Key>\n").arg(xmlSafeString(stateCustomData->key, true));
  815. if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
  816. {
  817. customData += " <Value>\n";
  818. customData += QString("%1\n").arg(xmlSafeString(stateCustomData->value, true));
  819. customData += " </Value>\n";
  820. }
  821. else
  822. customData += QString(" <Value>%1</Value>\n").arg(xmlSafeString(stateCustomData->value, true));
  823. customData += " </CustomData>\n";
  824. content += customData;
  825. }
  826. if (saveState.chunk != nullptr && saveState.chunk[0] != '\0')
  827. {
  828. QString chunk("\n"" <Chunk>\n");
  829. chunk += QString("%1\n").arg(saveState.chunk);
  830. chunk += " </Chunk>\n";
  831. content += chunk;
  832. }
  833. content += " </Data>\n";
  834. }
  835. #endif
  836. // -----------------------------------------------------------------------
  837. CARLA_BACKEND_END_NAMESPACE