Cleanup
[carla.git] / source / modules / ysfx / sources / ysfx_api_file.cpp
blob95a9f9a4495609e8f009d9dba1dc999b2c9db0f0
1 // Copyright 2021 Jean Pierre Cimalando
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 // SPDX-License-Identifier: Apache-2.0
18 #include "ysfx.hpp"
19 #include "ysfx_config.hpp"
20 #include "ysfx_api_file.hpp"
21 #include "ysfx_eel_utils.hpp"
22 #include <cstring>
23 #include <cstdio>
24 #include <cassert>
26 ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename)
27 : m_vm(vm),
28 m_stream(ysfx::fopen_utf8(filename, "rb"))
32 int32_t ysfx_raw_file_t::avail()
34 if (!m_stream)
35 return 0;
37 int64_t cur_off = ysfx::ftell_lfs(m_stream.get());
38 if (cur_off == -1)
39 return 0;
41 if (ysfx::fseek_lfs(m_stream.get(), 0, SEEK_END) == -1)
42 return 0;
44 int64_t end_off = ysfx::ftell_lfs(m_stream.get());
45 if (end_off == -1)
46 return 0;
48 if (ysfx::fseek_lfs(m_stream.get(), cur_off, SEEK_SET) == -1)
49 return 0;
51 if ((uint64_t)end_off < (uint64_t)cur_off)
52 return 0;
54 uint64_t byte_count = (uint64_t)end_off - (uint64_t)cur_off;
55 uint64_t f32_count = byte_count / 4;
56 return (f32_count > 0x7fffffff) ? 0x7fffffff : (uint32_t)f32_count;
59 void ysfx_raw_file_t::rewind()
61 if (!m_stream)
62 return;
64 ::rewind(m_stream.get());
67 bool ysfx_raw_file_t::var(ysfx_real *var)
69 if (!m_stream)
70 return false;
72 uint8_t data[4];
73 if (fread(data, 1, 4, m_stream.get()) != 4)
74 return false;
76 *var = (EEL_F)ysfx::unpack_f32le(data);
77 return true;
80 uint32_t ysfx_raw_file_t::mem(uint32_t offset, uint32_t length)
82 if (!m_stream)
83 return 0;
85 ysfx_eel_ram_writer writer{m_vm, offset};
87 uint32_t read;
88 for (read = 0; read < length; ++read) {
89 ysfx_real value;
90 if (!var(&value))
91 break;
92 writer.write_next(value);
95 return read;
98 uint32_t ysfx_raw_file_t::string(std::string &str)
100 if (!m_stream)
101 return 0;
103 uint8_t data[4];
104 if (fread(data, 1, 4, m_stream.get()) != 4)
105 return 0;
107 str.clear();
109 uint32_t srclen = ysfx::unpack_u32le(data);
110 str.reserve((srclen < ysfx_string_max_length) ? srclen : ysfx_string_max_length);
112 uint32_t count = 0;
113 for (int byte; count < srclen && (byte = fgetc(m_stream.get())) != EOF; ) {
114 if (str.size() < ysfx_string_max_length)
115 str.push_back((unsigned char)byte);
116 ++count;
118 return count;
121 //------------------------------------------------------------------------------
122 ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename)
123 : m_vm(vm),
124 m_stream(ysfx::fopen_utf8(filename, "rb"))
126 m_buf.reserve(256);
129 int32_t ysfx_text_file_t::avail()
131 if (!m_stream || ferror(m_stream.get()))
132 return -1;
134 return (feof(m_stream.get()) == 0) ? 0 : 1;
137 void ysfx_text_file_t::rewind()
139 if (!m_stream)
140 return;
142 ::rewind(m_stream.get());
145 bool ysfx_text_file_t::var(ysfx_real *var)
147 if (!m_stream)
148 return false;
150 //TODO support the expression language for arithmetic
152 int ch;
153 do {
154 // get the next number separated by newline or comma
155 // but skip invalid lines
156 m_buf.clear();
157 while ((ch = fgetc(m_stream.get())) != EOF && ch != '\n' && ch != ',')
158 m_buf.push_back((unsigned char)ch);
159 const char *startp = m_buf.c_str();
160 const char *endp = (char *)startp;
161 double value = ysfx::dot_strtod(startp, (char **)&endp);
162 if (endp != startp) {
163 *var = (EEL_F)value;
164 return true;
166 } while (ch != EOF);
168 return false;
171 uint32_t ysfx_text_file_t::mem(uint32_t offset, uint32_t length)
173 if (!m_stream)
174 return 0;
176 ysfx_eel_ram_writer writer{m_vm, offset};
178 uint32_t read;
179 for (read = 0; read < length; ++read) {
180 ysfx_real value;
181 if (!var(&value))
182 break;
183 writer.write_next(value);
186 return read;
189 uint32_t ysfx_text_file_t::string(std::string &str)
191 if (!m_stream)
192 return 0;
194 str.clear();
195 str.reserve(256);
197 int ch;
198 do {
199 ch = fgetc(m_stream.get());
200 if (ch != EOF && str.size() < ysfx_string_max_length)
201 str.push_back((unsigned char)ch);
202 } while (ch != EOF && ch != '\n');
204 return (uint32_t)str.size();
207 //------------------------------------------------------------------------------
208 ysfx_audio_file_t::ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename)
209 : m_vm(vm),
210 m_fmt(fmt),
211 m_reader(fmt.open(filename), fmt.close)
215 int32_t ysfx_audio_file_t::avail()
217 if (!m_reader)
218 return -1;
220 uint64_t avail = m_fmt.avail(m_reader.get());
221 return (avail > 0x7fffffff) ? 0x7fffffff : (int32_t)avail;
224 void ysfx_audio_file_t::rewind()
226 if (!m_reader)
227 return;
229 m_fmt.rewind(m_reader.get());
232 bool ysfx_audio_file_t::var(ysfx_real *var)
234 if (!m_reader)
235 return false;
237 return m_fmt.read(m_reader.get(), var, 1) == 1;
240 uint32_t ysfx_audio_file_t::mem(uint32_t offset, uint32_t length)
242 if (!m_reader)
243 return 0;
245 uint32_t numread = 0;
246 ysfx_real *buf = m_buf.get();
247 ysfx_eel_ram_writer writer(m_vm, offset);
249 while (numread < length) {
250 uint32_t n = length - numread;
251 if (n > buffer_size)
252 n = buffer_size;
254 uint32_t m = (uint32_t)m_fmt.read(m_reader.get(), buf, n);
255 for (uint32_t i = 0; i < m; ++i)
256 writer.write_next(buf[i]);
258 numread += m;
259 if (m < n)
260 break;
263 return numread;
266 uint32_t ysfx_audio_file_t::string(std::string &str)
268 (void)str;
269 return 0;
272 bool ysfx_audio_file_t::riff(uint32_t &nch, ysfx_real &samplerate)
274 if (!m_reader)
275 return false;
277 ysfx_audio_file_info_t info = m_fmt.info(m_reader.get());
278 nch = info.channels;
279 samplerate = info.sample_rate;
280 return true;
283 //------------------------------------------------------------------------------
284 ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm)
285 : m_vm(vm)
289 void ysfx_serializer_t::begin(bool write, std::string &buffer)
291 m_write = (int)write;
292 m_buffer = &buffer;
293 m_pos = 0;
296 void ysfx_serializer_t::end()
298 m_write = -1;
299 m_buffer = nullptr;
302 int32_t ysfx_serializer_t::avail()
304 if (m_write)
305 return -1;
306 else
307 return 0;
310 void ysfx_serializer_t::rewind()
314 bool ysfx_serializer_t::var(ysfx_real *var)
316 if (m_write == 1) {
317 uint8_t buf[4];
318 ysfx::pack_f32le((float)*var, buf);
319 m_buffer->append((char *)buf, 4);
320 return true;
322 else if (m_write == 0) {
323 if (m_pos + 4 > m_buffer->size()) {
324 m_pos = m_buffer->size();
325 *var = 0;
326 return false;
328 *var = (EEL_F)ysfx::unpack_f32le((uint8_t *)&(*m_buffer)[m_pos]);
329 m_pos += 4;
330 return true;
332 return false;
335 uint32_t ysfx_serializer_t::mem(uint32_t offset, uint32_t length)
337 if (m_write == 1) {
338 ysfx_eel_ram_reader reader{m_vm, offset};
339 for (uint32_t i = 0; i < length; ++i) {
340 ysfx_real value = reader.read_next();
341 if (!var(&value))
342 return i;
344 return length;
346 else if (m_write == 0) {
347 ysfx_eel_ram_writer writer{m_vm, offset};
348 for (uint32_t i = 0; i < length; ++i) {
349 ysfx_real value{};
350 if (!var(&value))
351 return i;
352 writer.write_next(value);
354 return length;
356 return 0;
359 uint32_t ysfx_serializer_t::string(std::string &str)
361 // TODO implement me; docs claim support in Reaper 4.59+ but it seems
362 // non-working (as of Reaper 6.40)
363 return 0;
366 //------------------------------------------------------------------------------
367 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_open(void *opaque, EEL_F *file_)
369 ysfx_t *fx = (ysfx_t *)opaque;
371 std::string filepath;
372 if (!ysfx_find_data_file(fx, file_, filepath))
373 return -1;
375 void *fmtobj = nullptr;
376 ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), &fmtobj);
378 ysfx_file_u file;
379 switch (ftype) {
380 case ysfx_file_type_txt:
381 file.reset(new ysfx_text_file_t(fx->vm.get(), filepath.c_str()));
382 break;
383 case ysfx_file_type_raw:
384 file.reset(new ysfx_raw_file_t(fx->vm.get(), filepath.c_str()));
385 break;
386 case ysfx_file_type_audio:
387 file.reset(new ysfx_audio_file_t(fx->vm.get(), *(ysfx_audio_format_t *)fmtobj, filepath.c_str()));
388 break;
389 case ysfx_file_type_none:
390 break;
391 default:
392 assert(false);
395 if (file) {
396 int32_t handle = ysfx_insert_file(fx, file.get());
397 if (handle == -1)
398 return -1;
399 (void)file.release();
400 return (EEL_F)(uint32_t)handle;
403 return -1;
406 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_close(void *opaque, EEL_F *handle_)
408 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
409 if (handle <= 0) //NOTE: cannot close the serializer handle (0)
410 return -1;
412 ysfx_t *fx = (ysfx_t *)opaque;
413 std::unique_ptr<ysfx::mutex> file_mutex;
414 std::unique_lock<ysfx::mutex> lock;
415 std::unique_lock<ysfx::mutex> list_lock;
417 // hold both locks to protect file and list access during removal
418 if (!ysfx_get_file(fx, (uint32_t)handle, lock, &list_lock))
419 return -1;
421 // preserve the locked mutex of the object being removed
422 file_mutex = std::move(fx->file.list[(uint32_t)handle]->m_mutex);
424 fx->file.list[(uint32_t)handle].reset();
425 return 0;
428 static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_rewind(void *opaque, EEL_F *handle_)
430 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
431 if (handle < 0)
432 return handle_;
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;
440 file->rewind();
441 return handle_;
444 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_var(void *opaque, EEL_F *handle_, EEL_F *var)
446 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
447 if (handle < 0)
448 return 0;
450 ysfx_t *fx = (ysfx_t *)opaque;
451 std::unique_lock<ysfx::mutex> lock;
452 ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
453 if (!file)
454 return 0;
456 if (!file->var(var))
457 return 0;
459 return 1;
462 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_mem(void *opaque, EEL_F *handle_, EEL_F *offset_, EEL_F *length_)
464 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
465 int32_t offset = ysfx_eel_round<int32_t>(*offset_);
466 int32_t length = ysfx_eel_round<int32_t>(*length_);
467 if (handle < 0 || offset < 0 || length <= 0)
468 return 0;
470 ysfx_t *fx = (ysfx_t *)opaque;
471 std::unique_lock<ysfx::mutex> lock;
472 ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
473 if (!file)
474 return 0;
476 return (EEL_F)file->mem((uint32_t)offset, (uint32_t)length);
479 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_avail(void *opaque, EEL_F *handle_)
481 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
482 if (handle < 0)
483 return 0;
485 ysfx_t *fx = (ysfx_t *)opaque;
486 std::unique_lock<ysfx::mutex> lock;
487 ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
488 if (!file)
489 return 0;
491 return file->avail();
494 static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_riff(void *opaque, EEL_F *handle_, EEL_F *nch_, EEL_F *samplerate_)
496 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
497 if (handle < 0)
498 return 0;
500 ysfx_t *fx = (ysfx_t *)opaque;
501 std::unique_lock<ysfx::mutex> lock;
502 ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
503 if (!file) {
504 *nch_ = 0;
505 *samplerate_ = 0;
506 return nch_;
509 uint32_t nch = 0;
510 ysfx_real samplerate = 0;
511 if (!file->riff(nch, samplerate)) {
512 *nch_ = 0;
513 *samplerate_ = 0;
514 return nch_;
517 *nch_ = (EEL_F)nch;
518 *samplerate_ = samplerate;
519 return nch_;
523 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_text(void *opaque, EEL_F *handle_)
525 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
526 if (handle < 0)
527 return 0;
529 ysfx_t *fx = (ysfx_t *)opaque;
530 std::unique_lock<ysfx::mutex> lock;
531 ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
532 if (!file)
533 return 0;
535 return (EEL_F)file->is_text();
538 static EEL_F NSEEL_CGEN_CALL ysfx_api_file_string(void *opaque, EEL_F *handle_, EEL_F *string_)
540 int32_t handle = ysfx_eel_round<int32_t>(*handle_);
541 if (handle < 0)
542 return 0;
544 ysfx_t *fx = (ysfx_t *)opaque;
545 std::unique_lock<ysfx::mutex> lock;
546 ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
547 if (!file)
548 return 0;
550 std::string txt;
551 uint32_t count;
552 if (!file->is_in_write_mode()) {
553 count = file->string(txt);
554 ysfx_string_set(fx, *string_, txt);
556 else {
557 ysfx_string_get(fx, *string_, txt);
558 count = file->string(txt);
560 return (EEL_F)count;
563 void ysfx_api_init_file()
565 NSEEL_addfunc_retval("file_open", 1, NSEEL_PProc_THIS, &ysfx_api_file_open);
566 NSEEL_addfunc_retval("file_close", 1, NSEEL_PProc_THIS, &ysfx_api_file_close);
567 NSEEL_addfunc_retptr("file_rewind", 1, NSEEL_PProc_THIS, &ysfx_api_file_rewind);
568 NSEEL_addfunc_retval("file_var", 2, NSEEL_PProc_THIS, &ysfx_api_file_var);
569 NSEEL_addfunc_retval("file_mem", 3, NSEEL_PProc_THIS, &ysfx_api_file_mem);
570 NSEEL_addfunc_retval("file_avail", 1, NSEEL_PProc_THIS, &ysfx_api_file_avail);
571 NSEEL_addfunc_retptr("file_riff", 3, NSEEL_PProc_THIS, &ysfx_api_file_riff);
572 NSEEL_addfunc_retval("file_text", 1, NSEEL_PProc_THIS, &ysfx_api_file_text);
573 NSEEL_addfunc_retval("file_string", 2, NSEEL_PProc_THIS, &ysfx_api_file_string);