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.

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