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.

436 lines
13KB

  1. /*
  2. * Carla Backend utils
  3. * Copyright (C) 2011-2013 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 GPL.txt file
  16. */
  17. #ifndef __CARLA_STATE_UTILS_HPP__
  18. #define __CARLA_STATE_UTILS_HPP__
  19. #include "carla_backend.hpp"
  20. #include "carla_utils.hpp"
  21. #include <QtXml/QDomNode>
  22. CARLA_BACKEND_START_NAMESPACE
  23. // -------------------------------------------------
  24. // Carla GUI stuff
  25. struct StateParameter {
  26. uint32_t index;
  27. const char* name;
  28. const char* symbol;
  29. double value;
  30. uint8_t midiChannel;
  31. int16_t midiCC;
  32. StateParameter()
  33. : index(0),
  34. name(nullptr),
  35. symbol(nullptr),
  36. value(0.0),
  37. midiChannel(1),
  38. midiCC(-1) {}
  39. ~StateParameter()
  40. {
  41. if (name != nullptr)
  42. delete[] name;
  43. if (symbol != nullptr)
  44. delete[] symbol;
  45. }
  46. };
  47. struct StateCustomData {
  48. const char* type;
  49. const char* key;
  50. const char* value;
  51. StateCustomData()
  52. : type(nullptr),
  53. key(nullptr),
  54. value(nullptr) {}
  55. ~StateCustomData()
  56. {
  57. if (type != nullptr)
  58. delete[] type;
  59. if (key != nullptr)
  60. delete[] key;
  61. if (value != nullptr)
  62. delete[] value;
  63. }
  64. };
  65. typedef std::vector<StateParameter> StateParameterVector;
  66. typedef std::vector<StateCustomData> StateCustomDataVector;
  67. struct SaveState {
  68. const char* type;
  69. const char* name;
  70. const char* label;
  71. const char* binary;
  72. long uniqueID;
  73. bool active;
  74. double dryWet;
  75. double volume;
  76. double balanceLeft;
  77. double balanceRight;
  78. double panning;
  79. int32_t currentProgramIndex;
  80. const char* currentProgramName;
  81. int32_t currentMidiBank;
  82. int32_t currentMidiProgram;
  83. const char* chunk;
  84. StateParameterVector parameters;
  85. StateCustomDataVector customData;
  86. SaveState()
  87. : type(nullptr),
  88. name(nullptr),
  89. label(nullptr),
  90. binary(nullptr),
  91. uniqueID(0),
  92. active(false),
  93. dryWet(1.0),
  94. volume(1.0),
  95. balanceLeft(-1.0),
  96. balanceRight(1.0),
  97. panning(0.0),
  98. currentProgramIndex(-1),
  99. currentProgramName(nullptr),
  100. currentMidiBank(-1),
  101. currentMidiProgram(-1),
  102. chunk(nullptr) {}
  103. ~SaveState()
  104. {
  105. reset();
  106. }
  107. void reset()
  108. {
  109. if (type != nullptr)
  110. {
  111. delete[] type;
  112. type = nullptr;
  113. }
  114. if (name != nullptr)
  115. {
  116. delete[] name;
  117. name = nullptr;
  118. }
  119. if (label != nullptr)
  120. {
  121. delete[] label;
  122. label = nullptr;
  123. }
  124. if (binary != nullptr)
  125. {
  126. delete[] binary;
  127. binary = nullptr;
  128. }
  129. if (currentProgramName != nullptr)
  130. {
  131. delete[] currentProgramName;
  132. currentProgramName = nullptr;
  133. }
  134. if (chunk != nullptr)
  135. {
  136. delete[] chunk;
  137. chunk = nullptr;
  138. }
  139. uniqueID = 0;
  140. active = false;
  141. dryWet = 1.0;
  142. volume = 1.0;
  143. balanceLeft = -1.0;
  144. balanceRight = 1.0;
  145. panning = 0.0;
  146. currentProgramIndex = -1;
  147. currentMidiBank = -1;
  148. currentMidiProgram = -1;
  149. parameters.clear();
  150. customData.clear();
  151. }
  152. };
  153. // -------------------------------------------------
  154. // Carla XML helpers (xml to state)
  155. static inline
  156. QString xmlSafeString(const QString& string, const bool toXml)
  157. {
  158. QString newString(string);
  159. if (toXml)
  160. return newString.replace("&", "&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
  161. else
  162. return newString.replace("&amp;", "&").replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"");
  163. }
  164. static inline
  165. const char* xmlSafeStringChar(const QString& string, const bool toXml)
  166. {
  167. return carla_strdup(xmlSafeString(string, toXml).toUtf8().constData());
  168. }
  169. static inline
  170. const SaveState& getSaveStateDictFromXML(const QDomNode& xmlNode)
  171. {
  172. static SaveState saveState;
  173. saveState.reset();
  174. QDomNode node(xmlNode.firstChild());
  175. while (! node.isNull())
  176. {
  177. // ------------------------------------------------------
  178. // Info
  179. if (node.toElement().tagName() == "Info")
  180. {
  181. QDomNode xmlInfo(node.toElement().firstChild());
  182. while (! xmlInfo.isNull())
  183. {
  184. const QString tag = xmlInfo.toElement().tagName();
  185. const QString text = xmlInfo.toElement().text(); //.strip();
  186. if (tag == "Type")
  187. saveState.type = xmlSafeStringChar(text, false);
  188. else if (tag == "Name")
  189. saveState.name = xmlSafeStringChar(text, false);
  190. else if (tag == "Label" || tag == "URI")
  191. saveState.label = xmlSafeStringChar(text, false);
  192. else if (tag == "Binary")
  193. saveState.binary = xmlSafeStringChar(text, false);
  194. else if (tag == "UniqueID")
  195. {
  196. bool ok;
  197. long uniqueID = text.toLong(&ok);
  198. if (ok) saveState.uniqueID = uniqueID;
  199. }
  200. xmlInfo = xmlInfo.nextSibling();
  201. }
  202. }
  203. // ------------------------------------------------------
  204. // Data
  205. else if (node.toElement().tagName() == "Data")
  206. {
  207. QDomNode xmlData(node.toElement().firstChild());
  208. while (! xmlData.isNull())
  209. {
  210. const QString tag = xmlData.toElement().tagName();
  211. const QString text = xmlData.toElement().text(); //.strip();
  212. // ----------------------------------------------
  213. // Internal Data
  214. if (tag == "Active")
  215. {
  216. saveState.active = bool(text == "Yes");
  217. }
  218. else if (tag == "DryWet")
  219. {
  220. bool ok;
  221. double value = text.toDouble(&ok);
  222. if (ok) saveState.dryWet = value;
  223. }
  224. else if (tag == "Volume")
  225. {
  226. bool ok;
  227. double value = text.toDouble(&ok);
  228. if (ok) saveState.volume = value;
  229. }
  230. else if (tag == "Balance-Left")
  231. {
  232. bool ok;
  233. double value = text.toDouble(&ok);
  234. if (ok) saveState.balanceLeft = value;
  235. }
  236. else if (tag == "Balance-Right")
  237. {
  238. bool ok;
  239. double value = text.toDouble(&ok);
  240. if (ok) saveState.balanceRight = value;
  241. }
  242. // ----------------------------------------------
  243. // Program (current)
  244. else if (tag == "CurrentProgramIndex")
  245. {
  246. bool ok;
  247. int value = text.toInt(&ok);
  248. if (ok) saveState.currentProgramIndex = value;
  249. }
  250. else if (tag == "CurrentProgramName")
  251. {
  252. saveState.currentProgramName = xmlSafeStringChar(text, false);
  253. }
  254. // ----------------------------------------------
  255. // Midi Program (current)
  256. else if (tag == "CurrentMidiBank")
  257. {
  258. bool ok;
  259. int value = text.toInt(&ok);
  260. if (ok) saveState.currentMidiBank = value;
  261. }
  262. else if (tag == "CurrentMidiProgram")
  263. {
  264. bool ok;
  265. int value = text.toInt(&ok);
  266. if (ok) saveState.currentMidiProgram = value;
  267. }
  268. // ----------------------------------------------
  269. // Parameters
  270. else if (tag == "Parameter")
  271. {
  272. StateParameter stateParameter;
  273. QDomNode xmlSubData(xmlData.toElement().firstChild());
  274. while (! xmlSubData.isNull())
  275. {
  276. const QString pTag = xmlSubData.toElement().tagName();
  277. const QString pText = xmlSubData.toElement().text(); //.strip();
  278. if (pTag == "index")
  279. {
  280. bool ok;
  281. uint index = pText.toUInt(&ok);
  282. if (ok) stateParameter.index = index;
  283. }
  284. else if (pTag == "name")
  285. {
  286. stateParameter.name = xmlSafeStringChar(pText, false);
  287. }
  288. else if (pTag == "symbol")
  289. {
  290. stateParameter.symbol = xmlSafeStringChar(pText, false);
  291. }
  292. else if (pTag == "value")
  293. {
  294. bool ok;
  295. double value = pText.toDouble(&ok);
  296. if (ok) stateParameter.value = value;
  297. }
  298. else if (pTag == "midiChannel")
  299. {
  300. bool ok;
  301. uint channel = pText.toUInt(&ok);
  302. if (ok && channel < 16)
  303. stateParameter.midiChannel = static_cast<uint8_t>(channel);
  304. }
  305. else if (pTag == "midiCC")
  306. {
  307. bool ok;
  308. int cc = pText.toInt(&ok);
  309. if (ok && cc < INT16_MAX)
  310. stateParameter.midiCC = static_cast<int16_t>(cc);
  311. }
  312. xmlSubData = xmlSubData.nextSibling();
  313. }
  314. saveState.parameters.push_back(stateParameter);
  315. }
  316. // ----------------------------------------------
  317. // Custom Data
  318. else if (tag == "CustomData")
  319. {
  320. StateCustomData stateCustomData;
  321. QDomNode xmlSubData(xmlData.toElement().firstChild());
  322. while (! xmlSubData.isNull())
  323. {
  324. const QString cTag = xmlSubData.toElement().tagName();
  325. const QString cText = xmlSubData.toElement().text(); //.strip();
  326. if (cTag == "type")
  327. stateCustomData.type = xmlSafeStringChar(cText, false);
  328. else if (cTag == "key")
  329. stateCustomData.key = xmlSafeStringChar(cText, false);
  330. else if (cTag == "value")
  331. stateCustomData.value = xmlSafeStringChar(cText, false);
  332. xmlSubData = xmlSubData.nextSibling();
  333. }
  334. saveState.customData.push_back(stateCustomData);
  335. }
  336. // ----------------------------------------------
  337. // Chunk
  338. else if (tag == "Chunk")
  339. {
  340. saveState.chunk = xmlSafeStringChar(text, false);
  341. }
  342. // ----------------------------------------------
  343. xmlData = xmlData.nextSibling();
  344. }
  345. }
  346. // ------------------------------------------------------
  347. node = node.nextSibling();
  348. }
  349. return saveState;
  350. }
  351. static inline
  352. QString getXMLFromSaveState(const SaveState& saveState)
  353. {
  354. return "";
  355. // TODO
  356. Q_UNUSED(saveState);
  357. }
  358. // -------------------------------------------------
  359. // Carla XML helpers (state to xml)
  360. // -------------------------------------------------
  361. CARLA_BACKEND_END_NAMESPACE
  362. #endif // __CARLA_STATE_UTILS_HPP__