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.

575 lines
14KB

  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_api_file.hpp"
  20. #include "ysfx_eel_utils.hpp"
  21. #include <cstring>
  22. #include <cstdio>
  23. #include <cassert>
  24. ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename)
  25. : m_vm(vm),
  26. m_stream(ysfx::fopen_utf8(filename, "rb"))
  27. {
  28. }
  29. int32_t ysfx_raw_file_t::avail()
  30. {
  31. if (!m_stream)
  32. return 0;
  33. int64_t cur_off = ysfx::ftell_lfs(m_stream.get());
  34. if (cur_off == -1)
  35. return 0;
  36. if (ysfx::fseek_lfs(m_stream.get(), 0, SEEK_END) == -1)
  37. return 0;
  38. int64_t end_off = ysfx::ftell_lfs(m_stream.get());
  39. if (end_off == -1)
  40. return 0;
  41. if (ysfx::fseek_lfs(m_stream.get(), cur_off, SEEK_SET) == -1)
  42. return 0;
  43. if ((uint64_t)end_off < (uint64_t)cur_off)
  44. return 0;
  45. uint64_t byte_count = (uint64_t)end_off - (uint64_t)cur_off;
  46. uint64_t f32_count = byte_count / 4;
  47. return (f32_count > 0x7fffffff) ? 0x7fffffff : (uint32_t)f32_count;
  48. }
  49. void ysfx_raw_file_t::rewind()
  50. {
  51. if (!m_stream)
  52. return;
  53. ::rewind(m_stream.get());
  54. }
  55. bool ysfx_raw_file_t::var(ysfx_real *var)
  56. {
  57. if (!m_stream)
  58. return false;
  59. uint8_t data[4];
  60. if (fread(data, 1, 4, m_stream.get()) != 4)
  61. return false;
  62. *var = (EEL_F)ysfx::unpack_f32le(data);
  63. return true;
  64. }
  65. uint32_t ysfx_raw_file_t::mem(uint32_t offset, uint32_t length)
  66. {
  67. if (!m_stream)
  68. return 0;
  69. ysfx_eel_ram_writer writer{m_vm, offset};
  70. uint32_t read;
  71. for (read = 0; read < length; ++read) {
  72. ysfx_real value;
  73. if (!var(&value))
  74. break;
  75. writer.write_next(value);
  76. }
  77. return read;
  78. }
  79. uint32_t ysfx_raw_file_t::string(std::string &str)
  80. {
  81. if (!m_stream)
  82. return 0;
  83. uint8_t data[4];
  84. if (fread(data, 1, 4, m_stream.get()) != 4)
  85. return 0;
  86. str.clear();
  87. uint32_t srclen = ysfx::unpack_u32le(data);
  88. str.reserve((srclen < ysfx_string_max_length) ? srclen : ysfx_string_max_length);
  89. uint32_t count = 0;
  90. for (int byte; count < srclen && (byte = fgetc(m_stream.get())) != EOF; ) {
  91. if (str.size() < ysfx_string_max_length)
  92. str.push_back((unsigned char)byte);
  93. ++count;
  94. }
  95. return count;
  96. }
  97. //------------------------------------------------------------------------------
  98. ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename)
  99. : m_vm(vm),
  100. m_stream(ysfx::fopen_utf8(filename, "rb"))
  101. {
  102. m_buf.reserve(256);
  103. }
  104. int32_t ysfx_text_file_t::avail()
  105. {
  106. if (!m_stream || ferror(m_stream.get()))
  107. return -1;
  108. return (feof(m_stream.get()) == 0) ? 0 : 1;
  109. }
  110. void ysfx_text_file_t::rewind()
  111. {
  112. if (!m_stream)
  113. return;
  114. ::rewind(m_stream.get());
  115. }
  116. bool ysfx_text_file_t::var(ysfx_real *var)
  117. {
  118. if (!m_stream)
  119. return false;
  120. //TODO support the expression language for arithmetic
  121. int ch;
  122. do {
  123. // get the next number separated by newline or comma
  124. // but skip invalid lines
  125. m_buf.clear();
  126. while ((ch = fgetc(m_stream.get())) != EOF && ch != '\n' && ch != ',')
  127. m_buf.push_back((unsigned char)ch);
  128. const char *startp = m_buf.c_str();
  129. const char *endp = (char *)startp;
  130. double value = ysfx::dot_strtod(startp, (char **)&endp);
  131. if (endp != startp) {
  132. *var = (EEL_F)value;
  133. return true;
  134. }
  135. } while (ch != EOF);
  136. return false;
  137. }
  138. uint32_t ysfx_text_file_t::mem(uint32_t offset, uint32_t length)
  139. {
  140. if (!m_stream)
  141. return 0;
  142. ysfx_eel_ram_writer writer{m_vm, offset};
  143. uint32_t read;
  144. for (read = 0; read < length; ++read) {
  145. ysfx_real value;
  146. if (!var(&value))
  147. break;
  148. writer.write_next(value);
  149. }
  150. return read;
  151. }
  152. uint32_t ysfx_text_file_t::string(std::string &str)
  153. {
  154. if (!m_stream)
  155. return 0;
  156. str.clear();
  157. str.reserve(256);
  158. int ch;
  159. do {
  160. ch = fgetc(m_stream.get());
  161. if (ch != EOF && str.size() < ysfx_string_max_length)
  162. str.push_back((unsigned char)ch);
  163. } while (ch != EOF && ch != '\n');
  164. return (uint32_t)str.size();
  165. }
  166. //------------------------------------------------------------------------------
  167. ysfx_audio_file_t::ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename)
  168. : m_vm(vm),
  169. m_fmt(fmt),
  170. m_reader(fmt.open(filename), fmt.close)
  171. {
  172. }
  173. int32_t ysfx_audio_file_t::avail()
  174. {
  175. if (!m_reader)
  176. return -1;
  177. uint64_t avail = m_fmt.avail(m_reader.get());
  178. return (avail > 0x7fffffff) ? 0x7fffffff : (int32_t)avail;
  179. }
  180. void ysfx_audio_file_t::rewind()
  181. {
  182. if (!m_reader)
  183. return;
  184. m_fmt.rewind(m_reader.get());
  185. }
  186. bool ysfx_audio_file_t::var(ysfx_real *var)
  187. {
  188. if (!m_reader)
  189. return false;
  190. return m_fmt.read(m_reader.get(), var, 1) == 1;
  191. }
  192. uint32_t ysfx_audio_file_t::mem(uint32_t offset, uint32_t length)
  193. {
  194. if (!m_reader)
  195. return 0;
  196. uint32_t numread = 0;
  197. ysfx_real *buf = m_buf.get();
  198. ysfx_eel_ram_writer writer(m_vm, offset);
  199. while (numread < length) {
  200. uint32_t n = length - numread;
  201. if (n > buffer_size)
  202. n = buffer_size;
  203. uint32_t m = (uint32_t)m_fmt.read(m_reader.get(), buf, n);
  204. for (uint32_t i = 0; i < m; ++i)
  205. writer.write_next(buf[i]);
  206. numread += m;
  207. if (m < n)
  208. break;
  209. }
  210. return numread;
  211. }
  212. uint32_t ysfx_audio_file_t::string(std::string &str)
  213. {
  214. (void)str;
  215. return 0;
  216. }
  217. bool ysfx_audio_file_t::riff(uint32_t &nch, ysfx_real &samplerate)
  218. {
  219. if (!m_reader)
  220. return false;
  221. ysfx_audio_file_info_t info = m_fmt.info(m_reader.get());
  222. nch = info.channels;
  223. samplerate = info.sample_rate;
  224. return true;
  225. }
  226. //------------------------------------------------------------------------------
  227. ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm)
  228. : m_vm(vm)
  229. {
  230. }
  231. void ysfx_serializer_t::begin(bool write, std::string &buffer)
  232. {
  233. m_write = (int)write;
  234. m_buffer = &buffer;
  235. m_pos = 0;
  236. }
  237. void ysfx_serializer_t::end()
  238. {
  239. m_write = -1;
  240. m_buffer = nullptr;
  241. }
  242. int32_t ysfx_serializer_t::avail()
  243. {
  244. if (m_write)
  245. return -1;
  246. else
  247. return 0;
  248. }
  249. void ysfx_serializer_t::rewind()
  250. {
  251. }
  252. bool ysfx_serializer_t::var(ysfx_real *var)
  253. {
  254. if (m_write == 1) {
  255. uint8_t buf[4];
  256. ysfx::pack_f32le((float)*var, buf);
  257. m_buffer->append((char *)buf, 4);
  258. return true;
  259. }
  260. else if (m_write == 0) {
  261. if (m_pos + 4 > m_buffer->size()) {
  262. m_pos = m_buffer->size();
  263. *var = 0;
  264. return false;
  265. }
  266. *var = (EEL_F)ysfx::unpack_f32le((uint8_t *)&(*m_buffer)[m_pos]);
  267. m_pos += 4;
  268. return true;
  269. }
  270. return false;
  271. }
  272. uint32_t ysfx_serializer_t::mem(uint32_t offset, uint32_t length)
  273. {
  274. if (m_write == 1) {
  275. ysfx_eel_ram_reader reader{m_vm, offset};
  276. for (uint32_t i = 0; i < length; ++i) {
  277. ysfx_real value = reader.read_next();
  278. if (!var(&value))
  279. return i;
  280. }
  281. return length;
  282. }
  283. else if (m_write == 0) {
  284. ysfx_eel_ram_writer writer{m_vm, offset};
  285. for (uint32_t i = 0; i < length; ++i) {
  286. ysfx_real value{};
  287. if (!var(&value))
  288. return i;
  289. writer.write_next(value);
  290. }
  291. return length;
  292. }
  293. return 0;
  294. }
  295. uint32_t ysfx_serializer_t::string(std::string &str)
  296. {
  297. // TODO implement me; docs claim support in Reaper 4.59+ but it seems
  298. // non-working (as of Reaper 6.40)
  299. return 0;
  300. }
  301. //------------------------------------------------------------------------------
  302. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_open(void *opaque, EEL_F *file_)
  303. {
  304. ysfx_t *fx = (ysfx_t *)opaque;
  305. std::string filepath;
  306. if (!ysfx_find_data_file(fx, file_, filepath))
  307. return -1;
  308. void *fmtobj = nullptr;
  309. ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), &fmtobj);
  310. ysfx_file_u file;
  311. switch (ftype) {
  312. case ysfx_file_type_txt:
  313. file.reset(new ysfx_text_file_t(fx->vm.get(), filepath.c_str()));
  314. break;
  315. case ysfx_file_type_raw:
  316. file.reset(new ysfx_raw_file_t(fx->vm.get(), filepath.c_str()));
  317. break;
  318. case ysfx_file_type_audio:
  319. file.reset(new ysfx_audio_file_t(fx->vm.get(), *(ysfx_audio_format_t *)fmtobj, filepath.c_str()));
  320. break;
  321. case ysfx_file_type_none:
  322. break;
  323. default:
  324. assert(false);
  325. }
  326. if (file) {
  327. int32_t handle = ysfx_insert_file(fx, file.get());
  328. if (handle == -1)
  329. return -1;
  330. (void)file.release();
  331. return (EEL_F)(uint32_t)handle;
  332. }
  333. return -1;
  334. }
  335. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_close(void *opaque, EEL_F *handle_)
  336. {
  337. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  338. if (handle <= 0) //NOTE: cannot close the serializer handle (0)
  339. return -1;
  340. ysfx_t *fx = (ysfx_t *)opaque;
  341. std::unique_ptr<ysfx::mutex> file_mutex;
  342. std::unique_lock<ysfx::mutex> lock;
  343. std::unique_lock<ysfx::mutex> list_lock;
  344. // hold both locks to protect file and list access during removal
  345. if (!ysfx_get_file(fx, (uint32_t)handle, lock, &list_lock))
  346. return -1;
  347. // preserve the locked mutex of the object being removed
  348. file_mutex = std::move(fx->file.list[(uint32_t)handle]->m_mutex);
  349. fx->file.list[(uint32_t)handle].reset();
  350. return 0;
  351. }
  352. static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_rewind(void *opaque, EEL_F *handle_)
  353. {
  354. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  355. if (handle < 0)
  356. return handle_;
  357. ysfx_t *fx = (ysfx_t *)opaque;
  358. std::unique_lock<ysfx::mutex> lock;
  359. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  360. if (!file)
  361. return 0;
  362. file->rewind();
  363. return handle_;
  364. }
  365. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_var(void *opaque, EEL_F *handle_, EEL_F *var)
  366. {
  367. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  368. if (handle < 0)
  369. return 0;
  370. ysfx_t *fx = (ysfx_t *)opaque;
  371. std::unique_lock<ysfx::mutex> lock;
  372. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  373. if (!file)
  374. return 0;
  375. if (!file->var(var))
  376. return 0;
  377. return 1;
  378. }
  379. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_mem(void *opaque, EEL_F *handle_, EEL_F *offset_, EEL_F *length_)
  380. {
  381. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  382. int32_t offset = ysfx_eel_round<int32_t>(*offset_);
  383. int32_t length = ysfx_eel_round<int32_t>(*length_);
  384. if (handle < 0 || offset < 0 || length <= 0)
  385. return 0;
  386. ysfx_t *fx = (ysfx_t *)opaque;
  387. std::unique_lock<ysfx::mutex> lock;
  388. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  389. if (!file)
  390. return 0;
  391. return (EEL_F)file->mem((uint32_t)offset, (uint32_t)length);
  392. }
  393. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_avail(void *opaque, EEL_F *handle_)
  394. {
  395. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  396. if (handle < 0)
  397. return 0;
  398. ysfx_t *fx = (ysfx_t *)opaque;
  399. std::unique_lock<ysfx::mutex> lock;
  400. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  401. if (!file)
  402. return 0;
  403. return file->avail();
  404. }
  405. static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_riff(void *opaque, EEL_F *handle_, EEL_F *nch_, EEL_F *samplerate_)
  406. {
  407. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  408. if (handle < 0)
  409. return 0;
  410. ysfx_t *fx = (ysfx_t *)opaque;
  411. std::unique_lock<ysfx::mutex> lock;
  412. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  413. if (!file) {
  414. *nch_ = 0;
  415. *samplerate_ = 0;
  416. return nch_;
  417. }
  418. uint32_t nch = 0;
  419. ysfx_real samplerate = 0;
  420. if (!file->riff(nch, samplerate)) {
  421. *nch_ = 0;
  422. *samplerate_ = 0;
  423. return nch_;
  424. }
  425. *nch_ = (EEL_F)nch;
  426. *samplerate_ = samplerate;
  427. return nch_;
  428. }
  429. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_text(void *opaque, EEL_F *handle_)
  430. {
  431. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  432. if (handle < 0)
  433. return 0;
  434. ysfx_t *fx = (ysfx_t *)opaque;
  435. std::unique_lock<ysfx::mutex> lock;
  436. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  437. if (!file)
  438. return 0;
  439. return (EEL_F)file->is_text();
  440. }
  441. static EEL_F NSEEL_CGEN_CALL ysfx_api_file_string(void *opaque, EEL_F *handle_, EEL_F *string_)
  442. {
  443. int32_t handle = ysfx_eel_round<int32_t>(*handle_);
  444. if (handle < 0)
  445. return 0;
  446. ysfx_t *fx = (ysfx_t *)opaque;
  447. std::unique_lock<ysfx::mutex> lock;
  448. ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
  449. if (!file)
  450. return 0;
  451. std::string txt;
  452. uint32_t count;
  453. if (!file->is_in_write_mode()) {
  454. count = file->string(txt);
  455. ysfx_string_set(fx, *string_, txt);
  456. }
  457. else {
  458. ysfx_string_get(fx, *string_, txt);
  459. count = file->string(txt);
  460. }
  461. return (EEL_F)count;
  462. }
  463. void ysfx_api_init_file()
  464. {
  465. NSEEL_addfunc_retval("file_open", 1, NSEEL_PProc_THIS, &ysfx_api_file_open);
  466. NSEEL_addfunc_retval("file_close", 1, NSEEL_PProc_THIS, &ysfx_api_file_close);
  467. NSEEL_addfunc_retptr("file_rewind", 1, NSEEL_PProc_THIS, &ysfx_api_file_rewind);
  468. NSEEL_addfunc_retval("file_var", 2, NSEEL_PProc_THIS, &ysfx_api_file_var);
  469. NSEEL_addfunc_retval("file_mem", 3, NSEEL_PProc_THIS, &ysfx_api_file_mem);
  470. NSEEL_addfunc_retval("file_avail", 1, NSEEL_PProc_THIS, &ysfx_api_file_avail);
  471. NSEEL_addfunc_retptr("file_riff", 3, NSEEL_PProc_THIS, &ysfx_api_file_riff);
  472. NSEEL_addfunc_retval("file_text", 1, NSEEL_PProc_THIS, &ysfx_api_file_text);
  473. NSEEL_addfunc_retval("file_string", 2, NSEEL_PProc_THIS, &ysfx_api_file_string);
  474. }