|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- // Copyright 2021 Jean Pierre Cimalando
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- // SPDX-License-Identifier: Apache-2.0
- //
-
- #include "ysfx.hpp"
- #include "ysfx_config.hpp"
- #include "ysfx_api_file.hpp"
- #include "ysfx_eel_utils.hpp"
- #include <cstring>
- #include <cstdio>
- #include <cassert>
-
- ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename)
- : m_vm(vm),
- m_stream(ysfx::fopen_utf8(filename, "rb"))
- {
- }
-
- int32_t ysfx_raw_file_t::avail()
- {
- if (!m_stream)
- return 0;
-
- int64_t cur_off = ysfx::ftell_lfs(m_stream.get());
- if (cur_off == -1)
- return 0;
-
- if (ysfx::fseek_lfs(m_stream.get(), 0, SEEK_END) == -1)
- return 0;
-
- int64_t end_off = ysfx::ftell_lfs(m_stream.get());
- if (end_off == -1)
- return 0;
-
- if (ysfx::fseek_lfs(m_stream.get(), cur_off, SEEK_SET) == -1)
- return 0;
-
- if ((uint64_t)end_off < (uint64_t)cur_off)
- return 0;
-
- uint64_t byte_count = (uint64_t)end_off - (uint64_t)cur_off;
- uint64_t f32_count = byte_count / 4;
- return (f32_count > 0x7fffffff) ? 0x7fffffff : (uint32_t)f32_count;
- }
-
- void ysfx_raw_file_t::rewind()
- {
- if (!m_stream)
- return;
-
- ::rewind(m_stream.get());
- }
-
- bool ysfx_raw_file_t::var(ysfx_real *var)
- {
- if (!m_stream)
- return false;
-
- uint8_t data[4];
- if (fread(data, 1, 4, m_stream.get()) != 4)
- return false;
-
- *var = (EEL_F)ysfx::unpack_f32le(data);
- return true;
- }
-
- uint32_t ysfx_raw_file_t::mem(uint32_t offset, uint32_t length)
- {
- if (!m_stream)
- return 0;
-
- ysfx_eel_ram_writer writer{m_vm, offset};
-
- uint32_t read;
- for (read = 0; read < length; ++read) {
- ysfx_real value;
- if (!var(&value))
- break;
- writer.write_next(value);
- }
-
- return read;
- }
-
- uint32_t ysfx_raw_file_t::string(std::string &str)
- {
- if (!m_stream)
- return 0;
-
- uint8_t data[4];
- if (fread(data, 1, 4, m_stream.get()) != 4)
- return 0;
-
- str.clear();
-
- uint32_t srclen = ysfx::unpack_u32le(data);
- str.reserve((srclen < ysfx_string_max_length) ? srclen : ysfx_string_max_length);
-
- uint32_t count = 0;
- for (int byte; count < srclen && (byte = fgetc(m_stream.get())) != EOF; ) {
- if (str.size() < ysfx_string_max_length)
- str.push_back((unsigned char)byte);
- ++count;
- }
- return count;
- }
-
- //------------------------------------------------------------------------------
- ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename)
- : m_vm(vm),
- m_stream(ysfx::fopen_utf8(filename, "rb"))
- {
- m_buf.reserve(256);
- }
-
- int32_t ysfx_text_file_t::avail()
- {
- if (!m_stream || ferror(m_stream.get()))
- return -1;
-
- return (feof(m_stream.get()) == 0) ? 0 : 1;
- }
-
- void ysfx_text_file_t::rewind()
- {
- if (!m_stream)
- return;
-
- ::rewind(m_stream.get());
- }
-
- bool ysfx_text_file_t::var(ysfx_real *var)
- {
- if (!m_stream)
- return false;
-
- //TODO support the expression language for arithmetic
-
- int ch;
- do {
- // get the next number separated by newline or comma
- // but skip invalid lines
- m_buf.clear();
- while ((ch = fgetc(m_stream.get())) != EOF && ch != '\n' && ch != ',')
- m_buf.push_back((unsigned char)ch);
- const char *startp = m_buf.c_str();
- const char *endp = (char *)startp;
- double value = ysfx::dot_strtod(startp, (char **)&endp);
- if (endp != startp) {
- *var = (EEL_F)value;
- return true;
- }
- } while (ch != EOF);
-
- return false;
- }
-
- uint32_t ysfx_text_file_t::mem(uint32_t offset, uint32_t length)
- {
- if (!m_stream)
- return 0;
-
- ysfx_eel_ram_writer writer{m_vm, offset};
-
- uint32_t read;
- for (read = 0; read < length; ++read) {
- ysfx_real value;
- if (!var(&value))
- break;
- writer.write_next(value);
- }
-
- return read;
- }
-
- uint32_t ysfx_text_file_t::string(std::string &str)
- {
- if (!m_stream)
- return 0;
-
- str.clear();
- str.reserve(256);
-
- int ch;
- do {
- ch = fgetc(m_stream.get());
- if (ch != EOF && str.size() < ysfx_string_max_length)
- str.push_back((unsigned char)ch);
- } while (ch != EOF && ch != '\n');
-
- return (uint32_t)str.size();
- }
-
- //------------------------------------------------------------------------------
- ysfx_audio_file_t::ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename)
- : m_vm(vm),
- m_fmt(fmt),
- m_reader(fmt.open(filename), fmt.close)
- {
- }
-
- int32_t ysfx_audio_file_t::avail()
- {
- if (!m_reader)
- return -1;
-
- uint64_t avail = m_fmt.avail(m_reader.get());
- return (avail > 0x7fffffff) ? 0x7fffffff : (int32_t)avail;
- }
-
- void ysfx_audio_file_t::rewind()
- {
- if (!m_reader)
- return;
-
- m_fmt.rewind(m_reader.get());
- }
-
- bool ysfx_audio_file_t::var(ysfx_real *var)
- {
- if (!m_reader)
- return false;
-
- return m_fmt.read(m_reader.get(), var, 1) == 1;
- }
-
- uint32_t ysfx_audio_file_t::mem(uint32_t offset, uint32_t length)
- {
- if (!m_reader)
- return 0;
-
- uint32_t numread = 0;
- ysfx_real *buf = m_buf.get();
- ysfx_eel_ram_writer writer(m_vm, offset);
-
- while (numread < length) {
- uint32_t n = length - numread;
- if (n > buffer_size)
- n = buffer_size;
-
- uint32_t m = (uint32_t)m_fmt.read(m_reader.get(), buf, n);
- for (uint32_t i = 0; i < m; ++i)
- writer.write_next(buf[i]);
-
- numread += m;
- if (m < n)
- break;
- }
-
- return numread;
- }
-
- uint32_t ysfx_audio_file_t::string(std::string &str)
- {
- (void)str;
- return 0;
- }
-
- bool ysfx_audio_file_t::riff(uint32_t &nch, ysfx_real &samplerate)
- {
- if (!m_reader)
- return false;
-
- ysfx_audio_file_info_t info = m_fmt.info(m_reader.get());
- nch = info.channels;
- samplerate = info.sample_rate;
- return true;
- }
-
- //------------------------------------------------------------------------------
- ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm)
- : m_vm(vm)
- {
- }
-
- void ysfx_serializer_t::begin(bool write, std::string &buffer)
- {
- m_write = (int)write;
- m_buffer = &buffer;
- m_pos = 0;
- }
-
- void ysfx_serializer_t::end()
- {
- m_write = -1;
- m_buffer = nullptr;
- }
-
- int32_t ysfx_serializer_t::avail()
- {
- if (m_write)
- return -1;
- else
- return 0;
- }
-
- void ysfx_serializer_t::rewind()
- {
- }
-
- bool ysfx_serializer_t::var(ysfx_real *var)
- {
- if (m_write == 1) {
- uint8_t buf[4];
- ysfx::pack_f32le((float)*var, buf);
- m_buffer->append((char *)buf, 4);
- return true;
- }
- else if (m_write == 0) {
- if (m_pos + 4 > m_buffer->size()) {
- m_pos = m_buffer->size();
- *var = 0;
- return false;
- }
- *var = (EEL_F)ysfx::unpack_f32le((uint8_t *)&(*m_buffer)[m_pos]);
- m_pos += 4;
- return true;
- }
- return false;
- }
-
- uint32_t ysfx_serializer_t::mem(uint32_t offset, uint32_t length)
- {
- if (m_write == 1) {
- ysfx_eel_ram_reader reader{m_vm, offset};
- for (uint32_t i = 0; i < length; ++i) {
- ysfx_real value = reader.read_next();
- if (!var(&value))
- return i;
- }
- return length;
- }
- else if (m_write == 0) {
- ysfx_eel_ram_writer writer{m_vm, offset};
- for (uint32_t i = 0; i < length; ++i) {
- ysfx_real value{};
- if (!var(&value))
- return i;
- writer.write_next(value);
- }
- return length;
- }
- return 0;
- }
-
- uint32_t ysfx_serializer_t::string(std::string &str)
- {
- // TODO implement me; docs claim support in Reaper 4.59+ but it seems
- // non-working (as of Reaper 6.40)
- return 0;
- }
-
- //------------------------------------------------------------------------------
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_open(void *opaque, EEL_F *file_)
- {
- ysfx_t *fx = (ysfx_t *)opaque;
-
- std::string filepath;
- if (!ysfx_find_data_file(fx, file_, filepath))
- return -1;
-
- void *fmtobj = nullptr;
- ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), &fmtobj);
-
- ysfx_file_u file;
- switch (ftype) {
- case ysfx_file_type_txt:
- file.reset(new ysfx_text_file_t(fx->vm.get(), filepath.c_str()));
- break;
- case ysfx_file_type_raw:
- file.reset(new ysfx_raw_file_t(fx->vm.get(), filepath.c_str()));
- break;
- case ysfx_file_type_audio:
- file.reset(new ysfx_audio_file_t(fx->vm.get(), *(ysfx_audio_format_t *)fmtobj, filepath.c_str()));
- break;
- case ysfx_file_type_none:
- break;
- default:
- assert(false);
- }
-
- if (file) {
- int32_t handle = ysfx_insert_file(fx, file.get());
- if (handle == -1)
- return -1;
- (void)file.release();
- return (EEL_F)(uint32_t)handle;
- }
-
- return -1;
- }
-
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_close(void *opaque, EEL_F *handle_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle <= 0) //NOTE: cannot close the serializer handle (0)
- return -1;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_ptr<ysfx::mutex> file_mutex;
- std::unique_lock<ysfx::mutex> lock;
- std::unique_lock<ysfx::mutex> list_lock;
-
- // hold both locks to protect file and list access during removal
- if (!ysfx_get_file(fx, (uint32_t)handle, lock, &list_lock))
- return -1;
-
- // preserve the locked mutex of the object being removed
- file_mutex = std::move(fx->file.list[(uint32_t)handle]->m_mutex);
-
- fx->file.list[(uint32_t)handle].reset();
- return 0;
- }
-
- static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_rewind(void *opaque, EEL_F *handle_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle < 0)
- return handle_;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file)
- return 0;
-
- file->rewind();
- return handle_;
- }
-
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_var(void *opaque, EEL_F *handle_, EEL_F *var)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle < 0)
- return 0;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file)
- return 0;
-
- if (!file->var(var))
- return 0;
-
- return 1;
- }
-
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_mem(void *opaque, EEL_F *handle_, EEL_F *offset_, EEL_F *length_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- int32_t offset = ysfx_eel_round<int32_t>(*offset_);
- int32_t length = ysfx_eel_round<int32_t>(*length_);
- if (handle < 0 || offset < 0 || length <= 0)
- return 0;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file)
- return 0;
-
- return (EEL_F)file->mem((uint32_t)offset, (uint32_t)length);
- }
-
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_avail(void *opaque, EEL_F *handle_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle < 0)
- return 0;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file)
- return 0;
-
- return file->avail();
- }
-
- static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_riff(void *opaque, EEL_F *handle_, EEL_F *nch_, EEL_F *samplerate_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle < 0)
- return 0;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file) {
- *nch_ = 0;
- *samplerate_ = 0;
- return nch_;
- }
-
- uint32_t nch = 0;
- ysfx_real samplerate = 0;
- if (!file->riff(nch, samplerate)) {
- *nch_ = 0;
- *samplerate_ = 0;
- return nch_;
- }
-
- *nch_ = (EEL_F)nch;
- *samplerate_ = samplerate;
- return nch_;
- }
-
-
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_text(void *opaque, EEL_F *handle_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle < 0)
- return 0;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file)
- return 0;
-
- return (EEL_F)file->is_text();
- }
-
- static EEL_F NSEEL_CGEN_CALL ysfx_api_file_string(void *opaque, EEL_F *handle_, EEL_F *string_)
- {
- int32_t handle = ysfx_eel_round<int32_t>(*handle_);
- if (handle < 0)
- return 0;
-
- ysfx_t *fx = (ysfx_t *)opaque;
- std::unique_lock<ysfx::mutex> lock;
- ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock);
- if (!file)
- return 0;
-
- std::string txt;
- uint32_t count;
- if (!file->is_in_write_mode()) {
- count = file->string(txt);
- ysfx_string_set(fx, *string_, txt);
- }
- else {
- ysfx_string_get(fx, *string_, txt);
- count = file->string(txt);
- }
- return (EEL_F)count;
- }
-
- void ysfx_api_init_file()
- {
- NSEEL_addfunc_retval("file_open", 1, NSEEL_PProc_THIS, &ysfx_api_file_open);
- NSEEL_addfunc_retval("file_close", 1, NSEEL_PProc_THIS, &ysfx_api_file_close);
- NSEEL_addfunc_retptr("file_rewind", 1, NSEEL_PProc_THIS, &ysfx_api_file_rewind);
- NSEEL_addfunc_retval("file_var", 2, NSEEL_PProc_THIS, &ysfx_api_file_var);
- NSEEL_addfunc_retval("file_mem", 3, NSEEL_PProc_THIS, &ysfx_api_file_mem);
- NSEEL_addfunc_retval("file_avail", 1, NSEEL_PProc_THIS, &ysfx_api_file_avail);
- NSEEL_addfunc_retptr("file_riff", 3, NSEEL_PProc_THIS, &ysfx_api_file_riff);
- NSEEL_addfunc_retval("file_text", 1, NSEEL_PProc_THIS, &ysfx_api_file_text);
- NSEEL_addfunc_retval("file_string", 2, NSEEL_PProc_THIS, &ysfx_api_file_string);
- }
|