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.

505 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->fThread, SIGNAL(pluginLook(float, QString)), SLOT(slot_handlePluginLook(float, QString)));
  173. connect(&self->fThread, SIGNAL(finished(int)), SLOT(slot_handlePluginThreadFinished()));
  174. // ----------------------------------------------------------------------------------------------------------------
  175. // Post-connect setup
  176. slot_checkTools();
  177. }
  178. PluginRefreshW::~PluginRefreshW()
  179. {
  180. delete self;
  181. }
  182. void PluginRefreshW::getValues(QString& audioDevice, uint& bufferSize, double& sampleRate)
  183. {
  184. }
  185. void PluginRefreshW::closeEvent(QCloseEvent* event)
  186. {
  187. }
  188. void PluginRefreshW::slot_saveSettings()
  189. {
  190. }
  191. void PluginRefreshW::slot_start()
  192. {
  193. }
  194. void PluginRefreshW::slot_skip()
  195. {
  196. }
  197. void PluginRefreshW::slot_checkTools()
  198. {
  199. }
  200. void PluginRefreshW::slot_handlePluginLook(float percent, QString plugin)
  201. {
  202. }
  203. void PluginRefreshW::slot_handlePluginThreadFinished()
  204. {
  205. }
  206. // --------------------------------------------------------------------------------------------------------------------
  207. // Plugin Database Dialog
  208. struct PluginDatabaseW::PrivateData {
  209. PrivateData(void*)
  210. {
  211. }
  212. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  213. };
  214. PluginDatabaseW::PluginDatabaseW(QWidget* parent, const CarlaHost& host, bool hasCanvas, bool hasCanvasGL)
  215. : QDialog(parent),
  216. self(new PrivateData(this))
  217. {
  218. }
  219. PluginDatabaseW::~PluginDatabaseW()
  220. {
  221. delete self;
  222. }
  223. void PluginDatabaseW::showEvent(QShowEvent* event)
  224. {
  225. }
  226. void PluginDatabaseW::slot_cellClicked(int row, int column)
  227. {
  228. }
  229. void PluginDatabaseW::slot_cellDoubleClicked(int row, int column)
  230. {
  231. }
  232. void PluginDatabaseW::slot_addPlugin()
  233. {
  234. }
  235. void PluginDatabaseW::slot_checkPlugin(int row)
  236. {
  237. }
  238. void PluginDatabaseW::slot_checkFilters()
  239. {
  240. }
  241. void PluginDatabaseW::slot_refreshPlugins()
  242. {
  243. }
  244. void PluginDatabaseW::slot_clearFilters()
  245. {
  246. }
  247. void PluginDatabaseW::slot_saveSettings()
  248. {
  249. }
  250. // --------------------------------------------------------------------------------------------------------------------
  251. // Jack Application Dialog
  252. // NOTE: index matches the one in the UI
  253. enum UiSessionManager {
  254. UI_SESSION_NONE,
  255. UI_SESSION_LADISH,
  256. UI_SESSION_NSM
  257. };
  258. struct JackApplicationW::PrivateData {
  259. Ui::Dialog ui;
  260. const QString fProjectFilename;
  261. PrivateData(JackApplicationW* const dialog, const QString& projectFilename)
  262. : ui(),
  263. fProjectFilename(projectFilename)
  264. {
  265. ui.setupUi(dialog);
  266. ui.group_error->setVisible(false);
  267. // ------------------------------------------------------------------------------------------------------------
  268. // Load settings
  269. loadSettings();
  270. }
  271. void checkIfButtonBoxShouldBeEnabled(int index, const QString text)
  272. {
  273. static QList<QChar> badFirstChars = { '.', '/' };
  274. bool enabled = text.length() > 0;
  275. QCarlaString showErr;
  276. // NSM applications must not be abstract or absolute paths, and must not contain arguments
  277. if (enabled && index == UI_SESSION_NSM)
  278. {
  279. if (badFirstChars.contains(text[0]))
  280. showErr = tr("NSM applications cannot use abstract or absolute paths");
  281. else if (text.contains(' ') || text.contains(';') || text.contains('&'))
  282. showErr = tr("NSM applications cannot use CLI arguments");
  283. else if (fProjectFilename.isEmpty())
  284. showErr = tr("You need to save the current Carla project before NSM can be used");
  285. }
  286. if (showErr.isNotEmpty())
  287. {
  288. enabled = false;
  289. ui.l_error->setText(showErr);
  290. ui.group_error->setVisible(true);
  291. }
  292. else
  293. {
  294. ui.group_error->setVisible(false);
  295. }
  296. if (QPushButton* const button = ui.buttonBox->button(QDialogButtonBox::Ok))
  297. button->setEnabled(enabled);
  298. }
  299. void loadSettings()
  300. {
  301. const QSafeSettings settings("falkTX", "CarlaAddJackApp");
  302. const QString smName = settings.valueString("SessionManager", "");
  303. if (smName == "LADISH (SIGUSR1)")
  304. ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH);
  305. else if (smName == "NSM")
  306. ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM);
  307. else
  308. ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE);
  309. ui.le_command->setText(settings.valueString("Command", ""));
  310. ui.le_name->setText(settings.valueString("Name", ""));
  311. ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  312. ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  313. ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2));
  314. ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0));
  315. ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0));
  316. ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true));
  317. ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false));
  318. ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false));
  319. checkIfButtonBoxShouldBeEnabled(ui.cb_session_mgr->currentIndex(), ui.le_command->text());
  320. }
  321. void saveSettings()
  322. {
  323. QSafeSettings settings("falkTX", "CarlaAddJackApp");
  324. settings.setValue("Command", ui.le_command->text());
  325. settings.setValue("Name", ui.le_name->text());
  326. settings.setValue("SessionManager", ui.cb_session_mgr->currentText());
  327. settings.setValue("NumAudioIns", ui.sb_audio_ins->value());
  328. settings.setValue("NumAudioOuts", ui.sb_audio_outs->value());
  329. settings.setValue("NumMidiIns", ui.sb_midi_ins->value());
  330. settings.setValue("NumMidiOuts", ui.sb_midi_outs->value());
  331. settings.setValue("ManageWindow", ui.cb_manage_window->isChecked());
  332. settings.setValue("CaptureFirstWindow", ui.cb_capture_first_window->isChecked());
  333. settings.setValue("MidiOutMixdown", ui.cb_out_midi_mixdown->isChecked());
  334. }
  335. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  336. };
  337. JackApplicationW::JackApplicationW(QWidget* parent, const QString& projectFilename)
  338. : QDialog(parent),
  339. self(new PrivateData(this, projectFilename))
  340. {
  341. adjustSize();
  342. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  343. // ----------------------------------------------------------------------------------------------------------------
  344. // Set-up connections
  345. connect(this, SIGNAL(finished(int)), SLOT(slot_saveSettings()));
  346. connect(self->ui.cb_session_mgr, SIGNAL(currentIndexChanged(int)), SLOT(slot_sessionManagerChanged(int)));
  347. connect(self->ui.le_command, SIGNAL(textChanged(QString)), SLOT(slot_commandChanged(QString)));
  348. }
  349. JackApplicationW::~JackApplicationW()
  350. {
  351. delete self;
  352. }
  353. void JackApplicationW::getCommandAndFlags(QString& command, QString& name, QString& labelSetup)
  354. {
  355. name = self->ui.le_name->text();
  356. command = self->ui.le_command->text();
  357. if (name.isEmpty())
  358. {
  359. name = QFileInfo(command.split(' ').first()).baseName();
  360. // FIXME
  361. name[0] = name[0].toTitleCase();
  362. }
  363. SessionManager smgr;
  364. switch (self->ui.cb_session_mgr->currentIndex())
  365. {
  366. case UI_SESSION_LADISH:
  367. smgr = LIBJACK_SESSION_MANAGER_LADISH;
  368. break;
  369. case UI_SESSION_NSM:
  370. smgr = LIBJACK_SESSION_MANAGER_NSM;
  371. break;
  372. default:
  373. smgr = LIBJACK_SESSION_MANAGER_NONE;
  374. break;
  375. }
  376. uint flags = 0x0;
  377. if (self->ui.cb_manage_window->isChecked())
  378. flags |= LIBJACK_FLAG_CONTROL_WINDOW;
  379. if (self->ui.cb_capture_first_window->isChecked())
  380. flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW;
  381. if (self->ui.cb_buffers_addition_mode->isChecked())
  382. flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION;
  383. if (self->ui.cb_out_midi_mixdown->isChecked())
  384. flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN;
  385. if (self->ui.cb_external_start->isChecked())
  386. flags |= LIBJACK_FLAG_EXTERNAL_START;
  387. labelSetup = QString("%1%2%3%4%5%6").arg(QChar('0' + self->ui.sb_audio_ins->value()))
  388. .arg(QChar('0' + self->ui.sb_audio_outs->value()))
  389. .arg(QChar('0' + self->ui.sb_midi_ins->value()))
  390. .arg(QChar('0' + self->ui.sb_midi_outs->value()))
  391. .arg(QChar('0' + smgr))
  392. .arg(QChar('0' + flags));
  393. }
  394. void JackApplicationW::slot_commandChanged(const QString text)
  395. {
  396. self->checkIfButtonBoxShouldBeEnabled(self->ui.cb_session_mgr->currentIndex(), text);
  397. }
  398. void JackApplicationW::slot_sessionManagerChanged(const int index)
  399. {
  400. self->checkIfButtonBoxShouldBeEnabled(index, self->ui.le_command->text());
  401. }
  402. void JackApplicationW::slot_saveSettings()
  403. {
  404. self->saveSettings();
  405. }
  406. // --------------------------------------------------------------------------------------------------------------------