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.

258 lines
7.8KB

  1. /*
  2. * Carla JSFX utils
  3. * Copyright (C) 2021 Jean Pierre Cimalando
  4. * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation; either version 2 of
  9. * the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  17. */
  18. #ifndef CARLA_JSFX_UTILS_HPP_INCLUDED
  19. #define CARLA_JSFX_UTILS_HPP_INCLUDED
  20. #include "CarlaDefines.h"
  21. #include "CarlaBackend.h"
  22. #include "CarlaUtils.hpp"
  23. #include "CarlaString.hpp"
  24. #include "CarlaBase64Utils.hpp"
  25. #include "CarlaJuceUtils.hpp"
  26. #include "water/files/File.h"
  27. #include "water/files/FileInputStream.h"
  28. #include "water/xml/XmlElement.h"
  29. #include "water/xml/XmlDocument.h"
  30. #include "water/streams/MemoryInputStream.h"
  31. #include "water/streams/MemoryOutputStream.h"
  32. #ifdef YSFX_API
  33. # error YSFX_API is not private
  34. #endif
  35. #include "ysfx/include/ysfx.h"
  36. #include <memory>
  37. CARLA_BACKEND_START_NAMESPACE
  38. // --------------------------------------------------------------------------------------------------------------------
  39. class CarlaJsfxLogging
  40. {
  41. public:
  42. static void logAll(intptr_t, ysfx_log_level level, const char *message)
  43. {
  44. switch (level)
  45. {
  46. case ysfx_log_info:
  47. carla_stdout("%s: %s", ysfx_log_level_string(level), message);
  48. break;
  49. case ysfx_log_warning:
  50. case ysfx_log_error:
  51. carla_stderr("%s: %s", ysfx_log_level_string(level), message);
  52. break;
  53. }
  54. };
  55. static void logErrorsOnly(intptr_t, ysfx_log_level level, const char *message)
  56. {
  57. switch (level)
  58. {
  59. case ysfx_log_info:
  60. break;
  61. case ysfx_log_warning:
  62. case ysfx_log_error:
  63. carla_stderr("%s: %s", ysfx_log_level_string(level), message);
  64. break;
  65. }
  66. };
  67. };
  68. // --------------------------------------------------------------------------------------------------------------------
  69. class CarlaJsfxCategories
  70. {
  71. public:
  72. static PluginCategory getFromEffect(ysfx_t* effect)
  73. {
  74. PluginCategory category = PLUGIN_CATEGORY_OTHER;
  75. water::Array<const char*> tags;
  76. int tagCount = (int)ysfx_get_tags(effect, nullptr, 0);
  77. tags.resize(tagCount);
  78. ysfx_get_tags(effect, tags.getRawDataPointer(), (uint32_t)tagCount);
  79. for (int i = 0; i < tagCount && category == PLUGIN_CATEGORY_OTHER; ++i)
  80. {
  81. water::CharPointer_UTF8 tag(tags[i]);
  82. PluginCategory current = getFromTag(tag);
  83. if (current != PLUGIN_CATEGORY_NONE)
  84. category = current;
  85. }
  86. return category;
  87. }
  88. static PluginCategory getFromTag(const water::CharPointer_UTF8 tag)
  89. {
  90. if (tag.compareIgnoreCase(water::CharPointer_UTF8("synthesis")) == 0)
  91. return PLUGIN_CATEGORY_SYNTH;
  92. if (tag.compareIgnoreCase(water::CharPointer_UTF8("delay")) == 0)
  93. return PLUGIN_CATEGORY_DELAY;
  94. if (tag.compareIgnoreCase(water::CharPointer_UTF8("equalizer")) == 0)
  95. return PLUGIN_CATEGORY_EQ;
  96. if (tag.compareIgnoreCase(water::CharPointer_UTF8("filter")) == 0)
  97. return PLUGIN_CATEGORY_FILTER;
  98. if (tag.compareIgnoreCase(water::CharPointer_UTF8("distortion")) == 0)
  99. return PLUGIN_CATEGORY_DISTORTION;
  100. if (tag.compareIgnoreCase(water::CharPointer_UTF8("dynamics")) == 0)
  101. return PLUGIN_CATEGORY_DYNAMICS;
  102. if (tag.compareIgnoreCase(water::CharPointer_UTF8("modulation")) == 0)
  103. return PLUGIN_CATEGORY_MODULATOR;
  104. if (tag.compareIgnoreCase(water::CharPointer_UTF8("utility")) == 0)
  105. return PLUGIN_CATEGORY_UTILITY;
  106. return PLUGIN_CATEGORY_NONE;
  107. }
  108. };
  109. // -------------------------------------------------------------------------------------------------------------------
  110. class CarlaJsfxState
  111. {
  112. public:
  113. static water::String convertToString(ysfx_state_t &state)
  114. {
  115. water::XmlElement root("JSFXState");
  116. for (uint32_t i = 0; i < state.slider_count; ++i)
  117. {
  118. water::XmlElement *slider = new water::XmlElement("Slider");
  119. slider->setAttribute("index", (int32_t)state.sliders[i].index);
  120. slider->setAttribute("value", state.sliders[i].value);
  121. root.addChildElement(slider);
  122. }
  123. {
  124. const CarlaString base64 = CarlaString::asBase64(state.data, state.data_size);
  125. water::XmlElement *var = new water::XmlElement("Serialization");
  126. var->addTextElement(base64.buffer());
  127. root.addChildElement(var);
  128. }
  129. water::MemoryOutputStream stream;
  130. root.writeToStream(stream, water::StringRef(), true);
  131. return stream.toUTF8();
  132. }
  133. static ysfx_state_u convertFromString(const water::String& string)
  134. {
  135. std::unique_ptr<water::XmlElement> root(water::XmlDocument::parse(string));
  136. CARLA_SAFE_ASSERT_RETURN(root != nullptr, nullptr);
  137. CARLA_SAFE_ASSERT_RETURN(root->getTagName() == "JSFXState", nullptr);
  138. std::vector<ysfx_state_slider_t> sliders;
  139. std::vector<uint8_t> serialization;
  140. sliders.reserve(ysfx_max_sliders);
  141. int numChildren = root->getNumChildElements();
  142. for (int i = 0; i < numChildren; ++i)
  143. {
  144. water::XmlElement* child = root->getChildElement(i);
  145. CARLA_SAFE_ASSERT_CONTINUE(child != nullptr);
  146. if (child->getTagName() == "Slider")
  147. {
  148. CARLA_SAFE_ASSERT_CONTINUE(child->hasAttribute("index"));
  149. CARLA_SAFE_ASSERT_CONTINUE(child->hasAttribute("value"));
  150. ysfx_state_slider_t item;
  151. int32_t index = child->getIntAttribute("index");
  152. CARLA_SAFE_ASSERT_CONTINUE(index >= 0 && index < ysfx_max_sliders);
  153. item.index = (uint32_t)index;
  154. item.value = child->getDoubleAttribute("value");
  155. sliders.push_back(item);
  156. }
  157. else if (child->getTagName() == "Serialization")
  158. {
  159. serialization = carla_getChunkFromBase64String(child->getAllSubText().toRawUTF8());
  160. }
  161. else
  162. {
  163. CARLA_SAFE_ASSERT_CONTINUE(false);
  164. }
  165. }
  166. ysfx_state_t state;
  167. state.sliders = sliders.data();
  168. state.slider_count = (uint32_t)sliders.size();
  169. state.data = serialization.data();
  170. state.data_size = (uint32_t)serialization.size();
  171. return ysfx_state_u(ysfx_state_dup(&state));
  172. }
  173. };
  174. // --------------------------------------------------------------------------------------------------------------------
  175. class CarlaJsfxUnit
  176. {
  177. public:
  178. CarlaJsfxUnit() = default;
  179. CarlaJsfxUnit(const water::File& rootPath, const water::File& filePath)
  180. : fRootPath(rootPath), fFileId(filePath.getRelativePathFrom(rootPath))
  181. {
  182. #ifdef CARLA_OS_WIN
  183. fFileId.replaceCharacter('\\', '/');
  184. #endif
  185. }
  186. explicit operator bool() const
  187. {
  188. return fFileId.isNotEmpty();
  189. }
  190. const water::File& getRootPath() const
  191. {
  192. return fRootPath;
  193. }
  194. const water::String& getFileId() const
  195. {
  196. return fFileId;
  197. }
  198. water::File getFilePath() const
  199. {
  200. return fRootPath.getChildFile(fFileId);
  201. }
  202. private:
  203. water::File fRootPath;
  204. water::String fFileId;
  205. };
  206. // --------------------------------------------------------------------------------------------------------------------
  207. CARLA_BACKEND_END_NAMESPACE
  208. #endif // CARLA_JSFX_UTILS_HPP_INCLUDED