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.

813 lines
24KB

  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. fPluginsFoundInBinary(false),
  96. fBinaryIndex(0),
  97. fBinaryCount(static_cast<uint>(binaries.size())),
  98. fBinaries(binaries),
  99. fDiscoveryTool(discoveryTool),
  100. fLastMessageTime(0),
  101. fNextLabel(nullptr),
  102. fNextMaker(nullptr),
  103. fNextName(nullptr)
  104. {
  105. start();
  106. }
  107. CarlaPluginDiscovery(const char* const discoveryTool,
  108. const BinaryType btype,
  109. const PluginType ptype,
  110. const CarlaPluginDiscoveryCallback discoveryCb,
  111. const CarlaPluginCheckCacheCallback checkCacheCb,
  112. void* const callbackPtr)
  113. : fBinaryType(btype),
  114. fPluginType(ptype),
  115. fDiscoveryCallback(discoveryCb),
  116. fCheckCacheCallback(checkCacheCb),
  117. fCallbackPtr(callbackPtr),
  118. fPluginsFoundInBinary(false),
  119. fBinaryIndex(0),
  120. fBinaryCount(1),
  121. fDiscoveryTool(discoveryTool),
  122. fLastMessageTime(0),
  123. fNextLabel(nullptr),
  124. fNextMaker(nullptr),
  125. fNextName(nullptr)
  126. {
  127. start();
  128. }
  129. ~CarlaPluginDiscovery()
  130. {
  131. stopPipeServer(5000);
  132. std::free(fNextLabel);
  133. std::free(fNextMaker);
  134. std::free(fNextName);
  135. }
  136. bool idle()
  137. {
  138. if (isPipeRunning())
  139. {
  140. idlePipe();
  141. // automatically skip a plugin if 30s passes without a reply
  142. const uint32_t timeNow = carla_gettime_ms();
  143. if (timeNow - fLastMessageTime < 30000)
  144. return true;
  145. carla_stdout("Plugin took too long to respond, skipping...");
  146. stopPipeServer(1000);
  147. }
  148. // report binary as having no plugins
  149. if (fCheckCacheCallback != nullptr && !fPluginsFoundInBinary && !fBinaries.empty())
  150. {
  151. const water::File file(fBinaries[fBinaryIndex]);
  152. const water::String filename(file.getFullPathName());
  153. makeHash(file, filename);
  154. if (! fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
  155. fDiscoveryCallback(fCallbackPtr, nullptr, fNextSha1Sum);
  156. }
  157. if (++fBinaryIndex == fBinaryCount)
  158. return false;
  159. start();
  160. return true;
  161. }
  162. void skip()
  163. {
  164. if (isPipeRunning())
  165. stopPipeServer(1000);
  166. }
  167. protected:
  168. bool msgReceived(const char* const msg) noexcept
  169. {
  170. fLastMessageTime = carla_gettime_ms();
  171. if (std::strcmp(msg, "warning") == 0 || std::strcmp(msg, "error") == 0)
  172. {
  173. const char* text = nullptr;
  174. readNextLineAsString(text, false);
  175. carla_stdout("discovery: %s", text);
  176. return true;
  177. }
  178. if (std::strcmp(msg, "init") == 0)
  179. {
  180. const char* _;
  181. readNextLineAsString(_, false);
  182. new (&fNextInfo) _CarlaPluginDiscoveryInfo();
  183. return true;
  184. }
  185. if (std::strcmp(msg, "end") == 0)
  186. {
  187. const char* _;
  188. readNextLineAsString(_, false);
  189. if (fNextInfo.label == nullptr)
  190. fNextInfo.label = gPluginsDiscoveryNullCharPtr;
  191. if (fNextInfo.metadata.maker == nullptr)
  192. fNextInfo.metadata.maker = gPluginsDiscoveryNullCharPtr;
  193. if (fNextInfo.metadata.name == nullptr)
  194. fNextInfo.metadata.name = gPluginsDiscoveryNullCharPtr;
  195. if (fBinaries.empty())
  196. {
  197. char* filename = nullptr;
  198. if (fPluginType == CB::PLUGIN_LV2)
  199. {
  200. do {
  201. const char* const slash = std::strchr(fNextLabel, CARLA_OS_SEP);
  202. CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
  203. filename = strdup(fNextLabel);
  204. filename[slash - fNextLabel] = '\0';
  205. fNextInfo.filename = filename;
  206. fNextInfo.label = slash + 1;
  207. } while (false);
  208. }
  209. fNextInfo.ptype = fPluginType;
  210. fDiscoveryCallback(fCallbackPtr, &fNextInfo, nullptr);
  211. std::free(filename);
  212. }
  213. else
  214. {
  215. CARLA_SAFE_ASSERT(fNextSha1Sum.isNotEmpty());
  216. const water::String filename(fBinaries[fBinaryIndex].getFullPathName());
  217. fNextInfo.filename = filename.toRawUTF8();
  218. fNextInfo.ptype = fPluginType;
  219. fPluginsFoundInBinary = true;
  220. carla_stdout("Found %s from %s", fNextInfo.metadata.name, fNextInfo.filename);
  221. fDiscoveryCallback(fCallbackPtr, &fNextInfo, fNextSha1Sum);
  222. }
  223. std::free(fNextLabel);
  224. fNextLabel = nullptr;
  225. std::free(fNextMaker);
  226. fNextMaker = nullptr;
  227. std::free(fNextName);
  228. fNextName = nullptr;
  229. return true;
  230. }
  231. if (std::strcmp(msg, "build") == 0)
  232. {
  233. uint8_t btype = 0;
  234. readNextLineAsByte(btype);
  235. fNextInfo.btype = static_cast<BinaryType>(btype);
  236. return true;
  237. }
  238. if (std::strcmp(msg, "hints") == 0)
  239. {
  240. readNextLineAsUInt(fNextInfo.metadata.hints);
  241. return true;
  242. }
  243. if (std::strcmp(msg, "category") == 0)
  244. {
  245. const char* category = nullptr;
  246. readNextLineAsString(category, false);
  247. fNextInfo.metadata.category = CB::getPluginCategoryFromString(category);
  248. return true;
  249. }
  250. if (std::strcmp(msg, "name") == 0)
  251. {
  252. fNextInfo.metadata.name = fNextName = readNextLineAsString();
  253. return true;
  254. }
  255. if (std::strcmp(msg, "label") == 0)
  256. {
  257. fNextInfo.label = fNextLabel = readNextLineAsString();
  258. return true;
  259. }
  260. if (std::strcmp(msg, "maker") == 0)
  261. {
  262. fNextInfo.metadata.maker = fNextMaker = readNextLineAsString();
  263. return true;
  264. }
  265. if (std::strcmp(msg, "uniqueId") == 0)
  266. {
  267. readNextLineAsULong(fNextInfo.uniqueId);
  268. return true;
  269. }
  270. if (std::strcmp(msg, "audio.ins") == 0)
  271. {
  272. readNextLineAsUInt(fNextInfo.io.audioIns);
  273. return true;
  274. }
  275. if (std::strcmp(msg, "audio.outs") == 0)
  276. {
  277. readNextLineAsUInt(fNextInfo.io.audioOuts);
  278. return true;
  279. }
  280. if (std::strcmp(msg, "cv.ins") == 0)
  281. {
  282. readNextLineAsUInt(fNextInfo.io.cvIns);
  283. return true;
  284. }
  285. if (std::strcmp(msg, "cv.outs") == 0)
  286. {
  287. readNextLineAsUInt(fNextInfo.io.cvOuts);
  288. return true;
  289. }
  290. if (std::strcmp(msg, "midi.ins") == 0)
  291. {
  292. readNextLineAsUInt(fNextInfo.io.midiIns);
  293. return true;
  294. }
  295. if (std::strcmp(msg, "midi.outs") == 0)
  296. {
  297. readNextLineAsUInt(fNextInfo.io.midiOuts);
  298. return true;
  299. }
  300. if (std::strcmp(msg, "parameters.ins") == 0)
  301. {
  302. readNextLineAsUInt(fNextInfo.io.parameterIns);
  303. return true;
  304. }
  305. if (std::strcmp(msg, "parameters.outs") == 0)
  306. {
  307. readNextLineAsUInt(fNextInfo.io.parameterOuts);
  308. return true;
  309. }
  310. if (std::strcmp(msg, "exiting") == 0)
  311. {
  312. stopPipeServer(1000);
  313. return true;
  314. }
  315. carla_stdout("discovery: unknown message '%s' received", msg);
  316. return true;
  317. }
  318. private:
  319. const BinaryType fBinaryType;
  320. const PluginType fPluginType;
  321. const CarlaPluginDiscoveryCallback fDiscoveryCallback;
  322. const CarlaPluginCheckCacheCallback fCheckCacheCallback;
  323. void* const fCallbackPtr;
  324. bool fPluginsFoundInBinary;
  325. uint fBinaryIndex;
  326. const uint fBinaryCount;
  327. const std::vector<water::File> fBinaries;
  328. const CarlaString fDiscoveryTool;
  329. uint32_t fLastMessageTime;
  330. CarlaPluginDiscoveryInfo fNextInfo;
  331. CarlaString fNextSha1Sum;
  332. char* fNextLabel;
  333. char* fNextMaker;
  334. char* fNextName;
  335. void start()
  336. {
  337. using water::File;
  338. using water::String;
  339. fLastMessageTime = carla_gettime_ms();
  340. fPluginsFoundInBinary = false;
  341. fNextSha1Sum.clear();
  342. #ifndef CARLA_OS_WIN
  343. const CarlaPluginDiscoveryOptions& options(CarlaPluginDiscoveryOptions::getInstance());
  344. String helperTool;
  345. switch (fBinaryType)
  346. {
  347. case CB::BINARY_WIN32:
  348. if (options.wine.executable.isNotEmpty())
  349. helperTool = options.wine.executable.buffer();
  350. else
  351. helperTool = "wine";
  352. break;
  353. case CB::BINARY_WIN64:
  354. if (options.wine.executable.isNotEmpty())
  355. {
  356. helperTool = options.wine.executable.buffer();
  357. if (helperTool[0] == CARLA_OS_SEP && File(helperTool + "64").existsAsFile())
  358. helperTool += "64";
  359. }
  360. else
  361. {
  362. helperTool = "wine";
  363. }
  364. break;
  365. default:
  366. break;
  367. }
  368. #endif
  369. if (fBinaries.empty())
  370. {
  371. #ifndef CARLA_OS_WIN
  372. if (helperTool.isNotEmpty())
  373. startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all");
  374. else
  375. #endif
  376. startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all");
  377. }
  378. else
  379. {
  380. const File file(fBinaries[fBinaryIndex]);
  381. const String filename(file.getFullPathName());
  382. if (fCheckCacheCallback != nullptr)
  383. {
  384. makeHash(file, filename);
  385. if (fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
  386. {
  387. fPluginsFoundInBinary = true;
  388. carla_debug("Skipping \"%s\", using cache", filename.toRawUTF8());
  389. return;
  390. }
  391. }
  392. #ifndef CARLA_OS_WIN
  393. String winePrefix;
  394. if (options.wine.autoPrefix)
  395. winePrefix = findWinePrefix(filename);
  396. if (winePrefix.isEmpty())
  397. {
  398. const char* const envWinePrefix = std::getenv("WINEPREFIX");
  399. if (envWinePrefix != nullptr && envWinePrefix[0] != '\0')
  400. winePrefix = envWinePrefix;
  401. else if (options.wine.fallbackPrefix != nullptr && options.wine.fallbackPrefix[0] != '\0')
  402. winePrefix = options.wine.fallbackPrefix.buffer();
  403. else
  404. winePrefix = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine";
  405. }
  406. const CarlaScopedEnvVar sev1("WINEDEBUG", "-all");
  407. const CarlaScopedEnvVar sev2("WINEPREFIX", winePrefix.toRawUTF8());
  408. #endif
  409. carla_stdout("Scanning \"%s\"...", filename.toRawUTF8());
  410. #ifndef CARLA_OS_WIN
  411. if (helperTool.isNotEmpty())
  412. startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8());
  413. else
  414. #endif
  415. startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8());
  416. }
  417. }
  418. void makeHash(const water::File& file, const water::String& filename)
  419. {
  420. CarlaSha1 sha1;
  421. /* do we want this? it is not exactly needed and makes discovery slow..
  422. if (file.existsAsFile() && file.getSize() < 20*1024*1024) // dont bother hashing > 20Mb files
  423. {
  424. water::FileInputStream stream(file);
  425. if (stream.openedOk())
  426. {
  427. uint8_t block[8192];
  428. for (int r; r = stream.read(block, sizeof(block)), r > 0;)
  429. sha1.write(block, r);
  430. }
  431. }
  432. */
  433. sha1.write(filename.toRawUTF8(), filename.length());
  434. const int64_t mtime = file.getLastModificationTime();
  435. sha1.write(&mtime, sizeof(mtime));
  436. fNextSha1Sum = sha1.resultAsString();
  437. }
  438. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginDiscovery)
  439. };
  440. // --------------------------------------------------------------------------------------------------------------------
  441. static bool findDirectories(std::vector<water::File>& files, const char* const pluginPath, const char* const wildcard)
  442. {
  443. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  444. if (pluginPath[0] == '\0')
  445. return true;
  446. using water::File;
  447. using water::String;
  448. using water::StringArray;
  449. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  450. if (splitPaths.size() == 0)
  451. return true;
  452. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  453. {
  454. const File dir(*it);
  455. std::vector<File> results;
  456. if (dir.findChildFiles(results, File::findDirectories|File::ignoreHiddenFiles, true, wildcard) > 0)
  457. {
  458. files.reserve(files.size() + results.size());
  459. files.insert(files.end(), results.begin(), results.end());
  460. }
  461. }
  462. return files.empty();
  463. }
  464. static bool findFiles(std::vector<water::File>& files,
  465. const BinaryType btype, const char* const pluginPath, const char* const wildcard)
  466. {
  467. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  468. if (pluginPath[0] == '\0')
  469. return true;
  470. using water::File;
  471. using water::String;
  472. using water::StringArray;
  473. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  474. if (splitPaths.size() == 0)
  475. return true;
  476. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  477. {
  478. const File dir(*it);
  479. std::vector<File> results;
  480. if (dir.findChildFiles(results, File::findFiles|File::ignoreHiddenFiles, true, wildcard) > 0)
  481. {
  482. files.reserve(files.size() + results.size());
  483. for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
  484. {
  485. const File file(*cit);
  486. if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
  487. files.push_back(file);
  488. }
  489. }
  490. }
  491. return files.empty();
  492. }
  493. static bool findVST3s(std::vector<water::File>& files,
  494. const BinaryType btype, const char* const pluginPath)
  495. {
  496. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  497. if (pluginPath[0] == '\0')
  498. return true;
  499. using water::File;
  500. using water::String;
  501. using water::StringArray;
  502. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  503. if (splitPaths.size() == 0)
  504. return true;
  505. const uint flags = btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64
  506. ? File::findDirectories|File::findFiles
  507. : File::findDirectories;
  508. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  509. {
  510. const File dir(*it);
  511. std::vector<File> results;
  512. if (dir.findChildFiles(results, flags|File::ignoreHiddenFiles, true, "*.vst3") > 0)
  513. {
  514. files.reserve(files.size() + results.size());
  515. for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
  516. {
  517. const File file(*cit);
  518. if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
  519. files.push_back(file);
  520. }
  521. }
  522. }
  523. return files.empty();
  524. }
  525. CarlaPluginDiscoveryHandle carla_plugin_discovery_start(const char* const discoveryTool,
  526. const BinaryType btype,
  527. const PluginType ptype,
  528. const char* const pluginPath,
  529. const CarlaPluginDiscoveryCallback discoveryCb,
  530. const CarlaPluginCheckCacheCallback checkCacheCb,
  531. void* const callbackPtr)
  532. {
  533. CARLA_SAFE_ASSERT_RETURN(btype != CB::BINARY_NONE, nullptr);
  534. CARLA_SAFE_ASSERT_RETURN(ptype != CB::PLUGIN_NONE, nullptr);
  535. CARLA_SAFE_ASSERT_RETURN(discoveryTool != nullptr && discoveryTool[0] != '\0', nullptr);
  536. CARLA_SAFE_ASSERT_RETURN(discoveryCb != nullptr, nullptr);
  537. bool directories = false;
  538. const char* wildcard = nullptr;
  539. switch (ptype)
  540. {
  541. case CB::PLUGIN_NONE:
  542. case CB::PLUGIN_JACK:
  543. case CB::PLUGIN_TYPE_COUNT:
  544. return nullptr;
  545. case CB::PLUGIN_SFZ:
  546. case CB::PLUGIN_JSFX:
  547. {
  548. const CarlaScopedEnvVar csev("CARLA_DISCOVERY_PATH", pluginPath);
  549. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
  550. }
  551. case CB::PLUGIN_INTERNAL:
  552. case CB::PLUGIN_LV2:
  553. case CB::PLUGIN_AU:
  554. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
  555. case CB::PLUGIN_LADSPA:
  556. case CB::PLUGIN_DSSI:
  557. #ifdef CARLA_OS_WIN
  558. wildcard = "*.dll";
  559. #else
  560. if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
  561. {
  562. wildcard = "*.dll";
  563. }
  564. else
  565. {
  566. #ifdef CARLA_OS_MAC
  567. wildcard = "*.dylib";
  568. #else
  569. wildcard = "*.so";
  570. #endif
  571. }
  572. #endif
  573. break;
  574. case CB::PLUGIN_VST2:
  575. #ifdef CARLA_OS_WIN
  576. wildcard = "*.dll";
  577. #else
  578. if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
  579. {
  580. wildcard = "*.dll";
  581. }
  582. else
  583. {
  584. #ifdef CARLA_OS_MAC
  585. directories = true;
  586. wildcard = "*.vst";
  587. #else
  588. wildcard = "*.so";
  589. #endif
  590. }
  591. #endif
  592. break;
  593. case CB::PLUGIN_VST3:
  594. directories = true;
  595. wildcard = "*.vst3";
  596. break;
  597. case CB::PLUGIN_CLAP:
  598. wildcard = "*.clap";
  599. #ifdef CARLA_OS_MAC
  600. directories = true;
  601. #endif
  602. break;
  603. case CB::PLUGIN_DLS:
  604. wildcard = "*.dls";
  605. break;
  606. case CB::PLUGIN_GIG:
  607. wildcard = "*.gig";
  608. break;
  609. case CB::PLUGIN_SF2:
  610. wildcard = "*.sf2";
  611. break;
  612. }
  613. CARLA_SAFE_ASSERT_RETURN(wildcard != nullptr, nullptr);
  614. std::vector<water::File> files;
  615. if (ptype == CB::PLUGIN_VST3)
  616. {
  617. if (findVST3s(files, btype, pluginPath))
  618. return nullptr;
  619. }
  620. else if (directories)
  621. {
  622. if (findDirectories(files, pluginPath, wildcard))
  623. return nullptr;
  624. }
  625. else
  626. {
  627. if (findFiles(files, btype, pluginPath, wildcard))
  628. return nullptr;
  629. }
  630. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, std::move(files),
  631. discoveryCb, checkCacheCb, callbackPtr);
  632. }
  633. bool carla_plugin_discovery_idle(const CarlaPluginDiscoveryHandle handle)
  634. {
  635. return static_cast<CarlaPluginDiscovery*>(handle)->idle();
  636. }
  637. void carla_plugin_discovery_skip(const CarlaPluginDiscoveryHandle handle)
  638. {
  639. static_cast<CarlaPluginDiscovery*>(handle)->skip();
  640. }
  641. void carla_plugin_discovery_stop(const CarlaPluginDiscoveryHandle handle)
  642. {
  643. delete static_cast<CarlaPluginDiscovery*>(handle);
  644. }
  645. void carla_plugin_discovery_set_option(const EngineOption option, const int value, const char* const valueStr)
  646. {
  647. switch (option)
  648. {
  649. #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
  650. case CB::ENGINE_OPTION_WINE_EXECUTABLE:
  651. if (valueStr != nullptr && valueStr[0] != '\0')
  652. CarlaPluginDiscoveryOptions::getInstance().wine.executable = valueStr;
  653. else
  654. CarlaPluginDiscoveryOptions::getInstance().wine.executable.clear();
  655. break;
  656. case CB::ENGINE_OPTION_WINE_AUTO_PREFIX:
  657. CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
  658. CarlaPluginDiscoveryOptions::getInstance().wine.autoPrefix = value != 0;
  659. break;
  660. case CB::ENGINE_OPTION_WINE_FALLBACK_PREFIX:
  661. if (valueStr != nullptr && valueStr[0] != '\0')
  662. CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix = valueStr;
  663. else
  664. CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix.clear();
  665. break;
  666. #endif
  667. default:
  668. break;
  669. }
  670. }
  671. // --------------------------------------------------------------------------------------------------------------------