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.

1389 lines
55KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla plugin host
  4. # Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the GPL.txt file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from PyQt4.QtCore import Qt, QSize
  20. from PyQt4.QtGui import QApplication, QDialogButtonBox, QMainWindow
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Custom Stuff)
  23. import ui_carla
  24. import ui_carla_settings
  25. from carla_backend import * # FIXME, remove later
  26. #from carla_shared import *
  27. # ------------------------------------------------------------------------------------------------------------
  28. # Global variables
  29. # Tab indexes
  30. TAB_INDEX_MAIN = 0
  31. TAB_INDEX_CANVAS = 1
  32. TAB_INDEX_CARLA_ENGINE = 2
  33. TAB_INDEX_CARLA_PATHS = 3
  34. TAB_INDEX_NONE = 4
  35. # Single and Multiple client mode is only for JACK,
  36. # but we still want to match QComboBox index to defines,
  37. # so add +2 pos padding if driverName != "JACK".
  38. PROCESS_MODE_NON_JACK_PADDING = 2
  39. # Carla defaults
  40. CARLA_DEFAULT_PROCESS_HIGH_PRECISION = False
  41. CARLA_DEFAULT_MAX_PARAMETERS = 200
  42. CARLA_DEFAULT_FORCE_STEREO = False
  43. CARLA_DEFAULT_USE_DSSI_VST_CHUNKS = False
  44. CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES = False
  45. CARLA_DEFAULT_PREFER_UI_BRIDGES = True
  46. CARLA_DEFAULT_OSC_UI_TIMEOUT = 4000
  47. CARLA_DEFAULT_DISABLE_CHECKS = False
  48. # PatchCanvas defines
  49. CANVAS_ANTIALIASING_SMALL = 1
  50. CANVAS_EYECANDY_SMALL = 1
  51. # ------------------------------------------------------------------------------------------------------------
  52. # Settings Dialog
  53. class CarlaSettingsW(QDialog):
  54. def __init__(self, parent):
  55. QDialog.__init__(self, parent)
  56. self.ui = ui_carla_settings.Ui_CarlaSettingsW()
  57. self.ui.setupUi(self)
  58. # TODO
  59. self.ui.lw_page.hideRow(TAB_INDEX_CANVAS)
  60. # -------------------------------------------------------------
  61. # Load settings
  62. self.loadSettings()
  63. # -------------------------------------------------------------
  64. # Set-up GUI
  65. driverCount = Carla.host.get_engine_driver_count()
  66. for i in range(driverCount):
  67. driverName = cString(Carla.host.get_engine_driver_name(i))
  68. self.ui.cb_engine_audio_driver.addItem(driverName)
  69. #if not hasOpenGL:
  70. #self.cb_canvas_use_opengl.setChecked(False)
  71. #self.cb_canvas_use_opengl.setEnabled(False)
  72. # -------------------------------------------------------------
  73. # Set-up connections
  74. self.connect(self, SIGNAL("accepted()"), SLOT("slot_saveSettings()"))
  75. self.connect(self.ui.buttonBox.button(QDialogButtonBox.Reset), SIGNAL("clicked()"), SLOT("slot_resetSettings()"))
  76. self.connect(self.ui.b_main_def_folder_open, SIGNAL("clicked()"), SLOT("slot_getAndSetProjectPath()"))
  77. self.connect(self.ui.cb_engine_audio_driver, SIGNAL("currentIndexChanged(int)"), SLOT("slot_engineAudioDriverChanged()"))
  78. self.connect(self.ui.b_paths_add, SIGNAL("clicked()"), SLOT("slot_addPluginPath()"))
  79. self.connect(self.ui.b_paths_remove, SIGNAL("clicked()"), SLOT("slot_removePluginPath()"))
  80. self.connect(self.ui.b_paths_change, SIGNAL("clicked()"), SLOT("slot_changePluginPath()"))
  81. self.connect(self.ui.tw_paths, SIGNAL("currentChanged(int)"), SLOT("slot_pluginPathTabChanged(int)"))
  82. self.connect(self.ui.lw_ladspa, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  83. self.connect(self.ui.lw_dssi, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  84. self.connect(self.ui.lw_lv2, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  85. self.connect(self.ui.lw_vst, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  86. self.connect(self.ui.lw_sf2, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  87. # -------------------------------------------------------------
  88. # Post-connect setup
  89. self.ui.lw_ladspa.setCurrentRow(0)
  90. self.ui.lw_dssi.setCurrentRow(0)
  91. self.ui.lw_lv2.setCurrentRow(0)
  92. self.ui.lw_vst.setCurrentRow(0)
  93. self.ui.lw_gig.setCurrentRow(0)
  94. self.ui.lw_sf2.setCurrentRow(0)
  95. self.ui.lw_sfz.setCurrentRow(0)
  96. #QTimer.singleShot(0, self, )
  97. #self.slot_pluginPathTabChanged(self.tw_paths.currentIndex())
  98. self.ui.lw_page.setCurrentCell(0, 0)
  99. def loadSettings(self):
  100. settings = QSettings()
  101. # ---------------------------------------
  102. self.ui.le_main_def_folder.setText(settings.value("Main/DefaultProjectFolder", HOME, type=str))
  103. self.ui.sb_gui_refresh.setValue(settings.value("Main/RefreshInterval", 50, type=int))
  104. # ---------------------------------------
  105. self.ui.cb_canvas_hide_groups.setChecked(settings.value("Canvas/AutoHideGroups", False, type=bool))
  106. self.ui.cb_canvas_bezier_lines.setChecked(settings.value("Canvas/UseBezierLines", True, type=bool))
  107. self.ui.cb_canvas_eyecandy.setCheckState(settings.value("Canvas/EyeCandy", CANVAS_EYECANDY_SMALL, type=int))
  108. self.ui.cb_canvas_use_opengl.setChecked(settings.value("Canvas/UseOpenGL", False, type=bool))
  109. self.ui.cb_canvas_render_aa.setCheckState(settings.value("Canvas/Antialiasing", CANVAS_ANTIALIASING_SMALL, type=int))
  110. self.ui.cb_canvas_render_hq_aa.setChecked(settings.value("Canvas/HighQualityAntialiasing", False, type=bool))
  111. #themeName = settings.value("Canvas/Theme", getDefaultThemeName(), type=str)
  112. #for i in range(Theme.THEME_MAX):
  113. #thisThemeName = getThemeName(i)
  114. #self.ui.cb_canvas_theme.addItem(thisThemeName)
  115. #if thisThemeName == themeName:
  116. #self.ui.cb_canvas_theme.setCurrentIndex(i)
  117. # --------------------------------------------
  118. audioDriver = settings.value("Engine/AudioDriver", "JACK", type=str)
  119. for i in range(self.ui.cb_engine_audio_driver.count()):
  120. if self.ui.cb_engine_audio_driver.itemText(i) == audioDriver:
  121. self.ui.cb_engine_audio_driver.setCurrentIndex(i)
  122. break
  123. else:
  124. self.ui.cb_engine_audio_driver.setCurrentIndex(-1)
  125. if audioDriver == "JACK":
  126. processModeIndex = settings.value("Engine/ProcessMode", PROCESS_MODE_MULTIPLE_CLIENTS, type=int)
  127. self.ui.cb_engine_process_mode_jack.setCurrentIndex(processModeIndex)
  128. self.ui.sw_engine_process_mode.setCurrentIndex(0)
  129. else:
  130. processModeIndex = settings.value("Engine/ProcessMode", PROCESS_MODE_CONTINUOUS_RACK, type=int)
  131. processModeIndex -= PROCESS_MODE_NON_JACK_PADDING
  132. self.ui.cb_engine_process_mode_other.setCurrentIndex(processModeIndex)
  133. self.ui.sw_engine_process_mode.setCurrentIndex(1)
  134. self.ui.sb_engine_max_params.setValue(settings.value("Engine/MaxParameters", CARLA_DEFAULT_MAX_PARAMETERS, type=int))
  135. self.ui.ch_engine_prefer_ui_bridges.setChecked(settings.value("Engine/PreferUiBridges", CARLA_DEFAULT_PREFER_UI_BRIDGES, type=bool))
  136. self.ui.sb_engine_oscgui_timeout.setValue(settings.value("Engine/OscUiTimeout", CARLA_DEFAULT_OSC_UI_TIMEOUT, type=int))
  137. self.ui.ch_engine_disable_checks.setChecked(settings.value("Engine/DisableChecks", CARLA_DEFAULT_DISABLE_CHECKS, type=bool))
  138. self.ui.ch_engine_dssi_chunks.setChecked(settings.value("Engine/UseDssiVstChunks", CARLA_DEFAULT_USE_DSSI_VST_CHUNKS, type=bool))
  139. self.ui.ch_engine_prefer_plugin_bridges.setChecked(settings.value("Engine/PreferPluginBridges", CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, type=bool))
  140. self.ui.ch_engine_force_stereo.setChecked(settings.value("Engine/ForceStereo", CARLA_DEFAULT_FORCE_STEREO, type=bool))
  141. # --------------------------------------------
  142. ladspas = toList(settings.value("Paths/LADSPA", Carla.LADSPA_PATH))
  143. dssis = toList(settings.value("Paths/DSSI", Carla.DSSI_PATH))
  144. lv2s = toList(settings.value("Paths/LV2", Carla.LV2_PATH))
  145. vsts = toList(settings.value("Paths/VST", Carla.VST_PATH))
  146. gigs = toList(settings.value("Paths/GIG", Carla.GIG_PATH))
  147. sf2s = toList(settings.value("Paths/SF2", Carla.SF2_PATH))
  148. sfzs = toList(settings.value("Paths/SFZ", Carla.SFZ_PATH))
  149. ladspas.sort()
  150. dssis.sort()
  151. lv2s.sort()
  152. vsts.sort()
  153. gigs.sort()
  154. sf2s.sort()
  155. sfzs.sort()
  156. for ladspa in ladspas:
  157. self.ui.lw_ladspa.addItem(ladspa)
  158. for dssi in dssis:
  159. self.ui.lw_dssi.addItem(dssi)
  160. for lv2 in lv2s:
  161. self.ui.lw_lv2.addItem(lv2)
  162. for vst in vsts:
  163. self.ui.lw_vst.addItem(vst)
  164. for gig in gigs:
  165. self.ui.lw_gig.addItem(gig)
  166. for sf2 in sf2s:
  167. self.ui.lw_sf2.addItem(sf2)
  168. for sfz in sfzs:
  169. self.ui.lw_sfz.addItem(sfz)
  170. @pyqtSlot()
  171. def slot_saveSettings(self):
  172. settings = QSettings()
  173. # ---------------------------------------
  174. settings.setValue("Main/RefreshInterval", self.ui.sb_gui_refresh.value())
  175. settings.setValue("Main/DefaultProjectFolder", self.ui.le_main_def_folder.text())
  176. # ---------------------------------------
  177. #settings.setValue("Canvas/Theme", self.ui.cb_canvas_theme.currentText())
  178. #settings.setValue("Canvas/AutoHideGroups", self.ui.cb_canvas_hide_groups.isChecked())
  179. #settings.setValue("Canvas/UseBezierLines", self.ui.cb_canvas_bezier_lines.isChecked())
  180. #settings.setValue("Canvas/UseOpenGL", self.ui.cb_canvas_use_opengl.isChecked())
  181. #settings.setValue("Canvas/HighQualityAntialiasing", self.ui.cb_canvas_render_hq_aa.isChecked())
  182. ## 0, 1, 2 match their enum variants
  183. #settings.setValue("Canvas/EyeCandy", self.ui.cb_canvas_eyecandy.checkState())
  184. #settings.setValue("Canvas/Antialiasing", self.ui.cb_canvas_render_aa.checkState())
  185. # --------------------------------------------
  186. audioDriver = self.ui.cb_engine_audio_driver.currentText()
  187. settings.setValue("Engine/AudioDriver", audioDriver)
  188. if audioDriver == "JACK":
  189. settings.setValue("Engine/ProcessMode", self.ui.cb_engine_process_mode_jack.currentIndex())
  190. else:
  191. settings.setValue("Engine/ProcessMode", self.ui.cb_engine_process_mode_other.currentIndex()+PROCESS_MODE_NON_JACK_PADDING)
  192. settings.setValue("Engine/MaxParameters", self.ui.sb_engine_max_params.value())
  193. settings.setValue("Engine/PreferUiBridges", self.ui.ch_engine_prefer_ui_bridges.isChecked())
  194. settings.setValue("Engine/OscUiTimeout", self.ui.sb_engine_oscgui_timeout.value())
  195. settings.setValue("Engine/DisableChecks", self.ui.ch_engine_disable_checks.isChecked())
  196. settings.setValue("Engine/UseDssiVstChunks", self.ui.ch_engine_dssi_chunks.isChecked())
  197. settings.setValue("Engine/PreferPluginBridges", self.ui.ch_engine_prefer_plugin_bridges.isChecked())
  198. settings.setValue("Engine/ForceStereo", self.ui.ch_engine_force_stereo.isChecked())
  199. # --------------------------------------------
  200. # FIXME - find a cleaner way to do this, *.items() ?
  201. ladspas = []
  202. dssis = []
  203. lv2s = []
  204. vsts = []
  205. gigs = []
  206. sf2s = []
  207. sfzs = []
  208. for i in range(self.ui.lw_ladspa.count()):
  209. ladspas.append(self.ui.lw_ladspa.item(i).text())
  210. for i in range(self.ui.lw_dssi.count()):
  211. dssis.append(self.ui.lw_dssi.item(i).text())
  212. for i in range(self.ui.lw_lv2.count()):
  213. lv2s.append(self.ui.lw_lv2.item(i).text())
  214. for i in range(self.ui.lw_vst.count()):
  215. vsts.append(self.ui.lw_vst.item(i).text())
  216. for i in range(self.ui.lw_gig.count()):
  217. gigs.append(self.ui.lw_gig.item(i).text())
  218. for i in range(self.ui.lw_sf2.count()):
  219. sf2s.append(self.ui.lw_sf2.item(i).text())
  220. for i in range(self.ui.lw_sfz.count()):
  221. sfzs.append(self.ui.lw_sfz.item(i).text())
  222. settings.setValue("Paths/LADSPA", ladspas)
  223. settings.setValue("Paths/DSSI", dssis)
  224. settings.setValue("Paths/LV2", lv2s)
  225. settings.setValue("Paths/VST", vsts)
  226. settings.setValue("Paths/GIG", gigs)
  227. settings.setValue("Paths/SF2", sf2s)
  228. settings.setValue("Paths/SFZ", sfzs)
  229. @pyqtSlot()
  230. def slot_resetSettings(self):
  231. if self.ui.lw_page.currentRow() == TAB_INDEX_MAIN:
  232. self.ui.le_main_def_folder.setText(HOME)
  233. self.ui.sb_gui_refresh.setValue(50)
  234. elif self.ui.lw_page.currentRow() == TAB_INDEX_CANVAS:
  235. self.ui.cb_canvas_theme.setCurrentIndex(0)
  236. self.ui.cb_canvas_hide_groups.setChecked(False)
  237. self.ui.cb_canvas_bezier_lines.setChecked(True)
  238. self.ui.cb_canvas_eyecandy.setCheckState(Qt.PartiallyChecked)
  239. self.ui.cb_canvas_use_opengl.setChecked(False)
  240. self.ui.cb_canvas_render_aa.setCheckState(Qt.PartiallyChecked)
  241. self.ui.cb_canvas_render_hq_aa.setChecked(False)
  242. elif self.ui.lw_page.currentRow() == TAB_INDEX_CARLA_ENGINE:
  243. self.ui.cb_engine_audio_driver.setCurrentIndex(0)
  244. self.ui.sb_engine_max_params.setValue(CARLA_DEFAULT_MAX_PARAMETERS)
  245. self.ui.ch_engine_prefer_ui_bridges.setChecked(CARLA_DEFAULT_PREFER_UI_BRIDGES)
  246. self.ui.sb_engine_oscgui_timeout.setValue(CARLA_DEFAULT_OSC_UI_TIMEOUT)
  247. self.ui.ch_engine_disable_checks.setChecked(CARLA_DEFAULT_DISABLE_CHECKS)
  248. self.ui.ch_engine_dssi_chunks.setChecked(CARLA_DEFAULT_USE_DSSI_VST_CHUNKS)
  249. self.ui.ch_engine_prefer_plugin_bridges.setChecked(CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES)
  250. self.ui.ch_engine_force_stereo.setChecked(CARLA_DEFAULT_FORCE_STEREO)
  251. if self.ui.cb_engine_audio_driver.currentText() == "JACK":
  252. self.ui.cb_engine_process_mode_jack.setCurrentIndex(PROCESS_MODE_MULTIPLE_CLIENTS)
  253. self.ui.sw_engine_process_mode.setCurrentIndex(0)
  254. else:
  255. self.ui.cb_engine_process_mode_other.setCurrentIndex(PROCESS_MODE_CONTINUOUS_RACK-PROCESS_MODE_NON_JACK_PADDING)
  256. self.ui.sw_engine_process_mode.setCurrentIndex(1)
  257. elif self.ui.lw_page.currentRow() == TAB_INDEX_CARLA_PATHS:
  258. if self.ui.tw_paths.currentIndex() == 0:
  259. Carla.LADSPA_PATH.sort()
  260. self.ui.lw_ladspa.clear()
  261. for ladspa in Carla.LADSPA_PATH:
  262. self.ui.lw_ladspa.addItem(ladspa)
  263. elif self.ui.tw_paths.currentIndex() == 1:
  264. Carla.DSSI_PATH.sort()
  265. self.ui.lw_dssi.clear()
  266. for dssi in Carla.DSSI_PATH:
  267. self.ui.lw_dssi.addItem(dssi)
  268. elif self.ui.tw_paths.currentIndex() == 2:
  269. Carla.LV2_PATH.sort()
  270. self.ui.lw_lv2.clear()
  271. for lv2 in Carla.LV2_PATH:
  272. self.ui.lw_lv2.addItem(lv2)
  273. elif self.ui.tw_paths.currentIndex() == 3:
  274. Carla.VST_PATH.sort()
  275. self.ui.lw_vst.clear()
  276. for vst in Carla.VST_PATH:
  277. self.ui.lw_vst.addItem(vst)
  278. elif self.ui.tw_paths.currentIndex() == 4:
  279. Carla.GIG_PATH.sort()
  280. self.ui.lw_gig.clear()
  281. for gig in Carla.GIG_PATH:
  282. self.ui.lw_gig.addItem(gig)
  283. elif self.ui.tw_paths.currentIndex() == 5:
  284. Carla.SF2_PATH.sort()
  285. self.ui.lw_sf2.clear()
  286. for sf2 in Carla.SF2_PATH:
  287. self.ui.lw_sf2.addItem(sf2)
  288. elif self.ui.tw_paths.currentIndex() == 6:
  289. Carla.SFZ_PATH.sort()
  290. self.ui.lw_sfz.clear()
  291. for sfz in Carla.SFZ_PATH:
  292. self.ui.lw_sfz.addItem(sfz)
  293. @pyqtSlot()
  294. def slot_getAndSetProjectPath(self):
  295. pass #getAndSetPath(self, self.le_main_def_folder.text(), self.le_main_def_folder)
  296. @pyqtSlot()
  297. def slot_engineAudioDriverChanged(self):
  298. if self.ui.cb_engine_audio_driver.currentText() == "JACK":
  299. self.ui.sw_engine_process_mode.setCurrentIndex(0)
  300. else:
  301. self.ui.sw_engine_process_mode.setCurrentIndex(1)
  302. @pyqtSlot()
  303. def slot_addPluginPath(self):
  304. newPath = QFileDialog.getExistingDirectory(self, self.tr("Add Path"), "", QFileDialog.ShowDirsOnly)
  305. if newPath:
  306. if self.ui.tw_paths.currentIndex() == 0:
  307. self.ui.lw_ladspa.addItem(newPath)
  308. elif self.ui.tw_paths.currentIndex() == 1:
  309. self.ui.lw_dssi.addItem(newPath)
  310. elif self.ui.tw_paths.currentIndex() == 2:
  311. self.ui.lw_lv2.addItem(newPath)
  312. elif self.ui.tw_paths.currentIndex() == 3:
  313. self.ui.lw_vst.addItem(newPath)
  314. elif self.ui.tw_paths.currentIndex() == 4:
  315. self.ui.lw_gig.addItem(newPath)
  316. elif self.ui.tw_paths.currentIndex() == 5:
  317. self.ui.lw_sf2.addItem(newPath)
  318. elif self.ui.tw_paths.currentIndex() == 6:
  319. self.ui.lw_sfz.addItem(newPath)
  320. @pyqtSlot()
  321. def slot_removePluginPath(self):
  322. if self.ui.tw_paths.currentIndex() == 0:
  323. self.ui.lw_ladspa.takeItem(self.ui.lw_ladspa.currentRow())
  324. elif self.ui.tw_paths.currentIndex() == 1:
  325. self.ui.lw_dssi.takeItem(self.ui.lw_dssi.currentRow())
  326. elif self.ui.tw_paths.currentIndex() == 2:
  327. self.ui.lw_lv2.takeItem(self.ui.lw_lv2.currentRow())
  328. elif self.ui.tw_paths.currentIndex() == 3:
  329. self.ui.lw_vst.takeItem(self.ui.lw_vst.currentRow())
  330. elif self.ui.tw_paths.currentIndex() == 4:
  331. self.ui.lw_gig.takeItem(self.ui.lw_gig.currentRow())
  332. elif self.ui.tw_paths.currentIndex() == 5:
  333. self.ui.lw_sf2.takeItem(self.ui.lw_sf2.currentRow())
  334. elif self.ui.tw_paths.currentIndex() == 6:
  335. self.ui.lw_sfz.takeItem(self.ui.lw_sfz.currentRow())
  336. @pyqtSlot()
  337. def slot_changePluginPath(self):
  338. if self.ui.tw_paths.currentIndex() == 0:
  339. currentPath = self.ui.lw_ladspa.currentItem().text()
  340. elif self.ui.tw_paths.currentIndex() == 1:
  341. currentPath = self.ui.lw_dssi.currentItem().text()
  342. elif self.ui.tw_paths.currentIndex() == 2:
  343. currentPath = self.ui.lw_lv2.currentItem().text()
  344. elif self.ui.tw_paths.currentIndex() == 3:
  345. currentPath = self.ui.lw_vst.currentItem().text()
  346. elif self.ui.tw_paths.currentIndex() == 4:
  347. currentPath = self.ui.lw_gig.currentItem().text()
  348. elif self.ui.tw_paths.currentIndex() == 5:
  349. currentPath = self.ui.lw_sf2.currentItem().text()
  350. elif self.ui.tw_paths.currentIndex() == 6:
  351. currentPath = self.ui.lw_sfz.currentItem().text()
  352. else:
  353. currentPath = ""
  354. newPath = QFileDialog.getExistingDirectory(self, self.tr("Add Path"), currentPath, QFileDialog.ShowDirsOnly)
  355. if newPath:
  356. if self.ui.tw_paths.currentIndex() == 0:
  357. self.ui.lw_ladspa.currentItem().setText(newPath)
  358. elif self.ui.tw_paths.currentIndex() == 1:
  359. self.ui.lw_dssi.currentItem().setText(newPath)
  360. elif self.ui.tw_paths.currentIndex() == 2:
  361. self.ui.lw_lv2.currentItem().setText(newPath)
  362. elif self.ui.tw_paths.currentIndex() == 3:
  363. self.ui.lw_vst.currentItem().setText(newPath)
  364. elif self.ui.tw_paths.currentIndex() == 4:
  365. self.ui.lw_gig.currentItem().setText(newPath)
  366. elif self.ui.tw_paths.currentIndex() == 5:
  367. self.ui.lw_sf2.currentItem().setText(newPath)
  368. elif self.ui.tw_paths.currentIndex() == 6:
  369. self.ui.lw_sfz.currentItem().setText(newPath)
  370. @pyqtSlot(int)
  371. def slot_pluginPathTabChanged(self, index):
  372. if index == 0:
  373. row = self.ui.lw_ladspa.currentRow()
  374. elif index == 1:
  375. row = self.ui.lw_dssi.currentRow()
  376. elif index == 2:
  377. row = self.ui.lw_lv2.currentRow()
  378. elif index == 3:
  379. row = self.ui.lw_vst.currentRow()
  380. elif index == 4:
  381. row = self.ui.lw_gig.currentRow()
  382. elif index == 5:
  383. row = self.ui.lw_sf2.currentRow()
  384. elif index == 6:
  385. row = self.ui.lw_sfz.currentRow()
  386. else:
  387. row = -1
  388. check = bool(row >= 0)
  389. self.ui.b_paths_remove.setEnabled(check)
  390. self.ui.b_paths_change.setEnabled(check)
  391. @pyqtSlot(int)
  392. def slot_pluginPathRowChanged(self, row):
  393. check = bool(row >= 0)
  394. self.ui.b_paths_remove.setEnabled(check)
  395. self.ui.b_paths_change.setEnabled(check)
  396. def done(self, r):
  397. QDialog.done(self, r)
  398. self.close()
  399. # ------------------------------------------------------------------------------------------------------------
  400. # Main Window
  401. class CarlaMainW(QMainWindow):
  402. def __init__(self, parent=None):
  403. QMainWindow.__init__(self, parent)
  404. self.ui = ui_carla.Ui_CarlaMainW()
  405. self.ui.setupUi(self)
  406. # -------------------------------------------------------------
  407. # Load Settings
  408. self.loadSettings(True)
  409. self.loadRDFs()
  410. self.setStyleSheet("""
  411. QWidget#centralwidget {
  412. background-color: qlineargradient(spread:pad,
  413. x1:0.0, y1:0.0,
  414. x2:0.2, y2:1.0,
  415. stop:0 rgb( 7, 7, 7),
  416. stop:1 rgb(28, 28, 28)
  417. );
  418. }
  419. """)
  420. # -------------------------------------------------------------
  421. # Internal stuff
  422. self.fEngineStarted = False
  423. self.fFirstEngineInit = False
  424. self.fProjectFilename = None
  425. self.fProjectLoading = False
  426. self.fPluginCount = 0
  427. self.fPluginList = []
  428. self.fIdleTimerFast = 0
  429. self.fIdleTimerSlow = 0
  430. #self._nsmAnnounce2str = ""
  431. #self._nsmOpen1str = ""
  432. #self._nsmOpen2str = ""
  433. #self.nsm_server = None
  434. #self.nsm_url = None
  435. # -------------------------------------------------------------
  436. # Set-up GUI stuff
  437. self.ui.act_engine_start.setEnabled(False)
  438. self.ui.act_engine_stop.setEnabled(False)
  439. self.ui.act_plugin_remove_all.setEnabled(False)
  440. self.resize(self.width(), 0)
  441. #self.m_fakeEdit = PluginEdit(self, -1)
  442. #self.m_curEdit = self.m_fakeEdit
  443. #self.w_edit.layout().addWidget(self.m_curEdit)
  444. #self.w_edit.layout().addStretch()
  445. # -------------------------------------------------------------
  446. # Connect actions to functions
  447. self.connect(self.ui.act_file_new, SIGNAL("triggered()"), SLOT("slot_fileNew()"))
  448. self.connect(self.ui.act_file_open, SIGNAL("triggered()"), SLOT("slot_fileOpen()"))
  449. self.connect(self.ui.act_file_save, SIGNAL("triggered()"), SLOT("slot_fileSave()"))
  450. self.connect(self.ui.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_fileSaveAs()"))
  451. self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_engineStart()"))
  452. self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_engineStop()"))
  453. self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_pluginAdd()"))
  454. self.connect(self.ui.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_pluginRemoveAll()"))
  455. self.connect(self.ui.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()"))
  456. self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()"))
  457. self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  458. self.connect(self.ui.ch_transport, SIGNAL("currentIndexChanged(int)"), SLOT("slot_transportModeChanged(int)"))
  459. self.connect(self.ui.b_transport_play, SIGNAL("clicked(bool)"), SLOT("slot_transportPlayPause(bool)"))
  460. self.connect(self.ui.b_transport_stop, SIGNAL("clicked()"), SLOT("slot_transportStop()"))
  461. self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()"))
  462. self.connect(self, SIGNAL("SIGTERM()"), SLOT("slot_handleSIGTERM()"))
  463. self.connect(self, SIGNAL("DebugCallback(int, int, int, double, QString)"), SLOT("slot_handleDebugCallback(int, int, int, double, QString)"))
  464. self.connect(self, SIGNAL("PluginAddedCallback(int)"), SLOT("slot_handlePluginAddedCallback(int)"))
  465. self.connect(self, SIGNAL("PluginRemovedCallback(int)"), SLOT("slot_handlePluginRemovedCallback(int)"))
  466. self.connect(self, SIGNAL("ParameterValueChangedCallback(int, int, double)"), SLOT("slot_handleParameterValueChangedCallback(int, int, double)"))
  467. self.connect(self, SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), SLOT("slot_handleParameterDefaultChangedCallback(int, int, double)"))
  468. self.connect(self, SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiChannelChangedCallback(int, int, int)"))
  469. self.connect(self, SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiCcChangedCallback(int, int, int)"))
  470. self.connect(self, SIGNAL("ProgramChangedCallback(int, int)"), SLOT("slot_handleProgramChangedCallback(int, int)"))
  471. self.connect(self, SIGNAL("MidiProgramChangedCallback(int, int)"), SLOT("slot_handleMidiProgramChangedCallback(int, int)"))
  472. self.connect(self, SIGNAL("NoteOnCallback(int, int, int, int)"), SLOT("slot_handleNoteOnCallback(int, int, int, int)"))
  473. self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), SLOT("slot_handleNoteOffCallback(int, int, int)"))
  474. self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), SLOT("slot_handleShowGuiCallback(int, int)"))
  475. self.connect(self, SIGNAL("UpdateCallback(int)"), SLOT("slot_handleUpdateCallback(int)"))
  476. self.connect(self, SIGNAL("ReloadInfoCallback(int)"), SLOT("slot_handleReloadInfoCallback(int)"))
  477. self.connect(self, SIGNAL("ReloadParametersCallback(int)"), SLOT("slot_handleReloadParametersCallback(int)"))
  478. self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), SLOT("slot_handleReloadProgramsCallback(int)"))
  479. self.connect(self, SIGNAL("ReloadAllCallback(int)"), SLOT("slot_handleReloadAllCallback(int)"))
  480. #self.connect(self, SIGNAL("NSM_AnnounceCallback()"), SLOT("slot_handleNSM_AnnounceCallback()"))
  481. #self.connect(self, SIGNAL("NSM_Open1Callback()"), SLOT("slot_handleNSM_Open1Callback()"))
  482. #self.connect(self, SIGNAL("NSM_Open2Callback()"), SLOT("slot_handleNSM_Open2Callback()"))
  483. #self.connect(self, SIGNAL("NSM_SaveCallback()"), SLOT("slot_handleNSM_SaveCallback()"))
  484. self.connect(self, SIGNAL("ErrorCallback(QString)"), SLOT("slot_handleErrorCallback(QString)"))
  485. self.connect(self, SIGNAL("QuitCallback()"), SLOT("slot_handleQuitCallback()"))
  486. #NSM_URL = os.getenv("NSM_URL")
  487. #if NSM_URL:
  488. #Carla.host.nsm_announce(NSM_URL, os.getpid())
  489. #else:
  490. QTimer.singleShot(0, self, SLOT("slot_engineStart()"))
  491. def startEngine(self, clientName = "Carla"):
  492. # ---------------------------------------------
  493. # Engine settings
  494. settings = QSettings()
  495. Carla.processMode = settings.value("Engine/ProcessMode", PROCESS_MODE_MULTIPLE_CLIENTS, type=int)
  496. Carla.maxParameters = settings.value("Engine/MaxParameters", MAX_DEFAULT_PARAMETERS, type=int)
  497. transportMode = settings.value("Engine/TransportMode", TRANSPORT_MODE_JACK, type=int)
  498. forceStereo = settings.value("Engine/ForceStereo", False, type=bool)
  499. preferPluginBridges = settings.value("Engine/PreferPluginBridges", False, type=bool)
  500. preferUiBridges = settings.value("Engine/PreferUiBridges", True, type=bool)
  501. useDssiVstChunks = settings.value("Engine/UseDssiVstChunks", False, type=bool)
  502. oscUiTimeout = settings.value("Engine/OscUiTimeout", 40, type=int)
  503. preferredBufferSize = settings.value("Engine/PreferredBufferSize", 512, type=int)
  504. preferredSampleRate = settings.value("Engine/PreferredSampleRate", 44100, type=int)
  505. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  506. forceStereo = True
  507. elif Carla.processMode == PROCESS_MODE_MULTIPLE_CLIENTS and os.getenv("LADISH_APP_NAME"):
  508. print("LADISH detected but using multiple clients (not allowed), forcing single client now")
  509. Carla.processMode = PROCESS_MODE_SINGLE_CLIENT
  510. Carla.host.set_engine_option(OPTION_PROCESS_MODE, Carla.processMode, "")
  511. Carla.host.set_engine_option(OPTION_MAX_PARAMETERS, Carla.maxParameters, "")
  512. Carla.host.set_engine_option(OPTION_FORCE_STEREO, forceStereo, "")
  513. Carla.host.set_engine_option(OPTION_PREFER_PLUGIN_BRIDGES, preferPluginBridges, "")
  514. Carla.host.set_engine_option(OPTION_PREFER_UI_BRIDGES, preferUiBridges, "")
  515. Carla.host.set_engine_option(OPTION_USE_DSSI_VST_CHUNKS, useDssiVstChunks, "")
  516. Carla.host.set_engine_option(OPTION_OSC_UI_TIMEOUT, oscUiTimeout, "")
  517. Carla.host.set_engine_option(OPTION_PREFERRED_BUFFER_SIZE, preferredBufferSize, "")
  518. Carla.host.set_engine_option(OPTION_PREFERRED_SAMPLE_RATE, preferredSampleRate, "")
  519. # ---------------------------------------------
  520. # Start
  521. audioDriver = settings.value("Engine/AudioDriver", "JACK", type=str)
  522. if not Carla.host.engine_init(audioDriver, clientName):
  523. if self.fFirstEngineInit:
  524. self.fFirstEngineInit = False
  525. return
  526. audioError = cString(Carla.host.get_last_error())
  527. if audioError:
  528. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
  529. else:
  530. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s'" % audioDriver))
  531. return
  532. self.fEngineStarted = True
  533. self.fFirstEngineInit = False
  534. self.fPluginCount = 0
  535. self.fPluginList = []
  536. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  537. maxCount = MAX_RACK_PLUGINS
  538. elif Carla.processMode == PROCESS_MODE_PATCHBAY:
  539. maxCount = MAX_PATCHBAY_PLUGINS
  540. else:
  541. maxCount = MAX_DEFAULT_PLUGINS
  542. self.ui.ch_transport.blockSignals(True)
  543. self.ui.ch_transport.addItem(self.tr("Internal"))
  544. if audioDriver == "JACK":
  545. self.ui.ch_transport.addItem(self.tr("JACK"))
  546. elif transportMode == TRANSPORT_MODE_JACK:
  547. transportMode = TRANSPORT_MODE_INTERNAL
  548. self.ui.ch_transport.setCurrentIndex(transportMode)
  549. self.ui.ch_transport.blockSignals(False)
  550. for x in range(maxCount):
  551. self.fPluginList.append(None)
  552. Carla.host.set_engine_option(OPTION_TRANSPORT_MODE, transportMode, "")
  553. # Peaks
  554. self.fIdleTimerFast = self.startTimer(self.fSavedSettings["Main/RefreshInterval"])
  555. # LEDs and edit dialog parameters
  556. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings["Main/RefreshInterval"]*2)
  557. def stopEngine(self):
  558. if self.fPluginCount > 0:
  559. ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n"
  560. "Do you want to do this now?"),
  561. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  562. if ask != QMessageBox.Yes:
  563. return
  564. self.removeAllPlugins()
  565. if Carla.host.is_engine_running() and not Carla.host.engine_close():
  566. print(cString(Carla.host.get_last_error()))
  567. self.fEngineStarted = False
  568. self.fPluginCount = 0
  569. self.fPluginList = []
  570. self.killTimer(self.fIdleTimerFast)
  571. self.killTimer(self.fIdleTimerSlow)
  572. settings = QSettings()
  573. settings.setValue("Engine/TransportMode", self.ui.ch_transport.currentIndex())
  574. self.ui.ch_transport.blockSignals(True)
  575. self.ui.ch_transport.clear()
  576. self.ui.ch_transport.blockSignals(False)
  577. def loadProject(self, filename):
  578. self.fProjectFilename = filename
  579. self.setWindowTitle("Carla - %s" % os.path.basename(filename))
  580. self.fProjectLoading = True
  581. Carla.host.load_project(filename)
  582. self.fProjectLoading = False
  583. def loadProjectLater(self, filename):
  584. self.fProjectFilename = filename
  585. self.setWindowTitle("Carla - %s" % os.path.basename(filename))
  586. QTimer.singleShot(0, self, SLOT("slot_loadProjectLater()"))
  587. def saveProject(self, filename):
  588. self.fProjectFilename = filename
  589. self.setWindowTitle("Carla - %s" % os.path.basename(filename))
  590. Carla.host.save_project(filename)
  591. def addPlugin(self, btype, ptype, filename, name, label, extraStuff):
  592. if not self.fEngineStarted:
  593. QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
  594. return False
  595. if not Carla.host.add_plugin(btype, ptype, filename, name, label, extraStuff):
  596. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  597. return False
  598. return True
  599. def removeAllPlugins(self):
  600. while (self.ui.w_plugins.layout().takeAt(0)):
  601. pass
  602. self.ui.act_plugin_remove_all.setEnabled(False)
  603. for i in range(self.fPluginCount):
  604. pwidget = self.fPluginList[i]
  605. if pwidget is None:
  606. break
  607. self.fPluginList[i] = None
  608. pwidget.ui.edit_dialog.close()
  609. pwidget.close()
  610. pwidget.deleteLater()
  611. del pwidget
  612. self.fPluginCount = 0
  613. Carla.host.remove_all_plugins()
  614. @pyqtSlot()
  615. def slot_fileNew(self):
  616. self.removeAllPlugins()
  617. self.fProjectFilename = None
  618. self.fProjectLoading = False
  619. self.setWindowTitle("Carla")
  620. @pyqtSlot()
  621. def slot_fileOpen(self):
  622. fileFilter = self.tr("Carla Project File (*.carxp)")
  623. filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  624. if filenameTry:
  625. # FIXME - show dialog to user
  626. self.removeAllPlugins()
  627. self.loadProject(filenameTry)
  628. @pyqtSlot()
  629. def slot_fileSave(self, saveAs=False):
  630. if self.fProjectFilename and not saveAs:
  631. return self.saveProject(self.fProjectFilename)
  632. fileFilter = self.tr("Carla Project File (*.carxp)")
  633. filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  634. if filenameTry:
  635. if not filenameTry.endswith(".carxp"):
  636. filenameTry += ".carxp"
  637. self.saveProject(filenameTry)
  638. @pyqtSlot()
  639. def slot_fileSaveAs(self):
  640. self.slot_fileSave(True)
  641. @pyqtSlot()
  642. def slot_loadProjectLater(self):
  643. self.fProjectLoading = True
  644. Carla.host.load_project(self.fProjectFilename)
  645. self.fProjectLoading = False
  646. @pyqtSlot()
  647. def slot_engineStart(self):
  648. self.startEngine()
  649. check = Carla.host.is_engine_running()
  650. self.ui.act_file_open.setEnabled(check)
  651. self.ui.act_engine_start.setEnabled(not check)
  652. self.ui.act_engine_stop.setEnabled(check)
  653. self.ui.act_engine_configure.setEnabled(not check)
  654. self.ui.frame_transport.setEnabled(check)
  655. @pyqtSlot()
  656. def slot_engineStop(self):
  657. self.stopEngine()
  658. check = Carla.host.is_engine_running()
  659. self.ui.act_file_open.setEnabled(check)
  660. self.ui.act_engine_start.setEnabled(not check)
  661. self.ui.act_engine_stop.setEnabled(check)
  662. self.ui.frame_transport.setEnabled(check)
  663. @pyqtSlot()
  664. def slot_pluginAdd(self):
  665. dialog = PluginDatabaseW(self)
  666. if dialog.exec_():
  667. btype = dialog.fRetPlugin['build']
  668. ptype = dialog.fRetPlugin['type']
  669. filename = dialog.fRetPlugin['binary']
  670. label = dialog.fRetPlugin['label']
  671. extraStuff = self.getExtraStuff(dialog.fRetPlugin)
  672. self.addPlugin(btype, ptype, filename, None, label, extraStuff)
  673. @pyqtSlot()
  674. def slot_pluginRemoveAll(self):
  675. self.removeAllPlugins()
  676. self.connect(self.ui.ch_transport, SIGNAL("currentIndexChanged(int)"), SLOT("(int)"))
  677. self.connect(self.ui.b_transport_play, SIGNAL("clicked(bool)"), SLOT("(bool)"))
  678. self.connect(self.ui.b_transport_stop, SIGNAL("clicked()"), SLOT("()"))
  679. @pyqtSlot(int)
  680. def slot_transportModeChanged(self, newMode):
  681. Carla.host.set_engine_option(OPTION_TRANSPORT_MODE, newMode, "")
  682. @pyqtSlot(bool)
  683. def slot_transportPlayPause(self, toggled):
  684. if toggled:
  685. Carla.host.transport_play()
  686. else:
  687. Carla.host.transport_pause()
  688. @pyqtSlot()
  689. def slot_transportStop(self):
  690. Carla.host.transport_pause()
  691. Carla.host.transport_relocate(0)
  692. @pyqtSlot()
  693. def slot_aboutCarla(self):
  694. CarlaAboutW(self).exec_()
  695. @pyqtSlot()
  696. def slot_configureCarla(self):
  697. dialog = CarlaSettingsW(self)
  698. if dialog.exec_():
  699. self.loadSettings(False)
  700. for pwidget in self.fPluginList:
  701. if pwidget is None:
  702. return
  703. pwidget.setRefreshRate(self.fSavedSettings["Main/RefreshInterval"])
  704. @pyqtSlot()
  705. def slot_handleSIGUSR1(self):
  706. print("Got SIGUSR1 -> Saving project now")
  707. QTimer.singleShot(0, self, SLOT("slot_fileSave()"))
  708. @pyqtSlot()
  709. def slot_handleSIGTERM(self):
  710. print("Got SIGTERM -> Closing now")
  711. self.close()
  712. @pyqtSlot(int, int, int, float, str)
  713. def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
  714. print("DEBUG :: %i, %i, %i, %f, \"%s\")" % (pluginId, value1, value2, value3, valueStr))
  715. @pyqtSlot(int)
  716. def slot_handlePluginAddedCallback(self, pluginId):
  717. pwidget = PluginWidget(self, pluginId)
  718. pwidget.setRefreshRate(self.fSavedSettings["Main/RefreshInterval"])
  719. self.ui.w_plugins.layout().addWidget(pwidget)
  720. self.fPluginCount += 1
  721. self.fPluginList[pluginId] = pwidget
  722. if not self.fProjectLoading:
  723. pwidget.setActive(True, True, True)
  724. if self.fPluginCount == 1:
  725. self.ui.act_plugin_remove_all.setEnabled(True)
  726. @pyqtSlot(int)
  727. def slot_handlePluginRemovedCallback(self, pluginId):
  728. if pluginId >= self.fPluginCount:
  729. return
  730. pwidget = self.fPluginList[pluginId]
  731. if pwidget is None:
  732. return
  733. self.fPluginList[pluginId] = None
  734. self.fPluginCount -= 1
  735. self.ui.w_plugins.layout().removeWidget(pwidget)
  736. pwidget.ui.edit_dialog.close()
  737. pwidget.close()
  738. pwidget.deleteLater()
  739. del pwidget
  740. # push all plugins 1 slot back
  741. for i in range(pluginId, self.fPluginCount):
  742. self.fPluginList[i] = self.fPluginList[i+1]
  743. self.fPluginList[i].setId(i)
  744. if self.fPluginCount == 0:
  745. self.ui.act_plugin_remove_all.setEnabled(False)
  746. @pyqtSlot(int, int, float)
  747. def slot_handleParameterValueChangedCallback(self, pluginId, parameterId, value):
  748. if pluginId >= self.fPluginCount:
  749. return
  750. pwidget = self.fPluginList[pluginId]
  751. if pwidget is None:
  752. return
  753. pwidget.setParameterValue(parameterId, value)
  754. @pyqtSlot(int, int, float)
  755. def slot_handleParameterDefaultChangedCallback(self, pluginId, parameterId, value):
  756. if pluginId >= self.fPluginCount:
  757. return
  758. pwidget = self.fPluginList[pluginId]
  759. if pwidget is None:
  760. return
  761. pwidget.setParameterDefault(parameterId, value)
  762. @pyqtSlot(int, int, int)
  763. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, parameterId, channel):
  764. if pluginId >= self.fPluginCount:
  765. return
  766. pwidget = self.fPluginList[pluginId]
  767. if pwidget is None:
  768. return
  769. pwidget.setParameterMidiChannel(parameterId, channel)
  770. @pyqtSlot(int, int, int)
  771. def slot_handleParameterMidiCcChangedCallback(self, pluginId, parameterId, cc):
  772. if pluginId >= self.fPluginCount:
  773. return
  774. pwidget = self.fPluginList[pluginId]
  775. if pwidget is None:
  776. return
  777. pwidget.setParameterMidiControl(parameterId, cc)
  778. @pyqtSlot(int, int)
  779. def slot_handleProgramChangedCallback(self, pluginId, programId):
  780. if pluginId >= self.fPluginCount:
  781. return
  782. pwidget = self.fPluginList[pluginId]
  783. if pwidget is None:
  784. return
  785. pwidget.setProgram(programId)
  786. @pyqtSlot(int, int)
  787. def slot_handleMidiProgramChangedCallback(self, pluginId, midiProgramId):
  788. if pluginId >= self.fPluginCount:
  789. return
  790. pwidget = self.fPluginList[pluginId]
  791. if pwidget is None:
  792. return
  793. pwidget.setMidiProgram(midiProgramId)
  794. @pyqtSlot(int, int, int, int)
  795. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  796. if pluginId >= self.fPluginCount:
  797. return
  798. pwidget = self.fPluginList[pluginId]
  799. if pwidget is None:
  800. return
  801. pwidget.sendNoteOn(channel, note)
  802. @pyqtSlot(int, int, int)
  803. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  804. if pluginId >= self.fPluginCount:
  805. return
  806. pwidget = self.fPluginList[pluginId]
  807. if pwidget is None:
  808. return
  809. pwidget.sendNoteOff(channel, note)
  810. @pyqtSlot(int, int)
  811. def slot_handleShowGuiCallback(self, pluginId, show):
  812. if pluginId >= self.fPluginCount:
  813. return
  814. pwidget = self.fPluginList[pluginId]
  815. if pwidget is None:
  816. return
  817. if show == 0:
  818. pwidget.ui.b_gui.setChecked(False)
  819. pwidget.ui.b_gui.setEnabled(True)
  820. elif show == 1:
  821. pwidget.ui.b_gui.setChecked(True)
  822. pwidget.ui.b_gui.setEnabled(True)
  823. elif show == -1:
  824. pwidget.ui.b_gui.setChecked(False)
  825. pwidget.ui.b_gui.setEnabled(False)
  826. @pyqtSlot(int)
  827. def slot_handleUpdateCallback(self, pluginId):
  828. if pluginId >= self.fPluginCount:
  829. return
  830. pwidget = self.fPluginList[pluginId]
  831. if pwidget is None:
  832. return
  833. pwidget.ui.edit_dialog.do_update()
  834. @pyqtSlot(int)
  835. def slot_handleReloadInfoCallback(self, pluginId):
  836. if pluginId >= self.fPluginCount:
  837. return
  838. pwidget = self.fPluginList[pluginId]
  839. if pwidget is None:
  840. return
  841. pwidget.ui.edit_dialog.reloadInfo()
  842. @pyqtSlot(int)
  843. def slot_handleReloadParametersCallback(self, pluginId):
  844. if pluginId >= self.fPluginCount:
  845. return
  846. pwidget = self.fPluginList[pluginId]
  847. if pwidget is None:
  848. return
  849. pwidget.ui.edit_dialog.reloadParameters()
  850. @pyqtSlot(int)
  851. def slot_handleReloadProgramsCallback(self, pluginId):
  852. if pluginId >= self.fPluginCount:
  853. return
  854. pwidget = self.fPluginList[pluginId]
  855. if pwidget is None:
  856. return
  857. pwidget.ui.edit_dialog.reloadPrograms()
  858. @pyqtSlot(int)
  859. def slot_handleReloadAllCallback(self, pluginId):
  860. if pluginId >= self.fPluginCount:
  861. return
  862. pwidget = self.fPluginList[pluginId]
  863. if pwidget is None:
  864. return
  865. pwidget.ui.edit_dialog.reloadAll()
  866. @pyqtSlot(str)
  867. def slot_handleErrorCallback(self, error):
  868. QMessageBox.critical(self, self.tr("Error"), error)
  869. @pyqtSlot()
  870. def slot_handleQuitCallback(self):
  871. CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"),
  872. self.tr("Engine has been stopped or crashed.\nPlease restart Carla"),
  873. self.tr("You may want to save your session now..."), QMessageBox.Ok, QMessageBox.Ok)
  874. def getExtraStuff(self, plugin):
  875. ptype = plugin['type']
  876. if ptype == PLUGIN_LADSPA:
  877. uniqueId = plugin['uniqueId']
  878. for rdfItem in self.fLadspaRdfList:
  879. if rdfItem.UniqueID == uniqueId:
  880. return pointer(rdfItem)
  881. elif ptype == PLUGIN_DSSI:
  882. if (plugin['hints'] & PLUGIN_HAS_GUI):
  883. gui = findDSSIGUI(plugin['binary'], plugin['name'], plugin['label'])
  884. if gui:
  885. return gui.encode("utf-8")
  886. elif ptype == PLUGIN_SF2:
  887. if plugin['name'].endswith(" (16 outputs)"):
  888. # return a dummy non-null pointer
  889. INTPOINTER = POINTER(c_int)
  890. ptr = c_int(0x1)
  891. addr = addressof(ptr)
  892. return cast(addr, INTPOINTER)
  893. return c_nullptr
  894. def loadRDFs(self):
  895. # Save RDF info for later
  896. self.fLadspaRdfList = []
  897. if not haveLRDF:
  898. return
  899. settingsDir = os.path.join(HOME, ".config", "Cadence")
  900. frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
  901. if os.path.exists(frLadspaFile):
  902. frLadspa = open(frLadspaFile, 'r')
  903. try:
  904. self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
  905. except:
  906. pass
  907. frLadspa.close()
  908. def saveSettings(self):
  909. settings = QSettings()
  910. settings.setValue("Geometry", self.saveGeometry())
  911. settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  912. def loadSettings(self, geometry):
  913. settings = QSettings()
  914. if geometry:
  915. self.restoreGeometry(settings.value("Geometry", ""))
  916. showToolbar = settings.value("ShowToolbar", True, type=bool)
  917. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  918. self.ui.toolBar.setVisible(showToolbar)
  919. self.fSavedSettings = {
  920. "Main/DefaultProjectFolder": settings.value("Main/DefaultProjectFolder", HOME, type=str),
  921. "Main/RefreshInterval": settings.value("Main/RefreshInterval", 50, type=int)
  922. }
  923. # ---------------------------------------------
  924. # plugin checks
  925. if settings.value("Engine/DisableChecks", False, type=bool):
  926. os.environ["CARLA_DISCOVERY_NO_PROCESSING_CHECKS"] = "true"
  927. elif os.getenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS"):
  928. os.environ.pop("CARLA_DISCOVERY_NO_PROCESSING_CHECKS")
  929. # ---------------------------------------------
  930. # plugin paths
  931. Carla.LADSPA_PATH = toList(settings.value("Paths/LADSPA", Carla.LADSPA_PATH))
  932. Carla.DSSI_PATH = toList(settings.value("Paths/DSSI", Carla.DSSI_PATH))
  933. Carla.LV2_PATH = toList(settings.value("Paths/LV2", Carla.LV2_PATH))
  934. Carla.VST_PATH = toList(settings.value("Paths/VST", Carla.VST_PATH))
  935. Carla.GIG_PATH = toList(settings.value("Paths/GIG", Carla.GIG_PATH))
  936. Carla.SF2_PATH = toList(settings.value("Paths/SF2", Carla.SF2_PATH))
  937. Carla.SFZ_PATH = toList(settings.value("Paths/SFZ", Carla.SFZ_PATH))
  938. os.environ["LADSPA_PATH"] = splitter.join(Carla.LADSPA_PATH)
  939. os.environ["DSSI_PATH"] = splitter.join(Carla.DSSI_PATH)
  940. os.environ["LV2_PATH"] = splitter.join(Carla.LV2_PATH)
  941. os.environ["VST_PATH"] = splitter.join(Carla.VST_PATH)
  942. os.environ["GIG_PATH"] = splitter.join(Carla.GIG_PATH)
  943. os.environ["SF2_PATH"] = splitter.join(Carla.SF2_PATH)
  944. os.environ["SFZ_PATH"] = splitter.join(Carla.SFZ_PATH)
  945. def timerEvent(self, event):
  946. if event.timerId() == self.fIdleTimerFast:
  947. Carla.host.engine_idle()
  948. for pwidget in self.fPluginList:
  949. if pwidget is None:
  950. break
  951. pwidget.idleFast()
  952. elif event.timerId() == self.fIdleTimerSlow:
  953. for pwidget in self.fPluginList:
  954. if pwidget is None:
  955. break
  956. pwidget.idleSlow()
  957. QMainWindow.timerEvent(self, event)
  958. def closeEvent(self, event):
  959. #if self.nsm_server:
  960. #self.nsm_server.stop()
  961. self.saveSettings()
  962. if Carla.host.is_engine_running():
  963. Carla.host.set_engine_about_to_close()
  964. self.removeAllPlugins()
  965. self.stopEngine()
  966. QMainWindow.closeEvent(self, event)
  967. # ------------------------------------------------------------------------------------------------
  968. def callbackFunction(ptr, action, pluginId, value1, value2, value3, valueStr):
  969. if pluginId < 0 or not Carla.gui:
  970. return
  971. if action == CALLBACK_DEBUG:
  972. Carla.gui.emit(SIGNAL("DebugCallback(int, int, int, double, QString)"), pluginId, value1, value2, value3, cString(valueStr))
  973. elif action == CALLBACK_PLUGIN_ADDED:
  974. Carla.gui.emit(SIGNAL("PluginAddedCallback(int)"), pluginId)
  975. elif action == CALLBACK_PLUGIN_REMOVED:
  976. Carla.gui.emit(SIGNAL("PluginRemovedCallback(int)"), pluginId)
  977. elif action == CALLBACK_PARAMETER_VALUE_CHANGED:
  978. Carla.gui.emit(SIGNAL("ParameterValueChangedCallback(int, int, double)"), pluginId, value1, value3)
  979. elif action == CALLBACK_PARAMETER_DEFAULT_CHANGED:
  980. Carla.gui.emit(SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), pluginId, value1, value3)
  981. elif action == CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  982. Carla.gui.emit(SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), pluginId, value1, value2)
  983. elif action == CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  984. Carla.gui.emit(SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), pluginId, value1, value2)
  985. elif action == CALLBACK_PROGRAM_CHANGED:
  986. Carla.gui.emit(SIGNAL("ProgramChangedCallback(int, int)"), pluginId, value1)
  987. elif action == CALLBACK_MIDI_PROGRAM_CHANGED:
  988. Carla.gui.emit(SIGNAL("MidiProgramChangedCallback(int, int)"), pluginId, value1)
  989. elif action == CALLBACK_NOTE_ON:
  990. Carla.gui.emit(SIGNAL("NoteOnCallback(int, int, int, int)"), pluginId, value1, value2, value3)
  991. elif action == CALLBACK_NOTE_OFF:
  992. Carla.gui.emit(SIGNAL("NoteOffCallback(int, int, int)"), pluginId, value1, value2)
  993. elif action == CALLBACK_SHOW_GUI:
  994. Carla.gui.emit(SIGNAL("ShowGuiCallback(int, int)"), pluginId, value1)
  995. elif action == CALLBACK_UPDATE:
  996. Carla.gui.emit(SIGNAL("UpdateCallback(int)"), pluginId)
  997. elif action == CALLBACK_RELOAD_INFO:
  998. Carla.gui.emit(SIGNAL("ReloadInfoCallback(int)"), pluginId)
  999. elif action == CALLBACK_RELOAD_PARAMETERS:
  1000. Carla.gui.emit(SIGNAL("ReloadParametersCallback(int)"), pluginId)
  1001. elif action == CALLBACK_RELOAD_PROGRAMS:
  1002. Carla.gui.emit(SIGNAL("ReloadProgramsCallback(int)"), pluginId)
  1003. elif action == CALLBACK_RELOAD_ALL:
  1004. Carla.gui.emit(SIGNAL("ReloadAllCallback(int)"), pluginId)
  1005. #elif action == CALLBACK_NSM_ANNOUNCE:
  1006. #Carla.gui._nsmAnnounce2str = cString(Carla.host.get_last_error())
  1007. #Carla.gui.emit(SIGNAL("NSM_AnnounceCallback()"))
  1008. #elif action == CALLBACK_NSM_OPEN1:
  1009. #Carla.gui._nsmOpen1str = cString(valueStr)
  1010. #Carla.gui.emit(SIGNAL("NSM_Open1Callback()"))
  1011. #elif action == CALLBACK_NSM_OPEN2:
  1012. #Carla.gui._nsmOpen2str = cString(valueStr)
  1013. #Carla.gui.emit(SIGNAL("NSM_Open2Callback()"))
  1014. #elif action == CALLBACK_NSM_SAVE:
  1015. #Carla.gui.emit(SIGNAL("NSM_SaveCallback()"))
  1016. elif action == CALLBACK_ERROR:
  1017. Carla.gui.emit(SIGNAL("ErrorCallback(QString)"), valueStr)
  1018. elif action == CALLBACK_QUIT:
  1019. Carla.gui.emit(SIGNAL("QuitCallback()"))
  1020. #--------------- main ------------------
  1021. if __name__ == '__main__':
  1022. # App initialization
  1023. app = QApplication(sys.argv)
  1024. app.setApplicationName("Carla")
  1025. app.setApplicationVersion(VERSION)
  1026. app.setOrganizationName("Cadence")
  1027. app.setWindowIcon(QIcon(":/scalable/carla.svg"))
  1028. libPrefix = None
  1029. projectFilename = None
  1030. for i in range(len(app.arguments())):
  1031. if i == 0: continue
  1032. argument = app.arguments()[i]
  1033. if argument.startswith("--with-libprefix="):
  1034. libPrefix = argument.replace("--with-libprefix=", "")
  1035. elif os.path.exists(argument):
  1036. projectFilename = argument
  1037. # Init backend
  1038. Carla.host = Host(libPrefix)
  1039. Carla.host.set_engine_callback(callbackFunction)
  1040. Carla.host.set_engine_option(OPTION_PROCESS_NAME, 0, "carla")
  1041. # Set bridge paths
  1042. if carla_bridge_native:
  1043. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_NATIVE, 0, carla_bridge_native)
  1044. if carla_bridge_posix32:
  1045. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_POSIX32, 0, carla_bridge_posix32)
  1046. if carla_bridge_posix64:
  1047. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_POSIX64, 0, carla_bridge_posix64)
  1048. if carla_bridge_win32:
  1049. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_WIN32, 0, carla_bridge_win32)
  1050. if carla_bridge_win64:
  1051. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_WIN64, 0, carla_bridge_win64)
  1052. if WINDOWS:
  1053. if carla_bridge_lv2_windows:
  1054. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_WINDOWS, 0, carla_bridge_lv2_windows)
  1055. if carla_bridge_vst_hwnd:
  1056. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_HWND, 0, carla_bridge_vst_hwnd)
  1057. elif MACOS:
  1058. if carla_bridge_lv2_cocoa:
  1059. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_COCOA, 0, carla_bridge_lv2_cocoa)
  1060. if carla_bridge_vst_cocoa:
  1061. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_COCOA, 0, carla_bridge_vst_cocoa)
  1062. else:
  1063. if carla_bridge_lv2_gtk2:
  1064. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_GTK2, 0, carla_bridge_lv2_gtk2)
  1065. if carla_bridge_lv2_gtk3:
  1066. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_GTK3, 0, carla_bridge_lv2_gtk3)
  1067. if carla_bridge_lv2_qt4:
  1068. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_QT4, 0, carla_bridge_lv2_qt4)
  1069. if carla_bridge_lv2_qt5:
  1070. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_QT5, 0, carla_bridge_lv2_qt5)
  1071. if carla_bridge_lv2_x11:
  1072. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_X11, 0, carla_bridge_lv2_x11)
  1073. if carla_bridge_vst_x11:
  1074. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_X11, 0, carla_bridge_vst_x11)
  1075. # Create GUI and start engine
  1076. Carla.gui = CarlaMainW()
  1077. # Set-up custom signal handling
  1078. setUpSignals()
  1079. # Show GUI
  1080. Carla.gui.show()
  1081. # Load project file if set
  1082. if projectFilename:
  1083. Carla.gui.loadProjectLater(projectFilename)
  1084. # App-Loop
  1085. ret = app.exec_()
  1086. # Exit properly
  1087. sys.exit(ret)