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.

1541 lines
44KB

  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.hpp"
  18. #include "ysfx_config.hpp"
  19. #include "ysfx_eel_utils.hpp"
  20. #include <type_traits>
  21. #include <algorithm>
  22. #include <functional>
  23. #include <deque>
  24. #include <set>
  25. #include <new>
  26. #include <stdexcept>
  27. #include <cstring>
  28. #include <cassert>
  29. static_assert(std::is_same<EEL_F, ysfx_real>::value,
  30. "ysfx_real is incorrectly defined");
  31. enum {
  32. ysfx_max_file_handles = 64, // change if it needs more
  33. };
  34. //------------------------------------------------------------------------------
  35. static thread_local ysfx_thread_id_t ysfx_thread_id;
  36. ysfx_thread_id_t ysfx_get_thread_id()
  37. {
  38. return ysfx_thread_id;
  39. }
  40. void ysfx_set_thread_id(ysfx_thread_id_t id)
  41. {
  42. ysfx_thread_id = id;
  43. }
  44. //------------------------------------------------------------------------------
  45. struct ysfx_api_initializer {
  46. private:
  47. ysfx_api_initializer();
  48. ~ysfx_api_initializer();
  49. public:
  50. static void init_once();
  51. };
  52. ysfx_api_initializer::ysfx_api_initializer()
  53. {
  54. if (NSEEL_init() != 0)
  55. throw std::runtime_error("NSEEL_init");
  56. ysfx_api_init_eel();
  57. ysfx_api_init_reaper();
  58. ysfx_api_init_file();
  59. ysfx_api_init_gfx();
  60. }
  61. ysfx_api_initializer::~ysfx_api_initializer()
  62. {
  63. NSEEL_quit();
  64. }
  65. void ysfx_api_initializer::init_once()
  66. {
  67. static ysfx_api_initializer init;
  68. }
  69. //------------------------------------------------------------------------------
  70. ysfx_t *ysfx_new(ysfx_config_t *config)
  71. {
  72. ysfx_u fx{new ysfx_t};
  73. ysfx_config_add_ref(config);
  74. fx->config.reset(config);
  75. fx->string_ctx.reset(ysfx_eel_string_context_new());
  76. ysfx_api_initializer::init_once();
  77. NSEEL_VMCTX vm = NSEEL_VM_alloc();
  78. if (!vm)
  79. throw std::bad_alloc();
  80. fx->vm.reset(vm);
  81. NSEEL_VM_SetCustomFuncThis(vm, fx.get());
  82. ysfx_eel_string_initvm(vm);
  83. #if !defined(YSFX_NO_GFX)
  84. fx->gfx.state.reset(ysfx_gfx_state_new(fx.get()));
  85. #endif
  86. auto var_resolver = [](void *userdata, const char *name) -> EEL_F * {
  87. ysfx_t *fx = (ysfx_t *)userdata;
  88. auto it = fx->source.slider_alias.find(name);
  89. if (it != fx->source.slider_alias.end())
  90. return fx->var.slider[it->second];
  91. return nullptr;
  92. };
  93. NSEEL_VM_set_var_resolver(vm, var_resolver, fx.get());
  94. for (uint32_t i = 0; i < ysfx_max_channels; ++i) {
  95. std::string name = "spl" + std::to_string(i);
  96. EEL_F *var = NSEEL_VM_regvar(vm, name.c_str());
  97. *(fx->var.spl[i] = var) = 0;
  98. }
  99. for (uint32_t i = 0; i < ysfx_max_sliders; ++i) {
  100. std::string name = "slider" + std::to_string(i + 1);
  101. EEL_F *var = NSEEL_VM_regvar(vm, name.c_str());
  102. *(fx->var.slider[i] = var) = 0;
  103. fx->slider_of_var[var] = i;
  104. }
  105. #define AUTOVAR(name, value) *(fx->var.name = NSEEL_VM_regvar(vm, #name)) = (value)
  106. AUTOVAR(srate, fx->sample_rate);
  107. AUTOVAR(num_ch, fx->valid_input_channels);
  108. AUTOVAR(samplesblock, fx->block_size);
  109. AUTOVAR(trigger, 0);
  110. AUTOVAR(tempo, 120);
  111. AUTOVAR(play_state, 1);
  112. AUTOVAR(play_position, 0);
  113. AUTOVAR(beat_position, 0);
  114. AUTOVAR(ts_num, 0);
  115. AUTOVAR(ts_denom, 4);
  116. AUTOVAR(ext_noinit, 0);
  117. AUTOVAR(ext_nodenorm, 0);
  118. AUTOVAR(ext_midi_bus, 0);
  119. AUTOVAR(midi_bus, 0);
  120. AUTOVAR(pdc_delay, 0);
  121. AUTOVAR(pdc_bot_ch, 0);
  122. AUTOVAR(pdc_top_ch, 0);
  123. AUTOVAR(pdc_midi, 0);
  124. // gfx variables
  125. AUTOVAR(gfx_r, 0);
  126. AUTOVAR(gfx_g, 0);
  127. AUTOVAR(gfx_b, 0);
  128. AUTOVAR(gfx_a, 0);
  129. AUTOVAR(gfx_a2, 0);
  130. AUTOVAR(gfx_w, 0);
  131. AUTOVAR(gfx_h, 0);
  132. AUTOVAR(gfx_x, 0);
  133. AUTOVAR(gfx_y, 0);
  134. AUTOVAR(gfx_mode, 0);
  135. AUTOVAR(gfx_clear, 0);
  136. AUTOVAR(gfx_texth, 0);
  137. AUTOVAR(gfx_dest, 0);
  138. AUTOVAR(gfx_ext_retina, 0);
  139. AUTOVAR(mouse_x, 0);
  140. AUTOVAR(mouse_y, 0);
  141. AUTOVAR(mouse_cap, 0);
  142. AUTOVAR(mouse_wheel, 0);
  143. AUTOVAR(mouse_hwheel, 0);
  144. #undef AUTOVAR
  145. fx->midi.in.reset(new ysfx_midi_buffer_t);
  146. fx->midi.out.reset(new ysfx_midi_buffer_t);
  147. ysfx_set_midi_capacity(fx.get(), 1024, true);
  148. fx->file.list.reserve(16);
  149. fx->file.list.emplace_back(new ysfx_serializer_t(fx->vm.get()));
  150. return fx.release();
  151. }
  152. void ysfx_free(ysfx_t *fx)
  153. {
  154. if (!fx)
  155. return;
  156. if (fx->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1)
  157. delete fx;
  158. }
  159. void ysfx_add_ref(ysfx_t *fx)
  160. {
  161. fx->ref_count.fetch_add(1, std::memory_order_relaxed);
  162. }
  163. ysfx_config_t *ysfx_get_config(ysfx_t *fx)
  164. {
  165. return fx->config.get();
  166. }
  167. bool ysfx_load_file(ysfx_t *fx, const char *filepath, uint32_t loadopts)
  168. {
  169. ysfx_unload(fx);
  170. //--------------------------------------------------------------------------
  171. // failure guard
  172. auto fail_guard = ysfx::defer([fx]() { ysfx_unload_source(fx); });
  173. //--------------------------------------------------------------------------
  174. // load the main file
  175. ysfx::file_uid main_uid;
  176. {
  177. ysfx_source_unit_u main{new ysfx_source_unit_t};
  178. ysfx::FILE_u stream{ysfx::fopen_utf8(filepath, "rb")};
  179. if (!stream || !ysfx::get_stream_file_uid(stream.get(), main_uid)) {
  180. ysfx_logf(*fx->config, ysfx_log_error, "%s: cannot open file for reading", ysfx::path_file_name(filepath).c_str());
  181. return false;
  182. }
  183. ysfx::stdio_text_reader reader(stream.get());
  184. ysfx_parse_error error;
  185. if (!ysfx_parse_toplevel(reader, main->toplevel, &error)) {
  186. ysfx_logf(*fx->config, ysfx_log_error, "%s:%u: %s", ysfx::path_file_name(filepath).c_str(), error.line + 1, error.message.c_str());
  187. return false;
  188. }
  189. ysfx_parse_header(main->toplevel.header.get(), main->header);
  190. // validity check
  191. if (main->header.desc.empty()) {
  192. ysfx_logf(*fx->config, ysfx_log_warning, "%s: the required `desc` field is missing", ysfx::path_file_name(filepath).c_str());
  193. main->header.desc = ysfx::path_file_name(filepath);
  194. }
  195. if (loadopts & ysfx_load_ignoring_imports)
  196. main->header.imports.clear();
  197. // if no pins are specified and we have @sample, the default is stereo
  198. if (main->toplevel.sample && !main->header.explicit_pins &&
  199. main->header.in_pins.empty() && main->header.out_pins.empty())
  200. {
  201. main->header.in_pins = {"JS input 1", "JS input 2"};
  202. main->header.out_pins = {"JS output 1", "JS output 2"};
  203. }
  204. // register variables aliased to sliders
  205. for (uint32_t i = 0; i < ysfx_max_sliders; ++i) {
  206. if (main->header.sliders[i].exists) {
  207. if (!main->header.sliders[i].var.empty())
  208. fx->source.slider_alias.insert({main->header.sliders[i].var, i});
  209. }
  210. }
  211. fx->source.main = std::move(main);
  212. fx->source.main_file_path.assign(filepath);
  213. // find the bank file, if present
  214. ysfx::case_resolve(
  215. ysfx::path_directory(filepath).c_str(),
  216. (ysfx::path_file_name(filepath) + ".rpl").c_str(),
  217. fx->source.bank_path);
  218. // fill the file enums with the contents of directories
  219. ysfx_fill_file_enums(fx);
  220. // find incorrect enums and fix them
  221. ysfx_fix_invalid_enums(fx);
  222. // set the initial mask of visible sliders
  223. ysfx_update_slider_visibility_mask(fx);
  224. }
  225. //--------------------------------------------------------------------------
  226. // load the imports
  227. // we load the imports recursively using post-order
  228. static constexpr uint32_t max_import_level = 32;
  229. std::set<ysfx::file_uid> seen;
  230. std::function<bool(const std::string &, const std::string &, uint32_t)> do_next_import =
  231. [fx, &seen, &do_next_import]
  232. (const std::string &name, const std::string &origin, uint32_t level) -> bool
  233. {
  234. if (level >= max_import_level) {
  235. ysfx_logf(*fx->config, ysfx_log_error, "%s: %s", ysfx::path_file_name(origin.c_str()).c_str(), "too many import levels");
  236. return false;
  237. }
  238. std::string imported_path = ysfx_resolve_import_path(fx, name, origin);
  239. if (imported_path.empty()) {
  240. ysfx_logf(*fx->config, ysfx_log_error, "%s: cannot find import: %s", ysfx::path_file_name(origin.c_str()).c_str(), name.c_str());
  241. return false;
  242. }
  243. ysfx::file_uid imported_uid;
  244. ysfx::FILE_u stream{ysfx::fopen_utf8(imported_path.c_str(), "rb")};
  245. if (!stream || !ysfx::get_stream_file_uid(stream.get(), imported_uid)) {
  246. ysfx_logf(*fx->config, ysfx_log_error, "%s: cannot open file for reading", ysfx::path_file_name(imported_path.c_str()).c_str());
  247. return false;
  248. }
  249. // this file was already visited, skip
  250. if (!seen.insert(imported_uid).second)
  251. return true;
  252. // parse it
  253. ysfx_source_unit_u unit{new ysfx_source_unit_t};
  254. ysfx::stdio_text_reader reader(stream.get());
  255. ysfx_parse_error error;
  256. if (!ysfx_parse_toplevel(reader, unit->toplevel, &error)) {
  257. ysfx_logf(*fx->config, ysfx_log_error, "%s:%u: %s", ysfx::path_file_name(imported_path.c_str()).c_str(), error.line + 1, error.message.c_str());
  258. return false;
  259. }
  260. ysfx_parse_header(unit->toplevel.header.get(), unit->header);
  261. // process the imported dependencies, *first*
  262. for (const std::string &name : unit->header.imports) {
  263. if (!do_next_import(name, imported_path.c_str(), level + 1))
  264. return false;
  265. }
  266. // add it to the import sources, *second*
  267. fx->source.imports.push_back(std::move(unit));
  268. return true;
  269. };
  270. for (const std::string &name : fx->source.main->header.imports) {
  271. if (!do_next_import(name, filepath, 0))
  272. return false;
  273. }
  274. //--------------------------------------------------------------------------
  275. // initialize the sliders to defaults
  276. for (uint32_t i = 0; i < ysfx_max_sliders; ++i)
  277. *fx->var.slider[i] = fx->source.main->header.sliders[i].def;
  278. //--------------------------------------------------------------------------
  279. fail_guard.disarm();
  280. return true;
  281. }
  282. bool ysfx_compile(ysfx_t *fx, uint32_t compileopts)
  283. {
  284. ysfx_unload_code(fx);
  285. if (!fx->source.main) {
  286. ysfx_logf(*fx->config, ysfx_log_error, "???: no source is loaded, cannot compile");
  287. return false;
  288. }
  289. //--------------------------------------------------------------------------
  290. // failure guard
  291. auto fail_guard = ysfx::defer([fx]() { ysfx_unload_code(fx); });
  292. //--------------------------------------------------------------------------
  293. // configure VM
  294. NSEEL_VMCTX vm = fx->vm.get();
  295. {
  296. uint32_t maxmem = fx->source.main->header.options.maxmem;
  297. if (maxmem == 0)
  298. maxmem = 8 * 1024 * 1024;
  299. if (maxmem > 32 * 1024 * 1024)
  300. maxmem = 32 * 1024 * 1024;
  301. NSEEL_VM_setramsize(vm, (int)maxmem);
  302. }
  303. //--------------------------------------------------------------------------
  304. // compile
  305. auto compile_section =
  306. [fx](ysfx_section_t *section, const char *name, NSEEL_CODEHANDLE_u &dest) -> bool
  307. {
  308. NSEEL_VMCTX vm = fx->vm.get();
  309. if (section->text.empty()) {
  310. // NOTE: check for empty source, which would return null code
  311. dest.reset();
  312. return true;
  313. }
  314. NSEEL_CODEHANDLE_u code{NSEEL_code_compile_ex(vm, section->text.c_str(), section->line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS)};
  315. if (!code) {
  316. ysfx_logf(*fx->config, ysfx_log_error, "%s: %s", name, NSEEL_code_getcodeerror(vm));
  317. return false;
  318. }
  319. dest = std::move(code);
  320. return true;
  321. };
  322. // compile the multiple @init sections, imports first
  323. {
  324. std::vector<ysfx_section_t *> secs;
  325. secs.reserve(fx->source.imports.size() + 1);
  326. // collect init sections: imports first, main second
  327. for (size_t i = 0; i < fx->source.imports.size(); ++i)
  328. secs.push_back(fx->source.imports[i]->toplevel.init.get());
  329. secs.push_back(fx->source.main->toplevel.init.get());
  330. for (ysfx_section_t *sec : secs) {
  331. NSEEL_CODEHANDLE_u code;
  332. if (sec && !compile_section(sec, "@init", code))
  333. return false;
  334. fx->code.init.push_back(std::move(code));
  335. }
  336. }
  337. // compile the other sections, single
  338. // a non-@init section is searched in the main file first;
  339. // if not found, it's inherited from the first import which has it.
  340. ysfx_section_t *slider = ysfx_search_section(fx, ysfx_section_slider);
  341. ysfx_section_t *block = ysfx_search_section(fx, ysfx_section_block);
  342. ysfx_section_t *sample = ysfx_search_section(fx, ysfx_section_sample);
  343. ysfx_section_t *gfx = nullptr;
  344. ysfx_section_t *serialize = nullptr;
  345. if ((compileopts & ysfx_compile_no_gfx) == 0)
  346. gfx = ysfx_search_section(fx, ysfx_section_gfx);
  347. if ((compileopts & ysfx_compile_no_serialize) == 0)
  348. serialize = ysfx_search_section(fx, ysfx_section_serialize);
  349. if (slider && !compile_section(slider, "@slider", fx->code.slider))
  350. return false;
  351. if (block && !compile_section(block, "@block", fx->code.block))
  352. return false;
  353. if (sample && !compile_section(sample, "@sample", fx->code.sample))
  354. return false;
  355. if (gfx && !compile_section(gfx, "@gfx", fx->code.gfx))
  356. return false;
  357. if (serialize && !compile_section(serialize, "@serialize", fx->code.serialize))
  358. return false;
  359. fx->code.compiled = true;
  360. fx->is_freshly_compiled = true;
  361. fx->must_compute_init = true;
  362. ///
  363. ysfx_eel_string_context_update_named_vars(fx->string_ctx.get(), vm);
  364. fail_guard.disarm();
  365. return true;
  366. }
  367. bool ysfx_is_compiled(ysfx_t *fx)
  368. {
  369. return fx->code.compiled;
  370. }
  371. void ysfx_unload_source(ysfx_t *fx)
  372. {
  373. fx->source = {};
  374. }
  375. void ysfx_unload_code(ysfx_t *fx)
  376. {
  377. #if !defined(YSFX_NO_GFX)
  378. // get rid of gfx first, to prevent a UI thread from trying
  379. // to access VM and invoke code
  380. {
  381. std::lock_guard<ysfx::mutex> lock{fx->gfx.mutex};
  382. fx->gfx.ready = false;
  383. fx->gfx.wants_retina = false;
  384. fx->gfx.must_init.store(false);
  385. }
  386. #endif
  387. fx->code = {};
  388. fx->is_freshly_compiled = false;
  389. fx->must_compute_init = false;
  390. fx->must_compute_slider = false;
  391. NSEEL_VMCTX vm = fx->vm.get();
  392. NSEEL_code_compile_ex(vm, nullptr, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET);
  393. NSEEL_VM_remove_unused_vars(vm);
  394. NSEEL_VM_remove_all_nonreg_vars(vm);
  395. NSEEL_VM_freeRAM(vm);
  396. }
  397. void ysfx_unload(ysfx_t *fx)
  398. {
  399. ysfx_unload_code(fx);
  400. ysfx_unload_source(fx);
  401. }
  402. bool ysfx_is_loaded(ysfx_t *fx)
  403. {
  404. return fx->source.main != nullptr;
  405. }
  406. void ysfx_fill_file_enums(ysfx_t *fx)
  407. {
  408. if (fx->config->data_root.empty())
  409. return;
  410. for (uint32_t i = 0; i < ysfx_max_sliders; ++i) {
  411. ysfx_slider_t &slider = fx->source.main->header.sliders[i];
  412. if (slider.path.empty())
  413. continue;
  414. std::string dirpath = ysfx::path_ensure_final_separator((fx->config->data_root + slider.path).c_str());
  415. ysfx::string_list entries = ysfx::list_directory(dirpath.c_str());
  416. for (const std::string &filename : entries) {
  417. if (!filename.empty() && ysfx::is_path_separator(filename.back()))
  418. continue;
  419. std::string filepath = dirpath + filename;
  420. ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), nullptr);
  421. if (ftype == ysfx_file_type_none)
  422. continue;
  423. slider.enum_names.push_back(std::move(filename));
  424. }
  425. if (!slider.enum_names.empty())
  426. slider.max = (EEL_F)(slider.enum_names.size() - 1);
  427. }
  428. }
  429. void ysfx_fix_invalid_enums(ysfx_t *fx)
  430. {
  431. //NOTE: regardless of the range of enum sliders in source, it is <0,N-1,1>
  432. // if there is a mismatch, correct and output a warning
  433. for (uint32_t i = 0; i < ysfx_max_sliders; ++i) {
  434. ysfx_slider_t &slider = fx->source.main->header.sliders[i];
  435. if (!slider.is_enum)
  436. continue;
  437. uint32_t count = (uint32_t)slider.enum_names.size();
  438. if (count == 0) {
  439. bool is_file = !slider.path.empty();
  440. ysfx_logf(*fx->config, ysfx_log_warning, "slider%u: the enumeration does not contain any %s", i + 1, is_file ? "files" : "items");
  441. slider.enum_names.emplace_back();
  442. slider.min = 0;
  443. slider.max = 0;
  444. slider.inc = 1;
  445. }
  446. else if (slider.min != 0 || slider.inc != 1 || slider.max != (EEL_F)(count - 1)) {
  447. ysfx_logf(*fx->config, ysfx_log_warning, "slider%u: the enumeration has an invalid range", i + 1);
  448. slider.min = 0;
  449. slider.max = (EEL_F)(count - 1);
  450. slider.inc = 1;
  451. }
  452. }
  453. }
  454. const char *ysfx_get_name(ysfx_t *fx)
  455. {
  456. ysfx_source_unit_t *main = fx->source.main.get();
  457. if (!main)
  458. return "";
  459. return main->header.desc.c_str();
  460. }
  461. const char *ysfx_get_file_path(ysfx_t *fx)
  462. {
  463. return fx->source.main_file_path.c_str();
  464. }
  465. const char *ysfx_get_author(ysfx_t *fx)
  466. {
  467. ysfx_source_unit_t *main = fx->source.main.get();
  468. if (!main)
  469. return "";
  470. return main->header.author.c_str();
  471. }
  472. uint32_t ysfx_get_tags(ysfx_t *fx, const char **dest, uint32_t destsize)
  473. {
  474. ysfx_source_unit_t *main = fx->source.main.get();
  475. if (!main)
  476. return 0;
  477. uint32_t count = (uint32_t)main->header.tags.size();
  478. uint32_t copysize = (destsize < count) ? destsize : count;
  479. for (uint32_t i = 0; i < copysize; ++i)
  480. dest[i] = main->header.tags[i].c_str();
  481. return count;
  482. }
  483. const char *ysfx_get_tag(ysfx_t *fx, uint32_t index)
  484. {
  485. ysfx_source_unit_t *main = fx->source.main.get();
  486. if (!main || index >= main->header.tags.size())
  487. return "";
  488. return main->header.tags[index].c_str();
  489. }
  490. uint32_t ysfx_get_num_inputs(ysfx_t *fx)
  491. {
  492. ysfx_source_unit_t *main = fx->source.main.get();
  493. if (!main)
  494. return 0;
  495. return (uint32_t)main->header.in_pins.size();
  496. }
  497. uint32_t ysfx_get_num_outputs(ysfx_t *fx)
  498. {
  499. ysfx_source_unit_t *main = fx->source.main.get();
  500. if (!main)
  501. return 0;
  502. return (uint32_t)main->header.out_pins.size();
  503. }
  504. const char *ysfx_get_input_name(ysfx_t *fx, uint32_t index)
  505. {
  506. ysfx_source_unit_t *main = fx->source.main.get();
  507. if (!main || index >= main->header.in_pins.size())
  508. return "";
  509. return main->header.in_pins[index].c_str();
  510. }
  511. const char *ysfx_get_output_name(ysfx_t *fx, uint32_t index)
  512. {
  513. ysfx_source_unit_t *main = fx->source.main.get();
  514. if (!main || index >= main->header.out_pins.size())
  515. return "";
  516. return main->header.out_pins[index].c_str();
  517. }
  518. bool ysfx_wants_meters(ysfx_t *fx)
  519. {
  520. ysfx_source_unit_t *main = fx->source.main.get();
  521. if (!main)
  522. return false;
  523. return !main->header.options.no_meter;
  524. }
  525. bool ysfx_get_gfx_dim(ysfx_t *fx, uint32_t dim[2])
  526. {
  527. ysfx_toplevel_t *origin = nullptr;
  528. ysfx_section_t *sec = ysfx_search_section(fx, ysfx_section_gfx, &origin);
  529. if (!sec) {
  530. if (dim) {
  531. dim[0] = 0;
  532. dim[1] = 0;
  533. }
  534. return false;
  535. }
  536. if (dim) {
  537. dim[0] = origin->gfx_w;
  538. dim[1] = origin->gfx_h;
  539. }
  540. return true;
  541. }
  542. ysfx_section_t *ysfx_search_section(ysfx_t *fx, uint32_t type, ysfx_toplevel_t **origin)
  543. {
  544. if (!fx->source.main)
  545. return nullptr;
  546. auto search =
  547. [fx](ysfx_section_t *(*test)(ysfx_toplevel_t &tl), ysfx_toplevel_t **origin) -> ysfx_section_t *
  548. {
  549. ysfx_toplevel_t *tl = &fx->source.main->toplevel;
  550. ysfx_section_t *sec = test(*tl);
  551. for (size_t i = 0; !sec && i < fx->source.imports.size(); ++i) {
  552. tl = &fx->source.imports[i]->toplevel;
  553. sec = test(*tl);
  554. }
  555. if (origin)
  556. *origin = sec ? tl : nullptr;
  557. return sec;
  558. };
  559. switch (type) {
  560. case ysfx_section_init:
  561. return search([](ysfx_toplevel_t &tl) { return tl.init.get(); }, origin);
  562. case ysfx_section_slider:
  563. return search([](ysfx_toplevel_t &tl) { return tl.slider.get(); }, origin);
  564. case ysfx_section_block:
  565. return search([](ysfx_toplevel_t &tl) { return tl.block.get(); }, origin);
  566. case ysfx_section_sample:
  567. return search([](ysfx_toplevel_t &tl) { return tl.sample.get(); }, origin);
  568. case ysfx_section_gfx:
  569. return search([](ysfx_toplevel_t &tl) { return tl.gfx.get(); }, origin);
  570. case ysfx_section_serialize:
  571. return search([](ysfx_toplevel_t &tl) { return tl.serialize.get(); }, origin);
  572. default:
  573. return nullptr;
  574. }
  575. }
  576. bool ysfx_has_section(ysfx_t *fx, uint32_t type)
  577. {
  578. return ysfx_search_section(fx, type) != nullptr;
  579. }
  580. bool ysfx_slider_exists(ysfx_t *fx, uint32_t index)
  581. {
  582. ysfx_source_unit_t *main = fx->source.main.get();
  583. if (index >= ysfx_max_sliders || !main)
  584. return false;
  585. ysfx_slider_t &slider = main->header.sliders[index];
  586. return slider.exists;
  587. }
  588. const char *ysfx_slider_get_name(ysfx_t *fx, uint32_t index)
  589. {
  590. ysfx_source_unit_t *main = fx->source.main.get();
  591. if (index >= ysfx_max_sliders || !main)
  592. return "";
  593. ysfx_slider_t &slider = main->header.sliders[index];
  594. return slider.desc.c_str();
  595. }
  596. bool ysfx_slider_get_range(ysfx_t *fx, uint32_t index, ysfx_slider_range_t *range)
  597. {
  598. ysfx_source_unit_t *main = fx->source.main.get();
  599. if (index >= ysfx_max_sliders || !main)
  600. return false;
  601. ysfx_slider_t &slider = main->header.sliders[index];
  602. range->def = slider.def;
  603. range->min = slider.min;
  604. range->max = slider.max;
  605. range->inc = slider.inc;
  606. return true;
  607. }
  608. bool ysfx_slider_is_enum(ysfx_t *fx, uint32_t index)
  609. {
  610. ysfx_source_unit_t *main = fx->source.main.get();
  611. if (index >= ysfx_max_sliders || !main)
  612. return false;
  613. ysfx_slider_t &slider = main->header.sliders[index];
  614. return slider.is_enum;
  615. }
  616. uint32_t ysfx_slider_get_enum_names(ysfx_t *fx, uint32_t index, const char **dest, uint32_t destsize)
  617. {
  618. ysfx_source_unit_t *main = fx->source.main.get();
  619. if (index >= ysfx_max_sliders || !main)
  620. return 0;
  621. ysfx_slider_t &slider = main->header.sliders[index];
  622. uint32_t count = (uint32_t)slider.enum_names.size();
  623. uint32_t copysize = (destsize < count) ? destsize : count;
  624. for (uint32_t i = 0; i < copysize; ++i)
  625. dest[i] = slider.enum_names[i].c_str();
  626. return count;
  627. }
  628. const char *ysfx_slider_get_enum_name(ysfx_t *fx, uint32_t slider_index, uint32_t enum_index)
  629. {
  630. ysfx_source_unit_t *main = fx->source.main.get();
  631. if (slider_index >= ysfx_max_sliders || !main)
  632. return 0;
  633. ysfx_slider_t &slider = main->header.sliders[slider_index];
  634. if (enum_index >= slider.enum_names.size())
  635. return "";
  636. return slider.enum_names[enum_index].c_str();
  637. }
  638. bool ysfx_slider_is_path(ysfx_t *fx, uint32_t index)
  639. {
  640. ysfx_source_unit_t *main = fx->source.main.get();
  641. if (index >= ysfx_max_sliders || !main)
  642. return false;
  643. ysfx_slider_t &slider = main->header.sliders[index];
  644. return !slider.path.empty();
  645. }
  646. bool ysfx_slider_is_initially_visible(ysfx_t *fx, uint32_t index)
  647. {
  648. ysfx_source_unit_t *main = fx->source.main.get();
  649. if (index >= ysfx_max_sliders || !main)
  650. return false;
  651. ysfx_slider_t &slider = main->header.sliders[index];
  652. return slider.initially_visible;
  653. }
  654. ysfx_real ysfx_slider_get_value(ysfx_t *fx, uint32_t index)
  655. {
  656. if (index >= ysfx_max_sliders)
  657. return 0;
  658. return *fx->var.slider[index];
  659. }
  660. void ysfx_slider_set_value(ysfx_t *fx, uint32_t index, ysfx_real value)
  661. {
  662. if (index >= ysfx_max_sliders)
  663. return;
  664. if (*fx->var.slider[index] != value) {
  665. *fx->var.slider[index] = value;
  666. fx->must_compute_slider = true;
  667. }
  668. }
  669. std::string ysfx_resolve_import_path(ysfx_t *fx, const std::string &name, const std::string &origin)
  670. {
  671. std::vector<std::string> dirs;
  672. // create the list of search directories
  673. {
  674. dirs.reserve(2);
  675. if (!origin.empty())
  676. dirs.push_back(ysfx::path_directory(origin.c_str()));
  677. const std::string &import_root = fx->config->import_root;
  678. if (!import_root.empty() && dirs[0] != import_root)
  679. dirs.push_back(import_root);
  680. }
  681. // the search should be case-insensitive
  682. static constexpr bool nocase = true;
  683. static auto *check_existence = +[](const std::string &dir, const std::string &file, std::string &result_path) -> int {
  684. if (nocase)
  685. return ysfx::case_resolve(dir.c_str(), file.c_str(), result_path);
  686. else {
  687. result_path = dir + file;
  688. return ysfx::exists(result_path.c_str());
  689. }
  690. };
  691. // search for the file in these directories directly
  692. for (const std::string &dir : dirs) {
  693. std::string resolved;
  694. if (check_existence(dir, name, resolved))
  695. return resolved;
  696. }
  697. // search for the file recursively
  698. for (const std::string &dir : dirs) {
  699. struct visit_data {
  700. const std::string *name = nullptr;
  701. std::string resolved;
  702. };
  703. visit_data vd;
  704. vd.name = &name;
  705. auto visit = [](const std::string &dir, void *data) -> bool {
  706. visit_data &vd = *(visit_data *)data;
  707. std::string resolved;
  708. if (check_existence(dir, *vd.name, resolved)) {
  709. vd.resolved = std::move(resolved);
  710. return false;
  711. }
  712. return true;
  713. };
  714. ysfx::visit_directories(dir.c_str(), +visit, &vd);
  715. if (!vd.resolved.empty())
  716. return vd.resolved;
  717. }
  718. return std::string{};
  719. }
  720. uint32_t ysfx_get_block_size(ysfx_t *fx)
  721. {
  722. return fx->block_size;
  723. }
  724. ysfx_real ysfx_get_sample_rate(ysfx_t *fx)
  725. {
  726. return fx->sample_rate;
  727. }
  728. void ysfx_set_block_size(ysfx_t *fx, uint32_t blocksize)
  729. {
  730. if (fx->block_size != blocksize) {
  731. fx->block_size = blocksize;
  732. fx->must_compute_init = true;
  733. }
  734. }
  735. void ysfx_set_sample_rate(ysfx_t *fx, ysfx_real samplerate)
  736. {
  737. if (fx->sample_rate != samplerate) {
  738. fx->sample_rate = samplerate;
  739. fx->must_compute_init = true;
  740. }
  741. }
  742. void ysfx_set_midi_capacity(ysfx_t *fx, uint32_t capacity, bool extensible)
  743. {
  744. ysfx_midi_reserve(fx->midi.in.get(), capacity, extensible);
  745. ysfx_midi_reserve(fx->midi.out.get(), capacity, extensible);
  746. }
  747. void ysfx_init(ysfx_t *fx)
  748. {
  749. if (!fx->code.compiled)
  750. return;
  751. if (fx->is_freshly_compiled) {
  752. ysfx_first_init(fx);
  753. fx->is_freshly_compiled = false;
  754. }
  755. ysfx_clear_files(fx);
  756. for (size_t i = 0; i < fx->code.init.size(); ++i)
  757. NSEEL_code_execute(fx->code.init[i].get());
  758. fx->must_compute_init = false;
  759. fx->must_compute_slider = true;
  760. #if !defined(YSFX_NO_GFX)
  761. // do initializations on next @gfx, on the gfx thread
  762. // release-acquire order is for VM `gfx_*` variables and `wants_retina`
  763. fx->gfx.wants_retina = *fx->var.gfx_ext_retina > 0;
  764. fx->gfx.must_init.store(true, std::memory_order_release);
  765. #endif
  766. }
  767. void ysfx_first_init(ysfx_t *fx)
  768. {
  769. assert(fx->code.compiled);
  770. assert(fx->is_freshly_compiled);
  771. *fx->var.samplesblock = (EEL_F)fx->block_size;
  772. *fx->var.srate = fx->sample_rate;
  773. ysfx_clear_files(fx);
  774. fx->slider.automate_mask.store(0);
  775. fx->slider.change_mask.store(0);
  776. ysfx_update_slider_visibility_mask(fx);
  777. *fx->var.pdc_delay = 0;
  778. *fx->var.pdc_bot_ch = 0;
  779. *fx->var.pdc_top_ch = 0;
  780. *fx->var.pdc_midi = 0;
  781. }
  782. ysfx_real ysfx_get_pdc_delay(ysfx_t *fx)
  783. {
  784. ysfx_real value = *fx->var.pdc_delay;
  785. return (value > 0) ? value : 0;
  786. }
  787. void ysfx_get_pdc_channels(ysfx_t *fx, uint32_t channels[2])
  788. {
  789. if (!channels)
  790. return;
  791. int64_t bot = (int64_t)*fx->var.pdc_bot_ch;
  792. bot = (bot > 0) ? bot : 0;
  793. bot = (bot < ysfx_max_channels) ? bot : ysfx_max_channels;
  794. channels[0] = (uint32_t)bot;
  795. int64_t top = (int64_t)*fx->var.pdc_top_ch;
  796. top = (top > bot) ? top : bot;
  797. top = (top < ysfx_max_channels) ? top : ysfx_max_channels;
  798. channels[1] = (uint32_t)top;
  799. }
  800. bool ysfx_get_pdc_midi(ysfx_t *fx)
  801. {
  802. return (bool)*fx->var.pdc_midi;
  803. }
  804. void ysfx_update_slider_visibility_mask(ysfx_t *fx)
  805. {
  806. uint64_t visible = 0;
  807. for (uint32_t i = 0; i < ysfx_max_sliders; ++i) {
  808. ysfx_slider_t &slider = fx->source.main->header.sliders[i];
  809. visible |= (uint64_t)slider.initially_visible << i;
  810. }
  811. fx->slider.visible_mask.store(visible);
  812. }
  813. void ysfx_set_time_info(ysfx_t *fx, const ysfx_time_info_t *info)
  814. {
  815. uint32_t prev_state = (uint32_t)*fx->var.play_state;
  816. uint32_t new_state = info->playback_state;
  817. // unless `ext_noinit`, we should call @init every transport restart
  818. if (!*fx->var.ext_noinit) {
  819. auto is_running = [](uint32_t state) {
  820. return state == ysfx_playback_playing ||
  821. state == ysfx_playback_recording;
  822. };
  823. if (!is_running(prev_state) && is_running(new_state))
  824. fx->must_compute_init = true;
  825. }
  826. *fx->var.tempo = info->tempo;
  827. *fx->var.play_state = (EEL_F)new_state;
  828. *fx->var.play_position = info->time_position;
  829. *fx->var.beat_position = info->beat_position;
  830. *fx->var.ts_num = (EEL_F)info->time_signature[0];
  831. *fx->var.ts_denom = (EEL_F)info->time_signature[1];
  832. }
  833. bool ysfx_send_midi(ysfx_t *fx, const ysfx_midi_event_t *event)
  834. {
  835. return ysfx_midi_push(fx->midi.in.get(), event);
  836. }
  837. bool ysfx_receive_midi(ysfx_t *fx, ysfx_midi_event_t *event)
  838. {
  839. return ysfx_midi_get_next(fx->midi.out.get(), event);
  840. }
  841. bool ysfx_receive_midi_from_bus(ysfx_t *fx, uint32_t bus, ysfx_midi_event_t *event)
  842. {
  843. return ysfx_midi_get_next_from_bus(fx->midi.out.get(), 0, event);
  844. }
  845. uint32_t ysfx_current_midi_bus(ysfx_t *fx)
  846. {
  847. uint32_t bus = 0;
  848. if (*fx->var.ext_midi_bus)
  849. bus = (int32_t)*fx->var.midi_bus;
  850. return bus;
  851. }
  852. bool ysfx_send_trigger(ysfx_t *fx, uint32_t index)
  853. {
  854. if (index >= ysfx_max_triggers)
  855. return false;
  856. fx->triggers |= 1u << index;
  857. return true;
  858. }
  859. uint64_t ysfx_fetch_slider_changes(ysfx_t *fx)
  860. {
  861. return fx->slider.change_mask.exchange(0);
  862. }
  863. uint64_t ysfx_fetch_slider_automations(ysfx_t *fx)
  864. {
  865. return fx->slider.automate_mask.exchange(0);
  866. }
  867. uint64_t ysfx_get_slider_visibility(ysfx_t *fx)
  868. {
  869. return fx->slider.visible_mask.load();
  870. }
  871. template <class Real>
  872. void ysfx_process_generic(ysfx_t *fx, const Real *const *ins, Real *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames)
  873. {
  874. ysfx_set_thread_id(ysfx_thread_id_dsp);
  875. // prepare MIDI input for reading, output for writing
  876. assert(fx->midi.in->read_pos == 0);
  877. ysfx_midi_clear(fx->midi.out.get());
  878. // prepare triggers
  879. *fx->var.trigger = (EEL_F)fx->triggers;
  880. fx->triggers = 0;
  881. if (!fx->code.compiled) {
  882. for (uint32_t ch = 0; ch < num_outs; ++ch)
  883. memset(outs[ch], 0, num_frames * sizeof(Real));
  884. }
  885. else {
  886. // compute @init if needed
  887. if (fx->must_compute_init)
  888. ysfx_init(fx);
  889. const uint32_t orig_num_outs = num_outs;
  890. const uint32_t num_code_ins = (uint32_t)fx->source.main->header.in_pins.size();
  891. const uint32_t num_code_outs = (uint32_t)fx->source.main->header.out_pins.size();
  892. if (num_ins > num_code_ins)
  893. num_ins = num_code_ins;
  894. if (num_outs > num_code_outs)
  895. num_outs = num_code_outs;
  896. fx->valid_input_channels = num_ins;
  897. *fx->var.samplesblock = (EEL_F)num_frames;
  898. *fx->var.num_ch = (EEL_F)num_ins;
  899. // compute @slider if needed
  900. if (fx->must_compute_slider) {
  901. NSEEL_code_execute(fx->code.slider.get());
  902. fx->must_compute_slider = false;
  903. }
  904. // compute @block
  905. NSEEL_code_execute(fx->code.block.get());
  906. // compute @sample, once per frame
  907. if (fx->code.sample) {
  908. EEL_F **spl = fx->var.spl;
  909. for (uint32_t i = 0; i < num_frames; ++i) {
  910. for (uint32_t ch = 0; ch < num_ins; ++ch)
  911. *spl[ch] = (EEL_F)ins[ch][i];
  912. for (uint32_t ch = num_ins; ch < num_code_ins; ++ch)
  913. *spl[ch] = 0;
  914. NSEEL_code_execute(fx->code.sample.get());
  915. for (uint32_t ch = 0; ch < num_outs; ++ch)
  916. outs[ch][i] = (Real)*spl[ch];
  917. }
  918. }
  919. // clear any output channels above the maximum count
  920. for (uint32_t ch = num_outs; ch < orig_num_outs; ++ch)
  921. memset(outs[ch], 0, num_frames * sizeof(Real));
  922. }
  923. // prepare MIDI input for writing, output for reading
  924. assert(fx->midi.out->read_pos == 0);
  925. ysfx_midi_clear(fx->midi.in.get());
  926. ysfx_set_thread_id(ysfx_thread_id_none);
  927. }
  928. void ysfx_process_float(ysfx_t *fx, const float *const *ins, float *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames)
  929. {
  930. ysfx_process_generic<float>(fx, ins, outs, num_ins, num_outs, num_frames);
  931. }
  932. void ysfx_process_double(ysfx_t *fx, const double *const *ins, double *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames)
  933. {
  934. ysfx_process_generic<double>(fx, ins, outs, num_ins, num_outs, num_frames);
  935. }
  936. void ysfx_clear_files(ysfx_t *fx)
  937. {
  938. std::lock_guard<ysfx::mutex> list_lock(fx->file.list_mutex);
  939. // delete all except the serializer
  940. while (fx->file.list.size() > 1) {
  941. ysfx_file_t *file = fx->file.list.back().get();
  942. std::unique_ptr<ysfx::mutex> file_mutex;
  943. std::unique_lock<ysfx::mutex> file_lock;
  944. if (file) {
  945. file_lock = std::unique_lock<ysfx::mutex>{*fx->file.list.back()->m_mutex};
  946. file_mutex = std::move(fx->file.list.back()->m_mutex);
  947. }
  948. fx->file.list.pop_back();
  949. }
  950. }
  951. ysfx_file_t *ysfx_get_file(ysfx_t *fx, uint32_t handle, std::unique_lock<ysfx::mutex> &lock, std::unique_lock<ysfx::mutex> *list_lock)
  952. {
  953. std::unique_lock<ysfx::mutex> local_list_lock;
  954. if (list_lock)
  955. *list_lock = std::unique_lock<ysfx::mutex>(fx->file.list_mutex);
  956. else
  957. local_list_lock = std::unique_lock<ysfx::mutex>(fx->file.list_mutex);
  958. if (handle >= fx->file.list.size())
  959. return nullptr;
  960. ysfx_file_t *file = fx->file.list[handle].get();
  961. if (!file)
  962. return nullptr;
  963. lock = std::unique_lock<ysfx::mutex>{*file->m_mutex};
  964. return file;
  965. }
  966. int32_t ysfx_insert_file(ysfx_t *fx, ysfx_file_t *file)
  967. {
  968. std::lock_guard<ysfx::mutex> lock(fx->file.list_mutex);
  969. //
  970. size_t noneidx = ~(size_t)0;
  971. size_t freeidx = noneidx;
  972. for (size_t i = 0, n = fx->file.list.size(); i < n && freeidx == noneidx; ++i) {
  973. if (!fx->file.list[i])
  974. freeidx = i;
  975. }
  976. if (freeidx != noneidx) {
  977. fx->file.list[freeidx].reset(file);
  978. return (uint32_t)freeidx;
  979. }
  980. enum { max_file_handles = 64 };
  981. size_t pos = fx->file.list.size();
  982. if (pos >= max_file_handles)
  983. return -1;
  984. fx->file.list.emplace_back(file);
  985. return (uint32_t)pos;
  986. }
  987. bool ysfx_load_state(ysfx_t *fx, ysfx_state_t *state)
  988. {
  989. if (!fx->code.compiled)
  990. return false;
  991. // restore the serialization
  992. std::string buffer((char *)state->data, state->data_size);
  993. // restore the sliders
  994. for (uint32_t i = 0; i < ysfx_max_sliders; ++i)
  995. *fx->var.slider[i] = fx->source.main->header.sliders[i].def;
  996. for (uint32_t i = 0, n = state->slider_count; i < n; ++i) {
  997. uint32_t j = state->sliders[i].index;
  998. if (j < ysfx_max_sliders && fx->source.main->header.sliders[j].exists)
  999. *fx->var.slider[j] = state->sliders[i].value;
  1000. }
  1001. fx->must_compute_slider = true;
  1002. // invoke @serialize
  1003. {
  1004. std::unique_lock<ysfx::mutex> lock;
  1005. ysfx_serializer_t *serializer = static_cast<ysfx_serializer_t *>(ysfx_get_file(fx, 0, lock));
  1006. assert(serializer);
  1007. serializer->begin(false, buffer);
  1008. lock.unlock();
  1009. ysfx_serialize(fx);
  1010. lock.lock();
  1011. serializer->end();
  1012. }
  1013. return true;
  1014. }
  1015. ysfx_state_t *ysfx_save_state(ysfx_t *fx)
  1016. {
  1017. if (!fx->code.compiled)
  1018. return nullptr;
  1019. std::string buffer;
  1020. // invoke @serialize
  1021. {
  1022. std::unique_lock<ysfx::mutex> lock;
  1023. ysfx_serializer_t *serializer = static_cast<ysfx_serializer_t *>(ysfx_get_file(fx, 0, lock));
  1024. assert(serializer);
  1025. serializer->begin(true, buffer);
  1026. lock.unlock();
  1027. ysfx_serialize(fx);
  1028. lock.lock();
  1029. serializer->end();
  1030. }
  1031. // save the sliders
  1032. ysfx_state_u state{new ysfx_state_t};
  1033. uint32_t slider_count = 0;
  1034. for (uint32_t i = 0; i < ysfx_max_sliders; ++i)
  1035. slider_count += fx->source.main->header.sliders[i].exists;
  1036. state->sliders = new ysfx_state_slider_t[slider_count]{};
  1037. state->slider_count = slider_count;
  1038. for (uint32_t i = 0, j = 0; i < slider_count; ++i) {
  1039. if (fx->source.main->header.sliders[i].exists) {
  1040. state->sliders[j].index = i;
  1041. state->sliders[j].value = *fx->var.slider[i];
  1042. ++j;
  1043. }
  1044. }
  1045. // save the serialization
  1046. state->data_size = buffer.size();
  1047. state->data = new uint8_t[state->data_size];
  1048. memcpy(state->data, buffer.data(), state->data_size);
  1049. //
  1050. return state.release();
  1051. }
  1052. void ysfx_state_free(ysfx_state_t *state)
  1053. {
  1054. if (!state)
  1055. return;
  1056. delete[] state->sliders;
  1057. delete[] state->data;
  1058. delete state;
  1059. }
  1060. ysfx_state_t *ysfx_state_dup(ysfx_state_t *state_in)
  1061. {
  1062. if (!state_in)
  1063. return nullptr;
  1064. ysfx_state_u state_out{new ysfx_state_t};
  1065. uint32_t slider_count = state_out->slider_count = state_in->slider_count;
  1066. size_t data_size = state_out->data_size = state_in->data_size;
  1067. state_out->sliders = new ysfx_state_slider_t[slider_count];
  1068. memcpy(state_out->sliders, state_in->sliders, slider_count * sizeof(ysfx_state_slider_t));
  1069. state_out->data = new uint8_t[data_size];
  1070. memcpy(state_out->data, state_in->data, data_size);
  1071. return state_out.release();
  1072. }
  1073. void ysfx_serialize(ysfx_t *fx)
  1074. {
  1075. if (fx->code.serialize) {
  1076. if (fx->must_compute_init)
  1077. ysfx_init(fx);
  1078. NSEEL_code_execute(fx->code.serialize.get());
  1079. }
  1080. }
  1081. uint32_t ysfx_get_slider_of_var(ysfx_t *fx, EEL_F *var)
  1082. {
  1083. auto it = fx->slider_of_var.find(var);
  1084. if (it == fx->slider_of_var.end())
  1085. return ~(uint32_t)0;
  1086. return it->second;
  1087. }
  1088. const char *ysfx_get_bank_path(ysfx_t *fx)
  1089. {
  1090. return fx->source.bank_path.c_str();
  1091. }
  1092. void ysfx_enum_vars(ysfx_t *fx, ysfx_enum_vars_callback_t *callback, void *userdata)
  1093. {
  1094. NSEEL_VM_enumallvars(fx->vm.get(), callback, userdata);
  1095. }
  1096. ysfx_real *ysfx_find_var(ysfx_t *fx, const char *name)
  1097. {
  1098. struct find_data {
  1099. ysfx_real *var = nullptr;
  1100. const char *name = nullptr;
  1101. };
  1102. find_data fd;
  1103. fd.name = name;
  1104. auto callback = [](const char *name, EEL_F *var, void *userdata) -> int {
  1105. find_data *fd = (find_data *)userdata;
  1106. if (strcmp(name, fd->name) != 0)
  1107. return 1;
  1108. fd->var = var;
  1109. return 0;
  1110. };
  1111. NSEEL_VM_enumallvars(fx->vm.get(), +callback, &fd);
  1112. return fd.var;
  1113. }
  1114. void ysfx_read_vmem(ysfx_t *fx, uint32_t addr, ysfx_real *dest, uint32_t count)
  1115. {
  1116. ysfx_eel_ram_reader reader(fx->vm.get(), addr);
  1117. for (uint32_t i = 0; i < count; ++i)
  1118. dest[i] = reader.read_next();
  1119. }
  1120. bool ysfx_find_data_file(ysfx_t *fx, EEL_F *file, std::string &result)
  1121. {
  1122. // 3 possibilities for file
  1123. // - slider
  1124. // - index of filename
  1125. // - string
  1126. std::string filepart;
  1127. bool accept_absolute = false;
  1128. bool accept_relative = false;
  1129. int32_t index = ysfx_eel_round<int32_t>(*file);
  1130. uint32_t slideridx = ysfx_get_slider_of_var(fx, file);
  1131. ysfx_slider_t *slider = nullptr;
  1132. if (slideridx != ~(uint32_t)0)
  1133. slider = &fx->source.main->header.sliders[slideridx];
  1134. if (slider && !slider->path.empty()) {
  1135. int32_t value = ysfx_eel_round<int32_t>(*fx->var.slider[slideridx]);
  1136. if (value < 0 || (uint32_t)value >= slider->enum_names.size())
  1137. return false;
  1138. filepart = slider->path + '/' + slider->enum_names[(uint32_t)value];
  1139. accept_relative = true;
  1140. }
  1141. else if (index >= 0 && (uint32_t)index < fx->source.main->header.filenames.size()) {
  1142. filepart = fx->source.main->header.filenames[(uint32_t)index];
  1143. accept_relative = true;
  1144. }
  1145. else if (ysfx_string_get(fx, *file, filepart)) {
  1146. accept_absolute = true;
  1147. accept_relative = true;
  1148. }
  1149. else
  1150. return false;
  1151. std::vector<std::string> filecandidates;
  1152. filecandidates.reserve(2);
  1153. if (accept_absolute && !ysfx::path_is_relative(filepart.c_str()))
  1154. filecandidates.push_back(filepart);
  1155. else if (accept_relative) {
  1156. filecandidates.push_back(ysfx::path_directory(fx->source.main_file_path.c_str()) + filepart);
  1157. if (!fx->config->data_root.empty())
  1158. filecandidates.push_back(fx->config->data_root + filepart);
  1159. }
  1160. for (const std::string &filepath : filecandidates) {
  1161. if (ysfx::exists(filepath.c_str())) {
  1162. result.assign(filepath);
  1163. return true;
  1164. }
  1165. }
  1166. return false;
  1167. }
  1168. ysfx_file_type_t ysfx_detect_file_type(ysfx_t *fx, const char *path, void **fmtobj)
  1169. {
  1170. if (ysfx::path_has_suffix(path, "txt"))
  1171. return ysfx_file_type_txt;
  1172. if (ysfx::path_has_suffix(path, "raw"))
  1173. return ysfx_file_type_raw;
  1174. for (ysfx_audio_format_t &fmt : fx->config->audio_formats) {
  1175. if (fmt.can_handle(path)) {
  1176. if (fmtobj)
  1177. *fmtobj = &fmt;
  1178. return ysfx_file_type_audio;
  1179. }
  1180. }
  1181. return ysfx_file_type_none;
  1182. }
  1183. void ysfx_gfx_setup(ysfx_t *fx, ysfx_gfx_config_t *gc)
  1184. {
  1185. #if !defined(YSFX_NO_GFX)
  1186. bool doinit = false;
  1187. ysfx_scoped_gfx_t scope{fx, doinit};
  1188. ysfx_gfx_state_set_bitmap(fx->gfx.state.get(), gc->pixels, gc->pixel_width, gc->pixel_height, gc->pixel_stride);
  1189. ysfx_real scale = fx->gfx.wants_retina ? gc->scale_factor : 1;
  1190. ysfx_gfx_state_set_scale_factor(fx->gfx.state.get(), scale);
  1191. ysfx_gfx_state_set_callback_data(fx->gfx.state.get(), gc->user_data);
  1192. ysfx_gfx_state_set_show_menu_callback(fx->gfx.state.get(), gc->show_menu);
  1193. ysfx_gfx_state_set_set_cursor_callback(fx->gfx.state.get(), gc->set_cursor);
  1194. ysfx_gfx_state_set_get_drop_file_callback(fx->gfx.state.get(), gc->get_drop_file);
  1195. #else
  1196. (void)fx;
  1197. (void)gc;
  1198. #endif
  1199. }
  1200. bool ysfx_gfx_wants_retina(ysfx_t *fx)
  1201. {
  1202. #if !defined(YSFX_NO_GFX)
  1203. return fx->gfx.wants_retina;
  1204. #else
  1205. (void)fx;
  1206. return false;
  1207. #endif
  1208. }
  1209. void ysfx_gfx_add_key(ysfx_t *fx, uint32_t mods, uint32_t key, bool press)
  1210. {
  1211. #if !defined(YSFX_NO_GFX)
  1212. bool doinit = true;
  1213. ysfx_scoped_gfx_t scope{fx, doinit};
  1214. if (!fx->gfx.ready)
  1215. return;
  1216. ysfx_gfx_state_add_key(fx->gfx.state.get(), mods, key, press);
  1217. #else
  1218. (void)fx;
  1219. (void)mods;
  1220. (void)key;
  1221. #endif
  1222. }
  1223. void ysfx_gfx_update_mouse(ysfx_t *fx, uint32_t mods, int32_t xpos, int32_t ypos, uint32_t buttons, ysfx_real wheel, ysfx_real hwheel)
  1224. {
  1225. #if !defined(YSFX_NO_GFX)
  1226. bool doinit = true;
  1227. ysfx_scoped_gfx_t scope{fx, doinit};
  1228. if (!fx->gfx.ready)
  1229. return;
  1230. *fx->var.mouse_x = (EEL_F)xpos;
  1231. *fx->var.mouse_y = (EEL_F)ypos;
  1232. *fx->var.mouse_wheel += 120 * wheel;
  1233. *fx->var.mouse_hwheel += 120 * hwheel;
  1234. uint32_t mouse_cap = 0;
  1235. if (mods & ysfx_mod_shift)
  1236. mouse_cap |= 8;
  1237. if (mods & ysfx_mod_ctrl)
  1238. mouse_cap |= 4;
  1239. if (mods & ysfx_mod_alt)
  1240. mouse_cap |= 16;
  1241. if (mods & ysfx_mod_super)
  1242. mouse_cap |= 32;
  1243. if (buttons & ysfx_button_left)
  1244. mouse_cap |= 1;
  1245. if (buttons & ysfx_button_middle)
  1246. mouse_cap |= 64;
  1247. if (buttons & ysfx_button_right)
  1248. mouse_cap |= 2;
  1249. *fx->var.mouse_cap = (EEL_F)mouse_cap;
  1250. #else
  1251. (void)fx;
  1252. (void)mods;
  1253. (void)xpos;
  1254. (void)ypos;
  1255. (void)buttons;
  1256. (void)vwheel;
  1257. (void)hwheel;
  1258. #endif
  1259. }
  1260. bool ysfx_gfx_run(ysfx_t *fx)
  1261. {
  1262. #if !defined(YSFX_NO_GFX)
  1263. bool doinit = true;
  1264. ysfx_scoped_gfx_t scope{fx, doinit};
  1265. if (!fx->gfx.ready)
  1266. return false;
  1267. ysfx_gfx_prepare(fx);
  1268. NSEEL_code_execute(fx->code.gfx.get());
  1269. return ysfx_gfx_state_is_dirty(fx->gfx.state.get());
  1270. #else
  1271. (void)fx;
  1272. #endif
  1273. }