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.

707 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. static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) {
  144. #ifndef USE_VST2
  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. #else
  221. return false;
  222. #endif // USE_VST2
  223. }
  224. static void loadPlugins(std::string path, bool _bFX) {
  225. #ifdef RACK_HOST
  226. std::string message;
  227. for (std::string pluginPath : systemListEntries(path)) {
  228. if (!systemIsDirectory(pluginPath))
  229. continue;
  230. if (!loadPlugin(pluginPath, _bFX)) {
  231. #ifndef USE_VST2
  232. // (note) skip message (some plugins are linked statically in VST2 build)
  233. message += stringf("Could not load plugin %s\n", pluginPath.c_str());
  234. #endif // USE_VST2
  235. }
  236. }
  237. if (!message.empty()) {
  238. message += "See log for details.";
  239. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  240. }
  241. #else
  242. (void)path;
  243. #endif // RACK_HOST
  244. }
  245. #ifndef USE_VST2
  246. /** Returns 0 if successful */
  247. static int extractZipHandle(zip_t *za, const char *dir) {
  248. int err;
  249. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  250. zip_stat_t zs;
  251. err = zip_stat_index(za, i, 0, &zs);
  252. if (err) {
  253. warn("zip_stat_index() failed: error %d", err);
  254. return err;
  255. }
  256. int nameLen = strlen(zs.name);
  257. char path[MAXPATHLEN];
  258. snprintf(path, sizeof(path), "%s/%s", dir, zs.name);
  259. if (zs.name[nameLen - 1] == '/') {
  260. if (mkdir(path, 0755)) {
  261. if (errno != EEXIST) {
  262. warn("mkdir(%s) failed: error %d", path, errno);
  263. return errno;
  264. }
  265. }
  266. }
  267. else {
  268. zip_file_t *zf = zip_fopen_index(za, i, 0);
  269. if (!zf) {
  270. warn("zip_fopen_index() failed");
  271. return -1;
  272. }
  273. FILE *outFile = fopen(path, "wb");
  274. if (!outFile)
  275. continue;
  276. while (1) {
  277. char buffer[1<<15];
  278. int len = zip_fread(zf, buffer, sizeof(buffer));
  279. if (len <= 0)
  280. break;
  281. fwrite(buffer, 1, len, outFile);
  282. }
  283. err = zip_fclose(zf);
  284. if (err) {
  285. warn("zip_fclose() failed: error %d", err);
  286. return err;
  287. }
  288. fclose(outFile);
  289. }
  290. }
  291. return 0;
  292. }
  293. #endif // USE_VST2
  294. #ifndef USE_VST2
  295. /** Returns 0 if successful */
  296. static int extractZip(const char *filename, const char *path) {
  297. int err;
  298. zip_t *za = zip_open(filename, 0, &err);
  299. if (!za) {
  300. warn("Could not open zip %s: error %d", filename, err);
  301. return err;
  302. }
  303. defer({
  304. zip_close(za);
  305. });
  306. err = extractZipHandle(za, path);
  307. return err;
  308. }
  309. #endif // USE_VST2
  310. #ifndef USE_VST2
  311. static void extractPackages(std::string path) {
  312. std::string message;
  313. for (std::string packagePath : systemListEntries(path)) {
  314. if (stringExtension(packagePath) != "zip")
  315. continue;
  316. info("Extracting package %s", packagePath.c_str());
  317. // Extract package
  318. if (extractZip(packagePath.c_str(), path.c_str())) {
  319. warn("Package %s failed to extract", packagePath.c_str());
  320. message += stringf("Could not extract package %s\n", packagePath.c_str());
  321. continue;
  322. }
  323. // Remove package
  324. if (remove(packagePath.c_str())) {
  325. warn("Could not delete file %s: error %d", packagePath.c_str(), errno);
  326. }
  327. }
  328. if (!message.empty()) {
  329. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  330. }
  331. }
  332. #endif // USE_VST2
  333. ////////////////////
  334. // public API
  335. ////////////////////
  336. void pluginInit(bool devMode, bool _bFX) {
  337. tagsInit();
  338. #ifdef RACK_HOST
  339. // Load core
  340. // This function is defined in core.cpp
  341. Plugin *corePlugin = new Plugin();
  342. init_plugin_Core(corePlugin);
  343. global->plugin.gPlugins.push_back(corePlugin);
  344. // Init statically linked plugins
  345. vst2_load_static_rack_plugins();
  346. // Get local plugins directory
  347. std::string localPlugins = assetLocal("plugins");
  348. mkdir(localPlugins.c_str(), 0755);
  349. #ifndef USE_VST2
  350. if (!devMode) {
  351. // Copy Fundamental package to plugins directory if folder does not exist
  352. std::string fundamentalSrc = assetGlobal("Fundamental.zip");
  353. std::string fundamentalDest = assetLocal("plugins/Fundamental.zip");
  354. std::string fundamentalDir = assetLocal("plugins/Fundamental");
  355. if (systemIsFile(fundamentalSrc) && !systemIsFile(fundamentalDest) && !systemIsDirectory(fundamentalDir)) {
  356. systemCopy(fundamentalSrc, fundamentalDest);
  357. }
  358. }
  359. #endif // USE_VST2
  360. // Extract packages and load plugins
  361. #ifndef USE_VST2
  362. extractPackages(localPlugins);
  363. #endif // USE_VST2
  364. // Load/init dynamically loaded plugins
  365. loadPlugins(localPlugins, _bFX);
  366. #endif // RACK_HOST
  367. }
  368. void pluginDestroy() {
  369. #ifndef USE_VST2
  370. for (Plugin *plugin : global->plugin.gPlugins) {
  371. // Free library handle
  372. #if ARCH_WIN
  373. if (plugin->handle)
  374. FreeLibrary((HINSTANCE)plugin->handle);
  375. #else
  376. if (plugin->handle)
  377. dlclose(plugin->handle);
  378. #endif
  379. // For some reason this segfaults.
  380. // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
  381. // delete plugin;
  382. }
  383. #endif // USE_VST2
  384. global->plugin.gPlugins.clear();
  385. }
  386. bool pluginSync(bool dryRun) {
  387. #ifndef USE_VST2
  388. if (global->plugin.gToken.empty())
  389. return false;
  390. bool available = false;
  391. if (!dryRun) {
  392. global->plugin.isDownloading = true;
  393. global->plugin.downloadProgress = 0.0;
  394. global->plugin.downloadName = "Updating plugins...";
  395. }
  396. defer({
  397. global->plugin.isDownloading = false;
  398. });
  399. // Get user's plugins list
  400. json_t *pluginsReqJ = json_object();
  401. json_object_set(pluginsReqJ, "token", json_string(global->plugin.gToken.c_str()));
  402. json_t *pluginsResJ = requestJson(METHOD_GET, global_ui->app.gApiHost + "/plugins", pluginsReqJ);
  403. json_decref(pluginsReqJ);
  404. if (!pluginsResJ) {
  405. warn("Request for user's plugins failed");
  406. return false;
  407. }
  408. defer({
  409. json_decref(pluginsResJ);
  410. });
  411. json_t *errorJ = json_object_get(pluginsResJ, "error");
  412. if (errorJ) {
  413. warn("Request for user's plugins returned an error: %s", json_string_value(errorJ));
  414. return false;
  415. }
  416. // Get community manifests
  417. json_t *manifestsResJ = requestJson(METHOD_GET, global_ui->app.gApiHost + "/community/manifests", NULL);
  418. if (!manifestsResJ) {
  419. warn("Request for community manifests failed");
  420. return false;
  421. }
  422. defer({
  423. json_decref(manifestsResJ);
  424. });
  425. // Check each plugin in list of plugin slugs
  426. json_t *pluginsJ = json_object_get(pluginsResJ, "plugins");
  427. if (!pluginsJ) {
  428. warn("No plugins array");
  429. return false;
  430. }
  431. json_t *manifestsJ = json_object_get(manifestsResJ, "manifests");
  432. if (!manifestsJ) {
  433. warn("No manifests object");
  434. return false;
  435. }
  436. size_t slugIndex;
  437. json_t *slugJ;
  438. json_array_foreach(pluginsJ, slugIndex, slugJ) {
  439. std::string slug = json_string_value(slugJ);
  440. // Search for slug in manifests
  441. const char *manifestSlug;
  442. json_t *manifestJ = NULL;
  443. json_object_foreach(manifestsJ, manifestSlug, manifestJ) {
  444. if (slug == std::string(manifestSlug))
  445. break;
  446. }
  447. if (!manifestJ)
  448. continue;
  449. if (syncPlugin(slug, manifestJ, dryRun)) {
  450. available = true;
  451. }
  452. }
  453. return available;
  454. #else
  455. return false;
  456. #endif // USE_VST2
  457. }
  458. void pluginLogIn(std::string email, std::string password) {
  459. #ifndef USE_VST2
  460. json_t *reqJ = json_object();
  461. json_object_set(reqJ, "email", json_string(email.c_str()));
  462. json_object_set(reqJ, "password", json_string(password.c_str()));
  463. json_t *resJ = requestJson(METHOD_POST, global_ui->app.gApiHost + "/token", reqJ);
  464. json_decref(reqJ);
  465. if (resJ) {
  466. json_t *errorJ = json_object_get(resJ, "error");
  467. if (errorJ) {
  468. const char *errorStr = json_string_value(errorJ);
  469. global->plugin.loginStatus = errorStr;
  470. }
  471. else {
  472. json_t *tokenJ = json_object_get(resJ, "token");
  473. if (tokenJ) {
  474. const char *tokenStr = json_string_value(tokenJ);
  475. global->plugin.gToken = tokenStr;
  476. global->plugin.loginStatus = "";
  477. }
  478. }
  479. json_decref(resJ);
  480. }
  481. #endif
  482. }
  483. void pluginLogOut() {
  484. global->plugin.gToken = "";
  485. }
  486. void pluginCancelDownload() {
  487. // TODO
  488. }
  489. bool pluginIsLoggedIn() {
  490. return global->plugin.gToken != "";
  491. }
  492. bool pluginIsDownloading() {
  493. return global->plugin.isDownloading;
  494. }
  495. float pluginGetDownloadProgress() {
  496. return global->plugin.downloadProgress;
  497. }
  498. std::string pluginGetDownloadName() {
  499. return global->plugin.downloadName;
  500. }
  501. std::string pluginGetLoginStatus() {
  502. return global->plugin.loginStatus;
  503. }
  504. Plugin *pluginGetPlugin(std::string pluginSlug) {
  505. for (Plugin *plugin : global->plugin.gPlugins) {
  506. // printf("xxx pluginGetPlugin: find pluginSlug=\"%s\" current=\"%s\"\n", pluginSlug.c_str(), plugin->slug.c_str());
  507. if (plugin->slug == pluginSlug) {
  508. return plugin;
  509. }
  510. }
  511. return NULL;
  512. }
  513. Model *pluginGetModel(std::string pluginSlug, std::string modelSlug) {
  514. Plugin *plugin = pluginGetPlugin(pluginSlug);
  515. // printf("xxx vstrack_plugin: plugSlug=\"%s\" modelSlug=\"%s\" => plugin=%p\n", pluginSlug.c_str(), modelSlug.c_str(), plugin);
  516. if (plugin) {
  517. for (Model *model : plugin->models) {
  518. if (model->slug == modelSlug) {
  519. // printf("xxx vstrack_plugin: plugSlug=\"%s\" modelSlug=\"%s\" => plugin=%p model=%p\n", pluginSlug.c_str(), modelSlug.c_str(), plugin, model);
  520. return model;
  521. }
  522. }
  523. }
  524. return NULL;
  525. }
  526. } // namespace rack
  527. using namespace rack;
  528. #ifdef USE_VST2
  529. #ifdef ARCH_WIN
  530. extern "C" { extern long seed_initialized; }
  531. #else
  532. extern "C" { extern volatile char seed_initialized; }
  533. #endif // ARCH_WIN
  534. extern "C" { extern volatile uint32_t hashtable_seed; }
  535. void vst2_set_shared_plugin_tls_globals(void) {
  536. // Called in audio thread (see vst2_main.cpp:VSTPluginProcessReplacingFloat32())
  537. for(Plugin *p : global->plugin.gPlugins) {
  538. if(NULL != p->set_tls_globals_fxn) {
  539. if(0)
  540. {
  541. static int xxx = 0;
  542. if(0 == (++xxx & 255))
  543. printf("xxx vstrack_plugin: calling p->set_tls_globals_fxn() global=%p p->global=%p\n", global, p->global);
  544. }
  545. p->json.hashtable_seed = hashtable_seed;
  546. p->json.seed_initialized = seed_initialized;
  547. p->set_tls_globals_fxn(p);
  548. // printf("xxx vstrack_plugin: calling p->set_tls_globals_fxn() OK\n");
  549. }
  550. }
  551. }
  552. #endif // USE_VST2
  553. RackScene *rack_plugin_ui_get_rackscene(void) {
  554. #ifdef USE_VST2
  555. return rack::global_ui->app.gRackScene;
  556. #else
  557. return gRackScene;
  558. #endif // USE_VST2
  559. }
  560. RackWidget *rack_plugin_ui_get_rackwidget(void) {
  561. #ifdef USE_VST2
  562. return rack::global_ui->app.gRackWidget;
  563. #else
  564. return gRackWidget;
  565. #endif // USE_VST2
  566. }
  567. Toolbar *rack_plugin_ui_get_toolbar(void) {
  568. #ifdef USE_VST2
  569. return rack::global_ui->app.gToolbar;
  570. #else
  571. return gToolbar;
  572. #endif // USE_VST2
  573. }
  574. Widget *rack_plugin_ui_get_hovered_widget(void) {
  575. #ifdef USE_VST2
  576. return rack::global_ui->widgets.gHoveredWidget;
  577. #else
  578. return gHoveredWidget;
  579. #endif // USE_VST2
  580. }
  581. Widget *rack_plugin_ui_get_dragged_widget(void) {
  582. #ifdef USE_VST2
  583. return rack::global_ui->widgets.gDraggedWidget;
  584. #else
  585. return gDraggedWidget;
  586. #endif // USE_VST2
  587. }
  588. void rack_plugin_ui_set_dragged_widget(Widget *w) {
  589. #ifdef USE_VST2
  590. rack::global_ui->widgets.gDraggedWidget = w;
  591. #else
  592. gDraggedWidget = w;
  593. #endif // USE_VST2
  594. }
  595. Widget *rack_plugin_ui_get_draghovered_widget(void) {
  596. #ifdef USE_VST2
  597. return rack::global_ui->widgets.gDragHoveredWidget;
  598. #else
  599. return gDragHovered;
  600. #endif // USE_VST2
  601. }
  602. Widget *rack_plugin_ui_get_focused_widget(void) {
  603. #ifdef USE_VST2
  604. return rack::global_ui->widgets.gFocusedWidget;
  605. #else
  606. return gFocusedWidget;
  607. #endif // USE_VST2
  608. }
  609. void rack_plugin_ui_set_focused_widget(Widget *w) {
  610. #ifdef USE_VST2
  611. rack::global_ui->widgets.gFocusedWidget = w;
  612. #else
  613. gFocusedWidget = w;
  614. #endif // USE_VST2
  615. }