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.

816 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. String winePrefix;
  369. if (options.wine.autoPrefix && !fBinaries.empty())
  370. {
  371. const File file(fBinaries[fBinaryIndex]);
  372. const String filename(file.getFullPathName());
  373. winePrefix = findWinePrefix(filename);
  374. }
  375. if (winePrefix.isEmpty())
  376. {
  377. const char* const envWinePrefix = std::getenv("WINEPREFIX");
  378. if (envWinePrefix != nullptr && envWinePrefix[0] != '\0')
  379. winePrefix = envWinePrefix;
  380. else if (options.wine.fallbackPrefix != nullptr && options.wine.fallbackPrefix[0] != '\0')
  381. winePrefix = options.wine.fallbackPrefix.buffer();
  382. else
  383. winePrefix = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine";
  384. }
  385. const CarlaScopedEnvVar sev1("WINEDEBUG", "-all");
  386. const CarlaScopedEnvVar sev2("WINEPREFIX", winePrefix.toRawUTF8());
  387. #endif
  388. if (fBinaries.empty())
  389. {
  390. #ifndef CARLA_OS_WIN
  391. if (helperTool.isNotEmpty())
  392. startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all");
  393. else
  394. #endif
  395. startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all");
  396. }
  397. else
  398. {
  399. const File file(fBinaries[fBinaryIndex]);
  400. const String filename(file.getFullPathName());
  401. if (fCheckCacheCallback != nullptr)
  402. {
  403. makeHash(file, filename);
  404. if (fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
  405. {
  406. fPluginsFoundInBinary = true;
  407. carla_debug("Skipping \"%s\", using cache", filename.toRawUTF8());
  408. return;
  409. }
  410. }
  411. carla_stdout("Scanning \"%s\"...", filename.toRawUTF8());
  412. #ifndef CARLA_OS_WIN
  413. if (helperTool.isNotEmpty())
  414. startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8());
  415. else
  416. #endif
  417. startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8());
  418. }
  419. }
  420. void makeHash(const water::File& file, const water::String& filename)
  421. {
  422. CarlaSha1 sha1;
  423. /* do we want this? it is not exactly needed and makes discovery slow..
  424. if (file.existsAsFile() && file.getSize() < 20*1024*1024) // dont bother hashing > 20Mb files
  425. {
  426. water::FileInputStream stream(file);
  427. if (stream.openedOk())
  428. {
  429. uint8_t block[8192];
  430. for (int r; r = stream.read(block, sizeof(block)), r > 0;)
  431. sha1.write(block, r);
  432. }
  433. }
  434. */
  435. sha1.write(filename.toRawUTF8(), filename.length());
  436. const int64_t mtime = file.getLastModificationTime();
  437. sha1.write(&mtime, sizeof(mtime));
  438. fNextSha1Sum = sha1.resultAsString();
  439. }
  440. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginDiscovery)
  441. };
  442. // --------------------------------------------------------------------------------------------------------------------
  443. static bool findDirectories(std::vector<water::File>& files, const char* const pluginPath, const char* const wildcard)
  444. {
  445. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  446. if (pluginPath[0] == '\0')
  447. return true;
  448. using water::File;
  449. using water::String;
  450. using water::StringArray;
  451. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  452. if (splitPaths.size() == 0)
  453. return true;
  454. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  455. {
  456. const File dir(*it);
  457. std::vector<File> results;
  458. if (dir.findChildFiles(results, File::findDirectories|File::ignoreHiddenFiles, true, wildcard) > 0)
  459. {
  460. files.reserve(files.size() + results.size());
  461. files.insert(files.end(), results.begin(), results.end());
  462. }
  463. }
  464. return files.empty();
  465. }
  466. static bool findFiles(std::vector<water::File>& files,
  467. const BinaryType btype, const char* const pluginPath, const char* const wildcard)
  468. {
  469. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  470. if (pluginPath[0] == '\0')
  471. return true;
  472. using water::File;
  473. using water::String;
  474. using water::StringArray;
  475. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  476. if (splitPaths.size() == 0)
  477. return true;
  478. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  479. {
  480. const File dir(*it);
  481. std::vector<File> results;
  482. if (dir.findChildFiles(results, File::findFiles|File::ignoreHiddenFiles, true, wildcard) > 0)
  483. {
  484. files.reserve(files.size() + results.size());
  485. for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
  486. {
  487. const File file(*cit);
  488. if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
  489. files.push_back(file);
  490. }
  491. }
  492. }
  493. return files.empty();
  494. }
  495. static bool findVST3s(std::vector<water::File>& files,
  496. const BinaryType btype, const char* const pluginPath)
  497. {
  498. CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
  499. if (pluginPath[0] == '\0')
  500. return true;
  501. using water::File;
  502. using water::String;
  503. using water::StringArray;
  504. const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
  505. if (splitPaths.size() == 0)
  506. return true;
  507. const uint flags = btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64
  508. ? File::findDirectories|File::findFiles
  509. : File::findDirectories;
  510. for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
  511. {
  512. const File dir(*it);
  513. std::vector<File> results;
  514. if (dir.findChildFiles(results, flags|File::ignoreHiddenFiles, true, "*.vst3") > 0)
  515. {
  516. files.reserve(files.size() + results.size());
  517. for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
  518. {
  519. const File file(*cit);
  520. if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
  521. files.push_back(file);
  522. }
  523. }
  524. }
  525. return files.empty();
  526. }
  527. CarlaPluginDiscoveryHandle carla_plugin_discovery_start(const char* const discoveryTool,
  528. const BinaryType btype,
  529. const PluginType ptype,
  530. const char* const pluginPath,
  531. const CarlaPluginDiscoveryCallback discoveryCb,
  532. const CarlaPluginCheckCacheCallback checkCacheCb,
  533. void* const callbackPtr)
  534. {
  535. CARLA_SAFE_ASSERT_RETURN(btype != CB::BINARY_NONE, nullptr);
  536. CARLA_SAFE_ASSERT_RETURN(ptype != CB::PLUGIN_NONE, nullptr);
  537. CARLA_SAFE_ASSERT_RETURN(discoveryTool != nullptr && discoveryTool[0] != '\0', nullptr);
  538. CARLA_SAFE_ASSERT_RETURN(discoveryCb != nullptr, nullptr);
  539. bool directories = false;
  540. const char* wildcard = nullptr;
  541. switch (ptype)
  542. {
  543. case CB::PLUGIN_NONE:
  544. case CB::PLUGIN_JACK:
  545. case CB::PLUGIN_TYPE_COUNT:
  546. return nullptr;
  547. case CB::PLUGIN_SFZ:
  548. case CB::PLUGIN_JSFX:
  549. {
  550. const CarlaScopedEnvVar csev("CARLA_DISCOVERY_PATH", pluginPath);
  551. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
  552. }
  553. case CB::PLUGIN_INTERNAL:
  554. case CB::PLUGIN_LV2:
  555. case CB::PLUGIN_AU:
  556. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
  557. case CB::PLUGIN_LADSPA:
  558. case CB::PLUGIN_DSSI:
  559. #ifdef CARLA_OS_WIN
  560. wildcard = "*.dll";
  561. #else
  562. if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
  563. {
  564. wildcard = "*.dll";
  565. }
  566. else
  567. {
  568. #ifdef CARLA_OS_MAC
  569. wildcard = "*.dylib";
  570. #else
  571. wildcard = "*.so";
  572. #endif
  573. }
  574. #endif
  575. break;
  576. case CB::PLUGIN_VST2:
  577. #ifdef CARLA_OS_WIN
  578. wildcard = "*.dll";
  579. #else
  580. if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
  581. {
  582. wildcard = "*.dll";
  583. }
  584. else
  585. {
  586. #ifdef CARLA_OS_MAC
  587. directories = true;
  588. wildcard = "*.vst";
  589. #else
  590. wildcard = "*.so";
  591. #endif
  592. }
  593. #endif
  594. break;
  595. case CB::PLUGIN_VST3:
  596. directories = true;
  597. wildcard = "*.vst3";
  598. break;
  599. case CB::PLUGIN_CLAP:
  600. wildcard = "*.clap";
  601. #ifdef CARLA_OS_MAC
  602. directories = true;
  603. #endif
  604. break;
  605. case CB::PLUGIN_DLS:
  606. wildcard = "*.dls";
  607. break;
  608. case CB::PLUGIN_GIG:
  609. wildcard = "*.gig";
  610. break;
  611. case CB::PLUGIN_SF2:
  612. wildcard = "*.sf2";
  613. break;
  614. }
  615. CARLA_SAFE_ASSERT_RETURN(wildcard != nullptr, nullptr);
  616. std::vector<water::File> files;
  617. if (ptype == CB::PLUGIN_VST3)
  618. {
  619. if (findVST3s(files, btype, pluginPath))
  620. return nullptr;
  621. }
  622. else if (directories)
  623. {
  624. if (findDirectories(files, pluginPath, wildcard))
  625. return nullptr;
  626. }
  627. else
  628. {
  629. if (findFiles(files, btype, pluginPath, wildcard))
  630. return nullptr;
  631. }
  632. return new CarlaPluginDiscovery(discoveryTool, btype, ptype, std::move(files),
  633. discoveryCb, checkCacheCb, callbackPtr);
  634. }
  635. bool carla_plugin_discovery_idle(const CarlaPluginDiscoveryHandle handle)
  636. {
  637. return static_cast<CarlaPluginDiscovery*>(handle)->idle();
  638. }
  639. void carla_plugin_discovery_skip(const CarlaPluginDiscoveryHandle handle)
  640. {
  641. static_cast<CarlaPluginDiscovery*>(handle)->skip();
  642. }
  643. void carla_plugin_discovery_stop(const CarlaPluginDiscoveryHandle handle)
  644. {
  645. delete static_cast<CarlaPluginDiscovery*>(handle);
  646. }
  647. void carla_plugin_discovery_set_option(const EngineOption option, const int value, const char* const valueStr)
  648. {
  649. switch (option)
  650. {
  651. #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
  652. case CB::ENGINE_OPTION_WINE_EXECUTABLE:
  653. if (valueStr != nullptr && valueStr[0] != '\0')
  654. CarlaPluginDiscoveryOptions::getInstance().wine.executable = valueStr;
  655. else
  656. CarlaPluginDiscoveryOptions::getInstance().wine.executable.clear();
  657. break;
  658. case CB::ENGINE_OPTION_WINE_AUTO_PREFIX:
  659. CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
  660. CarlaPluginDiscoveryOptions::getInstance().wine.autoPrefix = value != 0;
  661. break;
  662. case CB::ENGINE_OPTION_WINE_FALLBACK_PREFIX:
  663. if (valueStr != nullptr && valueStr[0] != '\0')
  664. CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix = valueStr;
  665. else
  666. CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix.clear();
  667. break;
  668. #endif
  669. default:
  670. break;
  671. }
  672. }
  673. // --------------------------------------------------------------------------------------------------------------------