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.

300 lines
10KB

  1. /*
  2. * Carla plugin host
  3. * Copyright (C) 2011-2022 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 "jackappdialog.hpp"
  18. #ifdef __clang__
  19. # pragma clang diagnostic push
  20. # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
  21. # pragma clang diagnostic ignored "-Wdeprecated-register"
  22. #elif defined(__GNUC__) && __GNUC__ >= 8
  23. # pragma GCC diagnostic push
  24. # pragma GCC diagnostic ignored "-Wclass-memaccess"
  25. # pragma GCC diagnostic ignored "-Wdeprecated-copy"
  26. #endif
  27. #include "jackappdialog_ui.hpp"
  28. #include <QtCore/QFileInfo>
  29. #include <QtCore/QVector>
  30. #include <QtWidgets/QPushButton>
  31. #ifdef __clang__
  32. # pragma clang diagnostic pop
  33. #elif defined(__GNUC__) && __GNUC__ >= 8
  34. # pragma GCC diagnostic pop
  35. #endif
  36. #include "qsafesettings.hpp"
  37. #include "CarlaLibJackHints.h"
  38. #include "CarlaString.hpp"
  39. // --------------------------------------------------------------------------------------------------------------------
  40. // Jack Application Dialog
  41. enum {
  42. UI_SESSION_NONE = 0,
  43. UI_SESSION_LADISH = 1,
  44. UI_SESSION_NSM = 2,
  45. };
  46. struct JackAppDialog::Self {
  47. Ui_JackAppDialog ui;
  48. const QString fProjectFilename;
  49. Self(const char* const projectFilename)
  50. : fProjectFilename(projectFilename) {}
  51. static Self& create(const char* const projectFilename)
  52. {
  53. Self* const self = new Self(projectFilename);
  54. return *self;
  55. }
  56. };
  57. JackAppDialog::JackAppDialog(QWidget* const parent, const char* const projectFilename)
  58. : QDialog(parent),
  59. self(Self::create(projectFilename))
  60. {
  61. self.ui.setupUi(this);
  62. // -------------------------------------------------------------------------------------------------------------
  63. // UI setup
  64. self.ui.group_error->setVisible(false);
  65. adjustSize();
  66. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  67. #ifdef CARLA_OS_MAC
  68. if (parent != nullptr)
  69. setWindowModality(Qt::WindowModal);
  70. #endif
  71. // -------------------------------------------------------------------------------------------------------------
  72. // Load settings
  73. loadSettings();
  74. // -------------------------------------------------------------------------------------------------------------
  75. // Set-up connections
  76. connect(this, &QDialog::finished,
  77. this, &JackAppDialog::slot_saveSettings);
  78. connect(self.ui.cb_session_mgr, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
  79. this, &JackAppDialog::slot_sessionManagerChanged);
  80. connect(self.ui.le_command, &QLineEdit::textChanged,
  81. this, &JackAppDialog::slot_commandChanged);
  82. }
  83. JackAppDialog::~JackAppDialog()
  84. {
  85. delete &self;
  86. }
  87. // -----------------------------------------------------------------------------------------------------------------
  88. // public methods
  89. JackAppDialog::CommandAndFlags JackAppDialog::getCommandAndFlags() const
  90. {
  91. const QString command = self.ui.le_command->text();
  92. QString name = self.ui.le_name->text();
  93. if (name.isEmpty())
  94. {
  95. name = QFileInfo(command.split(' ').first()).baseName();
  96. name[0] = name[0].toTitleCase();
  97. }
  98. SessionManager smgr;
  99. switch (self.ui.cb_session_mgr->currentIndex())
  100. {
  101. case UI_SESSION_LADISH:
  102. smgr = LIBJACK_SESSION_MANAGER_LADISH;
  103. break;
  104. case UI_SESSION_NSM:
  105. smgr = LIBJACK_SESSION_MANAGER_NSM;
  106. break;
  107. default:
  108. smgr = LIBJACK_SESSION_MANAGER_NONE;
  109. break;
  110. }
  111. uint flags = 0x0;
  112. if (self.ui.cb_manage_window->isChecked())
  113. flags |= LIBJACK_FLAG_CONTROL_WINDOW;
  114. if (self.ui.cb_capture_first_window->isChecked())
  115. flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW;
  116. if (self.ui.cb_buffers_addition_mode->isChecked())
  117. flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION;
  118. if (self.ui.cb_out_midi_mixdown->isChecked())
  119. flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN;
  120. if (self.ui.cb_external_start->isChecked())
  121. flags |= LIBJACK_FLAG_EXTERNAL_START;
  122. const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + self.ui.sb_audio_ins->value()))
  123. .arg(QChar('0' + self.ui.sb_audio_outs->value()))
  124. .arg(QChar('0' + self.ui.sb_midi_ins->value()))
  125. .arg(QChar('0' + self.ui.sb_midi_outs->value()))
  126. .arg(QChar('0' + smgr))
  127. .arg(QChar('0' + flags)));
  128. return {command, name, labelSetup};
  129. }
  130. // -----------------------------------------------------------------------------------------------------------------
  131. // private methods
  132. void JackAppDialog::checkIfButtonBoxShouldBeEnabled(const int index, const QCarlaString& command)
  133. {
  134. bool enabled = command.isNotEmpty();
  135. QCarlaString showErr;
  136. // NSM applications must not be abstract or absolute paths, and must not contain arguments
  137. if (enabled and index == UI_SESSION_NSM)
  138. {
  139. if (QVector<QChar>{'.', '/'}.contains(command[0]))
  140. showErr = tr("NSM applications cannot use abstract or absolute paths");
  141. else if (command.contains(' ') or command.contains(';') or command.contains('&'))
  142. showErr = tr("NSM applications cannot use CLI arguments");
  143. else if (self.fProjectFilename.isEmpty())
  144. showErr = tr("You need to save the current Carla project before NSM can be used");
  145. }
  146. if (showErr.isNotEmpty())
  147. {
  148. enabled = false;
  149. self.ui.l_error->setText(showErr);
  150. self.ui.group_error->setVisible(true);
  151. }
  152. else
  153. {
  154. self.ui.group_error->setVisible(false);
  155. }
  156. if (QPushButton* const button = self.ui.buttonBox->button(QDialogButtonBox::Ok))
  157. button->setEnabled(enabled);
  158. }
  159. void JackAppDialog::loadSettings()
  160. {
  161. const QSafeSettings settings("falkTX", "CarlaAddJackApp");
  162. const QString smName = settings.valueString("SessionManager", "");
  163. if (smName == "LADISH (SIGUSR1)")
  164. self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH);
  165. else if (smName == "NSM")
  166. self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM);
  167. else
  168. self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE);
  169. self.ui.le_command->setText(settings.valueString("Command", ""));
  170. self.ui.le_name->setText(settings.valueString("Name", ""));
  171. self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  172. self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2));
  173. self.ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2));
  174. self.ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0));
  175. self.ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0));
  176. self.ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true));
  177. self.ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false));
  178. self.ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false));
  179. checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(),
  180. self.ui.le_command->text());
  181. }
  182. // -----------------------------------------------------------------------------------------------------------------
  183. // private slots
  184. void JackAppDialog::slot_commandChanged(const QString& command)
  185. {
  186. checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), command);
  187. }
  188. void JackAppDialog::slot_sessionManagerChanged(const int index)
  189. {
  190. checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command->text());
  191. }
  192. void JackAppDialog::slot_saveSettings()
  193. {
  194. QSafeSettings settings("falkTX", "CarlaAddJackApp");
  195. settings.setValue("Command", self.ui.le_command->text());
  196. settings.setValue("Name", self.ui.le_name->text());
  197. settings.setValue("SessionManager", self.ui.cb_session_mgr->currentText());
  198. settings.setValue("NumAudioIns", self.ui.sb_audio_ins->value());
  199. settings.setValue("NumAudioOuts", self.ui.sb_audio_outs->value());
  200. settings.setValue("NumMidiIns", self.ui.sb_midi_ins->value());
  201. settings.setValue("NumMidiOuts", self.ui.sb_midi_outs->value());
  202. settings.setValue("ManageWindow", self.ui.cb_manage_window->isChecked());
  203. settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window->isChecked());
  204. settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown->isChecked());
  205. }
  206. // --------------------------------------------------------------------------------------------------------------------
  207. JackAppDialogResults* carla_frontend_createAndExecJackAppDialog(void* const parent, const char* const projectFilename)
  208. {
  209. JackAppDialog gui(reinterpret_cast<QWidget*>(parent), projectFilename);
  210. if (gui.exec())
  211. {
  212. static JackAppDialogResults ret = {};
  213. static CarlaString retCommand;
  214. static CarlaString retName;
  215. static CarlaString retLabelSetup;
  216. const JackAppDialog::CommandAndFlags cafs = gui.getCommandAndFlags();
  217. retCommand = cafs.command.toUtf8().constData();
  218. retName = cafs.name.toUtf8().constData();
  219. retLabelSetup = cafs.labelSetup.toUtf8().constData();
  220. ret.command = retCommand;
  221. ret.name = retName;
  222. ret.labelSetup = retLabelSetup;
  223. return &ret;
  224. }
  225. return nullptr;
  226. }
  227. #if 0
  228. // --------------------------------------------------------------------------------------------------------------------
  229. // Testing
  230. #include "../utils/qsafesettings.cpp"
  231. int main(int argc, char* argv[])
  232. {
  233. QApplication app(argc, argv);
  234. if (JackAppDialogResults* const res = carla_frontend_createAndExecJackAppDialog(nullptr, ""))
  235. {
  236. printf("Results:\n");
  237. printf("\tCommand: %s\n", res->command);
  238. printf("\tName: %s\n", res->name);
  239. printf("\tLabelSetup: %s\n", res->labelSetup);
  240. }
  241. return 0;
  242. }
  243. #endif
  244. // --------------------------------------------------------------------------------------------------------------------