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.

1540 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. *fx->var.samplesblock = (EEL_F)fx->block_size;
  752. *fx->var.srate = fx->sample_rate;
  753. *fx->var.pdc_delay = 0;
  754. *fx->var.pdc_bot_ch = 0;
  755. *fx->var.pdc_top_ch = 0;
  756. *fx->var.pdc_midi = 0;
  757. if (fx->is_freshly_compiled) {
  758. ysfx_first_init(fx);
  759. fx->is_freshly_compiled = false;
  760. }
  761. ysfx_clear_files(fx);
  762. for (size_t i = 0; i < fx->code.init.size(); ++i)
  763. NSEEL_code_execute(fx->code.init[i].get());
  764. fx->must_compute_init = false;
  765. fx->must_compute_slider = true;
  766. #if !defined(YSFX_NO_GFX)
  767. // do initializations on next @gfx, on the gfx thread
  768. // release-acquire order is for VM `gfx_*` variables and `wants_retina`
  769. fx->gfx.wants_retina = *fx->var.gfx_ext_retina > 0;
  770. fx->gfx.must_init.store(true, std::memory_order_release);
  771. #endif
  772. }
  773. void ysfx_first_init(ysfx_t *fx)
  774. {
  775. assert(fx->code.compiled);
  776. assert(fx->is_freshly_compiled);
  777. fx->slider.automate_mask.store(0);
  778. fx->slider.change_mask.store(0);
  779. ysfx_update_slider_visibility_mask(fx);
  780. }
  781. ysfx_real ysfx_get_pdc_delay(ysfx_t *fx)
  782. {
  783. ysfx_real value = *fx->var.pdc_delay;
  784. return (value > 0) ? value : 0;
  785. }
  786. void ysfx_get_pdc_channels(ysfx_t *fx, uint32_t channels[2])
  787. {
  788. if (!channels)
  789. return;
  790. int64_t bot = (int64_t)*fx->var.pdc_bot_ch;
  791. bot = (bot > 0) ? bot : 0;
  792. bot = (bot < ysfx_max_channels) ? bot : ysfx_max_channels;
  793. channels[0] = (uint32_t)bot;
  794. int64_t top = (int64_t)*fx->var.pdc_top_ch;
  795. top = (top > bot) ? top : bot;
  796. top = (top < ysfx_max_channels) ? top : ysfx_max_channels;
  797. channels[1] = (uint32_t)top;
  798. }
  799. bool ysfx_get_pdc_midi(ysfx_t *fx)
  800. {
  801. return (bool)*fx->var.pdc_midi;
  802. }
  803. void ysfx_update_slider_visibility_mask(ysfx_t *fx)
  804. {
  805. uint64_t visible = 0;
  806. for (uint32_t i = 0; i < ysfx_max_sliders; ++i) {
  807. ysfx_slider_t &slider = fx->source.main->header.sliders[i];
  808. visible |= (uint64_t)slider.initially_visible << i;
  809. }
  810. fx->slider.visible_mask.store(visible);
  811. }
  812. void ysfx_set_time_info(ysfx_t *fx, const ysfx_time_info_t *info)
  813. {
  814. uint32_t prev_state = (uint32_t)*fx->var.play_state;
  815. uint32_t new_state = info->playback_state;
  816. // unless `ext_noinit`, we should call @init every transport restart
  817. if (!*fx->var.ext_noinit) {
  818. auto is_running = [](uint32_t state) {
  819. return state == ysfx_playback_playing ||
  820. state == ysfx_playback_recording;
  821. };
  822. if (!is_running(prev_state) && is_running(new_state))
  823. fx->must_compute_init = true;
  824. }
  825. *fx->var.tempo = info->tempo;
  826. *fx->var.play_state = (EEL_F)new_state;
  827. *fx->var.play_position = info->time_position;
  828. *fx->var.beat_position = info->beat_position;
  829. *fx->var.ts_num = (EEL_F)info->time_signature[0];
  830. *fx->var.ts_denom = (EEL_F)info->time_signature[1];
  831. }
  832. bool ysfx_send_midi(ysfx_t *fx, const ysfx_midi_event_t *event)
  833. {
  834. return ysfx_midi_push(fx->midi.in.get(), event);
  835. }
  836. bool ysfx_receive_midi(ysfx_t *fx, ysfx_midi_event_t *event)
  837. {
  838. return ysfx_midi_get_next(fx->midi.out.get(), event);
  839. }
  840. bool ysfx_receive_midi_from_bus(ysfx_t *fx, uint32_t bus, ysfx_midi_event_t *event)
  841. {
  842. return ysfx_midi_get_next_from_bus(fx->midi.out.get(), 0, event);
  843. }
  844. uint32_t ysfx_current_midi_bus(ysfx_t *fx)
  845. {
  846. uint32_t bus = 0;
  847. if (*fx->var.ext_midi_bus)
  848. bus = (int32_t)*fx->var.midi_bus;
  849. return bus;
  850. }
  851. bool ysfx_send_trigger(ysfx_t *fx, uint32_t index)
  852. {
  853. if (index >= ysfx_max_triggers)
  854. return false;
  855. fx->triggers |= 1u << index;
  856. return true;
  857. }
  858. uint64_t ysfx_fetch_slider_changes(ysfx_t *fx)
  859. {
  860. return fx->slider.change_mask.exchange(0);
  861. }
  862. uint64_t ysfx_fetch_slider_automations(ysfx_t *fx)
  863. {
  864. return fx->slider.automate_mask.exchange(0);
  865. }
  866. uint64_t ysfx_get_slider_visibility(ysfx_t *fx)
  867. {
  868. return fx->slider.visible_mask.load();
  869. }
  870. template <class Real>
  871. 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)
  872. {
  873. ysfx_set_thread_id(ysfx_thread_id_dsp);
  874. // prepare MIDI input for reading, output for writing
  875. assert(fx->midi.in->read_pos == 0);
  876. ysfx_midi_clear(fx->midi.out.get());
  877. // prepare triggers
  878. *fx->var.trigger = (EEL_F)fx->triggers;
  879. fx->triggers = 0;
  880. if (!fx->code.compiled) {
  881. for (uint32_t ch = 0; ch < num_outs; ++ch)
  882. memset(outs[ch], 0, num_frames * sizeof(Real));
  883. }
  884. else {
  885. // compute @init if needed
  886. if (fx->must_compute_init)
  887. ysfx_init(fx);
  888. const uint32_t orig_num_outs = num_outs;
  889. const uint32_t num_code_ins = (uint32_t)fx->source.main->header.in_pins.size();
  890. const uint32_t num_code_outs = (uint32_t)fx->source.main->header.out_pins.size();
  891. if (num_ins > num_code_ins)
  892. num_ins = num_code_ins;
  893. if (num_outs > num_code_outs)
  894. num_outs = num_code_outs;
  895. fx->valid_input_channels = num_ins;
  896. *fx->var.samplesblock = (EEL_F)num_frames;
  897. *fx->var.num_ch = (EEL_F)num_ins;
  898. // compute @slider if needed
  899. if (fx->must_compute_slider) {
  900. NSEEL_code_execute(fx->code.slider.get());
  901. fx->must_compute_slider = false;
  902. }
  903. // compute @block
  904. NSEEL_code_execute(fx->code.block.get());
  905. // compute @sample, once per frame
  906. if (fx->code.sample) {
  907. EEL_F **spl = fx->var.spl;
  908. for (uint32_t i = 0; i < num_frames; ++i) {
  909. for (uint32_t ch = 0; ch < num_ins; ++ch)
  910. *spl[ch] = (EEL_F)ins[ch][i];
  911. for (uint32_t ch = num_ins; ch < num_code_ins; ++ch)
  912. *spl[ch] = 0;
  913. NSEEL_code_execute(fx->code.sample.get());
  914. for (uint32_t ch = 0; ch < num_outs; ++ch)
  915. outs[ch][i] = (Real)*spl[ch];
  916. }
  917. }
  918. // clear any output channels above the maximum count
  919. for (uint32_t ch = num_outs; ch < orig_num_outs; ++ch)
  920. memset(outs[ch], 0, num_frames * sizeof(Real));
  921. }
  922. // prepare MIDI input for writing, output for reading
  923. assert(fx->midi.out->read_pos == 0);
  924. ysfx_midi_clear(fx->midi.in.get());
  925. ysfx_set_thread_id(ysfx_thread_id_none);
  926. }
  927. 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)
  928. {
  929. ysfx_process_generic<float>(fx, ins, outs, num_ins, num_outs, num_frames);
  930. }
  931. 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)
  932. {
  933. ysfx_process_generic<double>(fx, ins, outs, num_ins, num_outs, num_frames);
  934. }
  935. void ysfx_clear_files(ysfx_t *fx)
  936. {
  937. std::lock_guard<ysfx::mutex> list_lock(fx->file.list_mutex);
  938. // delete all except the serializer
  939. while (fx->file.list.size() > 1) {
  940. ysfx_file_t *file = fx->file.list.back().get();
  941. std::unique_ptr<ysfx::mutex> file_mutex;
  942. std::unique_lock<ysfx::mutex> file_lock;
  943. if (file) {
  944. file_lock = std::unique_lock<ysfx::mutex>{*fx->file.list.back()->m_mutex};
  945. file_mutex = std::move(fx->file.list.back()->m_mutex);
  946. }
  947. fx->file.list.pop_back();
  948. }
  949. }
  950. 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)
  951. {
  952. std::unique_lock<ysfx::mutex> local_list_lock;
  953. if (list_lock)
  954. *list_lock = std::unique_lock<ysfx::mutex>(fx->file.list_mutex);
  955. else
  956. local_list_lock = std::unique_lock<ysfx::mutex>(fx->file.list_mutex);
  957. if (handle >= fx->file.list.size())
  958. return nullptr;
  959. ysfx_file_t *file = fx->file.list[handle].get();
  960. if (!file)
  961. return nullptr;
  962. lock = std::unique_lock<ysfx::mutex>{*file->m_mutex};
  963. return file;
  964. }
  965. int32_t ysfx_insert_file(ysfx_t *fx, ysfx_file_t *file)
  966. {
  967. std::lock_guard<ysfx::mutex> lock(fx->file.list_mutex);
  968. //
  969. size_t noneidx = ~(size_t)0;
  970. size_t freeidx = noneidx;
  971. for (size_t i = 0, n = fx->file.list.size(); i < n && freeidx == noneidx; ++i) {
  972. if (!fx->file.list[i])
  973. freeidx = i;
  974. }
  975. if (freeidx != noneidx) {
  976. fx->file.list[freeidx].reset(file);
  977. return (uint32_t)freeidx;
  978. }
  979. enum { max_file_handles = 64 };
  980. size_t pos = fx->file.list.size();
  981. if (pos >= max_file_handles)
  982. return -1;
  983. fx->file.list.emplace_back(file);
  984. return (uint32_t)pos;
  985. }
  986. bool ysfx_load_state(ysfx_t *fx, ysfx_state_t *state)
  987. {
  988. if (!fx->code.compiled)
  989. return false;
  990. // restore the serialization
  991. std::string buffer((char *)state->data, state->data_size);
  992. // restore the sliders
  993. for (uint32_t i = 0; i < ysfx_max_sliders; ++i)
  994. *fx->var.slider[i] = fx->source.main->header.sliders[i].def;
  995. for (uint32_t i = 0, n = state->slider_count; i < n; ++i) {
  996. uint32_t j = state->sliders[i].index;
  997. if (j < ysfx_max_sliders && fx->source.main->header.sliders[j].exists)
  998. *fx->var.slider[j] = state->sliders[i].value;
  999. }
  1000. fx->must_compute_slider = true;
  1001. // invoke @serialize
  1002. {
  1003. std::unique_lock<ysfx::mutex> lock;
  1004. ysfx_serializer_t *serializer = static_cast<ysfx_serializer_t *>(ysfx_get_file(fx, 0, lock));
  1005. assert(serializer);
  1006. serializer->begin(false, buffer);
  1007. lock.unlock();
  1008. ysfx_serialize(fx);
  1009. lock.lock();
  1010. serializer->end();
  1011. }
  1012. return true;
  1013. }
  1014. ysfx_state_t *ysfx_save_state(ysfx_t *fx)
  1015. {
  1016. if (!fx->code.compiled)
  1017. return nullptr;
  1018. std::string buffer;
  1019. // invoke @serialize
  1020. {
  1021. std::unique_lock<ysfx::mutex> lock;
  1022. ysfx_serializer_t *serializer = static_cast<ysfx_serializer_t *>(ysfx_get_file(fx, 0, lock));
  1023. assert(serializer);
  1024. serializer->begin(true, buffer);
  1025. lock.unlock();
  1026. ysfx_serialize(fx);
  1027. lock.lock();
  1028. serializer->end();
  1029. }
  1030. // save the sliders
  1031. ysfx_state_u state{new ysfx_state_t};
  1032. uint32_t slider_count = 0;
  1033. for (uint32_t i = 0; i < ysfx_max_sliders; ++i)
  1034. slider_count += fx->source.main->header.sliders[i].exists;
  1035. state->sliders = new ysfx_state_slider_t[slider_count]{};
  1036. state->slider_count = slider_count;
  1037. for (uint32_t i = 0, j = 0; i < slider_count; ++i) {
  1038. if (fx->source.main->header.sliders[i].exists) {
  1039. state->sliders[j].index = i;
  1040. state->sliders[j].value = *fx->var.slider[i];
  1041. ++j;
  1042. }
  1043. }
  1044. // save the serialization
  1045. state->data_size = buffer.size();
  1046. state->data = new uint8_t[state->data_size];
  1047. memcpy(state->data, buffer.data(), state->data_size);
  1048. //
  1049. return state.release();
  1050. }
  1051. void ysfx_state_free(ysfx_state_t *state)
  1052. {
  1053. if (!state)
  1054. return;
  1055. delete[] state->sliders;
  1056. delete[] state->data;
  1057. delete state;
  1058. }
  1059. ysfx_state_t *ysfx_state_dup(ysfx_state_t *state_in)
  1060. {
  1061. if (!state_in)
  1062. return nullptr;
  1063. ysfx_state_u state_out{new ysfx_state_t};
  1064. uint32_t slider_count = state_out->slider_count = state_in->slider_count;
  1065. size_t data_size = state_out->data_size = state_in->data_size;
  1066. state_out->sliders = new ysfx_state_slider_t[slider_count];
  1067. memcpy(state_out->sliders, state_in->sliders, slider_count * sizeof(ysfx_state_slider_t));
  1068. state_out->data = new uint8_t[data_size];
  1069. memcpy(state_out->data, state_in->data, data_size);
  1070. return state_out.release();
  1071. }
  1072. void ysfx_serialize(ysfx_t *fx)
  1073. {
  1074. if (fx->code.serialize) {
  1075. if (fx->must_compute_init)
  1076. ysfx_init(fx);
  1077. NSEEL_code_execute(fx->code.serialize.get());
  1078. }
  1079. }
  1080. uint32_t ysfx_get_slider_of_var(ysfx_t *fx, EEL_F *var)
  1081. {
  1082. auto it = fx->slider_of_var.find(var);
  1083. if (it == fx->slider_of_var.end())
  1084. return ~(uint32_t)0;
  1085. return it->second;
  1086. }
  1087. const char *ysfx_get_bank_path(ysfx_t *fx)
  1088. {
  1089. return fx->source.bank_path.c_str();
  1090. }
  1091. void ysfx_enum_vars(ysfx_t *fx, ysfx_enum_vars_callback_t *callback, void *userdata)
  1092. {
  1093. NSEEL_VM_enumallvars(fx->vm.get(), callback, userdata);
  1094. }
  1095. ysfx_real *ysfx_find_var(ysfx_t *fx, const char *name)
  1096. {
  1097. struct find_data {
  1098. ysfx_real *var = nullptr;
  1099. const char *name = nullptr;
  1100. };
  1101. find_data fd;
  1102. fd.name = name;
  1103. auto callback = [](const char *name, EEL_F *var, void *userdata) -> int {
  1104. find_data *fd = (find_data *)userdata;
  1105. if (strcmp(name, fd->name) != 0)
  1106. return 1;
  1107. fd->var = var;
  1108. return 0;
  1109. };
  1110. NSEEL_VM_enumallvars(fx->vm.get(), +callback, &fd);
  1111. return fd.var;
  1112. }
  1113. void ysfx_read_vmem(ysfx_t *fx, uint32_t addr, ysfx_real *dest, uint32_t count)
  1114. {
  1115. ysfx_eel_ram_reader reader(fx->vm.get(), addr);
  1116. for (uint32_t i = 0; i < count; ++i)
  1117. dest[i] = reader.read_next();
  1118. }
  1119. bool ysfx_find_data_file(ysfx_t *fx, EEL_F *file, std::string &result)
  1120. {
  1121. // 3 possibilities for file
  1122. // - slider
  1123. // - index of filename
  1124. // - string
  1125. std::string filepart;
  1126. bool accept_absolute = false;
  1127. bool accept_relative = false;
  1128. int32_t index = ysfx_eel_round<int32_t>(*file);
  1129. uint32_t slideridx = ysfx_get_slider_of_var(fx, file);
  1130. ysfx_slider_t *slider = nullptr;
  1131. if (slideridx != ~(uint32_t)0)
  1132. slider = &fx->source.main->header.sliders[slideridx];
  1133. if (slider && !slider->path.empty()) {
  1134. int32_t value = ysfx_eel_round<int32_t>(*fx->var.slider[slideridx]);
  1135. if (value < 0 || (uint32_t)value >= slider->enum_names.size())
  1136. return false;
  1137. filepart = slider->path + '/' + slider->enum_names[(uint32_t)value];
  1138. accept_relative = true;
  1139. }
  1140. else if (index >= 0 && (uint32_t)index < fx->source.main->header.filenames.size()) {
  1141. filepart = fx->source.main->header.filenames[(uint32_t)index];
  1142. accept_relative = true;
  1143. }
  1144. else if (ysfx_string_get(fx, *file, filepart)) {
  1145. accept_absolute = true;
  1146. accept_relative = true;
  1147. }
  1148. else
  1149. return false;
  1150. std::vector<std::string> filecandidates;
  1151. filecandidates.reserve(2);
  1152. if (accept_absolute && !ysfx::path_is_relative(filepart.c_str()))
  1153. filecandidates.push_back(filepart);
  1154. else if (accept_relative) {
  1155. filecandidates.push_back(ysfx::path_directory(fx->source.main_file_path.c_str()) + filepart);
  1156. if (!fx->config->data_root.empty())
  1157. filecandidates.push_back(fx->config->data_root + filepart);
  1158. }
  1159. for (const std::string &filepath : filecandidates) {
  1160. if (ysfx::exists(filepath.c_str())) {
  1161. result.assign(filepath);
  1162. return true;
  1163. }
  1164. }
  1165. return false;
  1166. }
  1167. ysfx_file_type_t ysfx_detect_file_type(ysfx_t *fx, const char *path, void **fmtobj)
  1168. {
  1169. if (ysfx::path_has_suffix(path, "txt"))
  1170. return ysfx_file_type_txt;
  1171. if (ysfx::path_has_suffix(path, "raw"))
  1172. return ysfx_file_type_raw;
  1173. for (ysfx_audio_format_t &fmt : fx->config->audio_formats) {
  1174. if (fmt.can_handle(path)) {
  1175. if (fmtobj)
  1176. *fmtobj = &fmt;
  1177. return ysfx_file_type_audio;
  1178. }
  1179. }
  1180. return ysfx_file_type_none;
  1181. }
  1182. void ysfx_gfx_setup(ysfx_t *fx, ysfx_gfx_config_t *gc)
  1183. {
  1184. #if !defined(YSFX_NO_GFX)
  1185. bool doinit = false;
  1186. ysfx_scoped_gfx_t scope{fx, doinit};
  1187. ysfx_gfx_state_set_bitmap(fx->gfx.state.get(), gc->pixels, gc->pixel_width, gc->pixel_height, gc->pixel_stride);
  1188. ysfx_real scale = fx->gfx.wants_retina ? gc->scale_factor : 1;
  1189. ysfx_gfx_state_set_scale_factor(fx->gfx.state.get(), scale);
  1190. ysfx_gfx_state_set_callback_data(fx->gfx.state.get(), gc->user_data);
  1191. ysfx_gfx_state_set_show_menu_callback(fx->gfx.state.get(), gc->show_menu);
  1192. ysfx_gfx_state_set_set_cursor_callback(fx->gfx.state.get(), gc->set_cursor);
  1193. ysfx_gfx_state_set_get_drop_file_callback(fx->gfx.state.get(), gc->get_drop_file);
  1194. #else
  1195. (void)fx;
  1196. (void)gc;
  1197. #endif
  1198. }
  1199. bool ysfx_gfx_wants_retina(ysfx_t *fx)
  1200. {
  1201. #if !defined(YSFX_NO_GFX)
  1202. return fx->gfx.wants_retina;
  1203. #else
  1204. (void)fx;
  1205. return false;
  1206. #endif
  1207. }
  1208. void ysfx_gfx_add_key(ysfx_t *fx, uint32_t mods, uint32_t key, bool press)
  1209. {
  1210. #if !defined(YSFX_NO_GFX)
  1211. bool doinit = true;
  1212. ysfx_scoped_gfx_t scope{fx, doinit};
  1213. if (!fx->gfx.ready)
  1214. return;
  1215. ysfx_gfx_state_add_key(fx->gfx.state.get(), mods, key, press);
  1216. #else
  1217. (void)fx;
  1218. (void)mods;
  1219. (void)key;
  1220. #endif
  1221. }
  1222. 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)
  1223. {
  1224. #if !defined(YSFX_NO_GFX)
  1225. bool doinit = true;
  1226. ysfx_scoped_gfx_t scope{fx, doinit};
  1227. if (!fx->gfx.ready)
  1228. return;
  1229. *fx->var.mouse_x = (EEL_F)xpos;
  1230. *fx->var.mouse_y = (EEL_F)ypos;
  1231. *fx->var.mouse_wheel += 120 * wheel;
  1232. *fx->var.mouse_hwheel += 120 * hwheel;
  1233. uint32_t mouse_cap = 0;
  1234. if (mods & ysfx_mod_shift)
  1235. mouse_cap |= 8;
  1236. if (mods & ysfx_mod_ctrl)
  1237. mouse_cap |= 4;
  1238. if (mods & ysfx_mod_alt)
  1239. mouse_cap |= 16;
  1240. if (mods & ysfx_mod_super)
  1241. mouse_cap |= 32;
  1242. if (buttons & ysfx_button_left)
  1243. mouse_cap |= 1;
  1244. if (buttons & ysfx_button_middle)
  1245. mouse_cap |= 64;
  1246. if (buttons & ysfx_button_right)
  1247. mouse_cap |= 2;
  1248. *fx->var.mouse_cap = (EEL_F)mouse_cap;
  1249. #else
  1250. (void)fx;
  1251. (void)mods;
  1252. (void)xpos;
  1253. (void)ypos;
  1254. (void)buttons;
  1255. (void)wheel;
  1256. (void)hwheel;
  1257. #endif
  1258. }
  1259. bool ysfx_gfx_run(ysfx_t *fx)
  1260. {
  1261. #if !defined(YSFX_NO_GFX)
  1262. bool doinit = true;
  1263. ysfx_scoped_gfx_t scope{fx, doinit};
  1264. if (!fx->gfx.ready)
  1265. return false;
  1266. ysfx_gfx_prepare(fx);
  1267. NSEEL_code_execute(fx->code.gfx.get());
  1268. return ysfx_gfx_state_is_dirty(fx->gfx.state.get());
  1269. #else
  1270. return false;
  1271. (void)fx;
  1272. #endif
  1273. }