Cleanup
[carla.git] / source / modules / ysfx / sources / ysfx_preset.cpp
blob49aef31014e27bb6f9db6c856b290b960e845f25
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.
15 // SPDX-License-Identifier: Apache-2.0
18 #include "ysfx.h"
19 #include "ysfx_preset.hpp"
20 #include "ysfx_utils.hpp"
21 #include <vector>
22 #include <string>
23 #include <cstring>
25 #include "WDL/lineparse.h"
27 static void ysfx_preset_clear(ysfx_preset_t *preset);
28 static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text);
29 static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data);
31 ysfx_bank_t *ysfx_load_bank(const char *path)
33 #if defined(_WIN32)
34 std::wstring wpath = ysfx::widen(path);
35 ysfx::FILE_u stream{_wfopen(wpath.c_str(), L"rb")};
36 #else
37 ysfx::FILE_u stream{fopen(path, "rb")};
38 #endif
40 if (!stream)
41 return nullptr;
43 std::string input;
44 constexpr uint32_t max_input = 1u << 24;
45 input.reserve(1u << 16);
47 for (int ch; input.size() < max_input && (ch = fgetc(stream.get())) != EOF; ) {
48 ch = (ch == '\r' || ch == '\n') ? ' ' : ch;
49 input.push_back((unsigned char)ch);
52 if (ferror(stream.get()))
53 return nullptr;
55 stream.reset();
56 return ysfx_load_bank_from_rpl_text(input);
59 void ysfx_bank_free(ysfx_bank_t *bank)
61 if (!bank)
62 return;
64 delete[] bank->name;
66 if (ysfx_preset_t *presets = bank->presets) {
67 uint32_t count = bank->preset_count;
68 for (uint32_t i = 0; i < count; ++i)
69 ysfx_preset_clear(&presets[i]);
70 delete[] presets;
73 delete bank;
76 static void ysfx_preset_clear(ysfx_preset_t *preset)
78 delete[] preset->name;
79 preset->name = nullptr;
81 ysfx_state_free(preset->state);
82 preset->state = nullptr;
85 static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text)
87 LineParser parser;
88 if (parser.parse(text.c_str()) < 0)
89 return nullptr;
91 ///
92 std::vector<ysfx_preset_t> preset_list;
93 preset_list.reserve(256);
95 auto list_cleanup = ysfx::defer([&preset_list]() {
96 for (ysfx_preset_t &pst : preset_list)
97 ysfx_preset_clear(&pst);
98 });
101 int ntok = parser.getnumtokens();
102 int itok = 0;
104 if (strcmp("<REAPER_PRESET_LIBRARY", parser.gettoken_str(itok++)) != 0)
105 return nullptr;
107 const char *bank_name = parser.gettoken_str(itok++);
109 while (itok < ntok) {
110 if (strcmp("<PRESET", parser.gettoken_str(itok++)) == 0) {
111 const char *preset_name = parser.gettoken_str(itok++);
113 std::vector<uint8_t> blob;
114 blob.reserve(64 * 1024);
116 for (const char *part; itok < ntok &&
117 strcmp(">", (part = parser.gettoken_str(itok++))) != 0; )
119 std::vector<uint8_t> blobChunk = ysfx::decode_base64(part);
120 blob.insert(blob.end(), blobChunk.begin(), blobChunk.end());
123 preset_list.emplace_back();
124 ysfx_preset_t &preset = preset_list.back();
126 ysfx_parse_preset_from_rpl_blob(&preset, preset_name, blob);
131 ysfx_bank_u bank{new ysfx_bank_t{}};
132 bank->name = ysfx::strdup_using_new(bank_name);
133 bank->presets = new ysfx_preset_t[(uint32_t)preset_list.size()]{};
134 bank->preset_count = (uint32_t)preset_list.size();
136 for (uint32_t i = (uint32_t)preset_list.size(); i-- > 0; ) {
137 bank->presets[i] = preset_list[i];
138 preset_list.pop_back();
141 return bank.release();
144 static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data)
146 ysfx_state_t state{};
147 std::vector<ysfx_state_slider_t> sliders;
149 const char *text = (const char *)data.data();
150 size_t len = data.size();
152 // find the null terminator
153 size_t pos = 0;
154 while (pos < len && data[pos] != 0)
155 ++pos;
157 // skip null terminator if there was one
158 // otherwise null-terminate the text
159 std::string textbuf;
160 if (pos < len)
161 ++pos;
162 else {
163 textbuf.assign(text, len);
164 text = textbuf.c_str();
167 // whatever follows null is the raw serialization
168 state.data = const_cast<uint8_t *>(&data[pos]);
169 state.data_size = len - pos;
171 // parse a line of 64 slider floats (or '-' if missing)
172 LineParser parser;
173 if (parser.parse(text) >= 0) {
174 sliders.reserve(ysfx_max_sliders);
176 for (uint32_t i = 0; i < 64; ++i) {
177 const char *str = parser.gettoken_str(i);
178 bool skip = str[0] == '-' && str[1] == '\0';
179 if (!skip) {
180 ysfx_state_slider_t slider{};
181 slider.index = i;
182 slider.value = (ysfx_real)ysfx::dot_atof(str);
183 sliders.push_back(slider);
187 state.sliders = sliders.data();
188 state.slider_count = (uint32_t)sliders.size();
191 preset->name = ysfx::strdup_using_new(name);
192 preset->state = ysfx_state_dup(&state);