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.

248 lines
7.7KB

  1. /*
  2. * Carla JSFX utils
  3. * Copyright (C) 2021 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. #ifndef CARLA_JSFX_UTILS_HPP_INCLUDED
  18. #define CARLA_JSFX_UTILS_HPP_INCLUDED
  19. #include "CarlaDefines.h"
  20. #include "CarlaBackend.h"
  21. #include "CarlaUtils.hpp"
  22. #include "CarlaString.hpp"
  23. #include "CarlaBase64Utils.hpp"
  24. #include "CarlaJuceUtils.hpp"
  25. #include "water/files/File.h"
  26. #include "water/files/FileInputStream.h"
  27. #include "water/xml/XmlElement.h"
  28. #include "water/xml/XmlDocument.h"
  29. #include "water/streams/MemoryInputStream.h"
  30. #include "water/streams/MemoryOutputStream.h"
  31. #include "ysfx.h"
  32. #include <memory>
  33. class CarlaJsfxLogging
  34. {
  35. public:
  36. static void logAll(intptr_t, ysfx_log_level level, const char *message)
  37. {
  38. switch (level)
  39. {
  40. case ysfx_log_info:
  41. carla_stdout("%s: %s", ysfx_log_level_string(level), message);
  42. break;
  43. case ysfx_log_warning:
  44. case ysfx_log_error:
  45. carla_stderr("%s: %s", ysfx_log_level_string(level), message);
  46. break;
  47. }
  48. };
  49. static void logErrorsOnly(intptr_t, ysfx_log_level level, const char *message)
  50. {
  51. switch (level)
  52. {
  53. case ysfx_log_info:
  54. break;
  55. case ysfx_log_warning:
  56. case ysfx_log_error:
  57. carla_stderr("%s: %s", ysfx_log_level_string(level), message);
  58. break;
  59. }
  60. };
  61. };
  62. // -------------------------------------------------------------------------------------------------------------------
  63. class CarlaJsfxCategories
  64. {
  65. public:
  66. static CarlaBackend::PluginCategory getFromEffect(ysfx_t* effect)
  67. {
  68. CarlaBackend::PluginCategory category = CarlaBackend::PLUGIN_CATEGORY_OTHER;
  69. water::Array<const char*> tags;
  70. int tagCount = (int)ysfx_get_tags(effect, nullptr, 0);
  71. tags.resize(tagCount);
  72. ysfx_get_tags(effect, tags.getRawDataPointer(), (uint32_t)tagCount);
  73. for (int i = 0; i < tagCount && category == CarlaBackend::PLUGIN_CATEGORY_OTHER; ++i)
  74. {
  75. water::CharPointer_UTF8 tag(tags[i]);
  76. CarlaBackend::PluginCategory current = getFromTag(tag);
  77. if (current != CarlaBackend::PLUGIN_CATEGORY_NONE)
  78. category = current;
  79. }
  80. return category;
  81. }
  82. static CarlaBackend::PluginCategory getFromTag(const water::CharPointer_UTF8 tag)
  83. {
  84. if (tag.compareIgnoreCase(water::CharPointer_UTF8("synthesis")) == 0)
  85. return CarlaBackend::PLUGIN_CATEGORY_SYNTH;
  86. if (tag.compareIgnoreCase(water::CharPointer_UTF8("delay")) == 0)
  87. return CarlaBackend::PLUGIN_CATEGORY_DELAY;
  88. if (tag.compareIgnoreCase(water::CharPointer_UTF8("equalizer")) == 0)
  89. return CarlaBackend::PLUGIN_CATEGORY_EQ;
  90. if (tag.compareIgnoreCase(water::CharPointer_UTF8("filter")) == 0)
  91. return CarlaBackend::PLUGIN_CATEGORY_FILTER;
  92. if (tag.compareIgnoreCase(water::CharPointer_UTF8("distortion")) == 0)
  93. return CarlaBackend::PLUGIN_CATEGORY_DISTORTION;
  94. if (tag.compareIgnoreCase(water::CharPointer_UTF8("dynamics")) == 0)
  95. return CarlaBackend::PLUGIN_CATEGORY_DYNAMICS;
  96. if (tag.compareIgnoreCase(water::CharPointer_UTF8("modulation")) == 0)
  97. return CarlaBackend::PLUGIN_CATEGORY_MODULATOR;
  98. if (tag.compareIgnoreCase(water::CharPointer_UTF8("utility")) == 0)
  99. return CarlaBackend::PLUGIN_CATEGORY_UTILITY;
  100. return CarlaBackend::PLUGIN_CATEGORY_NONE;
  101. }
  102. };
  103. // -------------------------------------------------------------------------------------------------------------------
  104. class CarlaJsfxState
  105. {
  106. public:
  107. static water::String convertToString(ysfx_state_t &state)
  108. {
  109. water::XmlElement root("JSFXState");
  110. for (uint32_t i = 0; i < state.slider_count; ++i)
  111. {
  112. water::XmlElement *slider = new water::XmlElement("Slider");
  113. slider->setAttribute("index", (int32_t)state.sliders[i].index);
  114. slider->setAttribute("value", state.sliders[i].value);
  115. root.addChildElement(slider);
  116. }
  117. {
  118. const CarlaString base64 = CarlaString::asBase64(state.data, state.data_size);
  119. water::XmlElement *var = new water::XmlElement("Serialization");
  120. var->addTextElement(base64.buffer());
  121. root.addChildElement(var);
  122. }
  123. water::MemoryOutputStream stream;
  124. root.writeToStream(stream, water::StringRef(), true);
  125. return stream.toUTF8();
  126. }
  127. static ysfx_state_u convertFromString(const water::String& string)
  128. {
  129. std::unique_ptr<water::XmlElement> root(water::XmlDocument::parse(string));
  130. CARLA_SAFE_ASSERT_RETURN(root != nullptr, nullptr);
  131. CARLA_SAFE_ASSERT_RETURN(root->getTagName() == "JSFXState", nullptr);
  132. std::vector<ysfx_state_slider_t> sliders;
  133. std::vector<uint8_t> serialization;
  134. sliders.reserve(ysfx_max_sliders);
  135. int numChildren = root->getNumChildElements();
  136. for (int i = 0; i < numChildren; ++i)
  137. {
  138. water::XmlElement* child = root->getChildElement(i);
  139. CARLA_SAFE_ASSERT_CONTINUE(child != nullptr);
  140. if (child->getTagName() == "Slider")
  141. {
  142. CARLA_SAFE_ASSERT_CONTINUE(child->hasAttribute("index"));
  143. CARLA_SAFE_ASSERT_CONTINUE(child->hasAttribute("value"));
  144. ysfx_state_slider_t item;
  145. int32_t index = child->getIntAttribute("index");
  146. CARLA_SAFE_ASSERT_CONTINUE(index >= 0 && index < ysfx_max_sliders);
  147. item.index = (uint32_t)index;
  148. item.value = child->getDoubleAttribute("value");
  149. sliders.push_back(item);
  150. }
  151. else if (child->getTagName() == "Serialization")
  152. {
  153. serialization = carla_getChunkFromBase64String(child->getAllSubText().toRawUTF8());
  154. }
  155. else
  156. {
  157. CARLA_SAFE_ASSERT_CONTINUE(false);
  158. }
  159. }
  160. ysfx_state_t state;
  161. state.sliders = sliders.data();
  162. state.slider_count = (uint32_t)sliders.size();
  163. state.data = serialization.data();
  164. state.data_size = (uint32_t)serialization.size();
  165. return ysfx_state_u(ysfx_state_dup(&state));
  166. }
  167. };
  168. // -------------------------------------------------------------------------------------------------------------------
  169. class CarlaJsfxUnit
  170. {
  171. public:
  172. CarlaJsfxUnit() = default;
  173. CarlaJsfxUnit(const water::File& rootPath, const water::File& filePath)
  174. : fRootPath(rootPath), fFileId(filePath.getRelativePathFrom(rootPath))
  175. {
  176. #ifdef CARLA_OS_WIN
  177. fFileId.replaceCharacter('\\', '/');
  178. #endif
  179. }
  180. explicit operator bool() const
  181. {
  182. return fFileId.isNotEmpty();
  183. }
  184. const water::File& getRootPath() const
  185. {
  186. return fRootPath;
  187. }
  188. const water::String& getFileId() const
  189. {
  190. return fFileId;
  191. }
  192. water::File getFilePath() const
  193. {
  194. return fRootPath.getChildFile(fFileId);
  195. }
  196. private:
  197. water::File fRootPath;
  198. water::String fFileId;
  199. };
  200. // -------------------------------------------------------------------------------------------------------------------
  201. #endif // CARLA_JSFX_UTILS_HPP_INCLUDED