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.

716 lines
17KB

  1. #include "global_pre.hpp"
  2. #include "plugin.hpp"
  3. #include "Core/Core.hpp"
  4. #include "app.hpp"
  5. #include "asset.hpp"
  6. #include "util/request.hpp"
  7. #include "osdialog.h"
  8. #include <stdio.h>
  9. #include <assert.h>
  10. #include <string.h>
  11. #ifdef YAC_POSIX
  12. #include <unistd.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <sys/param.h> // for MAXPATHLEN
  16. #include <fcntl.h>
  17. #include <dirent.h>
  18. #else
  19. #include "util/dirent_win32/dirent.h"
  20. #endif // YAC_POSIX
  21. #include <thread>
  22. #include <stdexcept>
  23. #include "global.hpp"
  24. #include "global_ui.hpp"
  25. #ifndef USE_VST2
  26. #define ZIP_STATIC
  27. #include <zip.h>
  28. #endif // USE_VST2
  29. #include <jansson.h>
  30. #if ARCH_WIN
  31. #include <windows.h>
  32. #include <direct.h>
  33. #define mkdir(_dir, _perms) _mkdir(_dir)
  34. #else
  35. #include <dlfcn.h>
  36. #endif
  37. extern "C" { extern void init_plugin_Core (rack::Plugin *p); }
  38. namespace rack {
  39. #ifdef USE_VST2
  40. #ifndef RACK_PLUGIN
  41. extern void vst2_load_static_rack_plugins(void);
  42. #endif // RACK_PLUGIN
  43. #endif // USE_VST2
  44. Plugin::~Plugin() {
  45. for (Model *model : models) {
  46. delete model;
  47. }
  48. }
  49. void Plugin::addModel(Model *model) {
  50. assert(!model->plugin);
  51. model->plugin = this;
  52. models.push_back(model);
  53. }
  54. ////////////////////
  55. // private API
  56. ////////////////////
  57. static bool loadPlugin(std::string path, bool _bFX) {
  58. #ifdef RACK_HOST
  59. std::string libraryFilename;
  60. #if ARCH_LIN
  61. libraryFilename = path + "/" + "plugin.so" + (_bFX ? ".fx" : ".instr");
  62. #elif ARCH_WIN
  63. libraryFilename = path + "/" + "plugin.dll" + (_bFX ? ".fx" : ".instr");
  64. #elif ARCH_MAC
  65. libraryFilename = path + "/" + "plugin.dylib" + (_bFX ? ".fx" : ".instr");
  66. #endif
  67. // Check file existence
  68. if (!systemIsFile(libraryFilename)) {
  69. // warn("Plugin file %s does not exist", libraryFilename.c_str());
  70. return false;
  71. }
  72. else {
  73. info("trying to load shared plugin file %s", libraryFilename.c_str());
  74. }
  75. // Load dynamic/shared library
  76. #if ARCH_WIN
  77. SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  78. // #ifdef USE_VST2
  79. // std::wstring libNameW = std::wstring(libraryFilename.begin(), libraryFilename.end());
  80. // HINSTANCE handle = LoadLibraryW(libNameW.c_str());
  81. // #else
  82. HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
  83. // #endif
  84. SetErrorMode(0);
  85. if (!handle) {
  86. int error = GetLastError();
  87. warn("Failed to load library %s: code %d", libraryFilename.c_str(), error);
  88. return false;
  89. }
  90. #else
  91. void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
  92. if (!handle) {
  93. warn("Failed to load library %s: %s", libraryFilename.c_str(), dlerror());
  94. return false;
  95. }
  96. #endif
  97. // Call plugin's init() function
  98. InitCallback initCallback;
  99. #ifdef USE_VST2
  100. #define init_symbol_name "init_plugin"
  101. #else
  102. #define init_symbol_name "init"
  103. #endif // USE_VST2
  104. #if ARCH_WIN
  105. initCallback = (InitCallback) GetProcAddress(handle, init_symbol_name);
  106. #else
  107. initCallback = (InitCallback) dlsym(handle, init_symbol_name);
  108. #endif
  109. if (!initCallback) {
  110. warn("Failed to read init() symbol in %s", libraryFilename.c_str());
  111. return false;
  112. }
  113. // Construct and initialize Plugin instance
  114. Plugin *plugin = new Plugin();
  115. plugin->path = path;
  116. plugin->handle = handle;
  117. #ifdef USE_VST2
  118. #ifdef RACK_HOST
  119. plugin->vst2_handle_ui_param_fxn = &vst2_handle_ui_param;
  120. plugin->vst2_queue_param_sync_fxn = &vst2_queue_param_sync;
  121. plugin->global = global;
  122. plugin->global_ui = global_ui;
  123. printf("xxx vstrack_plugin: loadPlugin: global=%p plugin->global=%p\n", global, plugin->global);
  124. #endif // RACK_HOST
  125. #endif // USE_VST2
  126. initCallback(plugin);
  127. // Reject plugin if slug already exists
  128. Plugin *oldPlugin = pluginGetPlugin(plugin->slug);
  129. if (oldPlugin) {
  130. warn("Plugin \"%s\" is already loaded, not attempting to load it again", plugin->slug.c_str());
  131. // TODO
  132. // Fix memory leak with `plugin` here
  133. return false;
  134. }
  135. // Add plugin to list
  136. global->plugin.gPlugins.push_back(plugin);
  137. info("Loaded plugin %s %s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str());
  138. #else
  139. (void)path;
  140. #endif // RACK_HOST
  141. return true;
  142. }
  143. #ifndef USE_VST2
  144. static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) {
  145. // Check that "status" is "available"
  146. json_t *statusJ = json_object_get(manifestJ, "status");
  147. if (!statusJ) {
  148. return false;
  149. }
  150. std::string status = json_string_value(statusJ);
  151. if (status != "available") {
  152. return false;
  153. }
  154. // Get latest version
  155. json_t *latestVersionJ = json_object_get(manifestJ, "latestVersion");
  156. if (!latestVersionJ) {
  157. warn("Could not get latest version of plugin %s", slug.c_str());
  158. return false;
  159. }
  160. std::string latestVersion = json_string_value(latestVersionJ);
  161. // Check whether we already have a plugin with the same slug and version
  162. Plugin *plugin = pluginGetPlugin(slug);
  163. if (plugin && plugin->version == latestVersion) {
  164. return false;
  165. }
  166. json_t *nameJ = json_object_get(manifestJ, "name");
  167. std::string name;
  168. if (nameJ) {
  169. name = json_string_value(nameJ);
  170. }
  171. else {
  172. name = slug;
  173. }
  174. #if ARCH_WIN
  175. std::string arch = "win";
  176. #elif ARCH_MAC
  177. std::string arch = "mac";
  178. #elif ARCH_LIN
  179. std::string arch = "lin";
  180. #endif
  181. std::string downloadUrl;
  182. downloadUrl = global_ui->app.gApiHost;
  183. downloadUrl += "/download";
  184. if (dryRun) {
  185. downloadUrl += "/available";
  186. }
  187. downloadUrl += "?token=" + requestEscape(global->plugin.gToken);
  188. downloadUrl += "&slug=" + requestEscape(slug);
  189. downloadUrl += "&version=" + requestEscape(latestVersion);
  190. downloadUrl += "&arch=" + requestEscape(arch);
  191. #ifdef USE_VST2
  192. downloadUrl += "&format=vst2";
  193. #endif // USE_VST2
  194. if (dryRun) {
  195. // Check if available
  196. json_t *availableResJ = requestJson(METHOD_GET, downloadUrl, NULL);
  197. if (!availableResJ) {
  198. warn("Could not check whether download is available");
  199. return false;
  200. }
  201. defer({
  202. json_decref(availableResJ);
  203. });
  204. json_t *successJ = json_object_get(availableResJ, "success");
  205. return json_boolean_value(successJ);
  206. }
  207. else {
  208. global->plugin.downloadName = name;
  209. global->plugin.downloadProgress = 0.0;
  210. info("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str());
  211. // Download zip
  212. std::string pluginDest = assetLocal("plugins/" + slug + ".zip");
  213. if (!requestDownload(downloadUrl, pluginDest, &global->plugin.downloadProgress)) {
  214. warn("Plugin %s download was unsuccessful", slug.c_str());
  215. return false;
  216. }
  217. global->plugin.downloadName = "";
  218. return true;
  219. }
  220. }
  221. #endif // USE_VST2
  222. static void loadPlugins(std::string path, bool _bFX) {
  223. #ifdef RACK_HOST
  224. std::string message;
  225. for (std::string pluginPath : systemListEntries(path)) {
  226. if (!systemIsDirectory(pluginPath))
  227. continue;
  228. if (!loadPlugin(pluginPath, _bFX)) {
  229. #ifndef USE_VST2
  230. // (note) skip message (some plugins are linked statically in VST2 build)
  231. message += stringf("Could not load plugin %s\n", pluginPath.c_str());
  232. #endif // USE_VST2
  233. }
  234. }
  235. if (!message.empty()) {
  236. message += "See log for details.";
  237. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  238. }
  239. #else
  240. (void)path;
  241. #endif // RACK_HOST
  242. }
  243. #ifndef USE_VST2
  244. /** Returns 0 if successful */
  245. static int extractZipHandle(zip_t *za, const char *dir) {
  246. int err;
  247. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  248. zip_stat_t zs;
  249. err = zip_stat_index(za, i, 0, &zs);
  250. if (err) {
  251. warn("zip_stat_index() failed: error %d", err);
  252. return err;
  253. }
  254. int nameLen = strlen(zs.name);
  255. char path[MAXPATHLEN];
  256. snprintf(path, sizeof(path), "%s/%s", dir, zs.name);
  257. if (zs.name[nameLen - 1] == '/') {
  258. if (mkdir(path, 0755)) {
  259. if (errno != EEXIST) {
  260. warn("mkdir(%s) failed: error %d", path, errno);
  261. return errno;
  262. }
  263. }
  264. }
  265. else {
  266. zip_file_t *zf = zip_fopen_index(za, i, 0);
  267. if (!zf) {
  268. warn("zip_fopen_index() failed");
  269. return -1;
  270. }
  271. FILE *outFile = fopen(path, "wb");
  272. if (!outFile)
  273. continue;
  274. while (1) {
  275. char buffer[1<<15];
  276. int len = zip_fread(zf, buffer, sizeof(buffer));
  277. if (len <= 0)
  278. break;
  279. fwrite(buffer, 1, len, outFile);
  280. }
  281. err = zip_fclose(zf);
  282. if (err) {
  283. warn("zip_fclose() failed: error %d", err);
  284. return err;
  285. }
  286. fclose(outFile);
  287. }
  288. }
  289. return 0;
  290. }
  291. #endif // USE_VST2
  292. #ifndef USE_VST2
  293. /** Returns 0 if successful */
  294. static int extractZip(const char *filename, const char *path) {
  295. int err;
  296. zip_t *za = zip_open(filename, 0, &err);
  297. if (!za) {
  298. warn("Could not open zip %s: error %d", filename, err);
  299. return err;
  300. }
  301. defer({
  302. zip_close(za);
  303. });
  304. err = extractZipHandle(za, path);
  305. return err;
  306. }
  307. #endif // USE_VST2
  308. #ifndef USE_VST2
  309. static void extractPackages(std::string path) {
  310. std::string message;
  311. for (std::string packagePath : systemListEntries(path)) {
  312. if (stringExtension(packagePath) != "zip")
  313. continue;
  314. info("Extracting package %s", packagePath.c_str());
  315. // Extract package
  316. if (extractZip(packagePath.c_str(), path.c_str())) {
  317. warn("Package %s failed to extract", packagePath.c_str());
  318. message += stringf("Could not extract package %s\n", packagePath.c_str());
  319. continue;
  320. }
  321. // Remove package
  322. if (remove(packagePath.c_str())) {
  323. warn("Could not delete file %s: error %d", packagePath.c_str(), errno);
  324. }
  325. }
  326. if (!message.empty()) {
  327. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  328. }
  329. }
  330. #endif // USE_VST2
  331. ////////////////////
  332. // public API
  333. ////////////////////
  334. void pluginInit(bool devMode, bool _bFX) {
  335. tagsInit();
  336. #ifdef RACK_HOST
  337. // Load core
  338. // This function is defined in core.cpp
  339. Plugin *corePlugin = new Plugin();
  340. init_plugin_Core(corePlugin);
  341. global->plugin.gPlugins.push_back(corePlugin);
  342. // Init statically linked plugins
  343. vst2_load_static_rack_plugins();
  344. // Get local plugins directory
  345. std::string localPlugins = assetLocal("plugins");
  346. mkdir(localPlugins.c_str(), 0755);
  347. #ifndef USE_VST2
  348. if (!devMode) {
  349. // Copy Fundamental package to plugins directory if folder does not exist
  350. std::string fundamentalSrc = assetGlobal("Fundamental.zip");
  351. std::string fundamentalDest = assetLocal("plugins/Fundamental.zip");
  352. std::string fundamentalDir = assetLocal("plugins/Fundamental");
  353. if (systemIsFile(fundamentalSrc) && !systemIsFile(fundamentalDest) && !systemIsDirectory(fundamentalDir)) {
  354. systemCopy(fundamentalSrc, fundamentalDest);
  355. }
  356. }
  357. #endif // USE_VST2
  358. // Extract packages and load plugins
  359. #ifndef USE_VST2
  360. extractPackages(localPlugins);
  361. #endif // USE_VST2
  362. // Load/init dynamically loaded plugins
  363. loadPlugins(localPlugins, _bFX);
  364. #endif // RACK_HOST
  365. }
  366. void pluginDestroy() {
  367. #ifndef USE_VST2
  368. for (Plugin *plugin : global->plugin.gPlugins) {
  369. // Free library handle
  370. #if ARCH_WIN
  371. if (plugin->handle)
  372. FreeLibrary((HINSTANCE)plugin->handle);
  373. #else
  374. if (plugin->handle)
  375. dlclose(plugin->handle);
  376. #endif
  377. // For some reason this segfaults.
  378. // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
  379. // delete plugin;
  380. }
  381. #endif // USE_VST2
  382. global->plugin.gPlugins.clear();
  383. }
  384. bool pluginSync(bool dryRun) {
  385. #ifndef USE_VST2
  386. if (global->plugin.gToken.empty())
  387. return false;
  388. bool available = false;
  389. if (!dryRun) {
  390. global->plugin.isDownloading = true;
  391. global->plugin.downloadProgress = 0.0;
  392. global->plugin.downloadName = "Updating plugins...";
  393. }
  394. defer({
  395. global->plugin.isDownloading = false;
  396. });
  397. // Get user's plugins list
  398. json_t *pluginsReqJ = json_object();
  399. json_object_set(pluginsReqJ, "token", json_string(global->plugin.gToken.c_str()));
  400. json_t *pluginsResJ = requestJson(METHOD_GET, global_ui->app.gApiHost + "/plugins", pluginsReqJ);
  401. json_decref(pluginsReqJ);
  402. if (!pluginsResJ) {
  403. warn("Request for user's plugins failed");
  404. return false;
  405. }
  406. defer({
  407. json_decref(pluginsResJ);
  408. });
  409. json_t *errorJ = json_object_get(pluginsResJ, "error");
  410. if (errorJ) {
  411. warn("Request for user's plugins returned an error: %s", json_string_value(errorJ));
  412. return false;
  413. }
  414. // Get community manifests
  415. json_t *manifestsResJ = requestJson(METHOD_GET, global_ui->app.gApiHost + "/community/manifests", NULL);
  416. if (!manifestsResJ) {
  417. warn("Request for community manifests failed");
  418. return false;
  419. }
  420. defer({
  421. json_decref(manifestsResJ);
  422. });
  423. // Check each plugin in list of plugin slugs
  424. json_t *pluginsJ = json_object_get(pluginsResJ, "plugins");
  425. if (!pluginsJ) {
  426. warn("No plugins array");
  427. return false;
  428. }
  429. json_t *manifestsJ = json_object_get(manifestsResJ, "manifests");
  430. if (!manifestsJ) {
  431. warn("No manifests object");
  432. return false;
  433. }
  434. size_t slugIndex;
  435. json_t *slugJ;
  436. json_array_foreach(pluginsJ, slugIndex, slugJ) {
  437. std::string slug = json_string_value(slugJ);
  438. // Search for slug in manifests
  439. const char *manifestSlug;
  440. json_t *manifestJ = NULL;
  441. json_object_foreach(manifestsJ, manifestSlug, manifestJ) {
  442. if (slug == std::string(manifestSlug))
  443. break;
  444. }
  445. if (!manifestJ)
  446. continue;
  447. if (syncPlugin(slug, manifestJ, dryRun)) {
  448. available = true;
  449. }
  450. }
  451. return available;
  452. #else
  453. return false;
  454. #endif // USE_VST2
  455. }
  456. void pluginLogIn(std::string email, std::string password) {
  457. #ifndef USE_VST2
  458. json_t *reqJ = json_object();
  459. json_object_set(reqJ, "email", json_string(email.c_str()));
  460. json_object_set(reqJ, "password", json_string(password.c_str()));
  461. json_t *resJ = requestJson(METHOD_POST, global_ui->app.gApiHost + "/token", reqJ);
  462. json_decref(reqJ);
  463. if (resJ) {
  464. json_t *errorJ = json_object_get(resJ, "error");
  465. if (errorJ) {
  466. const char *errorStr = json_string_value(errorJ);
  467. global->plugin.loginStatus = errorStr;
  468. }
  469. else {
  470. json_t *tokenJ = json_object_get(resJ, "token");
  471. if (tokenJ) {
  472. const char *tokenStr = json_string_value(tokenJ);
  473. global->plugin.gToken = tokenStr;
  474. global->plugin.loginStatus = "";
  475. }
  476. }
  477. json_decref(resJ);
  478. }
  479. #endif
  480. }
  481. void pluginLogOut() {
  482. global->plugin.gToken = "";
  483. }
  484. void pluginCancelDownload() {
  485. // TODO
  486. }
  487. bool pluginIsLoggedIn() {
  488. return global->plugin.gToken != "";
  489. }
  490. bool pluginIsDownloading() {
  491. return global->plugin.isDownloading;
  492. }
  493. float pluginGetDownloadProgress() {
  494. return global->plugin.downloadProgress;
  495. }
  496. std::string pluginGetDownloadName() {
  497. return global->plugin.downloadName;
  498. }
  499. std::string pluginGetLoginStatus() {
  500. return global->plugin.loginStatus;
  501. }
  502. Plugin *pluginGetPlugin(std::string pluginSlug) {
  503. for (Plugin *plugin : global->plugin.gPlugins) {
  504. // printf("xxx pluginGetPlugin: find pluginSlug=\"%s\" current=\"%s\"\n", pluginSlug.c_str(), plugin->slug.c_str());
  505. if (plugin->slug == pluginSlug) {
  506. return plugin;
  507. }
  508. }
  509. return NULL;
  510. }
  511. Model *pluginGetModel(std::string pluginSlug, std::string modelSlug) {
  512. Plugin *plugin = pluginGetPlugin(pluginSlug);
  513. // printf("xxx vstrack_plugin: plugSlug=\"%s\" modelSlug=\"%s\" => plugin=%p\n", pluginSlug.c_str(), modelSlug.c_str(), plugin);
  514. if (plugin) {
  515. for (Model *model : plugin->models) {
  516. if (model->slug == modelSlug) {
  517. // printf("xxx vstrack_plugin: plugSlug=\"%s\" modelSlug=\"%s\" => plugin=%p model=%p\n", pluginSlug.c_str(), modelSlug.c_str(), plugin, model);
  518. return model;
  519. }
  520. }
  521. }
  522. return NULL;
  523. }
  524. } // namespace rack
  525. using namespace rack;
  526. #ifdef USE_VST2
  527. #ifdef ARCH_WIN
  528. extern "C" { extern long seed_initialized; }
  529. #else
  530. extern "C" { extern volatile char seed_initialized; }
  531. #endif // ARCH_WIN
  532. extern "C" { extern volatile uint32_t hashtable_seed; }
  533. void vst2_set_shared_plugin_tls_globals(void) {
  534. // Called in audio thread (see vst2_main.cpp:VSTPluginProcessReplacingFloat32())
  535. for(Plugin *p : global->plugin.gPlugins) {
  536. if(NULL != p->set_tls_globals_fxn) {
  537. if(0)
  538. {
  539. static int xxx = 0;
  540. if(0 == (++xxx & 255))
  541. printf("xxx vstrack_plugin: calling p->set_tls_globals_fxn() global=%p p->global=%p\n", global, p->global);
  542. }
  543. p->json.hashtable_seed = hashtable_seed;
  544. p->json.seed_initialized = seed_initialized;
  545. p->set_tls_globals_fxn(p);
  546. // printf("xxx vstrack_plugin: calling p->set_tls_globals_fxn() OK\n");
  547. }
  548. }
  549. }
  550. #ifdef USE_BEGIN_REDRAW_FXN
  551. void vst2_begin_shared_plugin_redraw(void) {
  552. // Called in UI thread
  553. for(Plugin *p : global->plugin.gPlugins) {
  554. if(NULL != p->begin_redraw_fxn) {
  555. // printf("xxx call begin_redraw_fxn()\n");
  556. p->begin_redraw_fxn();
  557. }
  558. }
  559. }
  560. #endif // USE_BEGIN_REDRAW_FXN
  561. #endif // USE_VST2
  562. RackScene *rack_plugin_ui_get_rackscene(void) {
  563. #ifdef USE_VST2
  564. return rack::global_ui->app.gRackScene;
  565. #else
  566. return gRackScene;
  567. #endif // USE_VST2
  568. }
  569. RackWidget *rack_plugin_ui_get_rackwidget(void) {
  570. #ifdef USE_VST2
  571. return rack::global_ui->app.gRackWidget;
  572. #else
  573. return gRackWidget;
  574. #endif // USE_VST2
  575. }
  576. Toolbar *rack_plugin_ui_get_toolbar(void) {
  577. #ifdef USE_VST2
  578. return rack::global_ui->app.gToolbar;
  579. #else
  580. return gToolbar;
  581. #endif // USE_VST2
  582. }
  583. Widget *rack_plugin_ui_get_hovered_widget(void) {
  584. #ifdef USE_VST2
  585. return rack::global_ui->widgets.gHoveredWidget;
  586. #else
  587. return gHoveredWidget;
  588. #endif // USE_VST2
  589. }
  590. Widget *rack_plugin_ui_get_dragged_widget(void) {
  591. #ifdef USE_VST2
  592. return rack::global_ui->widgets.gDraggedWidget;
  593. #else
  594. return gDraggedWidget;
  595. #endif // USE_VST2
  596. }
  597. void rack_plugin_ui_set_dragged_widget(Widget *w) {
  598. #ifdef USE_VST2
  599. rack::global_ui->widgets.gDraggedWidget = w;
  600. #else
  601. gDraggedWidget = w;
  602. #endif // USE_VST2
  603. }
  604. Widget *rack_plugin_ui_get_draghovered_widget(void) {
  605. #ifdef USE_VST2
  606. return rack::global_ui->widgets.gDragHoveredWidget;
  607. #else
  608. return gDragHovered;
  609. #endif // USE_VST2
  610. }
  611. Widget *rack_plugin_ui_get_focused_widget(void) {
  612. #ifdef USE_VST2
  613. return rack::global_ui->widgets.gFocusedWidget;
  614. #else
  615. return gFocusedWidget;
  616. #endif // USE_VST2
  617. }
  618. void rack_plugin_ui_set_focused_widget(Widget *w) {
  619. #ifdef USE_VST2
  620. rack::global_ui->widgets.gFocusedWidget = w;
  621. #else
  622. gFocusedWidget = w;
  623. #endif // USE_VST2
  624. }