1 // Copyright 2021 Jean Pierre Cimalando
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
19 #include "ysfx_config.hpp"
20 #include "ysfx_api_file.hpp"
21 #include "ysfx_eel_utils.hpp"
26 ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm
, const char *filename
)
28 m_stream(ysfx::fopen_utf8(filename
, "rb"))
32 int32_t ysfx_raw_file_t::avail()
37 int64_t cur_off
= ysfx::ftell_lfs(m_stream
.get());
41 if (ysfx::fseek_lfs(m_stream
.get(), 0, SEEK_END
) == -1)
44 int64_t end_off
= ysfx::ftell_lfs(m_stream
.get());
48 if (ysfx::fseek_lfs(m_stream
.get(), cur_off
, SEEK_SET
) == -1)
51 if ((uint64_t)end_off
< (uint64_t)cur_off
)
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()
64 ::rewind(m_stream
.get());
67 bool ysfx_raw_file_t::var(ysfx_real
*var
)
73 if (fread(data
, 1, 4, m_stream
.get()) != 4)
76 *var
= (EEL_F
)ysfx::unpack_f32le(data
);
80 uint32_t ysfx_raw_file_t::mem(uint32_t offset
, uint32_t length
)
85 ysfx_eel_ram_writer writer
{m_vm
, offset
};
88 for (read
= 0; read
< length
; ++read
) {
92 writer
.write_next(value
);
98 uint32_t ysfx_raw_file_t::string(std::string
&str
)
104 if (fread(data
, 1, 4, m_stream
.get()) != 4)
109 uint32_t srclen
= ysfx::unpack_u32le(data
);
110 str
.reserve((srclen
< ysfx_string_max_length
) ? srclen
: ysfx_string_max_length
);
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
);
121 //------------------------------------------------------------------------------
122 ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm
, const char *filename
)
124 m_stream(ysfx::fopen_utf8(filename
, "rb"))
129 int32_t ysfx_text_file_t::avail()
131 if (!m_stream
|| ferror(m_stream
.get()))
134 return (feof(m_stream
.get()) == 0) ? 0 : 1;
137 void ysfx_text_file_t::rewind()
142 ::rewind(m_stream
.get());
145 bool ysfx_text_file_t::var(ysfx_real
*var
)
150 //TODO support the expression language for arithmetic
154 // get the next number separated by newline or comma
155 // but skip invalid lines
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
) {
171 uint32_t ysfx_text_file_t::mem(uint32_t offset
, uint32_t length
)
176 ysfx_eel_ram_writer writer
{m_vm
, offset
};
179 for (read
= 0; read
< length
; ++read
) {
183 writer
.write_next(value
);
189 uint32_t ysfx_text_file_t::string(std::string
&str
)
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
)
211 m_reader(fmt
.open(filename
), fmt
.close
)
215 int32_t ysfx_audio_file_t::avail()
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()
229 m_fmt
.rewind(m_reader
.get());
232 bool ysfx_audio_file_t::var(ysfx_real
*var
)
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
)
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
;
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
]);
266 uint32_t ysfx_audio_file_t::string(std::string
&str
)
272 bool ysfx_audio_file_t::riff(uint32_t &nch
, ysfx_real
&samplerate
)
277 ysfx_audio_file_info_t info
= m_fmt
.info(m_reader
.get());
279 samplerate
= info
.sample_rate
;
283 //------------------------------------------------------------------------------
284 ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm
)
289 void ysfx_serializer_t::begin(bool write
, std::string
&buffer
)
291 m_write
= (int)write
;
296 void ysfx_serializer_t::end()
302 int32_t ysfx_serializer_t::avail()
310 void ysfx_serializer_t::rewind()
314 bool ysfx_serializer_t::var(ysfx_real
*var
)
318 ysfx::pack_f32le((float)*var
, buf
);
319 m_buffer
->append((char *)buf
, 4);
322 else if (m_write
== 0) {
323 if (m_pos
+ 4 > m_buffer
->size()) {
324 m_pos
= m_buffer
->size();
328 *var
= (EEL_F
)ysfx::unpack_f32le((uint8_t *)&(*m_buffer
)[m_pos
]);
335 uint32_t ysfx_serializer_t::mem(uint32_t offset
, uint32_t length
)
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();
346 else if (m_write
== 0) {
347 ysfx_eel_ram_writer writer
{m_vm
, offset
};
348 for (uint32_t i
= 0; i
< length
; ++i
) {
352 writer
.write_next(value
);
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)
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
))
375 void *fmtobj
= nullptr;
376 ysfx_file_type_t ftype
= ysfx_detect_file_type(fx
, filepath
.c_str(), &fmtobj
);
380 case ysfx_file_type_txt
:
381 file
.reset(new ysfx_text_file_t(fx
->vm
.get(), filepath
.c_str()));
383 case ysfx_file_type_raw
:
384 file
.reset(new ysfx_raw_file_t(fx
->vm
.get(), filepath
.c_str()));
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()));
389 case ysfx_file_type_none
:
396 int32_t handle
= ysfx_insert_file(fx
, file
.get());
399 (void)file
.release();
400 return (EEL_F
)(uint32_t)handle
;
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)
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
))
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();
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_
);
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
);
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_
);
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
);
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)
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
);
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_
);
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
);
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_
);
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
);
510 ysfx_real samplerate
= 0;
511 if (!file
->riff(nch
, samplerate
)) {
518 *samplerate_
= samplerate
;
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_
);
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
);
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_
);
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
);
552 if (!file
->is_in_write_mode()) {
553 count
= file
->string(txt
);
554 ysfx_string_set(fx
, *string_
, txt
);
557 ysfx_string_get(fx
, *string_
, txt
);
558 count
= file
->string(txt
);
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
);