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.

506 lines
16KB

  1. /*
  2. * Carla plugin database code
  3. * Copyright (C) 2011-2020 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 "carla_database.hpp"
  18. //---------------------------------------------------------------------------------------------------------------------
  19. // Imports (Global)
  20. #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  21. # pragma GCC diagnostic push
  22. # pragma GCC diagnostic ignored "-Wconversion"
  23. # pragma GCC diagnostic ignored "-Weffc++"
  24. # pragma GCC diagnostic ignored "-Wsign-conversion"
  25. #endif
  26. //---------------------------------------------------------------------------------------------------------------------
  27. #include <QtCore/QDir>
  28. #include <QtCore/QFileInfo>
  29. #include <QtWidgets/QPushButton>
  30. //---------------------------------------------------------------------------------------------------------------------
  31. #include "ui_carla_add_jack.hpp"
  32. #include "ui_carla_database.hpp"
  33. #include "ui_carla_refresh.hpp"
  34. //---------------------------------------------------------------------------------------------------------------------
  35. #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  36. # pragma GCC diagnostic pop
  37. #endif
  38. //---------------------------------------------------------------------------------------------------------------------
  39. // Imports (Custom)
  40. #include "carla_host.hpp"
  41. #include "CarlaHost.h"
  42. #include "CarlaLibJackHints.h"
  43. // --------------------------------------------------------------------------------------------------------------------
  44. // Separate Thread for Plugin Search
  45. struct WineSettings {
  46. QString executable;
  47. bool autoPrefix;
  48. QString fallbackPrefix;
  49. WineSettings()
  50. : executable(),
  51. autoPrefix(false),
  52. fallbackPrefix()
  53. {
  54. const QSafeSettings settings("falkTX", "Carla2");
  55. executable = settings.valueString(CARLA_KEY_WINE_EXECUTABLE, CARLA_DEFAULT_WINE_EXECUTABLE);
  56. autoPrefix = settings.valueBool(CARLA_KEY_WINE_AUTO_PREFIX, CARLA_DEFAULT_WINE_AUTO_PREFIX);
  57. fallbackPrefix = settings.valueString(CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_DEFAULT_WINE_FALLBACK_PREFIX);
  58. }
  59. };
  60. struct SearchPluginsThread::PrivateData {
  61. bool fContinueChecking;
  62. QString fPathBinaries;
  63. bool fCheckNative;
  64. bool fCheckPosix32;
  65. bool fCheckPosix64;
  66. bool fCheckWin32;
  67. bool fCheckWin64;
  68. bool fCheckLADSPA;
  69. bool fCheckDSSI;
  70. bool fCheckLV2;
  71. bool fCheckVST2;
  72. bool fCheckVST3;
  73. bool fCheckAU;
  74. bool fCheckSF2;
  75. bool fCheckSFZ;
  76. WineSettings fWineSettings;
  77. QString fToolNative;
  78. uint fCurCount;
  79. uint fCurPercentValue;
  80. uint fLastCheckValue;
  81. bool fSomethingChanged;
  82. PrivateData(void*, const QString pathBinaries)
  83. : fContinueChecking(false),
  84. fPathBinaries(pathBinaries),
  85. fCheckNative(false),
  86. fCheckPosix32(false),
  87. fCheckPosix64(false),
  88. fCheckWin32(false),
  89. fCheckWin64(false),
  90. fCheckLADSPA(false),
  91. fCheckDSSI(false),
  92. fCheckLV2(false),
  93. fCheckVST2(false),
  94. fCheckVST3(false),
  95. fCheckAU(false),
  96. fCheckSF2(false),
  97. fCheckSFZ(false),
  98. fWineSettings(),
  99. fToolNative(),
  100. fCurCount(0),
  101. fCurPercentValue(0),
  102. fLastCheckValue(0),
  103. fSomethingChanged(false)
  104. {
  105. }
  106. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  107. };
  108. SearchPluginsThread::SearchPluginsThread(QObject* parent, const QString pathBinaries)
  109. : QThread(parent),
  110. self(new PrivateData(this, pathBinaries))
  111. {
  112. }
  113. SearchPluginsThread::~SearchPluginsThread()
  114. {
  115. delete self;
  116. }
  117. void SearchPluginsThread::run()
  118. {
  119. }
  120. // --------------------------------------------------------------------------------------------------------------------
  121. // Plugin Refresh Dialog
  122. struct PluginRefreshW::PrivateData {
  123. Ui::PluginRefreshW ui;
  124. SearchPluginsThread fThread;
  125. QPixmap fIconYes;
  126. QPixmap fIconNo;
  127. PrivateData(PluginRefreshW* const refreshDialog, const CarlaHost& host)
  128. : ui(),
  129. fThread(refreshDialog, host.pathBinaries),
  130. fIconYes(":/16x16/dialog-ok-apply.svgz"),
  131. fIconNo(":/16x16/dialog-error.svgz")
  132. {
  133. ui.setupUi(refreshDialog);
  134. // ------------------------------------------------------------------------------------------------------------
  135. // Internal stuff
  136. const bool hasNative = QFileInfo::exists(host.pathBinaries + CARLA_OS_SEP_STR "carla-discovery-native");
  137. const bool hasPosix32 = QFileInfo::exists(host.pathBinaries + CARLA_OS_SEP_STR "carla-discovery-posix32");
  138. const bool hasPosix64 = QFileInfo::exists(host.pathBinaries + CARLA_OS_SEP_STR "carla-discovery-posix64");
  139. const bool hasWin32 = QFileInfo::exists(host.pathBinaries + CARLA_OS_SEP_STR "carla-discovery-win32.exe");
  140. const bool hasWin64 = QFileInfo::exists(host.pathBinaries + CARLA_OS_SEP_STR "carla-discovery-win64.exe");
  141. }
  142. void loadSettings()
  143. {
  144. }
  145. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  146. };
  147. PluginRefreshW::PluginRefreshW(QWidget* const parent, const CarlaHost& host)
  148. : QDialog(parent),
  149. self(new PrivateData(this, host))
  150. {
  151. // ----------------------------------------------------------------------------------------------------------------
  152. // Resize to minimum size, as it's very likely UI stuff was hidden
  153. resize(minimumSize());
  154. // ----------------------------------------------------------------------------------------------------------------
  155. // Set-up connections
  156. connect(this, SIGNAL(finished(int)), SLOT(slot_saveSettings()));
  157. connect(self->ui.b_start, SIGNAL(clicked()), SLOT(slot_start()));
  158. connect(self->ui.b_skip, SIGNAL(clicked()), SLOT(slot_skip()));
  159. connect(self->ui.ch_native, SIGNAL(clicked()), SLOT(slot_checkTools()));
  160. connect(self->ui.ch_posix32, SIGNAL(clicked()), SLOT(slot_checkTools()));
  161. connect(self->ui.ch_posix64, SIGNAL(clicked()), SLOT(slot_checkTools()));
  162. connect(self->ui.ch_win32, SIGNAL(clicked()), SLOT(slot_checkTools()));
  163. connect(self->ui.ch_win64, SIGNAL(clicked()), SLOT(slot_checkTools()));
  164. connect(self->ui.ch_ladspa, SIGNAL(clicked()), SLOT(slot_checkTools()));
  165. connect(self->ui.ch_dssi, SIGNAL(clicked()), SLOT(slot_checkTools()));
  166. connect(self->ui.ch_lv2, SIGNAL(clicked()), SLOT(slot_checkTools()));
  167. connect(self->ui.ch_vst, SIGNAL(clicked()), SLOT(slot_checkTools()));
  168. connect(self->ui.ch_vst3, SIGNAL(clicked()), SLOT(slot_checkTools()));
  169. connect(self->ui.ch_au, SIGNAL(clicked()), SLOT(slot_checkTools()));
  170. connect(self->ui.ch_sf2, SIGNAL(clicked()), SLOT(slot_checkTools()));
  171. connect(self->ui.ch_sfz, SIGNAL(clicked()), SLOT(slot_checkTools()));
  172. connect(self->ui.ch_jsfx, SIGNAL(clicked()), SLOT(slot_checkTools()));
  173. connect(&self->fThread, SIGNAL(pluginLook(float, QString)), SLOT(slot_handlePluginLook(float, QString)));
  174. connect(&self->fThread, SIGNAL(finished(int)), SLOT(slot_handlePluginThreadFinished()));
  175. // ----------------------------------------------------------------------------------------------------------------
  176. // Post-connect setup
  177. slot_checkTools();
  178. }
  179. PluginRefreshW::~PluginRefreshW()
  180. {
  181. delete self;
  182. }
  183. void PluginRefreshW::getValues(QString& audioDevice, uint& bufferSize, double& sampleRate)
  184. {
  185. }
  186. void PluginRefreshW::closeEvent(QCloseEvent* event)
  187. {
  188. }
  189. void PluginRefreshW::slot_saveSettings()
  190. {
  191. }
  192. void PluginRefreshW::slot_start()
  193. {
  194. }
  195. void PluginRefreshW::slot_skip()
  196. {
  197. }
  198. void PluginRefreshW::slot_checkTools()
  199. {
  200. }
  201. void PluginRefreshW::slot_handlePluginLook(float percent, QString plugin)
  202. {
  203. }
  204. void PluginRefreshW::slot_handlePluginThreadFinished()
  205. {
  206. }
  207. // --------------------------------------------------------------------------------------------------------------------
  208. // Plugin Database Dialog
  209. struct PluginDatabaseW::PrivateData {
  210. PrivateData(void*)
  211. {
  212. }
  213. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  214. };
  215. PluginDatabaseW::PluginDatabaseW(QWidget* parent, const CarlaHost& host, bool hasCanvas, bool hasCanvasGL)
  216. : QDialog(parent),
  217. self(new PrivateData(this))
  218. {
  219. }
  220. PluginDatabaseW::~PluginDatabaseW()
  221. {
  222. delete self;
  223. }
  224. void PluginDatabaseW::showEvent(QShowEvent* event)
  225. {
  226. }
  227. void PluginDatabaseW::slot_cellClicked(int row, int column)
  228. {
  229. }
  230. void PluginDatabaseW::slot_cellDoubleClicked(int row, int column)
  231. {
  232. }
  233. void PluginDatabaseW::slot_addPlugin()
  234. {
  235. }
  236. void PluginDatabaseW::slot_checkPlugin(int row)
  237. {
  238. }
  239. void PluginDatabaseW::slot_checkFilters()
  240. {
  241. }
  242. void PluginDatabaseW::slot_refreshPlugins()
  243. {
  244. }
  245. void PluginDatabaseW::slot_clearFilters()
  246. {
  247. }
  248. void PluginDatabaseW::slot_saveSettings()
  249. {
  250. }
  251. // --------------------------------------------------------------------------------------------------------------------
  252. // Jack Application Dialog
  253. // NOTE: index matches the one in the UI
  254. enum UiSessionManager {
  255. UI_SESSION_NONE,
  256. UI_SESSION_LADISH,
  257. UI_SESSION_NSM
  258. };
  259. struct JackApplicationW::PrivateData {
  260. Ui::Dialog ui;
  261. const QString fProjectFilename;
  262. PrivateData(JackApplicationW* const dialog, const QString& projectFilename)
  263. : ui(),
  264. fProjectFilename(projectFilename)
  265. {
  266. ui.setupUi(dialog);
  267. ui.group_error->setVisible(false);
  268. // ------------------------------------------------------------------------------------------------------------
  269. // Load settings
  270. loadSettings();
  271. }
  272. void checkIfButtonBoxShouldBeEnabled(int index, const QString text)
  273. {
  274. static QList<QChar> badFirstChars = { '.', '/' };
  275. bool enabled = text.length() > 0;
  276. QCarlaString showErr;
  277. // NSM applications must not be abstract or absolute paths, and must not contain arguments
  278. if (enabled && index == UI_SESSION_NSM)
  279. {
  280. if (badFirstChars.contains(text[0]))
  281. showErr = tr("NSM applications cannot use abstract or absolute paths");
  282. else if (text.contains(' ') || text.contains(';') || text.contains('&'))
  283. showErr = tr("NSM applications cannot use CLI arguments");
  284. else if (fProjectFilename.isEmpty())
  285. showErr = tr("You need to save the current Carla project before NSM can be used");
  286. }
  287. if (showErr.isNotEmpty())
  288. {
  289. enabled = false;
  290. ui.l_error->setText(showErr);
  291. ui.group_error->setVisible(true);
  292. }
  293. else
  294. {
  295. ui.group_error->setVisible(false);
  296. }
  297. if (QPushButton* const button = ui.buttonBox->button(QDialogButtonBox::Ok))
  298. button->setEnabled(enabled);
  299. }
  300. void loadSettings()
  301. {
  302. const QSafeSettings settings("falkTX", "CarlaAddJackApp");
  303. const QString smName = settings.valueString("SessionManager", "");
  304. if (smName == "LADISH (SIGUSR1)")
  305. ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH);
  306. else if (smName == "NSM")
  307. ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM);
  308. else
  309. ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE);
  310. ui.le_command->setText(settings.valueString("Command", ""));
  311. ui.le_name->setText(settings.valueString("Name", ""));
  312. ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  313. ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  314. ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2));
  315. ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0));
  316. ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0));
  317. ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true));
  318. ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false));
  319. ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false));
  320. checkIfButtonBoxShouldBeEnabled(ui.cb_session_mgr->currentIndex(), ui.le_command->text());
  321. }
  322. void saveSettings()
  323. {
  324. QSafeSettings settings("falkTX", "CarlaAddJackApp");
  325. settings.setValue("Command", ui.le_command->text());
  326. settings.setValue("Name", ui.le_name->text());
  327. settings.setValue("SessionManager", ui.cb_session_mgr->currentText());
  328. settings.setValue("NumAudioIns", ui.sb_audio_ins->value());
  329. settings.setValue("NumAudioOuts", ui.sb_audio_outs->value());
  330. settings.setValue("NumMidiIns", ui.sb_midi_ins->value());
  331. settings.setValue("NumMidiOuts", ui.sb_midi_outs->value());
  332. settings.setValue("ManageWindow", ui.cb_manage_window->isChecked());
  333. settings.setValue("CaptureFirstWindow", ui.cb_capture_first_window->isChecked());
  334. settings.setValue("MidiOutMixdown", ui.cb_out_midi_mixdown->isChecked());
  335. }
  336. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  337. };
  338. JackApplicationW::JackApplicationW(QWidget* parent, const QString& projectFilename)
  339. : QDialog(parent),
  340. self(new PrivateData(this, projectFilename))
  341. {
  342. adjustSize();
  343. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  344. // ----------------------------------------------------------------------------------------------------------------
  345. // Set-up connections
  346. connect(this, SIGNAL(finished(int)), SLOT(slot_saveSettings()));
  347. connect(self->ui.cb_session_mgr, SIGNAL(currentIndexChanged(int)), SLOT(slot_sessionManagerChanged(int)));
  348. connect(self->ui.le_command, SIGNAL(textChanged(QString)), SLOT(slot_commandChanged(QString)));
  349. }
  350. JackApplicationW::~JackApplicationW()
  351. {
  352. delete self;
  353. }
  354. void JackApplicationW::getCommandAndFlags(QString& command, QString& name, QString& labelSetup)
  355. {
  356. name = self->ui.le_name->text();
  357. command = self->ui.le_command->text();
  358. if (name.isEmpty())
  359. {
  360. name = QFileInfo(command.split(' ').first()).baseName();
  361. // FIXME
  362. name[0] = name[0].toTitleCase();
  363. }
  364. SessionManager smgr;
  365. switch (self->ui.cb_session_mgr->currentIndex())
  366. {
  367. case UI_SESSION_LADISH:
  368. smgr = LIBJACK_SESSION_MANAGER_LADISH;
  369. break;
  370. case UI_SESSION_NSM:
  371. smgr = LIBJACK_SESSION_MANAGER_NSM;
  372. break;
  373. default:
  374. smgr = LIBJACK_SESSION_MANAGER_NONE;
  375. break;
  376. }
  377. uint flags = 0x0;
  378. if (self->ui.cb_manage_window->isChecked())
  379. flags |= LIBJACK_FLAG_CONTROL_WINDOW;
  380. if (self->ui.cb_capture_first_window->isChecked())
  381. flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW;
  382. if (self->ui.cb_buffers_addition_mode->isChecked())
  383. flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION;
  384. if (self->ui.cb_out_midi_mixdown->isChecked())
  385. flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN;
  386. if (self->ui.cb_external_start->isChecked())
  387. flags |= LIBJACK_FLAG_EXTERNAL_START;
  388. labelSetup = QString("%1%2%3%4%5%6").arg(QChar('0' + self->ui.sb_audio_ins->value()))
  389. .arg(QChar('0' + self->ui.sb_audio_outs->value()))
  390. .arg(QChar('0' + self->ui.sb_midi_ins->value()))
  391. .arg(QChar('0' + self->ui.sb_midi_outs->value()))
  392. .arg(QChar('0' + smgr))
  393. .arg(QChar('0' + flags));
  394. }
  395. void JackApplicationW::slot_commandChanged(const QString text)
  396. {
  397. self->checkIfButtonBoxShouldBeEnabled(self->ui.cb_session_mgr->currentIndex(), text);
  398. }
  399. void JackApplicationW::slot_sessionManagerChanged(const int index)
  400. {
  401. self->checkIfButtonBoxShouldBeEnabled(index, self->ui.le_command->text());
  402. }
  403. void JackApplicationW::slot_saveSettings()
  404. {
  405. self->saveSettings();
  406. }
  407. // --------------------------------------------------------------------------------------------------------------------