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.

896 lines
28KB

  1. /*
  2. * Carla Plugin Host
  3. * Copyright (C) 2011-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaUtils.h"
  18. #include "CarlaBackendUtils.hpp"
  19. #include "CarlaBinaryUtils.hpp"
  20. #include "CarlaJuceUtils.hpp"
  21. #include "CarlaPipeUtils.hpp"
  22. #include "CarlaSha1Utils.hpp"
  23. #include "CarlaTimeUtils.hpp"
  24. #include "water/files/File.h"
  25. #include "water/files/FileInputStream.h"
  26. #include "water/threads/ChildProcess.h"
  27. #include "water/text/StringArray.h"
  28. namespace CB = CARLA_BACKEND_NAMESPACE;
  29. // --------------------------------------------------------------------------------------------------------------------
  30. #ifndef CARLA_OS_WIN
  31. static water::String findWinePrefix(const water::String filename, const int recursionLimit = 10)
  32. {
  33. if (recursionLimit == 0 || filename.length() < 5 || ! filename.contains("/"))
  34. return "";
  35. const water::String path(filename.upToLastOccurrenceOf("/", false, false));
  36. if (water::File(path + "/dosdevices").isDirectory())
  37. return path;
  38. return findWinePrefix(path, recursionLimit-1);
  39. }
  40. #endif
  41. // --------------------------------------------------------------------------------------------------------------------
  42. static const char* const gPluginsDiscoveryNullCharPtr = "";
  43. _CarlaPluginDiscoveryMetadata::_CarlaPluginDiscoveryMetadata() noexcept
  44. : name(gPluginsDiscoveryNullCharPtr),
  45. maker(gPluginsDiscoveryNullCharPtr),
  46. category(CB::PLUGIN_CATEGORY_NONE),
  47. hints(0x0) {}
  48. _CarlaPluginDiscoveryIO::_CarlaPluginDiscoveryIO() noexcept
  49. : audioIns(0),
  50. audioOuts(0),
  51. cvIns(0),
  52. cvOuts(0),
  53. midiIns(0),
  54. midiOuts(0),
  55. parameterIns(0),
  56. parameterOuts(0) {}
  57. _CarlaPluginDiscoveryInfo::_CarlaPluginDiscoveryInfo() noexcept
  58. : btype(CB::BINARY_NONE),
  59. ptype(CB::PLUGIN_NONE),
  60. filename(gPluginsDiscoveryNullCharPtr),
  61. label(gPluginsDiscoveryNullCharPtr),
  62. uniqueId(0),
  63. metadata() {}
  64. // --------------------------------------------------------------------------------------------------------------------
  65. struct CarlaPluginDiscoveryOptions {
  66. #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
  67. struct {
  68. bool autoPrefix;
  69. CarlaString executable;
  70. CarlaString fallbackPrefix;
  71. } wine;
  72. #endif
  73. static CarlaPluginDiscoveryOptions& getInstance() noexcept
  74. {
  75. static CarlaPluginDiscoveryOptions instance;
  76. return instance;
  77. }
  78. };
  79. // --------------------------------------------------------------------------------------------------------------------
  80. class CarlaPluginDiscovery : private CarlaPipeServer
  81. {
  82. public:
  83. CarlaPluginDiscovery(const char* const discoveryTool,
  84. const BinaryType btype,
  85. const PluginType ptype,
  86. const std::vector<water::File>&& binaries,
  87. const CarlaPluginDiscoveryCallback discoveryCb,
  88. const CarlaPluginCheckCacheCallback checkCacheCb,
  89. void* const callbackPtr)
  90. : fBinaryType(btype),
  91. fPluginType(ptype),
  92. fDiscoveryCallback(discoveryCb),
  93. fCheckCacheCallback(checkCacheCb),
  94. fCallbackPtr(callbackPtr),
  95. fPluginPath(nullptr),
  96. fPluginsFoundInBinary(false),
  97. fBinaryIndex(0),
  98. fBinaryCount(static_cast<uint>(binaries.size())),
  99. fBinaries(binaries),
  100. fDiscoveryTool(discoveryTool),
  101. fLastMessageTime(0),
  102. fNextLabel(nullptr),
  103. fNextMaker(nullptr),
  104. fNextName(nullptr)
  105. {
  106. start();
  107. }
  108. CarlaPluginDiscovery(const char* const discoveryTool,
  109. const BinaryType btype,
  110. const PluginType ptype,
  111. const CarlaPluginDiscoveryCallback discoveryCb,
  112. const CarlaPluginCheckCacheCallback checkCacheCb,
  113. void* const callbackPtr,
  114. const char* const pluginPath = nullptr)
  115. : fBinaryType(btype),
  116. fPluginType(ptype),
  117. fDiscoveryCallback(discoveryCb),
  118. fCheckCacheCallback(checkCacheCb),
  119. fCallbackPtr(callbackPtr),
  120. fPluginPath(pluginPath != nullptr ? carla_strdup_safe(pluginPath) : nullptr),
  121. fPluginsFoundInBinary(false),
  122. fBinaryIndex(0),
  123. fBinaryCount(1),
  124. fDiscoveryTool(discoveryTool),
  125. fLastMessageTime(0),
  126. fNextLabel(nullptr),
  127. fNextMaker(nullptr),
  128. fNextName(nullptr)
  129. {
  130. start();
  131. }
  132. ~CarlaPluginDiscovery()
  133. {
  134. stopPipeServer(5000);
  135. std::free(fNextLabel);
  136. std::free(fNextMaker);
  137. std::free(fNextName);
  138. delete[] fPluginPath;
  139. }
  140. bool idle()
  141. {
  142. if (isPipeRunning())
  143. {
  144. idlePipe();
  145. // automatically skip a plugin if 30s passes without a reply
  146. const uint32_t timeNow = carla_gettime_ms();
  147. if (timeNow - fLastMessageTime < 30000)
  148. return true;
  149. carla_stdout("Plugin took too long to respond, skipping...");
  150. stopPipeServer(1000);
  151. }
  152. // report binary as having no plugins
  153. if (fCheckCacheCallback != nullptr && !fPluginsFoundInBinary && !fBinaries.empty())
  154. {
  155. const water::File file(fBinaries[fBinaryIndex]);
  156. const water::String filename(file.getFullPathName());
  157. makeHash(file, filename);
  158. if (! fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
  159. fDiscoveryCallback(fCallbackPtr, nullptr, fNextSha1Sum);
  160. }
  161. if (++fBinaryIndex == fBinaryCount)
  162. return false;
  163. start();
  164. return true;
  165. }
  166. void skip()
  167. {
  168. if (isPipeRunning())
  169. stopPipeServer(1000);
  170. }
  171. protected:
  172. bool msgReceived(const char* const msg) noexcept
  173. {
  174. fLastMessageTime = carla_gettime_ms();
  175. if (std::strcmp(msg, "warning") == 0 || std::strcmp(msg, "error") == 0)
  176. {
  177. const char* text = nullptr;
  178. readNextLineAsString(text, false);
  179. carla_stdout("discovery: %s", text);
  180. return true;
  181. }
  182. if (std::strcmp(msg, "init") == 0)
  183. {
  184. const char* _;
  185. readNextLineAsString(_, false);
  186. new (&fNextInfo) _CarlaPluginDiscoveryInfo();
  187. return true;
  188. }
  189. if (std::strcmp(msg, "end") == 0)
  190. {
  191. const char* _;
  192. readNextLineAsString(_, false);
  193. if (fNextInfo.label == nullptr)
  194. fNextInfo.label = gPluginsDiscoveryNullCharPtr;
  195. if (fNextInfo.metadata.maker == nullptr)
  196. fNextInfo.metadata.maker = gPluginsDiscoveryNullCharPtr;
  197. if (fNextInfo.metadata.name == nullptr)
  198. fNextInfo.metadata.name = gPluginsDiscoveryNullCharPtr;
  199. if (fBinaries.empty())
  200. {
  201. char* filename = nullptr;
  202. if (fPluginType == CB::PLUGIN_LV2)
  203. {
  204. do {
  205. const char* const slash = std::strchr(fNextLabel, CARLA_OS_SEP);
  206. CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
  207. filename = strdup(fNextLabel);
  208. filename[slash - fNextLabel] = '\0';
  209. fNextInfo.filename = filename;
  210. fNextInfo.label = slash + 1;
  211. } while (false);
  212. }
  213. fNextInfo.ptype = fPluginType;
  214. fDiscoveryCallback(fCallbackPtr, &fNextInfo, nullptr);
  215. std::free(filename);
  216. }
  217. else
  218. {
  219. CARLA_SAFE_ASSERT(fNextSha1Sum.isNotEmpty());
  220. const water::String filename(fBinaries[fBinaryIndex].getFullPathName());
  221. fNextInfo.filename = filename.toRawUTF8();
  222. fNextInfo.ptype = fPluginType;
  223. fPluginsFoundInBinary = true;
  224. carla_stdout("Found %s from %s", fNextInfo.metadata.name, fNextInfo.filename);
  225. fDiscoveryCallback(fCallbackPtr, &fNextInfo, fNextSha1Sum);
  226. }
  227. std::free(fNextLabel);
  228. fNextLabel = nullptr;
  229. std::free(fNextMaker);
  230. fNextMaker = nullptr;
  231. std::free(fNextName);
  232. fNextName = nullptr;
  233. return true;
  234. }
  235. if (std::strcmp(msg, "build") == 0)
  236. {
  237. uint8_t btype = 0;
  238. readNextLineAsByte(btype);
  239. fNextInfo.btype = static_cast<BinaryType>(btype);
  240. return true;
  241. }
  242. if (std::strcmp(msg, "hints") == 0)
  243. {
  244. readNextLineAsUInt(fNextInfo.metadata.hints);
  245. return true;
  246. }
  247. if (std::strcmp(msg, "category") == 0)
  248. {
  249. const char* category = nullptr;
  250. readNextLineAsString(category, false);
  251. fNextInfo.metadata.category = CB::getPluginCategoryFromString(category);
  252. return true;
  253. }
  254. if (std::strcmp(msg, "name") == 0)
  255. {
  256. fNextInfo.metadata.name = fNextName = readNextLineAsString();
  257. return true;
  258. }
  259. if (std::strcmp(msg, "label") == 0)
  260. {
  261. fNextInfo.label = fNextLabel = readNextLineAsString();
  262. return true;
  263. }
  264. if (std::strcmp(msg, "maker") == 0)
  265. {
  266. fNextInfo.metadata.maker = fNextMaker = readNextLineAsString();
  267. return true;
  268. }
  269. if (std::strcmp(msg, "uniqueId") == 0)
  270. {
  271. readNextLineAsULong(fNextInfo.uniqueId);
  272. return true;
  273. }
  274. if (std::strcmp(msg, "audio.ins") == 0)
  275. {
  276. readNextLineAsUInt(fNextInfo.io.audioIns);
  277. return true;
  278. }
  279. if (std::strcmp(msg, "audio.outs") == 0)
  280. {
  281. readNextLineAsUInt(fNextInfo.io.audioOuts);
  282. return true;
  283. }
  284. if (std::strcmp(msg, "cv.ins") == 0)
  285. {
  286. readNextLineAsUInt(fNextInfo.io.cvIns);
  287. return true;
  288. }
  289. if (std::strcmp(msg, "cv.outs") == 0)
  290. {
  291. readNextLineAsUInt(fNextInfo.io.cvOuts);
  292. return true;
  293. }
  294. if (std::strcmp(msg, "midi.ins") == 0)
  295. {
  296. readNextLineAsUInt(fNextInfo.io.midiIns);
  297. return true;
  298. }
  299. if (std::strcmp(msg, "midi.outs") == 0)
  300. {
  301. readNextLineAsUInt(fNextInfo.io.midiOuts);
  302. return true;
  303. }
  304. if (std::strcmp(msg, "parameters.ins") == 0)
  305. {
  306. readNextLineAsUInt(fNextInfo.io.parameterIns);
  307. return true;
  308. }
  309. if (std::strcmp(msg, "parameters.outs") == 0)
  310. {
  311. readNextLineAsUInt(fNextInfo.io.parameterOuts);
  312. return true;
  313. }
  314. if (std::strcmp(msg, "exiting") == 0)
  315. {
  316. stopPipeServer(1000);
  317. return true;
  318. }
  319. carla_stdout("discovery: unknown message '%s' received", msg);
  320. return true;
  321. }
  322. private:
  323. const BinaryType fBinaryType;
  324. const PluginType fPluginType;
  325. const CarlaPluginDiscoveryCallback fDiscoveryCallback;
  326. const CarlaPluginCheckCacheCallback fCheckCacheCallback;
  327. void* const fCallbackPtr;
  328. const char* fPluginPath;
  329. bool fPluginsFoundInBinary;
  330. uint fBinaryIndex;
  331. const uint fBinaryCount;
  332. const std::vector<water::File> fBinaries;
  333. const CarlaString fDiscoveryTool;
  334. uint32_t fLastMessageTime;
  335. CarlaPluginDiscoveryInfo fNextInfo;
  336. CarlaString fNextSha1Sum;
  337. char* fNextLabel;
  338. char* fNextMaker;
  339. char* fNextName;
  340. void start()
  341. {
  342. using water::File;
  343. using water::String;
  344. fLastMessageTime = carla_gettime_ms();
  345. fPluginsFoundInBinary = false;
  346. fNextSha1Sum.clear();
  347. #ifndef CARLA_OS_WIN
  348. const CarlaPluginDiscoveryOptions& options(CarlaPluginDiscoveryOptions::getInstance());
  349. String helperTool;
  350. switch (fBinaryType)
  351. {
  352. case CB::BINARY_WIN32:
  353. if (options.wine.executable.isNotEmpty())
  354. helperTool = options.wine.executable.buffer();
  355. else
  356. helperTool = "wine";
  357. break;
  358. case CB::BINARY_WIN64:
  359. if (options.wine.executable.isNotEmpty())
  360. {
  361. helperTool = options.wine.executable.buffer();
  362. if (helperTool[0] == CARLA_OS_SEP && File(helperTool + "64").existsAsFile())
  363. helperTool += "64";
  364. }
  365. else
  366. {
  367. helperTool = "wine";
  368. }
  369. break;
  370. default:
  371. break;
  372. }
  373. String winePrefix;
  374. if (options.wine.autoPrefix && !fBinaries.empty())
  375. {
  376. const File file(fBinaries[fBinaryIndex]);
  377. const String filename(file.getFullPathName());
  378. winePrefix = findWinePrefix(filename);
  379. }
  380. if (winePrefix.isEmpty())
  381. {
  382. const char* const envWinePrefix = std::getenv("WINEPREFIX");
  383. if (envWinePrefix != nullptr && envWinePrefix[0] != '\0')
  384. winePrefix = envWinePrefix;
  385. else if (options.wine.fallbackPrefix != nullptr && options.wine.fallbackPrefix[0] != '\0')
  386. winePrefix = options.wine.fallbackPrefix.buffer();
  387. else
  388. winePrefix = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine";
  389. }
  390. const CarlaScopedEnvVar sev1("WINEDEBUG", "-all");
  391. const CarlaScopedEnvVar sev2("WINEPREFIX", winePrefix.toRawUTF8());
  392. #endif
  393. if (fBinaries.empty())
  394. {
  395. if (fBinaryType == CB::BINARY_NATIVE)
  396. {
  397. switch (fPluginType)
  398. {
  399. default:
  400. break;
  401. case CB::PLUGIN_INTERNAL:
  402. case CB::PLUGIN_LV2:
  403. case CB::PLUGIN_JSFX:
  404. case CB::PLUGIN_SFZ:
  405. if (const uint count = carla_get_cached_plugin_count(fPluginType, fPluginPath))
  406. {
  407. for (uint i=0; i<count; ++i)
  408. {
  409. const CarlaCachedPluginInfo* const pinfo = carla_get_cached_plugin_info(fPluginType, i);
  410. if (pinfo == nullptr || !pinfo->valid)
  411. continue;
  412. char* filename = nullptr;
  413. CarlaPluginDiscoveryInfo info = {};
  414. info.btype = CB::BINARY_NATIVE;
  415. info.ptype = fPluginType;
  416. info.metadata.name = pinfo->name;
  417. info.metadata.maker = pinfo->maker;
  418. info.metadata.category = pinfo->category;
  419. info.metadata.hints = pinfo->hints;
  420. info.io.audioIns = pinfo->audioIns;
  421. info.io.audioOuts = pinfo->audioOuts;
  422. info.io.cvIns = pinfo->cvIns;
  423. info.io.cvOuts = pinfo->cvOuts;
  424. info.io.midiIns = pinfo->midiIns;
  425. info.io.midiOuts = pinfo->midiOuts;
  426. info.io.parameterIns = pinfo->parameterIns;
  427. info.io.parameterOuts = pinfo->parameterOuts;
  428. if (fPluginType == CB::PLUGIN_LV2)
  429. {
  430. const char* const slash = std::strchr(pinfo->label, CARLA_OS_SEP);
  431. CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
  432. filename = strdup(pinfo->label);
  433. filename[slash - pinfo->label] = '\0';
  434. info.filename = filename;
  435. info.label = slash + 1;
  436. }
  437. else
  438. {
  439. info.filename = gPluginsDiscoveryNullCharPtr;
  440. info.label = pinfo->label;
  441. }
  442. fDiscoveryCallback(fCallbackPtr, &info, nullptr);
  443. std::free(filename);
  444. }
  445. }
  446. return;
  447. }
  448. }
  449. #ifndef CARLA_OS_WIN
  450. if (helperTool.isNotEmpty())
  451. startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all");
  452. else
  453. #endif
  454. startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all");
  455. }
  456. else
  457. {
  458. const File file(fBinaries[fBinaryIndex]);
  459. const String filename(file.getFullPathName());
  460. if (fCheckCacheCallback != nullptr)
  461. {
  462. makeHash(file, filename);
  463. if (fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
  464. {
  465. fPluginsFoundInBinary = true;
  466. carla_debug("Skipping \"%s\", using cache", filename.toRawUTF8());
  467. return;
  468. }
  469. }
  470. carla_stdout("Scanning \"%s\"...", filename.toRawUTF8());
  471. #ifndef CARLA_OS_WIN
  472. if (helperTool.isNotEmpty())
  473. startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8());
  474. else
  475. #endif
  476. startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8());
  477. }
  478. }
  479. void makeHash(const water::File& file, const water::String& filename)
  480. {
  481. CarlaSha1 sha1;
  482. /* do we want this? it is not exactly needed and makes discovery slow..
  483. if (file.existsAsFile() && file.getSize() < 20*1024*1024) // dont bother hashing > 20Mb files
  484. {
  485. water::FileInputStream stream(file);
  486. if (stream.openedOk())
  487. {
  488. uint8_t block[8192];
  489. for (int r; r = stream.read(block, sizeof(block)), r > 0;)
  490. sha1.write(block, r);
  491. }
  492. }
  493. */
  494. sha1.write(filename.toRawUTF8(), filename.length());
  495. const int64_t mtime = file.getLastModificationTime();
  496. sha1.write(&mtime, sizeof(mtime));
  497. fNextSha1Sum = sha1.resultAsString();
  498. }
  499. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginDiscovery)
  500. };
  501. // --------------------------------------------------------------------------------------------------------------------
  502. static bool findDirectories(std::vector<water::File>& files, const char* const pluginPath, const char* const wildcard)
  503. {
  504. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  505. if (pluginPath[0] == '\0')
  506. return true;
  507. using water::File;
  508. using water::String;
  509. using water::StringArray;
  510. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  511. if (splitPaths.size() == 0)
  512. return true;
  513. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  514. {
  515. const File dir(*it);
  516. std::vector<File> results;
  517. if (dir.findChildFiles(results, File::findDirectories|File::ignoreHiddenFiles, true, wildcard) > 0)
  518. {
  519. files.reserve(files.size() + results.size());
  520. files.insert(files.end(), results.begin(), results.end());
  521. }
  522. }
  523. return files.empty();
  524. }
  525. static bool findFiles(std::vector<water::File>& files,
  526. const BinaryType btype, const char* const pluginPath, const char* const wildcard)
  527. {
  528. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  529. if (pluginPath[0] == '\0')
  530. return true;
  531. using water::File;
  532. using water::String;
  533. using water::StringArray;
  534. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  535. if (splitPaths.size() == 0)
  536. return true;
  537. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  538. {
  539. const File dir(*it);
  540. std::vector<File> results;
  541. if (dir.findChildFiles(results, File::findFiles|File::ignoreHiddenFiles, true, wildcard) > 0)
  542. {
  543. files.reserve(files.size() + results.size());
  544. for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
  545. {
  546. const File file(*cit);
  547. if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
  548. files.push_back(file);
  549. }
  550. }
  551. }
  552. return files.empty();
  553. }
  554. static bool findVST3s(std::vector<water::File>& files,
  555. const BinaryType btype, const char* const pluginPath)
  556. {
  557. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  558. if (pluginPath[0] == '\0')
  559. return true;
  560. using water::File;
  561. using water::String;
  562. using water::StringArray;
  563. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  564. if (splitPaths.size() == 0)
  565. return true;
  566. const uint flags = btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64
  567. ? File::findDirectories|File::findFiles
  568. : File::findDirectories;
  569. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  570. {
  571. const File dir(*it);
  572. std::vector<File> results;
  573. if (dir.findChildFiles(results, flags|File::ignoreHiddenFiles, true, "*.vst3") > 0)
  574. {
  575. files.reserve(files.size() + results.size());
  576. for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
  577. {
  578. const File file(*cit);
  579. if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
  580. files.push_back(file);
  581. }
  582. }
  583. }
  584. return files.empty();
  585. }
  586. CarlaPluginDiscoveryHandle carla_plugin_discovery_start(const char* const discoveryTool,
  587. const BinaryType btype,
  588. const PluginType ptype,
  589. const char* const pluginPath,
  590. const CarlaPluginDiscoveryCallback discoveryCb,
  591. const CarlaPluginCheckCacheCallback checkCacheCb,
  592. void* const callbackPtr)
  593. {
  594. CARLA_SAFE_ASSERT_RETURN(btype != CB::BINARY_NONE, nullptr);
  595. CARLA_SAFE_ASSERT_RETURN(ptype != CB::PLUGIN_NONE, nullptr);
  596. CARLA_SAFE_ASSERT_RETURN(discoveryTool != nullptr && discoveryTool[0] != '\0', nullptr);
  597. CARLA_SAFE_ASSERT_RETURN(discoveryCb != nullptr, nullptr);
  598. bool directories = false;
  599. const char* wildcard = nullptr;
  600. switch (ptype)
  601. {
  602. case CB::PLUGIN_INTERNAL:
  603. case CB::PLUGIN_LV2:
  604. case CB::PLUGIN_SFZ:
  605. case CB::PLUGIN_JSFX:
  606. case CB::PLUGIN_DLS:
  607. case CB::PLUGIN_GIG:
  608. case CB::PLUGIN_SF2:
  609. CARLA_SAFE_ASSERT_UINT_RETURN(btype == CB::BINARY_NATIVE, btype, nullptr);
  610. break;
  611. default:
  612. break;
  613. }
  614. switch (ptype)
  615. {
  616. case CB::PLUGIN_NONE:
  617. case CB::PLUGIN_JACK:
  618. case CB::PLUGIN_TYPE_COUNT:
  619. return nullptr;
  620. case CB::PLUGIN_LV2:
  621. case CB::PLUGIN_SFZ:
  622. case CB::PLUGIN_JSFX:
  623. {
  624. const CarlaScopedEnvVar csev("CARLA_DISCOVERY_PATH", pluginPath);
  625. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr, pluginPath);
  626. }
  627. case CB::PLUGIN_INTERNAL:
  628. case CB::PLUGIN_AU:
  629. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
  630. case CB::PLUGIN_LADSPA:
  631. case CB::PLUGIN_DSSI:
  632. #ifdef CARLA_OS_WIN
  633. wildcard = "*.dll";
  634. #else
  635. if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
  636. {
  637. wildcard = "*.dll";
  638. }
  639. else
  640. {
  641. #ifdef CARLA_OS_MAC
  642. wildcard = "*.dylib";
  643. #else
  644. wildcard = "*.so";
  645. #endif
  646. }
  647. #endif
  648. break;
  649. case CB::PLUGIN_VST2:
  650. #ifdef CARLA_OS_WIN
  651. wildcard = "*.dll";
  652. #else
  653. if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
  654. {
  655. wildcard = "*.dll";
  656. }
  657. else
  658. {
  659. #ifdef CARLA_OS_MAC
  660. directories = true;
  661. wildcard = "*.vst";
  662. #else
  663. wildcard = "*.so";
  664. #endif
  665. }
  666. #endif
  667. break;
  668. case CB::PLUGIN_VST3:
  669. directories = true;
  670. wildcard = "*.vst3";
  671. break;
  672. case CB::PLUGIN_CLAP:
  673. wildcard = "*.clap";
  674. #ifdef CARLA_OS_MAC
  675. directories = true;
  676. #endif
  677. break;
  678. case CB::PLUGIN_DLS:
  679. wildcard = "*.dls";
  680. break;
  681. case CB::PLUGIN_GIG:
  682. wildcard = "*.gig";
  683. break;
  684. case CB::PLUGIN_SF2:
  685. wildcard = "*.sf2";
  686. break;
  687. }
  688. CARLA_SAFE_ASSERT_RETURN(wildcard != nullptr, nullptr);
  689. std::vector<water::File> files;
  690. if (ptype == CB::PLUGIN_VST3)
  691. {
  692. if (findVST3s(files, btype, pluginPath))
  693. return nullptr;
  694. }
  695. else if (directories)
  696. {
  697. if (findDirectories(files, pluginPath, wildcard))
  698. return nullptr;
  699. }
  700. else
  701. {
  702. if (findFiles(files, btype, pluginPath, wildcard))
  703. return nullptr;
  704. }
  705. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, std::move(files),
  706. discoveryCb, checkCacheCb, callbackPtr);
  707. }
  708. bool carla_plugin_discovery_idle(const CarlaPluginDiscoveryHandle handle)
  709. {
  710. return static_cast<CarlaPluginDiscovery*>(handle)->idle();
  711. }
  712. void carla_plugin_discovery_skip(const CarlaPluginDiscoveryHandle handle)
  713. {
  714. static_cast<CarlaPluginDiscovery*>(handle)->skip();
  715. }
  716. void carla_plugin_discovery_stop(const CarlaPluginDiscoveryHandle handle)
  717. {
  718. delete static_cast<CarlaPluginDiscovery*>(handle);
  719. }
  720. void carla_plugin_discovery_set_option(const EngineOption option, const int value, const char* const valueStr)
  721. {
  722. switch (option)
  723. {
  724. #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
  725. case CB::ENGINE_OPTION_WINE_EXECUTABLE:
  726. if (valueStr != nullptr && valueStr[0] != '\0')
  727. CarlaPluginDiscoveryOptions::getInstance().wine.executable = valueStr;
  728. else
  729. CarlaPluginDiscoveryOptions::getInstance().wine.executable.clear();
  730. break;
  731. case CB::ENGINE_OPTION_WINE_AUTO_PREFIX:
  732. CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
  733. CarlaPluginDiscoveryOptions::getInstance().wine.autoPrefix = value != 0;
  734. break;
  735. case CB::ENGINE_OPTION_WINE_FALLBACK_PREFIX:
  736. if (valueStr != nullptr && valueStr[0] != '\0')
  737. CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix = valueStr;
  738. else
  739. CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix.clear();
  740. break;
  741. #endif
  742. default:
  743. break;
  744. }
  745. }
  746. // --------------------------------------------------------------------------------------------------------------------