Cleanup
[carla.git] / source / modules / ysfx / sources / ysfx_api_reaper.cpp
blob2fdcb7274df85ffeda5ffbc4048898a331faa993
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_api_reaper.hpp"
20 #include "ysfx_api_eel.hpp"
21 #include "ysfx_eel_utils.hpp"
22 #include "ysfx_utils.hpp"
23 #include <cmath>
24 #include <cstring>
25 #include <cassert>
27 #include "WDL/wdlstring.h"
29 #define REAPER_GET_INTERFACE(opaque) ((opaque) ? (ysfx_t *)(opaque) : nullptr)
31 static EEL_F *NSEEL_CGEN_CALL ysfx_api_spl(void *opaque, EEL_F *n_)
33 //NOTE: callable from @gfx thread
35 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
36 int32_t n = ysfx_eel_round<int32_t>(*n_);
38 if (n < 0 || n >= ysfx_max_channels) {
39 fx->var.ret_temp = 0;
40 return &fx->var.ret_temp;
43 return fx->var.spl[(uint32_t)n];
46 static EEL_F *NSEEL_CGEN_CALL ysfx_api_slider(void *opaque, EEL_F *n_)
48 //NOTE: callable from @gfx thread
50 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
51 int32_t n = ysfx_eel_round<int32_t>(*n_);
53 if (n < 1 || n > ysfx_max_sliders) {
54 fx->var.ret_temp = 0;
55 return &fx->var.ret_temp;
58 n -= 1;
59 return fx->var.slider[(uint32_t)n];
62 static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_next_chg(void *opaque, EEL_F *index_, EEL_F *val_)
64 //TODO frame-accurate slider changes
65 (void)opaque;
66 (void)index_;
67 (void)val_;
68 return -1;
71 static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_automate(void *opaque, EEL_F *mask_or_slider_)
73 //NOTE: callable from @gfx thread
75 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
76 uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
78 uint64_t mask;
79 if (slider < ysfx_max_sliders)
80 mask = (uint64_t)1 << slider;
81 else
82 mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
84 fx->slider.automate_mask |= mask;
85 fx->slider.change_mask |= mask;
86 return 0;
89 static EEL_F NSEEL_CGEN_CALL ysfx_api_sliderchange(void *opaque, EEL_F *mask_or_slider_)
91 //NOTE: callable from @gfx thread
93 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
94 uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
96 uint64_t mask;
97 if (slider < ysfx_max_sliders)
98 mask = (uint64_t)1 << slider;
99 else
100 mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
102 fx->slider.change_mask |= mask;
103 return 0;
106 static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_show(void *opaque, EEL_F *mask_or_slider_, EEL_F *value_)
108 //NOTE: callable from @gfx thread
110 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
111 uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
113 uint64_t mask;
114 if (slider < ysfx_max_sliders)
115 mask = (uint64_t)1 << slider;
116 else
117 mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
119 if (*value_ >= (EEL_F)+0.5) {
120 // show
121 fx->slider.visible_mask |= mask;
123 else if (*value_ >= (EEL_F)-0.5) {
124 // hide
125 mask = ~mask;
126 fx->slider.visible_mask &= mask;
128 else {
129 // toggle
130 mask = fx->slider.visible_mask.fetch_xor(mask) ^ mask;
133 return (EEL_F)mask;
136 static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend(void *opaque, INT_PTR np, EEL_F **parms)
138 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
139 return 0;
141 int32_t offset;
142 uint8_t msg1;
143 uint8_t msg2;
144 uint8_t msg3;
146 switch (np) {
147 case 3:
149 offset = ysfx_eel_round<int32_t>(*parms[0]);
150 msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
151 const uint32_t msg23 = ysfx_eel_round<int32_t>(*parms[2]);
152 msg2 = (uint8_t)(msg23 & 0xff);
153 msg3 = (uint8_t)(msg23 >> 8);
154 break;
156 case 4:
157 offset = ysfx_eel_round<int32_t>(*parms[0]);
158 msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
159 msg2 = (uint8_t)ysfx_eel_round<int32_t>(*parms[2]);
160 msg3 = (uint8_t)ysfx_eel_round<int32_t>(*parms[3]);
161 break;
162 default:
163 assert(false);
164 return 0;
167 if (offset < 0)
168 offset = 0;
170 // NOTE(jpc) correct the length of the message
171 // in case it should be less than 3 bytes
172 uint32_t length = ysfx_midi_sizeof(msg1);
173 if (length == 0) // don't know what message this is
174 length = 3;
176 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
177 ysfx_midi_event_t event;
178 const uint8_t data[] = {msg1, msg2, msg3};
179 event.bus = ysfx_current_midi_bus(fx);
180 event.offset = (uint32_t)offset;
181 event.size = length;
182 event.data = data;
183 if (!ysfx_midi_push(fx->midi.out.get(), &event))
184 return 0;
186 return msg1;
189 static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
191 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
192 return 0;
194 int32_t offset = ysfx_eel_round<int32_t>(*offset_);
195 int32_t buf = ysfx_eel_round<int32_t>(*buf_);
196 int32_t len = ysfx_eel_round<int32_t>(*len_);
198 if (len <= 0)
199 return 0;
200 if (offset < 0)
201 offset = 0;
203 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
205 ysfx_midi_push_t mp;
206 if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
207 return 0;
209 ysfx_eel_ram_reader reader{fx->vm.get(), buf};
210 for (uint32_t i = 0; i < (uint32_t)len; ++i) {
211 uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
212 if (!ysfx_midi_push_data(&mp, &byte, 1))
213 break;
216 if (!ysfx_midi_push_end(&mp))
217 return 0;
219 return len;
222 static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_str(void *opaque, EEL_F *offset_, EEL_F *str_)
224 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
225 return 0;
227 int32_t offset = ysfx_eel_round<int32_t>(*offset_);
229 if (offset < 0)
230 offset = 0;
232 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
235 struct process_data {
236 ysfx_t *fx = nullptr;
237 uint32_t offset = 0;
238 uint32_t result = 0;
241 process_data pdata;
242 pdata.offset = (uint32_t)offset;
243 pdata.fx = fx;
245 auto process_str = [](void *userdata, WDL_FastString &str) {
246 process_data *pdata = (process_data *)userdata;
247 ysfx_t *fx = pdata->fx;
248 ysfx_midi_event_t event;
249 event.bus = ysfx_current_midi_bus(fx);
250 event.offset = pdata->offset;
251 event.size = (uint32_t)str.GetLength();
252 event.data = (const uint8_t *)str.Get();
253 pdata->result = ysfx_midi_push(fx->midi.out.get(), &event) ? event.size : 0;
257 if (!ysfx_string_access(fx, *str_, false, +process_str, &pdata))
258 return 0;
260 return pdata.result;
263 static EEL_F NSEEL_CGEN_CALL ysfx_api_midisyx(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
265 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
266 return 0;
268 int32_t offset = ysfx_eel_round<int32_t>(*offset_);
269 int32_t buf = ysfx_eel_round<int32_t>(*buf_);
270 int32_t len = ysfx_eel_round<int32_t>(*len_);
272 if (len <= 0)
273 return 0;
274 if (offset < 0)
275 offset = 0;
277 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
279 ysfx_midi_push_t mp;
280 if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
281 return 0;
283 ysfx_eel_ram_reader reader{fx->vm.get(), buf};
284 for (uint32_t i = 0; i < (uint32_t)len; ++i) {
285 uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
286 const uint8_t head = 0xf0;
287 const uint8_t tail = 0xf7;
288 if (i == 0 && byte != head) {
289 if (!ysfx_midi_push_data(&mp, &head, 1))
290 break;
292 if (!ysfx_midi_push_data(&mp, &byte, 1))
293 break;
294 if (i + 1 == (uint32_t)len && byte != tail) {
295 if (!ysfx_midi_push_data(&mp, &tail, 1))
296 break;
300 if (!ysfx_midi_push_end(&mp))
301 return 0;
303 return len;
306 static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv(void *opaque, INT_PTR np, EEL_F **parms)
308 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
309 return 0;
311 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
312 uint32_t bus = ysfx_current_midi_bus(fx);
314 ysfx_midi_event_t event;
315 bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
316 // pass through the sysex events
317 while (have_event && event.size > 3) {
318 ysfx_midi_push(fx->midi.out.get(), &event);
319 have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
321 if (!have_event)
322 return 0;
324 uint8_t msg1 = 0;
325 uint8_t msg2 = 0;
326 uint8_t msg3 = 0;
328 switch (event.size) {
329 case 3: msg3 = event.data[2]; // fall through
330 case 2: msg2 = event.data[1]; // fall through
331 case 1: msg1 = event.data[0]; break;
334 *parms[0] = (EEL_F)event.offset;
335 *parms[1] = (EEL_F)msg1;
336 switch (np) {
337 case 4:
338 *parms[2] = (EEL_F)msg2;
339 *parms[3] = (EEL_F)msg3;
340 break;
341 case 3:
342 *parms[2] = (EEL_F)(msg2 + (msg3 << 8));
343 break;
344 default:
345 assert(false);
346 return 0;
349 return 1;
352 static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *recvlen_)
354 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
355 return 0;
357 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
358 NSEEL_VMCTX vm = fx->vm.get();
360 int32_t buf = ysfx_eel_round<int32_t>(*buf_);
361 int32_t recvlen = ysfx_eel_round<int32_t>(*recvlen_);
363 if (recvlen < 0)
364 recvlen = 0;
366 uint32_t bus = ysfx_current_midi_bus(fx);
368 ysfx_midi_event_t event;
369 bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
370 // pass through the events larger than the buffer
371 while (have_event && event.size > (uint32_t)recvlen) {
372 ysfx_midi_push(fx->midi.out.get(), &event);
373 have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
375 if (!have_event)
376 return 0;
378 *offset_ = (EEL_F)event.offset;
380 ysfx_eel_ram_writer writer{vm, buf};
381 for (uint32_t i = 0; i < event.size; ++i)
382 writer.write_next(event.data[i]);
384 return event.size;
387 static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_str(void *opaque, EEL_F *offset_, EEL_F *str_)
389 if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
390 return 0;
392 ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
394 uint32_t bus = ysfx_current_midi_bus(fx);
396 ysfx_midi_event_t event;
397 bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
398 // pass through the events larger than the maximum string
399 while (have_event && event.size > ysfx_string_max_length) {
400 ysfx_midi_push(fx->midi.out.get(), &event);
401 have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
403 if (!have_event)
404 return 0;
406 auto process_str = [](void *userdata, WDL_FastString &str) {
407 ysfx_midi_event_t *event = (ysfx_midi_event_t *)userdata;
408 str.SetRaw((const char *)event->data, (int32_t)event->size);
412 if (!ysfx_string_access(fx, *str_, true, +process_str, &event))
413 return 0;
415 *offset_ = (EEL_F)event.offset;
416 return event.size;
419 //------------------------------------------------------------------------------
420 void ysfx_api_init_reaper()
422 NSEEL_addfunc_retptr("spl", 1, NSEEL_PProc_THIS, &ysfx_api_spl);
423 NSEEL_addfunc_retptr("slider", 1, NSEEL_PProc_THIS, &ysfx_api_slider);
425 NSEEL_addfunc_retval("slider_next_chg", 2, NSEEL_PProc_THIS, &ysfx_api_slider_next_chg);
426 NSEEL_addfunc_retval("slider_automate", 1, NSEEL_PProc_THIS, &ysfx_api_slider_automate);
427 NSEEL_addfunc_retval("sliderchange", 1, NSEEL_PProc_THIS, &ysfx_api_sliderchange);
428 NSEEL_addfunc_retval("slider_show", 2, NSEEL_PProc_THIS, &ysfx_api_slider_show);
430 NSEEL_addfunc_exparms("midisend", 3, NSEEL_PProc_THIS, &ysfx_api_midisend);
431 NSEEL_addfunc_exparms("midisend", 4, NSEEL_PProc_THIS, &ysfx_api_midisend);
432 NSEEL_addfunc_retval("midisend_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midisend_buf);
433 NSEEL_addfunc_retval("midisend_str", 2, NSEEL_PProc_THIS, &ysfx_api_midisend_str);
434 NSEEL_addfunc_exparms("midirecv", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv);
435 NSEEL_addfunc_exparms("midirecv", 4, NSEEL_PProc_THIS, &ysfx_api_midirecv);
436 NSEEL_addfunc_retval("midirecv_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv_buf);
437 NSEEL_addfunc_retval("midirecv_str", 2, NSEEL_PProc_THIS, &ysfx_api_midirecv_str);
438 NSEEL_addfunc_retval("midisyx", 3, NSEEL_PProc_THIS, &ysfx_api_midisyx);