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_api_reaper.hpp"
20 #include "ysfx_api_eel.hpp"
21 #include "ysfx_eel_utils.hpp"
22 #include "ysfx_utils.hpp"
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
) {
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
) {
55 return &fx
->var
.ret_temp
;
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
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_
);
79 if (slider
< ysfx_max_sliders
)
80 mask
= (uint64_t)1 << slider
;
82 mask
= ysfx_eel_round
<uint64_t>(std::fabs(*mask_or_slider_
));
84 fx
->slider
.automate_mask
|= mask
;
85 fx
->slider
.change_mask
|= mask
;
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_
);
97 if (slider
< ysfx_max_sliders
)
98 mask
= (uint64_t)1 << slider
;
100 mask
= ysfx_eel_round
<uint64_t>(std::fabs(*mask_or_slider_
));
102 fx
->slider
.change_mask
|= mask
;
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_
);
114 if (slider
< ysfx_max_sliders
)
115 mask
= (uint64_t)1 << slider
;
117 mask
= ysfx_eel_round
<uint64_t>(std::fabs(*mask_or_slider_
));
119 if (*value_
>= (EEL_F
)+0.5) {
121 fx
->slider
.visible_mask
|= mask
;
123 else if (*value_
>= (EEL_F
)-0.5) {
126 fx
->slider
.visible_mask
&= mask
;
130 mask
= fx
->slider
.visible_mask
.fetch_xor(mask
) ^ 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
)
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);
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]);
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
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
;
183 if (!ysfx_midi_push(fx
->midi
.out
.get(), &event
))
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
)
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_
);
203 ysfx_t
*fx
= REAPER_GET_INTERFACE(opaque
);
206 if (!ysfx_midi_push_begin(fx
->midi
.out
.get(), ysfx_current_midi_bus(fx
), (uint32_t)offset
, &mp
))
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))
216 if (!ysfx_midi_push_end(&mp
))
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
)
227 int32_t offset
= ysfx_eel_round
<int32_t>(*offset_
);
232 ysfx_t
*fx
= REAPER_GET_INTERFACE(opaque
);
235 struct process_data
{
236 ysfx_t
*fx
= nullptr;
242 pdata
.offset
= (uint32_t)offset
;
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
))
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
)
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_
);
277 ysfx_t
*fx
= REAPER_GET_INTERFACE(opaque
);
280 if (!ysfx_midi_push_begin(fx
->midi
.out
.get(), ysfx_current_midi_bus(fx
), (uint32_t)offset
, &mp
))
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))
292 if (!ysfx_midi_push_data(&mp
, &byte
, 1))
294 if (i
+ 1 == (uint32_t)len
&& byte
!= tail
) {
295 if (!ysfx_midi_push_data(&mp
, &tail
, 1))
300 if (!ysfx_midi_push_end(&mp
))
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
)
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
);
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
;
338 *parms
[2] = (EEL_F
)msg2
;
339 *parms
[3] = (EEL_F
)msg3
;
342 *parms
[2] = (EEL_F
)(msg2
+ (msg3
<< 8));
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
)
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_
);
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
);
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
]);
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
)
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
);
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
))
415 *offset_
= (EEL_F
)event
.offset
;
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
);