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.

1293 lines
32KB

  1. /*
  2. * Copyright 2014-2015 Pascal Gauthier
  3. * Copyright 2018 Pascal Gauthier, Marcel Smit
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * *distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. #include "jsusfx.h"
  18. #include "jsusfx_file.h"
  19. #include "jsusfx_gfx.h"
  20. #include "jsusfx_serialize.h"
  21. #include <string.h>
  22. #ifndef WIN32
  23. #include <unistd.h>
  24. #endif
  25. #include "WDL/ptrlist.h"
  26. #include "WDL/assocarray.h"
  27. #include "WDL/mutex.h"
  28. #define REAPER_GET_INTERFACE(opaque) ((opaque) ? ((JsusFx*)opaque) : nullptr)
  29. #define AUTOVAR(name) name = NSEEL_VM_regvar(m_vm, #name); *name = 0
  30. #define AUTOVARV(name,value) name = NSEEL_VM_regvar(m_vm, #name); *name = value
  31. #define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((JsusFx *)opaque)->m_string_context)
  32. #ifdef EEL_STRING_STDOUT_WRITE
  33. #ifndef EELSCRIPT_NO_STDIO
  34. #define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); }
  35. #endif
  36. #endif
  37. #include "WDL/eel2/eel_strings.h"
  38. #include "WDL/eel2/eel_misc.h"
  39. #include "WDL/eel2/eel_fft.h"
  40. #include "WDL/eel2/eel_mdct.h"
  41. #include <fstream> // to check if files exist
  42. // Reaper API
  43. static EEL_F * NSEEL_CGEN_CALL _reaper_slider(void *opaque, EEL_F *n)
  44. {
  45. JsusFx *ctx = REAPER_GET_INTERFACE(opaque);
  46. const int index = *n;
  47. if (index >= 0 && index < ctx->kMaxSliders)
  48. return ctx->sliders[index].owner;
  49. else {
  50. ctx->dummyValue = 0;
  51. return &ctx->dummyValue;
  52. }
  53. }
  54. static EEL_F * NSEEL_CGEN_CALL _reaper_spl(void *opaque, EEL_F *n)
  55. {
  56. JsusFx *ctx = REAPER_GET_INTERFACE(opaque);
  57. const int index = *n;
  58. if (index >= 0 && index < ctx->numValidInputChannels)
  59. return ctx->spl[index];
  60. else {
  61. ctx->dummyValue = 0;
  62. return &ctx->dummyValue;
  63. }
  64. }
  65. static EEL_F NSEEL_CGEN_CALL _midirecv(void *opaque, INT_PTR np, EEL_F **parms)
  66. {
  67. JsusFx *ctx = REAPER_GET_INTERFACE(opaque);
  68. if (ctx->midiInputReadPos >= ctx->midiInput.size())
  69. return 0;
  70. JsusFx::EventHeader event;
  71. const uint8_t *data = nullptr;
  72. while (!data && ctx->midiInputReadPos < ctx->midiInput.size()) {
  73. data = &ctx->midiInput[ctx->midiInputReadPos];
  74. memcpy(&event, data, sizeof(event));
  75. data += sizeof(event);
  76. ctx->midiInputReadPos += sizeof(event) + event.length;
  77. // pass through the sysex events
  78. if (event.length > 3) {
  79. ctx->addOutputEvent(event.offset, data, event.length);
  80. data = nullptr;
  81. }
  82. }
  83. if (!data)
  84. return 0;
  85. uint8_t msg1 = 0;
  86. uint8_t msg2 = 0;
  87. uint8_t msg3 = 0;
  88. switch (event.length)
  89. {
  90. case 3:
  91. msg3 = data[2];
  92. // fall through
  93. case 2:
  94. msg2 = data[1];
  95. // fall through
  96. case 1:
  97. msg1 = data[0];
  98. break;
  99. }
  100. *parms[0] = event.offset;
  101. *parms[1] = msg1;
  102. if (np >= 4) {
  103. *parms[2] = msg2;
  104. *parms[3] = msg3;
  105. } else {
  106. *parms[2] = msg2 + msg3 * 256;
  107. }
  108. return 1;
  109. }
  110. // determine the length of a midi message according to its status byte
  111. // if length is dynamic, returns 0
  112. unsigned midi_sizeof(uint8_t id)
  113. {
  114. if ((id >> 7) == 0) {
  115. return 0;
  116. }
  117. else if ((id >> 4) != 0b1111) {
  118. static const uint8_t sizetable[8] = {
  119. 3, 3, 3, 3, 2, 2, 3 };
  120. return sizetable[(id >> 4) & 0b111];
  121. }
  122. else {
  123. static const uint8_t sizetable[16] = {
  124. 0, 2, 3, 2, 1, 1, 1, 0,
  125. 1, 1, 1, 1, 1, 1, 1, 1 };
  126. return sizetable[id & 0b1111];
  127. }
  128. }
  129. static EEL_F NSEEL_CGEN_CALL _midisend(void *opaque, INT_PTR np, EEL_F **parms)
  130. {
  131. JsusFx *ctx = REAPER_GET_INTERFACE(opaque);
  132. int offset;
  133. uint8_t msg1;
  134. uint8_t msg2;
  135. uint8_t msg3;
  136. if (np == 3) {
  137. offset = (int)*parms[0];
  138. msg1 = (uint8_t)*parms[1];
  139. const unsigned msg23 = (unsigned)*parms[2];
  140. msg2 = (uint8_t)(msg23 & 0xff);
  141. msg3 = (uint8_t)(msg23 >> 8);
  142. } else if (np == 4) {
  143. offset = (int)*parms[0];
  144. msg1 = (uint8_t)*parms[1];
  145. msg2 = (uint8_t)*parms[2];
  146. msg3 = (uint8_t)*parms[3];
  147. } else {
  148. return 0;
  149. }
  150. const uint8_t data[] = {msg1, msg2, msg3};
  151. // NOTE(jpc) correct the length of the message
  152. // in case it should be less than 3 bytes
  153. int length = midi_sizeof(msg1);
  154. if (length == 0) // don't know what message this is
  155. length = 3;
  156. if (!ctx->addOutputEvent(offset, data, length))
  157. return 0;
  158. return msg1;
  159. }
  160. static EEL_F NSEEL_CGEN_CALL _midisend_buf(void *opaque, INT_PTR np, EEL_F **parms)
  161. {
  162. JsusFx *ctx = REAPER_GET_INTERFACE(opaque);
  163. if (np == 3) {
  164. const int offset = (int)*parms[0];
  165. const uint8_t *buf = (const uint8_t*)parms[1];
  166. const int len = (int)*parms[2];
  167. if (!ctx->addOutputEvent(offset, buf, len))
  168. return 0;
  169. return len;
  170. } else {
  171. return 0;
  172. }
  173. }
  174. // todo : remove __stub
  175. static EEL_F NSEEL_CGEN_CALL __stub(void *opaque, INT_PTR np, EEL_F **parms)
  176. {
  177. return 0.0;
  178. }
  179. //
  180. struct JsusFx_Section {
  181. WDL_String code;
  182. int lineOffset;
  183. JsusFx_Section()
  184. {
  185. lineOffset = 0;
  186. }
  187. };
  188. struct JsusFx_Sections {
  189. JsusFx_Section init;
  190. JsusFx_Section slider;
  191. JsusFx_Section block;
  192. JsusFx_Section sample;
  193. JsusFx_Section gfx;
  194. JsusFx_Section serialize;
  195. };
  196. //
  197. static const char *skipWhite(const char *text) {
  198. while ( *text && isspace(*text) )
  199. text++;
  200. return text;
  201. }
  202. static const char *nextToken(const char *text) {
  203. while ( *text && *text != ',' && *text != '=' && *text != '<' && *text != '>' && *text != '{' && *text != '}' )
  204. text++;
  205. return text;
  206. }
  207. bool JsusFx_Slider::config(JsusFx &fx, const int index, const char *param, const int lnumber) {
  208. char buffer[2048];
  209. strncpy(buffer, param, 2048);
  210. def = min = max = inc = 0;
  211. exists = false;
  212. enumNames.clear();
  213. isEnum = false;
  214. bool hasName = false;
  215. const char *tmp = strchr(buffer, '>');
  216. if ( tmp != NULL ) {
  217. tmp++;
  218. while (*tmp == ' ')
  219. tmp++;
  220. strncpy(desc, tmp, 64);
  221. tmp = 0;
  222. } else {
  223. desc[0] = 0;
  224. }
  225. tmp = buffer;
  226. if ( isalpha(*tmp) ) {
  227. // extended syntax of format "slider1:variable_name=5<0,10,1>slider description"
  228. const char *begin = tmp;
  229. while ( *tmp && *tmp != '=' )
  230. tmp++;
  231. if ( *tmp != '=' ) {
  232. fx.displayError("Expected '=' at end of slider name %d", lnumber);
  233. return false;
  234. }
  235. const char *end = tmp;
  236. int len = end - begin;
  237. if ( len > JsusFx_Slider::kMaxName ) {
  238. fx.displayError("Slider name too long %d", lnumber);
  239. return false;
  240. }
  241. for ( int i = 0; i < len; ++i )
  242. name[i] = begin[i];
  243. name[len] = 0;
  244. hasName = true;
  245. tmp++;
  246. }
  247. if ( !sscanf(tmp, "%f", &def) )
  248. return false;
  249. tmp = nextToken(tmp);
  250. if ( *tmp != '<' )
  251. {
  252. fx.displayError("slider info is missing");
  253. return false;
  254. }
  255. else
  256. {
  257. tmp++;
  258. if ( !sscanf(tmp, "%f", &min) )
  259. {
  260. fx.displayError("failed to read min value");
  261. return false;
  262. }
  263. tmp = nextToken(tmp);
  264. if ( *tmp != ',' )
  265. {
  266. fx.displayError("max value is missing");
  267. return false;
  268. }
  269. else
  270. {
  271. tmp++;
  272. if ( !sscanf(tmp, "%f", &max) )
  273. {
  274. fx.displayError("failed to read max value");
  275. return false;
  276. }
  277. tmp = nextToken(tmp);
  278. if ( *tmp == ',')
  279. {
  280. tmp++;
  281. tmp = skipWhite(tmp);
  282. if ( !sscanf(tmp, "%f", &inc) )
  283. {
  284. //log("failed to read increment value");
  285. //return false;
  286. inc = 0;
  287. }
  288. tmp = nextToken(tmp);
  289. if ( *tmp == '{' )
  290. {
  291. isEnum = true;
  292. inc = 1;
  293. tmp++;
  294. while ( true )
  295. {
  296. const char *end = nextToken(tmp);
  297. const std::string name(tmp, end);
  298. enumNames.push_back(name);
  299. tmp = end;
  300. if ( *tmp == 0 )
  301. {
  302. fx.displayError("enum value list not properly terminated");
  303. return false;
  304. }
  305. if ( *tmp == '}' )
  306. {
  307. break;
  308. }
  309. tmp++;
  310. }
  311. tmp++;
  312. }
  313. }
  314. }
  315. }
  316. if (hasName == false) {
  317. sprintf(name, "slider%d", index);
  318. }
  319. owner = NSEEL_VM_regvar(fx.m_vm, name);
  320. *owner = def;
  321. exists = true;
  322. return true;
  323. }
  324. //
  325. JsusFxPathLibrary_Basic::JsusFxPathLibrary_Basic(const char * _dataRoot) {
  326. if ( _dataRoot != nullptr )
  327. dataRoot = _dataRoot;
  328. }
  329. void JsusFxPathLibrary_Basic::addSearchPath(const std::string & path) {
  330. if ( path.empty() )
  331. return;
  332. // make sure it ends with '/' or '\\'
  333. if ( path.back() == '/' || path.back() == '\\' )
  334. searchPaths.push_back(path);
  335. else
  336. searchPaths.push_back(path + "/");
  337. }
  338. bool JsusFxPathLibrary_Basic::fileExists(const std::string &filename) {
  339. std::ifstream is(filename);
  340. return is.is_open();
  341. }
  342. bool JsusFxPathLibrary_Basic::resolveImportPath(const std::string &importPath, const std::string &parentPath, std::string &resolvedPath) {
  343. const size_t pos = parentPath.rfind('/');
  344. if ( pos != std::string::npos )
  345. resolvedPath = parentPath.substr(0, pos + 1);
  346. if ( fileExists(resolvedPath + importPath) ) {
  347. resolvedPath = resolvedPath + importPath;
  348. return true;
  349. }
  350. for ( std::string & searchPath : searchPaths ) {
  351. if ( fileExists(resolvedPath + searchPath + importPath) ) {
  352. resolvedPath = resolvedPath + searchPath + importPath;
  353. return true;
  354. }
  355. }
  356. return false;
  357. }
  358. bool JsusFxPathLibrary_Basic::resolveDataPath(const std::string &importPath, std::string &resolvedPath) {
  359. if ( !dataRoot.empty() )
  360. resolvedPath = dataRoot + "/" + importPath;
  361. else
  362. resolvedPath = importPath;
  363. return fileExists(resolvedPath);
  364. }
  365. std::istream* JsusFxPathLibrary_Basic::open(const std::string &path) {
  366. std::ifstream *stream = new std::ifstream(path);
  367. if ( stream->is_open() == false ) {
  368. delete stream;
  369. stream = nullptr;
  370. }
  371. return stream;
  372. }
  373. void JsusFxPathLibrary_Basic::close(std::istream *&stream) {
  374. delete stream;
  375. stream = nullptr;
  376. }
  377. //
  378. JsusFx::JsusFx(JsusFxPathLibrary &_pathLibrary)
  379. : pathLibrary(_pathLibrary) {
  380. m_vm = NSEEL_VM_alloc();
  381. codeInit = codeSlider = codeBlock = codeSample = codeGfx = codeSerialize = NULL;
  382. NSEEL_VM_SetCustomFuncThis(m_vm,this);
  383. m_string_context = new eel_string_context_state();
  384. eel_string_initvm(m_vm);
  385. computeSlider = false;
  386. srate = 0;
  387. pathLibrary = _pathLibrary;
  388. fileAPI = nullptr;
  389. midiInput.reserve(8192);
  390. midiOutput.reserve(8192);
  391. midiInputReadPos = 0;
  392. gfx = nullptr;
  393. gfx_w = 0;
  394. gfx_h = 0;
  395. serializer = nullptr;
  396. for (int i = 0; i < kMaxSamples; ++i) {
  397. char name[16];
  398. sprintf(name, "spl%d", i);
  399. spl[i] = NSEEL_VM_regvar(m_vm, name);
  400. *spl[i] = 0;
  401. }
  402. numInputs = 0;
  403. numOutputs = 0;
  404. inputNames.reserve(16);
  405. outputNames.reserve(16);
  406. numValidInputChannels = 0;
  407. AUTOVAR(srate);
  408. AUTOVARV(num_ch, 2);
  409. AUTOVAR(samplesblock);
  410. AUTOVAR(trigger);
  411. // transport state. use setTransportValues to set these
  412. AUTOVARV(tempo, 120); // playback tempo in beats per minute
  413. AUTOVARV(play_state, 1); // playback state. see the PlaybackState enum for details
  414. AUTOVAR(play_position); // current playback position in seconds
  415. AUTOVAR(beat_position); // current playback position in beats (beats = quarternotes in /4 time signatures)
  416. AUTOVARV(ts_num, 0); // time signature nominator. i.e. 3 if using 3/4 time
  417. AUTOVARV(ts_denom, 4); // time signature denominator. i.e. 4 if using 3/4 time
  418. AUTOVAR(ext_noinit);
  419. AUTOVAR(ext_nodenorm); // set to 1 to disable noise added to signals to avoid denormals from popping up
  420. // midi bus support
  421. AUTOVAR(ext_midi_bus); // when set to 1, support for midi buses is enabled. otherwise, only bus 0 is active and others will pass through
  422. AUTOVAR(midi_bus);
  423. // Reaper API
  424. NSEEL_addfunc_varparm("slider_automate",1,NSEEL_PProc_THIS,&__stub); // todo : implement slider_automate. add Reaper api interface?
  425. NSEEL_addfunc_varparm("slider_next_chg",2,NSEEL_PProc_THIS,&__stub); // todo : implement slider_next_chg. add Reaper api interface?
  426. NSEEL_addfunc_varparm("sliderchange",1,NSEEL_PProc_THIS,&__stub); // todo : implement sliderchange. add Reaper api interface?
  427. NSEEL_addfunc_retptr("slider",1,NSEEL_PProc_THIS,&_reaper_slider);
  428. NSEEL_addfunc_retptr("spl",1,NSEEL_PProc_THIS,&_reaper_spl);
  429. NSEEL_addfunc_varparm("midirecv",3,NSEEL_PProc_THIS,&_midirecv);
  430. NSEEL_addfunc_varparm("midisend",3,NSEEL_PProc_THIS,&_midisend);
  431. NSEEL_addfunc_varparm("midisend_buf",3,NSEEL_PProc_THIS,&_midisend_buf);
  432. }
  433. JsusFx::~JsusFx() {
  434. releaseCode();
  435. if (m_vm)
  436. NSEEL_VM_free(m_vm);
  437. delete m_string_context;
  438. }
  439. bool JsusFx::compileSection(int state, const char *code, int line_offset) {
  440. if ( code[0] == 0 )
  441. return true;
  442. char errorMsg[4096];
  443. //printf("section code:\n");
  444. //printf("%s", code);
  445. switch(state) {
  446. case 0:
  447. codeInit = NSEEL_code_compile_ex(m_vm, code, line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
  448. if ( codeInit == NULL ) {
  449. snprintf(errorMsg, 4096, "@init line %s", NSEEL_code_getcodeerror(m_vm));
  450. displayError(errorMsg);
  451. return false;
  452. }
  453. break;
  454. case 1:
  455. codeSlider = NSEEL_code_compile_ex(m_vm, code, line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
  456. if ( codeSlider == NULL ) {
  457. snprintf(errorMsg, 4096, "@slider line %s", NSEEL_code_getcodeerror(m_vm));
  458. displayError(errorMsg);
  459. return false;
  460. }
  461. break;
  462. case 2:
  463. codeBlock = NSEEL_code_compile_ex(m_vm, code, line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
  464. if ( codeBlock == NULL ) {
  465. snprintf(errorMsg, 4096, "@block line %s", NSEEL_code_getcodeerror(m_vm));
  466. displayError(errorMsg);
  467. return false;
  468. }
  469. break;
  470. case 3:
  471. codeSample = NSEEL_code_compile_ex(m_vm, code, line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
  472. if ( codeSample == NULL ) {
  473. snprintf(errorMsg, 4096, "@sample line %s", NSEEL_code_getcodeerror(m_vm));
  474. displayError(errorMsg);
  475. return false;
  476. }
  477. break;
  478. case 4:
  479. codeGfx = NSEEL_code_compile_ex(m_vm, code, line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
  480. if ( codeGfx == NULL ) {
  481. snprintf(errorMsg, 4096, "@gfx line %s", NSEEL_code_getcodeerror(m_vm));
  482. displayError(errorMsg);
  483. return false;
  484. }
  485. break;
  486. case 5:
  487. codeSerialize = NSEEL_code_compile_ex(m_vm, code, line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
  488. if ( codeSerialize == NULL ) {
  489. snprintf(errorMsg, 4096, "@serialize line %s", NSEEL_code_getcodeerror(m_vm));
  490. displayError(errorMsg);
  491. return false;
  492. }
  493. break;
  494. default:
  495. //printf("unknown block");
  496. break;
  497. }
  498. m_string_context->update_named_vars(m_vm);
  499. return true;
  500. }
  501. bool JsusFx::processImport(JsusFxPathLibrary &pathLibrary, const std::string &path, const std::string &importPath, JsusFx_Sections &sections, const int compileFlags) {
  502. bool result = true;
  503. //displayMsg("Importing %s", path.c_str());
  504. std::string resolvedPath;
  505. if ( ! pathLibrary.resolveImportPath(importPath, path, resolvedPath) ) {
  506. displayError("Failed to resolve import file path %s", importPath.c_str());
  507. return false;
  508. }
  509. std::istream *is = pathLibrary.open(resolvedPath);
  510. if ( is != nullptr ) {
  511. result &= readSections(pathLibrary, resolvedPath, *is, sections, compileFlags);
  512. } else {
  513. displayError("Failed to open imported file %s", importPath.c_str());
  514. result &= false;
  515. }
  516. pathLibrary.close(is);
  517. return result;
  518. }
  519. static char *trim(char *line, bool trimStart, bool trimEnd)
  520. {
  521. if (trimStart) {
  522. while (*line && isspace(*line))
  523. line++;
  524. }
  525. if (trimEnd) {
  526. char *last = line;
  527. while (last[0] && last[1])
  528. last++;
  529. for (char *b = last; isspace(*b) && b >= line; b--)
  530. *b = 0;
  531. }
  532. return line;
  533. }
  534. bool JsusFx::readHeader(JsusFxPathLibrary &pathLibrary, const std::string &path, std::istream &input) {
  535. char line[4096];
  536. inputNames.clear();
  537. outputNames.clear();
  538. for(int lnumber = 1; ! input.eof(); lnumber++) {
  539. input.getline(line, sizeof(line), '\n');
  540. if ( line[0] == '@' )
  541. break;
  542. if ( ! strnicmp(line, "slider", 6) ) {
  543. int target = 0;
  544. if ( ! sscanf(line, "slider%d:", &target) )
  545. continue;
  546. if ( target < 0 || target >= kMaxSliders )
  547. continue;
  548. JsusFx_Slider &slider = sliders[target];
  549. char *p = line+7;
  550. while ( *p && *p != ':' )
  551. p++;
  552. if ( *p != ':' )
  553. continue;
  554. p++;
  555. if ( ! slider.config(*this, target, p, lnumber) ) {
  556. displayError("Incomplete slider @line %d (%s)", lnumber, line);
  557. return false;
  558. }
  559. trim(slider.desc, false, true);
  560. continue;
  561. }
  562. else if ( ! strncmp(line, "desc:", 5) ) {
  563. char *src = line+5;
  564. src = trim(src, true, true);
  565. strncpy(desc, src, 64);
  566. continue;
  567. }
  568. else if ( ! strncmp(line, "in_pin:", 7) ) {
  569. char *src = line+7;
  570. src = trim(src, true, true);
  571. if ( ! strncmp(src, "none", 4) ) {
  572. inputNames.clear();
  573. numInputs = -1;
  574. } else {
  575. if ( numInputs != -1 ) {
  576. inputNames.push_back(src);
  577. numInputs++;
  578. }
  579. }
  580. }
  581. else if ( ! strncmp(line, "out_pin:", 8) ) {
  582. char *src = line+8;
  583. src = trim(src, true, true);
  584. if ( ! strncmp(src, "none", 4) ) {
  585. outputNames.clear();
  586. numOutputs = -1;
  587. } else {
  588. if ( numOutputs != -1 ) {
  589. outputNames.push_back(src);
  590. numOutputs++;
  591. }
  592. }
  593. }
  594. }
  595. return true;
  596. }
  597. bool JsusFx::readSections(JsusFxPathLibrary &pathLibrary, const std::string &path, std::istream &input, JsusFx_Sections &sections, const int compileFlags) {
  598. WDL_String * code = nullptr;
  599. char line[4096];
  600. inputNames.clear();
  601. outputNames.clear();
  602. // are we reading the header or sections?
  603. bool isHeader = true;
  604. for(int lnumber = 1; ! input.eof(); lnumber++) {
  605. input.getline(line, sizeof(line), '\n');
  606. const int l = input.gcount();
  607. if ( line[0] == '@' ) {
  608. char *b = line + 1;
  609. b = trim(b, false, true);
  610. // we've begun reading sections now
  611. isHeader = false;
  612. JsusFx_Section *section = nullptr;
  613. if ( ! strnicmp(b, "init", 4) )
  614. section = &sections.init;
  615. else if ( ! strnicmp(b, "slider", 6) )
  616. section = &sections.slider;
  617. else if ( ! strnicmp(b, "block", 5) )
  618. section = &sections.block;
  619. else if ( ! strnicmp(b, "sample", 6) )
  620. section = &sections.sample;
  621. else if ( ! strnicmp(b, "gfx", 3) && (compileFlags & kCompileFlag_CompileGraphicsSection) != 0 ) {
  622. if ( sscanf(b+3, "%d %d", &gfx_w, &gfx_h) != 2 ) {
  623. gfx_w = 0;
  624. gfx_h = 0;
  625. }
  626. section = &sections.gfx;
  627. }
  628. else if ( ! strnicmp(b, "serialize", 9) && (compileFlags & kCompileFlag_CompileSerializeSection) != 0 )
  629. section = &sections.serialize;
  630. if ( section != nullptr ) {
  631. code = &section->code;
  632. section->lineOffset = lnumber;
  633. } else {
  634. code = nullptr;
  635. }
  636. continue;
  637. }
  638. if ( code != nullptr ) {
  639. //int l = strlen(line);
  640. if ( l > 0 && line[l-1] == '\r' )
  641. line[l-1] = 0;
  642. if ( line[0] != 0 ) {
  643. code->Append(line);
  644. }
  645. code->Append("\n");
  646. continue;
  647. }
  648. if (isHeader) {
  649. if ( ! strnicmp(line, "slider", 6) ) {
  650. int target = 0;
  651. if ( ! sscanf(line, "slider%d:", &target) )
  652. continue;
  653. if ( target < 0 || target >= kMaxSliders )
  654. continue;
  655. JsusFx_Slider &slider = sliders[target];
  656. char *p = line+7;
  657. while ( *p && *p != ':' )
  658. p++;
  659. if ( *p != ':' )
  660. continue;
  661. p++;
  662. if ( ! slider.config(*this, target, p, lnumber) ) {
  663. displayError("Incomplete slider @line %d (%s)", lnumber, line);
  664. return false;
  665. }
  666. trim(slider.desc, false, true);
  667. continue;
  668. }
  669. else if ( ! strncmp(line, "desc:", 5) ) {
  670. char *src = line+5;
  671. src = trim(src, true, true);
  672. strncpy(desc, src, 64);
  673. continue;
  674. }
  675. else if ( ! strnicmp(line, "filename:", 9) ) {
  676. // filename:0,filename.wav
  677. char *src = line+8;
  678. src = trim(src, true, false);
  679. if ( *src != ':' )
  680. return false;
  681. src++;
  682. src = trim(src, true, false);
  683. int index;
  684. if ( sscanf(src, "%d", &index) != 1 )
  685. return false;
  686. while ( isdigit(*src) )
  687. src++;
  688. src = trim(src, true, false);
  689. if ( *src != ',' )
  690. return false;
  691. src++;
  692. src = trim(src, true, true);
  693. std::string resolvedPath;
  694. if ( pathLibrary.resolveImportPath(src, path, resolvedPath) ) {
  695. if ( ! handleFile(index, resolvedPath.c_str() ) ) {
  696. return false;
  697. }
  698. }
  699. }
  700. else if ( ! strncmp(line, "import ", 7) ) {
  701. char *src = line+7;
  702. src = trim(src, true, true);
  703. if (*src) {
  704. processImport(pathLibrary, path, src, sections, compileFlags);
  705. }
  706. continue;
  707. }
  708. else if ( ! strncmp(line, "in_pin:", 7) ) {
  709. char *src = line+7;
  710. src = trim(src, true, true);
  711. if ( ! strncmp(src, "none", 4) ) {
  712. inputNames.clear();
  713. numInputs = -1;
  714. } else {
  715. if ( numInputs != -1 ) {
  716. inputNames.push_back(src);
  717. numInputs++;
  718. }
  719. }
  720. }
  721. else if ( ! strncmp(line, "out_pin:", 8) ) {
  722. char *src = line+8;
  723. src = trim(src, true, true);
  724. if ( ! strncmp(src, "none", 4) ) {
  725. outputNames.clear();
  726. numOutputs = -1;
  727. } else {
  728. if ( numOutputs != -1 ) {
  729. outputNames.push_back(src);
  730. numOutputs++;
  731. }
  732. }
  733. }
  734. }
  735. }
  736. return true;
  737. }
  738. bool JsusFx::compileSections(JsusFx_Sections &sections, const int compileFlags) {
  739. bool result = true;
  740. // 0 init
  741. // 1 slider
  742. // 2 block
  743. // 3 sample
  744. // 4 gfx
  745. // 5 serialize
  746. if (sections.init.code.GetLength() != 0)
  747. result &= compileSection(0, sections.init.code.Get(), sections.init.lineOffset);
  748. if (sections.slider.code.GetLength() != 0)
  749. result &= compileSection(1, sections.slider.code.Get(), sections.slider.lineOffset);
  750. if (sections.block.code.GetLength() != 0)
  751. result &= compileSection(2, sections.block.code.Get(), sections.block.lineOffset);
  752. if (sections.sample.code.GetLength() != 0)
  753. result &= compileSection(3, sections.sample.code.Get(), sections.sample.lineOffset);
  754. if (sections.gfx.code.GetLength() != 0 && (compileFlags & kCompileFlag_CompileGraphicsSection) != 0)
  755. result &= compileSection(4, sections.gfx.code.Get(), sections.gfx.lineOffset);
  756. if (sections.serialize.code.GetLength() != 0 && (compileFlags & kCompileFlag_CompileSerializeSection) != 0)
  757. result &= compileSection(5, sections.serialize.code.Get(), sections.serialize.lineOffset);
  758. if (result == false)
  759. releaseCode();
  760. return result;
  761. }
  762. bool JsusFx::compile(JsusFxPathLibrary &pathLibrary, const std::string &path, const int compileFlags) {
  763. releaseCode();
  764. std::string resolvedPath;
  765. if ( ! pathLibrary.resolveImportPath(path, "", resolvedPath) ) {
  766. displayError("Failed to open %s", path.c_str());
  767. return false;
  768. }
  769. std::istream *input = pathLibrary.open(resolvedPath);
  770. if ( input == nullptr ) {
  771. displayError("Failed to open %s", resolvedPath.c_str());
  772. return false;
  773. }
  774. // read code for the various sections inside the jsusfx script
  775. JsusFx_Sections sections;
  776. if ( ! readSections(pathLibrary, resolvedPath, *input, sections, compileFlags) )
  777. return false;
  778. pathLibrary.close(input);
  779. // compile the sections
  780. if ( ! compileSections(sections, compileFlags) ) {
  781. releaseCode();
  782. return false;
  783. }
  784. computeSlider = 1;
  785. // in_pin and out_pin is optional, we default it to 2 in / 2 out if nothing is specified.
  786. // if you really want no in or out, specify in_pin:none/out_pin:none
  787. if ( numInputs == 0 )
  788. numInputs = 2;
  789. else if ( numInputs == -1 )
  790. numInputs = 0;
  791. if ( numOutputs == 0 )
  792. numOutputs = 2;
  793. else if ( numOutputs == -1 )
  794. numOutputs = 0;
  795. return true;
  796. }
  797. bool JsusFx::readHeader(JsusFxPathLibrary &pathLibrary, const std::string &path) {
  798. std::string resolvedPath;
  799. if ( ! pathLibrary.resolveImportPath(path, "", resolvedPath) ) {
  800. displayError("Failed to open %s", path.c_str());
  801. return false;
  802. }
  803. std::istream *input = pathLibrary.open(resolvedPath);
  804. if ( input == nullptr ) {
  805. displayError("Failed to open %s", resolvedPath.c_str());
  806. return false;
  807. }
  808. if ( ! readHeader(pathLibrary, resolvedPath, *input) )
  809. return false;
  810. pathLibrary.close(input);
  811. return true;
  812. }
  813. void JsusFx::prepare(int sampleRate, int blockSize) {
  814. *srate = (double) sampleRate;
  815. *samplesblock = blockSize;
  816. NSEEL_code_execute(codeInit);
  817. }
  818. void JsusFx::moveSlider(int idx, float value, int normalizeSlider) {
  819. if ( idx < 0 || idx >= kMaxSliders || !sliders[idx].exists )
  820. return;
  821. if ( normalizeSlider != 0 ) {
  822. float steps = sliders[idx].max - sliders[idx].min;
  823. value = (value * steps) / normalizeSlider;
  824. value += sliders[idx].min;
  825. }
  826. if ( sliders[idx].inc != 0 ) {
  827. int tmp = roundf(value / sliders[idx].inc);
  828. value = sliders[idx].inc * tmp;
  829. }
  830. computeSlider |= sliders[idx].setValue(value);
  831. }
  832. bool JsusFx::addInputEvent(int offset, const uint8_t *data, int length)
  833. {
  834. EventHeader event;
  835. event.offset = offset;
  836. event.length = length;
  837. std::copy((char*)&event, (char*)&event + sizeof(event), std::back_inserter(midiInput));
  838. std::copy(data, data + length, std::back_inserter(midiInput));
  839. return true;
  840. }
  841. bool JsusFx::addOutputEvent(int offset, const uint8_t *data, int length)
  842. {
  843. EventHeader event;
  844. event.offset = offset;
  845. event.length = length;
  846. std::copy((char*)&event, (char*)&event + sizeof(event), std::back_inserter(midiOutput));
  847. std::copy(data, data + length, std::back_inserter(midiOutput));
  848. return true;
  849. }
  850. bool JsusFx::iterateOutputEvents(size_t &iterPos, int &offset, const uint8_t *&data, int &length)
  851. {
  852. if (iterPos < midiOutput.size()) {
  853. EventHeader event;
  854. data = &midiOutput[iterPos];
  855. memcpy(&event, data, sizeof(event));
  856. data += sizeof(event);
  857. iterPos += sizeof(event) + event.length;
  858. offset = event.offset;
  859. length = event.length;
  860. return true;
  861. }
  862. else {
  863. return false;
  864. }
  865. }
  866. void JsusFx::setTransportValues(
  867. const double in_tempo,
  868. const PlaybackState in_playbackState,
  869. const double in_playbackPositionInSeconds,
  870. const double in_beatPosition,
  871. const int in_timeSignatureNumerator,
  872. const int in_timeSignatureDenumerator)
  873. {
  874. *tempo = in_tempo;
  875. *play_state = in_playbackState;
  876. *play_position = in_playbackPositionInSeconds;
  877. *beat_position = in_beatPosition;
  878. *ts_num = in_timeSignatureNumerator;
  879. *ts_denom = in_timeSignatureDenumerator;
  880. }
  881. bool JsusFx::process(const float **input, float **output, int size, int numInputChannels, int numOutputChannels) {
  882. midiInputReadPos = 0;
  883. midiOutput.clear();
  884. if ( codeSample == NULL )
  885. return false;
  886. if ( computeSlider ) {
  887. NSEEL_code_execute(codeSlider);
  888. computeSlider = false;
  889. }
  890. numValidInputChannels = numInputChannels;
  891. *samplesblock = size;
  892. *num_ch = numValidInputChannels;
  893. NSEEL_code_execute(codeBlock);
  894. for(int i=0;i<size;i++) {
  895. for (int c = 0; c < numInputChannels; ++c)
  896. *spl[c] = input[c][i];
  897. NSEEL_code_execute(codeSample);
  898. for (int c = 0; c < numOutputChannels; ++c)
  899. output[c][i] = *spl[c];
  900. }
  901. midiInput.clear();
  902. return true;
  903. }
  904. bool JsusFx::process64(const double **input, double **output, int size, int numInputChannels, int numOutputChannels) {
  905. midiInputReadPos = 0;
  906. midiOutput.clear();
  907. if ( codeSample == NULL )
  908. return false;
  909. if ( computeSlider ) {
  910. NSEEL_code_execute(codeSlider);
  911. computeSlider = false;
  912. }
  913. numValidInputChannels = numInputChannels;
  914. *samplesblock = size;
  915. *num_ch = numValidInputChannels;
  916. NSEEL_code_execute(codeBlock);
  917. for(int i=0;i<size;i++) {
  918. for (int c = 0; c < numInputChannels; ++c)
  919. *spl[c] = input[c][i];
  920. NSEEL_code_execute(codeSample);
  921. for (int c = 0; c < numOutputChannels; ++c)
  922. output[c][i] = *spl[c];
  923. }
  924. midiInput.clear();
  925. return true;
  926. }
  927. void JsusFx::draw() {
  928. if ( codeGfx == NULL )
  929. return;
  930. if ( gfx != nullptr )
  931. gfx->beginDraw();
  932. NSEEL_code_execute(codeGfx);
  933. if ( gfx != nullptr )
  934. gfx->endDraw();
  935. }
  936. bool JsusFx::serialize(JsusFxSerializer & _serializer, const bool write) {
  937. serializer = &_serializer;
  938. serializer->begin(*this, write);
  939. {
  940. if (codeSerialize != nullptr)
  941. NSEEL_code_execute(codeSerialize);
  942. if (write == false)
  943. {
  944. if (codeSlider != nullptr)
  945. NSEEL_code_execute(codeSlider);
  946. computeSlider = false;
  947. }
  948. }
  949. serializer->end();
  950. serializer = nullptr;
  951. return true;
  952. }
  953. const char * JsusFx::getString(const int index, WDL_FastString ** fs) {
  954. void * opaque = this;
  955. return EEL_STRING_GET_FOR_INDEX(index, fs);
  956. }
  957. bool JsusFx::handleFile(int index, const char *filename)
  958. {
  959. if (index < 0 || index >= kMaxFileInfos)
  960. {
  961. displayError("file index out of bounds %d:%s", index, filename);
  962. return false;
  963. }
  964. if (fileInfos[index].isValid())
  965. {
  966. displayMsg("file already exists %d:%s", index, filename);
  967. fileInfos[index] = JsusFx_FileInfo();
  968. }
  969. //
  970. if (fileInfos[index].init(filename))
  971. {
  972. const char *ext = nullptr;
  973. for (const char *p = filename; *p; ++p)
  974. if (*p == '.')
  975. ext = p + 1;
  976. if (ext != nullptr && (stricmp(ext, "png") == 0 || stricmp(ext, "jpg") == 0))
  977. {
  978. if (gfx != nullptr)
  979. {
  980. gfx->gfx_loadimg(*this, index, index);
  981. }
  982. }
  983. return true;
  984. }
  985. else
  986. {
  987. displayError("failed to find file %d:%s", index, filename);
  988. return false;
  989. }
  990. }
  991. void JsusFx::releaseCode() {
  992. desc[0] = 0;
  993. if ( codeInit )
  994. NSEEL_code_free(codeInit);
  995. if ( codeSlider )
  996. NSEEL_code_free(codeSlider);
  997. if ( codeBlock )
  998. NSEEL_code_free(codeBlock);
  999. if ( codeSample )
  1000. NSEEL_code_free(codeSample);
  1001. if ( codeGfx )
  1002. NSEEL_code_free(codeGfx);
  1003. codeInit = codeSlider = codeBlock = codeSample = codeGfx = codeSerialize = NULL;
  1004. NSEEL_code_compile_ex(m_vm, nullptr, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET);
  1005. numInputs = 0;
  1006. numOutputs = 0;
  1007. for(int i=0;i<kMaxSliders;i++)
  1008. sliders[i].exists = false;
  1009. gfx_w = 0;
  1010. gfx_h = 0;
  1011. NSEEL_VM_remove_unused_vars(m_vm);
  1012. NSEEL_VM_remove_all_nonreg_vars(m_vm);
  1013. }
  1014. static WDL_Mutex initMutex;
  1015. static volatile bool initFlag = false;
  1016. void JsusFx::init() {
  1017. if (initFlag)
  1018. return;
  1019. WDL_MutexLock lock(&initMutex);
  1020. if (initFlag)
  1021. return;
  1022. EEL_string_register();
  1023. EEL_fft_register();
  1024. EEL_mdct_register();
  1025. EEL_string_register();
  1026. EEL_misc_register();
  1027. initFlag = true;
  1028. }
  1029. static int dumpvarsCallback(const char *name, EEL_F *val, void *ctx) {
  1030. JsusFx *fx = (JsusFx *) ctx;
  1031. int target;
  1032. if ( sscanf(name, "slider%d", &target) ) {
  1033. if ( target >= 0 && target < JsusFx::kMaxSliders ) {
  1034. if ( ! fx->sliders[target].exists ) {
  1035. return 1;
  1036. } else {
  1037. fx->displayMsg("%s --> %f (%s)", name, *val, fx->sliders[target].desc);
  1038. return 1;
  1039. }
  1040. }
  1041. }
  1042. fx->displayMsg("%s --> %f", name, *val);
  1043. return 1;
  1044. }
  1045. void JsusFx::dumpvars() {
  1046. NSEEL_VM_enumallvars(m_vm, dumpvarsCallback, this);
  1047. }
  1048. #ifndef JSUSFX_OWNSCRIPTMUTEXT
  1049. // todo : implement mutex interface ?
  1050. void NSEEL_HOSTSTUB_EnterMutex() { }
  1051. void NSEEL_HOSTSTUB_LeaveMutex() { }
  1052. #endif