Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ysfx_api_reaper.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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.
  14. //
  15. // SPDX-License-Identifier: Apache-2.0
  16. //
  17. #include "ysfx.hpp"
  18. #include "ysfx_api_reaper.hpp"
  19. #include "ysfx_api_eel.hpp"
  20. #include "ysfx_eel_utils.hpp"
  21. #include "ysfx_utils.hpp"
  22. #include <cmath>
  23. #include <cstring>
  24. #include <cassert>
  25. #include "WDL/wdlstring.h"
  26. #define REAPER_GET_INTERFACE(opaque) ((opaque) ? (ysfx_t *)(opaque) : nullptr)
  27. static EEL_F *NSEEL_CGEN_CALL ysfx_api_spl(void *opaque, EEL_F *n_)
  28. {
  29. //NOTE: callable from @gfx thread
  30. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  31. int32_t n = ysfx_eel_round<int32_t>(*n_);
  32. if (n < 0 || n >= ysfx_max_channels) {
  33. fx->var.ret_temp = 0;
  34. return &fx->var.ret_temp;
  35. }
  36. return fx->var.spl[(uint32_t)n];
  37. }
  38. static EEL_F *NSEEL_CGEN_CALL ysfx_api_slider(void *opaque, EEL_F *n_)
  39. {
  40. //NOTE: callable from @gfx thread
  41. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  42. int32_t n = ysfx_eel_round<int32_t>(*n_);
  43. if (n < 1 || n > ysfx_max_sliders) {
  44. fx->var.ret_temp = 0;
  45. return &fx->var.ret_temp;
  46. }
  47. n -= 1;
  48. return fx->var.slider[(uint32_t)n];
  49. }
  50. static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_next_chg(void *opaque, EEL_F *index_, EEL_F *val_)
  51. {
  52. //TODO frame-accurate slider changes
  53. (void)opaque;
  54. (void)index_;
  55. (void)val_;
  56. return -1;
  57. }
  58. static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_automate(void *opaque, EEL_F *mask_or_slider_)
  59. {
  60. //NOTE: callable from @gfx thread
  61. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  62. uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
  63. uint64_t mask;
  64. if (slider < ysfx_max_sliders)
  65. mask = (uint64_t)1 << slider;
  66. else
  67. mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
  68. fx->slider.automate_mask |= mask;
  69. fx->slider.change_mask |= mask;
  70. return 0;
  71. }
  72. static EEL_F NSEEL_CGEN_CALL ysfx_api_sliderchange(void *opaque, EEL_F *mask_or_slider_)
  73. {
  74. //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_);
  77. uint64_t mask;
  78. if (slider < ysfx_max_sliders)
  79. mask = (uint64_t)1 << slider;
  80. else
  81. mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
  82. fx->slider.change_mask |= mask;
  83. return 0;
  84. }
  85. static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_show(void *opaque, EEL_F *mask_or_slider_, EEL_F *value_)
  86. {
  87. //NOTE: callable from @gfx thread
  88. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  89. uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_);
  90. uint64_t mask;
  91. if (slider < ysfx_max_sliders)
  92. mask = (uint64_t)1 << slider;
  93. else
  94. mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_));
  95. if (*value_ >= (EEL_F)+0.5) {
  96. // show
  97. fx->slider.visible_mask |= mask;
  98. }
  99. else if (*value_ >= (EEL_F)-0.5) {
  100. // hide
  101. mask = ~mask;
  102. fx->slider.visible_mask &= mask;
  103. }
  104. else {
  105. // toggle
  106. mask = fx->slider.visible_mask.fetch_xor(mask) ^ mask;
  107. }
  108. return (EEL_F)mask;
  109. }
  110. static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend(void *opaque, INT_PTR np, EEL_F **parms)
  111. {
  112. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  113. return 0;
  114. int32_t offset;
  115. uint8_t msg1;
  116. uint8_t msg2;
  117. uint8_t msg3;
  118. switch (np) {
  119. case 3:
  120. {
  121. offset = ysfx_eel_round<int32_t>(*parms[0]);
  122. msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
  123. const uint32_t msg23 = ysfx_eel_round<int32_t>(*parms[2]);
  124. msg2 = (uint8_t)(msg23 & 0xff);
  125. msg3 = (uint8_t)(msg23 >> 8);
  126. break;
  127. }
  128. case 4:
  129. offset = ysfx_eel_round<int32_t>(*parms[0]);
  130. msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]);
  131. msg2 = (uint8_t)ysfx_eel_round<int32_t>(*parms[2]);
  132. msg3 = (uint8_t)ysfx_eel_round<int32_t>(*parms[3]);
  133. break;
  134. default:
  135. assert(false);
  136. return 0;
  137. }
  138. if (offset < 0)
  139. offset = 0;
  140. // NOTE(jpc) correct the length of the message
  141. // in case it should be less than 3 bytes
  142. uint32_t length = ysfx_midi_sizeof(msg1);
  143. if (length == 0) // don't know what message this is
  144. length = 3;
  145. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  146. ysfx_midi_event_t event;
  147. const uint8_t data[] = {msg1, msg2, msg3};
  148. event.bus = ysfx_current_midi_bus(fx);
  149. event.offset = (uint32_t)offset;
  150. event.size = length;
  151. event.data = data;
  152. if (!ysfx_midi_push(fx->midi.out.get(), &event))
  153. return 0;
  154. return msg1;
  155. }
  156. static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
  157. {
  158. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  159. return 0;
  160. int32_t offset = ysfx_eel_round<int32_t>(*offset_);
  161. int32_t buf = ysfx_eel_round<int32_t>(*buf_);
  162. int32_t len = ysfx_eel_round<int32_t>(*len_);
  163. if (len <= 0)
  164. return 0;
  165. if (offset < 0)
  166. offset = 0;
  167. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  168. ysfx_midi_push_t mp;
  169. if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
  170. return 0;
  171. ysfx_eel_ram_reader reader{fx->vm.get(), buf};
  172. for (uint32_t i = 0; i < (uint32_t)len; ++i) {
  173. uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
  174. if (!ysfx_midi_push_data(&mp, &byte, 1))
  175. break;
  176. }
  177. if (!ysfx_midi_push_end(&mp))
  178. return 0;
  179. return len;
  180. }
  181. static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_str(void *opaque, EEL_F *offset_, EEL_F *str_)
  182. {
  183. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  184. return 0;
  185. int32_t offset = ysfx_eel_round<int32_t>(*offset_);
  186. if (offset < 0)
  187. offset = 0;
  188. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  189. ///
  190. struct process_data {
  191. ysfx_t *fx = nullptr;
  192. uint32_t offset = 0;
  193. uint32_t result = 0;
  194. };
  195. process_data pdata;
  196. pdata.offset = (uint32_t)offset;
  197. pdata.fx = fx;
  198. auto process_str = [](void *userdata, WDL_FastString &str) {
  199. process_data *pdata = (process_data *)userdata;
  200. ysfx_t *fx = pdata->fx;
  201. ysfx_midi_event_t event;
  202. event.bus = ysfx_current_midi_bus(fx);
  203. event.offset = pdata->offset;
  204. event.size = (uint32_t)str.GetLength();
  205. event.data = (const uint8_t *)str.Get();
  206. pdata->result = ysfx_midi_push(fx->midi.out.get(), &event) ? event.size : 0;
  207. };
  208. ///
  209. if (!ysfx_string_access(fx, *str_, false, +process_str, &pdata))
  210. return 0;
  211. return pdata.result;
  212. }
  213. static EEL_F NSEEL_CGEN_CALL ysfx_api_midisyx(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_)
  214. {
  215. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  216. return 0;
  217. int32_t offset = ysfx_eel_round<int32_t>(*offset_);
  218. int32_t buf = ysfx_eel_round<int32_t>(*buf_);
  219. int32_t len = ysfx_eel_round<int32_t>(*len_);
  220. if (len <= 0)
  221. return 0;
  222. if (offset < 0)
  223. offset = 0;
  224. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  225. ysfx_midi_push_t mp;
  226. if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp))
  227. return 0;
  228. ysfx_eel_ram_reader reader{fx->vm.get(), buf};
  229. for (uint32_t i = 0; i < (uint32_t)len; ++i) {
  230. uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next());
  231. const uint8_t head = 0xf0;
  232. const uint8_t tail = 0xf7;
  233. if (i == 0 && byte != head) {
  234. if (!ysfx_midi_push_data(&mp, &head, 1))
  235. break;
  236. }
  237. if (!ysfx_midi_push_data(&mp, &byte, 1))
  238. break;
  239. if (i + 1 == (uint32_t)len && byte != tail) {
  240. if (!ysfx_midi_push_data(&mp, &tail, 1))
  241. break;
  242. }
  243. }
  244. if (!ysfx_midi_push_end(&mp))
  245. return 0;
  246. return len;
  247. }
  248. static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv(void *opaque, INT_PTR np, EEL_F **parms)
  249. {
  250. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  251. return 0;
  252. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  253. uint32_t bus = ysfx_current_midi_bus(fx);
  254. ysfx_midi_event_t event;
  255. bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
  256. // pass through the sysex events
  257. while (have_event && event.size > 3) {
  258. ysfx_midi_push(fx->midi.out.get(), &event);
  259. have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
  260. }
  261. if (!have_event)
  262. return 0;
  263. uint8_t msg1 = 0;
  264. uint8_t msg2 = 0;
  265. uint8_t msg3 = 0;
  266. switch (event.size) {
  267. case 3: msg3 = event.data[2]; // fall through
  268. case 2: msg2 = event.data[1]; // fall through
  269. case 1: msg1 = event.data[0]; break;
  270. }
  271. *parms[0] = (EEL_F)event.offset;
  272. *parms[1] = (EEL_F)msg1;
  273. switch (np) {
  274. case 4:
  275. *parms[2] = (EEL_F)msg2;
  276. *parms[3] = (EEL_F)msg3;
  277. break;
  278. case 3:
  279. *parms[2] = (EEL_F)(msg2 + (msg3 << 8));
  280. break;
  281. default:
  282. assert(false);
  283. return 0;
  284. }
  285. return 1;
  286. }
  287. static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *recvlen_)
  288. {
  289. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  290. return 0;
  291. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  292. NSEEL_VMCTX vm = fx->vm.get();
  293. int32_t buf = ysfx_eel_round<int32_t>(*buf_);
  294. int32_t recvlen = ysfx_eel_round<int32_t>(*recvlen_);
  295. if (recvlen < 0)
  296. recvlen = 0;
  297. uint32_t bus = ysfx_current_midi_bus(fx);
  298. ysfx_midi_event_t event;
  299. bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
  300. // pass through the events larger than the buffer
  301. while (have_event && event.size > (uint32_t)recvlen) {
  302. ysfx_midi_push(fx->midi.out.get(), &event);
  303. have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
  304. }
  305. if (!have_event)
  306. return 0;
  307. *offset_ = (EEL_F)event.offset;
  308. ysfx_eel_ram_writer writer{vm, buf};
  309. for (uint32_t i = 0; i < event.size; ++i)
  310. writer.write_next(event.data[i]);
  311. return event.size;
  312. }
  313. static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_str(void *opaque, EEL_F *offset_, EEL_F *str_)
  314. {
  315. if (ysfx_get_thread_id() != ysfx_thread_id_dsp)
  316. return 0;
  317. ysfx_t *fx = REAPER_GET_INTERFACE(opaque);
  318. uint32_t bus = ysfx_current_midi_bus(fx);
  319. ysfx_midi_event_t event;
  320. bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
  321. // pass through the events larger than the maximum string
  322. while (have_event && event.size > ysfx_string_max_length) {
  323. ysfx_midi_push(fx->midi.out.get(), &event);
  324. have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event);
  325. }
  326. if (!have_event)
  327. return 0;
  328. auto process_str = [](void *userdata, WDL_FastString &str) {
  329. ysfx_midi_event_t *event = (ysfx_midi_event_t *)userdata;
  330. str.SetRaw((const char *)event->data, (int32_t)event->size);
  331. };
  332. ///
  333. if (!ysfx_string_access(fx, *str_, true, +process_str, &event))
  334. return 0;
  335. *offset_ = (EEL_F)event.offset;
  336. return event.size;
  337. }
  338. //------------------------------------------------------------------------------
  339. void ysfx_api_init_reaper()
  340. {
  341. NSEEL_addfunc_retptr("spl", 1, NSEEL_PProc_THIS, &ysfx_api_spl);
  342. NSEEL_addfunc_retptr("slider", 1, NSEEL_PProc_THIS, &ysfx_api_slider);
  343. NSEEL_addfunc_retval("slider_next_chg", 2, NSEEL_PProc_THIS, &ysfx_api_slider_next_chg);
  344. NSEEL_addfunc_retval("slider_automate", 1, NSEEL_PProc_THIS, &ysfx_api_slider_automate);
  345. NSEEL_addfunc_retval("sliderchange", 1, NSEEL_PProc_THIS, &ysfx_api_sliderchange);
  346. NSEEL_addfunc_retval("slider_show", 2, NSEEL_PProc_THIS, &ysfx_api_slider_show);
  347. NSEEL_addfunc_exparms("midisend", 3, NSEEL_PProc_THIS, &ysfx_api_midisend);
  348. NSEEL_addfunc_exparms("midisend", 4, NSEEL_PProc_THIS, &ysfx_api_midisend);
  349. NSEEL_addfunc_retval("midisend_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midisend_buf);
  350. NSEEL_addfunc_retval("midisend_str", 2, NSEEL_PProc_THIS, &ysfx_api_midisend_str);
  351. NSEEL_addfunc_exparms("midirecv", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv);
  352. NSEEL_addfunc_exparms("midirecv", 4, NSEEL_PProc_THIS, &ysfx_api_midirecv);
  353. NSEEL_addfunc_retval("midirecv_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv_buf);
  354. NSEEL_addfunc_retval("midirecv_str", 2, NSEEL_PProc_THIS, &ysfx_api_midirecv_str);
  355. NSEEL_addfunc_retval("midisyx", 3, NSEEL_PProc_THIS, &ysfx_api_midisyx);
  356. }