| 
							- // 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_api_reaper.hpp"
 - #include "ysfx_api_eel.hpp"
 - #include "ysfx_eel_utils.hpp"
 - #include "ysfx_utils.hpp"
 - #include <cmath>
 - #include <cstring>
 - #include <cassert>
 - 
 - #include "WDL/wdlstring.h"
 - 
 - #define REAPER_GET_INTERFACE(opaque) ((opaque) ? (ysfx_t *)(opaque) : nullptr)
 - 
 - static EEL_F *NSEEL_CGEN_CALL ysfx_api_spl(void *opaque, EEL_F *n_)
 - {
 -     //NOTE: callable from @gfx thread
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     int32_t n = ysfx_eel_round<int32_t>(*n_);
 - 
 -     if (n < 0 || n >= ysfx_max_channels) {
 -         fx->var.ret_temp = 0;
 -         return &fx->var.ret_temp;
 -     }
 - 
 -     return fx->var.spl[(uint32_t)n];
 - }
 - 
 - static EEL_F *NSEEL_CGEN_CALL ysfx_api_slider(void *opaque, EEL_F *n_)
 - {
 -     //NOTE: callable from @gfx thread
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     int32_t n = ysfx_eel_round<int32_t>(*n_);
 - 
 -     if (n < 1 || n > ysfx_max_sliders) {
 -         fx->var.ret_temp = 0;
 -         return &fx->var.ret_temp;
 -     }
 - 
 -     n -= 1;
 -     return fx->var.slider[(uint32_t)n];
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_next_chg(void *opaque, EEL_F *index_, EEL_F *val_)
 - {
 -     //TODO frame-accurate slider changes
 -     (void)opaque;
 -     (void)index_;
 -     (void)val_;
 -     return -1;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_automate(void *opaque, EEL_F *mask_or_slider_)
 - {
 -     //NOTE: callable from @gfx thread
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
 - 
 -     uint64_t mask;
 -     if (slider < ysfx_max_sliders)
 -         mask = (uint64_t)1 << slider;
 -     else
 -         mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
 - 
 -     fx->slider.automate_mask |= mask;
 -     fx->slider.change_mask |= mask;
 -     return 0;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_sliderchange(void *opaque, EEL_F *mask_or_slider_)
 - {
 -     //NOTE: callable from @gfx thread
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
 - 
 -     uint64_t mask;
 -     if (slider < ysfx_max_sliders)
 -         mask = (uint64_t)1 << slider;
 -     else
 -         mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
 - 
 -     fx->slider.change_mask |= mask;
 -     return 0;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_show(void *opaque, EEL_F *mask_or_slider_, EEL_F *value_)
 - {
 -     //NOTE: callable from @gfx thread
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
 - 
 -     uint64_t mask;
 -     if (slider < ysfx_max_sliders)
 -         mask = (uint64_t)1 << slider;
 -     else
 -         mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
 - 
 -     if (*value_ >= (EEL_F)+0.5) {
 -         // show
 -         fx->slider.visible_mask |= mask;
 -     }
 -     else if (*value_ >= (EEL_F)-0.5) {
 -         // hide
 -         mask = ~mask;
 -         fx->slider.visible_mask &= mask;
 -     }
 -     else {
 -         // toggle
 -         mask = fx->slider.visible_mask.fetch_xor(mask) ^ mask;
 -     }
 - 
 -     return (EEL_F)mask;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend(void *opaque, INT_PTR np, EEL_F **parms)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     int32_t offset;
 -     uint8_t msg1;
 -     uint8_t msg2;
 -     uint8_t msg3;
 - 
 -     switch (np) {
 -     case 3:
 -     {
 -         offset = ysfx_eel_round<int32_t>(*parms[0]);
 -         msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
 -         const uint32_t msg23 = ysfx_eel_round<int32_t>(*parms[2]);
 -         msg2 = (uint8_t)(msg23 & 0xff);
 -         msg3 = (uint8_t)(msg23 >> 8);
 -         break;
 -     }
 -     case 4:
 -         offset = ysfx_eel_round<int32_t>(*parms[0]);
 -         msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
 -         msg2 = (uint8_t)ysfx_eel_round<int32_t>(*parms[2]);
 -         msg3 = (uint8_t)ysfx_eel_round<int32_t>(*parms[3]);
 -         break;
 -     default:
 -         assert(false);
 -         return 0;
 -     }
 - 
 -     if (offset < 0)
 -         offset = 0;
 - 
 -     // NOTE(jpc) correct the length of the message
 -     // in case it should be less than 3 bytes
 -     uint32_t length = ysfx_midi_sizeof(msg1);
 -     if (length == 0) // don't know what message this is
 -         length = 3;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     ysfx_midi_event_t event;
 -     const uint8_t data[] = {msg1, msg2, msg3};
 -     event.bus = ysfx_current_midi_bus(fx);
 -     event.offset = (uint32_t)offset;
 -     event.size = length;
 -     event.data = data;
 -     if (!ysfx_midi_push(fx->midi.out.get(), &event))
 -         return 0;
 - 
 -     return msg1;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     int32_t offset = ysfx_eel_round<int32_t>(*offset_);
 -     int32_t buf = ysfx_eel_round<int32_t>(*buf_);
 -     int32_t len = ysfx_eel_round<int32_t>(*len_);
 - 
 -     if (len <= 0)
 -         return 0;
 -     if (offset < 0)
 -         offset = 0;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 - 
 -     ysfx_midi_push_t mp;
 -     if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
 -         return 0;
 - 
 -     ysfx_eel_ram_reader reader{fx->vm.get(), buf};
 -     for (uint32_t i = 0; i < (uint32_t)len; ++i) {
 -         uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
 -         if (!ysfx_midi_push_data(&mp, &byte, 1))
 -             break;
 -     }
 - 
 -     if (!ysfx_midi_push_end(&mp))
 -         return 0;
 - 
 -     return len;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_str(void *opaque, EEL_F *offset_, EEL_F *str_)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     int32_t offset = ysfx_eel_round<int32_t>(*offset_);
 - 
 -     if (offset < 0)
 -         offset = 0;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 - 
 -     ///
 -     struct process_data {
 -         ysfx_t *fx = nullptr;
 -         uint32_t offset = 0;
 -         uint32_t result = 0;
 -     };
 - 
 -     process_data pdata;
 -     pdata.offset = (uint32_t)offset;
 -     pdata.fx = fx;
 - 
 -     auto process_str = [](void *userdata, WDL_FastString &str) {
 -         process_data *pdata = (process_data *)userdata;
 -         ysfx_t *fx = pdata->fx;
 -         ysfx_midi_event_t event;
 -         event.bus = ysfx_current_midi_bus(fx);
 -         event.offset = pdata->offset;
 -         event.size = (uint32_t)str.GetLength();
 -         event.data = (const uint8_t *)str.Get();
 -         pdata->result = ysfx_midi_push(fx->midi.out.get(), &event) ? event.size : 0;
 -     };
 - 
 -     ///
 -     if (!ysfx_string_access(fx, *str_, false, +process_str, &pdata))
 -         return 0;
 - 
 -     return pdata.result;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midisyx(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     int32_t offset = ysfx_eel_round<int32_t>(*offset_);
 -     int32_t buf = ysfx_eel_round<int32_t>(*buf_);
 -     int32_t len = ysfx_eel_round<int32_t>(*len_);
 - 
 -     if (len <= 0)
 -         return 0;
 -     if (offset < 0)
 -         offset = 0;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 - 
 -     ysfx_midi_push_t mp;
 -     if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
 -         return 0;
 - 
 -     ysfx_eel_ram_reader reader{fx->vm.get(), buf};
 -     for (uint32_t i = 0; i < (uint32_t)len; ++i) {
 -         uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
 -         const uint8_t head = 0xf0;
 -         const uint8_t tail = 0xf7;
 -         if (i == 0 && byte != head) {
 -             if (!ysfx_midi_push_data(&mp, &head, 1))
 -                 break;
 -         }
 -         if (!ysfx_midi_push_data(&mp, &byte, 1))
 -             break;
 -         if (i + 1 == (uint32_t)len && byte != tail) {
 -             if (!ysfx_midi_push_data(&mp, &tail, 1))
 -                 break;
 -         }
 -     }
 - 
 -     if (!ysfx_midi_push_end(&mp))
 -         return 0;
 - 
 -     return len;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv(void *opaque, INT_PTR np, EEL_F **parms)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     uint32_t bus = ysfx_current_midi_bus(fx);
 - 
 -     ysfx_midi_event_t event;
 -     bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
 -     // pass through the sysex events
 -     while (have_event && event.size > 3) {
 -         ysfx_midi_push(fx->midi.out.get(), &event);
 -         have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
 -     }
 -     if (!have_event)
 -         return 0;
 - 
 -     uint8_t msg1 = 0;
 -     uint8_t msg2 = 0;
 -     uint8_t msg3 = 0;
 - 
 -     switch (event.size) {
 -         case 3: msg3 = event.data[2]; // fall through
 -         case 2: msg2 = event.data[1]; // fall through
 -         case 1: msg1 = event.data[0]; break;
 -     }
 - 
 -     *parms[0] = (EEL_F)event.offset;
 -     *parms[1] = (EEL_F)msg1;
 -     switch (np) {
 -     case 4:
 -         *parms[2] = (EEL_F)msg2;
 -         *parms[3] = (EEL_F)msg3;
 -         break;
 -     case 3:
 -         *parms[2] = (EEL_F)(msg2 + (msg3 << 8));
 -         break;
 -     default:
 -         assert(false);
 -         return 0;
 -     }
 - 
 -     return 1;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *recvlen_)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 -     NSEEL_VMCTX vm = fx->vm.get();
 - 
 -     int32_t buf = ysfx_eel_round<int32_t>(*buf_);
 -     int32_t recvlen = ysfx_eel_round<int32_t>(*recvlen_);
 - 
 -     if (recvlen < 0)
 -         recvlen = 0;
 - 
 -     uint32_t bus = ysfx_current_midi_bus(fx);
 - 
 -     ysfx_midi_event_t event;
 -     bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
 -     // pass through the events larger than the buffer
 -     while (have_event && event.size > (uint32_t)recvlen) {
 -         ysfx_midi_push(fx->midi.out.get(), &event);
 -         have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
 -     }
 -     if (!have_event)
 -         return 0;
 - 
 -     *offset_ = (EEL_F)event.offset;
 - 
 -     ysfx_eel_ram_writer writer{vm, buf};
 -     for (uint32_t i = 0; i < event.size; ++i)
 -         writer.write_next(event.data[i]);
 - 
 -     return event.size;
 - }
 - 
 - static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_str(void *opaque, EEL_F *offset_, EEL_F *str_)
 - {
 -     if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
 -         return 0;
 - 
 -     ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
 - 
 -     uint32_t bus = ysfx_current_midi_bus(fx);
 - 
 -     ysfx_midi_event_t event;
 -     bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
 -     // pass through the events larger than the maximum string
 -     while (have_event && event.size > ysfx_string_max_length) {
 -         ysfx_midi_push(fx->midi.out.get(), &event);
 -         have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
 -     }
 -     if (!have_event)
 -         return 0;
 - 
 -     auto process_str = [](void *userdata, WDL_FastString &str) {
 -         ysfx_midi_event_t *event = (ysfx_midi_event_t *)userdata;
 -         str.SetRaw((const char *)event->data, (int32_t)event->size);
 -     };
 - 
 -     ///
 -     if (!ysfx_string_access(fx, *str_, true, +process_str, &event))
 -         return 0;
 - 
 -     *offset_ = (EEL_F)event.offset;
 -     return event.size;
 - }
 - 
 - //------------------------------------------------------------------------------
 - void ysfx_api_init_reaper()
 - {
 -     NSEEL_addfunc_retptr("spl", 1, NSEEL_PProc_THIS, &ysfx_api_spl);
 -     NSEEL_addfunc_retptr("slider", 1, NSEEL_PProc_THIS, &ysfx_api_slider);
 - 
 -     NSEEL_addfunc_retval("slider_next_chg", 2, NSEEL_PProc_THIS, &ysfx_api_slider_next_chg);
 -     NSEEL_addfunc_retval("slider_automate", 1, NSEEL_PProc_THIS, &ysfx_api_slider_automate);
 -     NSEEL_addfunc_retval("sliderchange", 1, NSEEL_PProc_THIS, &ysfx_api_sliderchange);
 -     NSEEL_addfunc_retval("slider_show", 2, NSEEL_PProc_THIS, &ysfx_api_slider_show);
 - 
 -     NSEEL_addfunc_exparms("midisend", 3, NSEEL_PProc_THIS, &ysfx_api_midisend);
 -     NSEEL_addfunc_exparms("midisend", 4, NSEEL_PProc_THIS, &ysfx_api_midisend);
 -     NSEEL_addfunc_retval("midisend_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midisend_buf);
 -     NSEEL_addfunc_retval("midisend_str", 2, NSEEL_PProc_THIS, &ysfx_api_midisend_str);
 -     NSEEL_addfunc_exparms("midirecv", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv);
 -     NSEEL_addfunc_exparms("midirecv", 4, NSEEL_PProc_THIS, &ysfx_api_midirecv);
 -     NSEEL_addfunc_retval("midirecv_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv_buf);
 -     NSEEL_addfunc_retval("midirecv_str", 2, NSEEL_PProc_THIS, &ysfx_api_midirecv_str);
 -     NSEEL_addfunc_retval("midisyx", 3, NSEEL_PProc_THIS, &ysfx_api_midisyx);
 - }
 
 
  |