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.

194 lines
5.4KB

  1. // Copyright 2021 Jean Pierre Cimalando
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // SPDX-License-Identifier: Apache-2.0
  16. //
  17. #include "ysfx.h"
  18. #include "ysfx_preset.hpp"
  19. #include "ysfx_utils.hpp"
  20. #include <vector>
  21. #include <string>
  22. #include <cstring>
  23. #include "WDL/lineparse.h"
  24. static void ysfx_preset_clear(ysfx_preset_t *preset);
  25. static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text);
  26. static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data);
  27. ysfx_bank_t *ysfx_load_bank(const char *path)
  28. {
  29. #if defined(_WIN32)
  30. std::wstring wpath = ysfx::widen(path);
  31. ysfx::FILE_u stream{_wfopen(wpath.c_str(), L"rb")};
  32. #else
  33. ysfx::FILE_u stream{fopen(path, "rb")};
  34. #endif
  35. if (!stream)
  36. return nullptr;
  37. std::string input;
  38. constexpr uint32_t max_input = 1u << 24;
  39. input.reserve(1u << 16);
  40. for (int ch; input.size() < max_input && (ch = fgetc(stream.get())) != EOF; ) {
  41. ch = (ch == '\r' || ch == '\n') ? ' ' : ch;
  42. input.push_back((unsigned char)ch);
  43. }
  44. if (ferror(stream.get()))
  45. return nullptr;
  46. stream.reset();
  47. return ysfx_load_bank_from_rpl_text(input);
  48. }
  49. void ysfx_bank_free(ysfx_bank_t *bank)
  50. {
  51. if (!bank)
  52. return;
  53. delete[] bank->name;
  54. if (ysfx_preset_t *presets = bank->presets) {
  55. uint32_t count = bank->preset_count;
  56. for (uint32_t i = 0; i < count; ++i)
  57. ysfx_preset_clear(&presets[i]);
  58. delete[] presets;
  59. }
  60. delete bank;
  61. }
  62. static void ysfx_preset_clear(ysfx_preset_t *preset)
  63. {
  64. delete[] preset->name;
  65. preset->name = nullptr;
  66. ysfx_state_free(preset->state);
  67. preset->state = nullptr;
  68. }
  69. static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text)
  70. {
  71. LineParser parser;
  72. if (parser.parse(text.c_str()) < 0)
  73. return nullptr;
  74. ///
  75. std::vector<ysfx_preset_t> preset_list;
  76. preset_list.reserve(256);
  77. auto list_cleanup = ysfx::defer([&preset_list]() {
  78. for (ysfx_preset_t &pst : preset_list)
  79. ysfx_preset_clear(&pst);
  80. });
  81. ///
  82. int ntok = parser.getnumtokens();
  83. int itok = 0;
  84. if (strcmp("<REAPER_PRESET_LIBRARY", parser.gettoken_str(itok++)) != 0)
  85. return nullptr;
  86. const char *bank_name = parser.gettoken_str(itok++);
  87. while (itok < ntok) {
  88. if (strcmp("<PRESET", parser.gettoken_str(itok++)) == 0) {
  89. const char *preset_name = parser.gettoken_str(itok++);
  90. std::vector<uint8_t> blob;
  91. blob.reserve(64 * 1024);
  92. for (const char *part; itok < ntok &&
  93. strcmp(">", (part = parser.gettoken_str(itok++))) != 0; )
  94. {
  95. std::vector<uint8_t> blobChunk = ysfx::decode_base64(part);
  96. blob.insert(blob.end(), blobChunk.begin(), blobChunk.end());
  97. }
  98. preset_list.emplace_back();
  99. ysfx_preset_t &preset = preset_list.back();
  100. ysfx_parse_preset_from_rpl_blob(&preset, preset_name, blob);
  101. }
  102. }
  103. ///
  104. ysfx_bank_u bank{new ysfx_bank_t{}};
  105. bank->name = ysfx::strdup_using_new(bank_name);
  106. bank->presets = new ysfx_preset_t[(uint32_t)preset_list.size()]{};
  107. bank->preset_count = (uint32_t)preset_list.size();
  108. for (uint32_t i = (uint32_t)preset_list.size(); i-- > 0; ) {
  109. bank->presets[i] = preset_list[i];
  110. preset_list.pop_back();
  111. }
  112. return bank.release();
  113. }
  114. static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data)
  115. {
  116. ysfx_state_t state{};
  117. std::vector<ysfx_state_slider_t> sliders;
  118. const char *text = (const char *)data.data();
  119. size_t len = data.size();
  120. // find the null terminator
  121. size_t pos = 0;
  122. while (pos < len && data[pos] != 0)
  123. ++pos;
  124. // skip null terminator if there was one
  125. // otherwise null-terminate the text
  126. std::string textbuf;
  127. if (pos < len)
  128. ++pos;
  129. else {
  130. textbuf.assign(text, len);
  131. text = textbuf.c_str();
  132. }
  133. // whatever follows null is the raw serialization
  134. state.data = const_cast<uint8_t *>(&data[pos]);
  135. state.data_size = len - pos;
  136. // parse a line of 64 slider floats (or '-' if missing)
  137. LineParser parser;
  138. if (parser.parse(text) >= 0) {
  139. sliders.reserve(ysfx_max_sliders);
  140. for (uint32_t i = 0; i < 64; ++i) {
  141. const char *str = parser.gettoken_str(i);
  142. bool skip = str[0] == '-' && str[1] == '\0';
  143. if (!skip) {
  144. ysfx_state_slider_t slider{};
  145. slider.index = i;
  146. slider.value = (ysfx_real)ysfx::dot_atof(str);
  147. sliders.push_back(slider);
  148. }
  149. }
  150. state.sliders = sliders.data();
  151. state.slider_count = (uint32_t)sliders.size();
  152. }
  153. preset->name = ysfx::strdup_using_new(name);
  154. preset->state = ysfx_state_dup(&state);
  155. }