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.

1835 lines
75KB

  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 time import sleep
  20. from PyQt4.QtCore import Qt, QModelIndex, QPointF, QSize
  21. from PyQt4.QtGui import QApplication, QDialogButtonBox, QFileSystemModel, QMainWindow, QResizeEvent
  22. from PyQt4.QtGui import QImage, QPrinter, QPrintDialog
  23. # ------------------------------------------------------------------------------------------------------------
  24. # Imports (Custom Stuff)
  25. import patchcanvas
  26. import ui_carla
  27. import ui_carla_settings
  28. from carla_backend import * # FIXME, remove later
  29. from carla_shared import *
  30. # ------------------------------------------------------------------------------------------------------------
  31. # Try Import OpenGL
  32. try:
  33. from PyQt4.QtOpenGL import QGLWidget
  34. hasGL = True
  35. except:
  36. hasGL = False
  37. # ------------------------------------------------------------------------------------------------------------
  38. # Static Variables
  39. DEFAULT_CANVAS_WIDTH = 3100
  40. DEFAULT_CANVAS_HEIGHT = 2400
  41. # Tab indexes
  42. TAB_INDEX_MAIN = 0
  43. TAB_INDEX_CANVAS = 1
  44. TAB_INDEX_CARLA_ENGINE = 2
  45. TAB_INDEX_CARLA_PATHS = 3
  46. TAB_INDEX_NONE = 4
  47. # Single and Multiple client mode is only for JACK,
  48. # but we still want to match QComboBox index to defines,
  49. # so add +2 pos padding if driverName != "JACK".
  50. PROCESS_MODE_NON_JACK_PADDING = 2
  51. # Carla defaults
  52. CARLA_DEFAULT_PROCESS_HIGH_PRECISION = False
  53. CARLA_DEFAULT_MAX_PARAMETERS = 200
  54. CARLA_DEFAULT_FORCE_STEREO = False
  55. CARLA_DEFAULT_USE_DSSI_VST_CHUNKS = False
  56. CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES = False
  57. CARLA_DEFAULT_PREFER_UI_BRIDGES = True
  58. CARLA_DEFAULT_OSC_UI_TIMEOUT = 4000
  59. CARLA_DEFAULT_DISABLE_CHECKS = False
  60. # PatchCanvas defines
  61. CANVAS_ANTIALIASING_SMALL = 1
  62. CANVAS_EYECANDY_SMALL = 1
  63. # ------------------------------------------------------------------------------------------------------------
  64. # Settings Dialog
  65. class CarlaSettingsW(QDialog):
  66. def __init__(self, parent):
  67. QDialog.__init__(self, parent)
  68. self.ui = ui_carla_settings.Ui_CarlaSettingsW()
  69. self.ui.setupUi(self)
  70. # -------------------------------------------------------------
  71. # Set-up GUI
  72. driverCount = Carla.host.get_engine_driver_count()
  73. for i in range(driverCount):
  74. driverName = cString(Carla.host.get_engine_driver_name(i))
  75. self.ui.cb_engine_audio_driver.addItem(driverName)
  76. # -------------------------------------------------------------
  77. # Load settings
  78. self.loadSettings()
  79. if not hasGL:
  80. self.ui.cb_canvas_use_opengl.setChecked(False)
  81. self.ui.cb_canvas_use_opengl.setEnabled(False)
  82. if WINDOWS:
  83. self.ui.ch_engine_dssi_chunks.setChecked(False)
  84. self.ui.ch_engine_dssi_chunks.setEnabled(False)
  85. # -------------------------------------------------------------
  86. # Set-up connections
  87. self.connect(self, SIGNAL("accepted()"), SLOT("slot_saveSettings()"))
  88. self.connect(self.ui.buttonBox.button(QDialogButtonBox.Reset), SIGNAL("clicked()"), SLOT("slot_resetSettings()"))
  89. self.connect(self.ui.b_main_def_folder_open, SIGNAL("clicked()"), SLOT("slot_getAndSetProjectPath()"))
  90. self.connect(self.ui.cb_engine_audio_driver, SIGNAL("currentIndexChanged(int)"), SLOT("slot_engineAudioDriverChanged()"))
  91. self.connect(self.ui.b_paths_add, SIGNAL("clicked()"), SLOT("slot_addPluginPath()"))
  92. self.connect(self.ui.b_paths_remove, SIGNAL("clicked()"), SLOT("slot_removePluginPath()"))
  93. self.connect(self.ui.b_paths_change, SIGNAL("clicked()"), SLOT("slot_changePluginPath()"))
  94. self.connect(self.ui.tw_paths, SIGNAL("currentChanged(int)"), SLOT("slot_pluginPathTabChanged(int)"))
  95. self.connect(self.ui.lw_ladspa, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  96. self.connect(self.ui.lw_dssi, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  97. self.connect(self.ui.lw_lv2, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  98. self.connect(self.ui.lw_vst, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  99. self.connect(self.ui.lw_sf2, SIGNAL("currentRowChanged(int)"), SLOT("slot_pluginPathRowChanged(int)"))
  100. # -------------------------------------------------------------
  101. # Post-connect setup
  102. self.ui.lw_ladspa.setCurrentRow(0)
  103. self.ui.lw_dssi.setCurrentRow(0)
  104. self.ui.lw_lv2.setCurrentRow(0)
  105. self.ui.lw_vst.setCurrentRow(0)
  106. self.ui.lw_gig.setCurrentRow(0)
  107. self.ui.lw_sf2.setCurrentRow(0)
  108. self.ui.lw_sfz.setCurrentRow(0)
  109. self.ui.lw_page.setCurrentCell(0, 0)
  110. def loadSettings(self):
  111. settings = QSettings()
  112. # ---------------------------------------
  113. self.ui.le_main_def_folder.setText(settings.value("Main/DefaultProjectFolder", HOME, type=str))
  114. self.ui.sb_gui_refresh.setValue(settings.value("Main/RefreshInterval", 50, type=int))
  115. # ---------------------------------------
  116. self.ui.cb_canvas_hide_groups.setChecked(settings.value("Canvas/AutoHideGroups", False, type=bool))
  117. self.ui.cb_canvas_bezier_lines.setChecked(settings.value("Canvas/UseBezierLines", True, type=bool))
  118. self.ui.cb_canvas_eyecandy.setCheckState(settings.value("Canvas/EyeCandy", CANVAS_EYECANDY_SMALL, type=int))
  119. self.ui.cb_canvas_use_opengl.setChecked(settings.value("Canvas/UseOpenGL", False, type=bool))
  120. self.ui.cb_canvas_render_aa.setCheckState(settings.value("Canvas/Antialiasing", CANVAS_ANTIALIASING_SMALL, type=int))
  121. self.ui.cb_canvas_render_hq_aa.setChecked(settings.value("Canvas/HighQualityAntialiasing", False, type=bool))
  122. themeName = settings.value("Canvas/Theme", patchcanvas.getDefaultThemeName(), type=str)
  123. for i in range(patchcanvas.Theme.THEME_MAX):
  124. thisThemeName = patchcanvas.getThemeName(i)
  125. self.ui.cb_canvas_theme.addItem(thisThemeName)
  126. if thisThemeName == themeName:
  127. self.ui.cb_canvas_theme.setCurrentIndex(i)
  128. # --------------------------------------------
  129. if WINDOWS:
  130. defaultDriver = "DirectSound"
  131. elif MACOS:
  132. defaultDriver = "CoreAudio"
  133. else:
  134. defaultDriver = "JACK"
  135. audioDriver = settings.value("Engine/AudioDriver", defaultDriver, type=str)
  136. for i in range(self.ui.cb_engine_audio_driver.count()):
  137. if self.ui.cb_engine_audio_driver.itemText(i) == audioDriver:
  138. self.ui.cb_engine_audio_driver.setCurrentIndex(i)
  139. break
  140. else:
  141. self.ui.cb_engine_audio_driver.setCurrentIndex(-1)
  142. if audioDriver == "JACK":
  143. processModeIndex = settings.value("Engine/ProcessMode", PROCESS_MODE_MULTIPLE_CLIENTS, type=int)
  144. self.ui.cb_engine_process_mode_jack.setCurrentIndex(processModeIndex)
  145. self.ui.sw_engine_process_mode.setCurrentIndex(0)
  146. else:
  147. processModeIndex = settings.value("Engine/ProcessMode", PROCESS_MODE_CONTINUOUS_RACK, type=int)
  148. processModeIndex -= PROCESS_MODE_NON_JACK_PADDING
  149. self.ui.cb_engine_process_mode_other.setCurrentIndex(processModeIndex)
  150. self.ui.sw_engine_process_mode.setCurrentIndex(1)
  151. self.ui.sb_engine_max_params.setValue(settings.value("Engine/MaxParameters", CARLA_DEFAULT_MAX_PARAMETERS, type=int))
  152. self.ui.ch_engine_prefer_ui_bridges.setChecked(settings.value("Engine/PreferUiBridges", CARLA_DEFAULT_PREFER_UI_BRIDGES, type=bool))
  153. self.ui.sb_engine_oscgui_timeout.setValue(settings.value("Engine/OscUiTimeout", CARLA_DEFAULT_OSC_UI_TIMEOUT, type=int))
  154. self.ui.ch_engine_disable_checks.setChecked(settings.value("Engine/DisableChecks", CARLA_DEFAULT_DISABLE_CHECKS, type=bool))
  155. self.ui.ch_engine_dssi_chunks.setChecked(settings.value("Engine/UseDssiVstChunks", CARLA_DEFAULT_USE_DSSI_VST_CHUNKS, type=bool))
  156. self.ui.ch_engine_prefer_plugin_bridges.setChecked(settings.value("Engine/PreferPluginBridges", CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, type=bool))
  157. self.ui.ch_engine_force_stereo.setChecked(settings.value("Engine/ForceStereo", CARLA_DEFAULT_FORCE_STEREO, type=bool))
  158. # --------------------------------------------
  159. ladspas = toList(settings.value("Paths/LADSPA", Carla.LADSPA_PATH))
  160. dssis = toList(settings.value("Paths/DSSI", Carla.DSSI_PATH))
  161. lv2s = toList(settings.value("Paths/LV2", Carla.LV2_PATH))
  162. vsts = toList(settings.value("Paths/VST", Carla.VST_PATH))
  163. gigs = toList(settings.value("Paths/GIG", Carla.GIG_PATH))
  164. sf2s = toList(settings.value("Paths/SF2", Carla.SF2_PATH))
  165. sfzs = toList(settings.value("Paths/SFZ", Carla.SFZ_PATH))
  166. ladspas.sort()
  167. dssis.sort()
  168. lv2s.sort()
  169. vsts.sort()
  170. gigs.sort()
  171. sf2s.sort()
  172. sfzs.sort()
  173. for ladspa in ladspas:
  174. self.ui.lw_ladspa.addItem(ladspa)
  175. for dssi in dssis:
  176. self.ui.lw_dssi.addItem(dssi)
  177. for lv2 in lv2s:
  178. self.ui.lw_lv2.addItem(lv2)
  179. for vst in vsts:
  180. self.ui.lw_vst.addItem(vst)
  181. for gig in gigs:
  182. self.ui.lw_gig.addItem(gig)
  183. for sf2 in sf2s:
  184. self.ui.lw_sf2.addItem(sf2)
  185. for sfz in sfzs:
  186. self.ui.lw_sfz.addItem(sfz)
  187. @pyqtSlot()
  188. def slot_saveSettings(self):
  189. settings = QSettings()
  190. # ---------------------------------------
  191. settings.setValue("Main/RefreshInterval", self.ui.sb_gui_refresh.value())
  192. settings.setValue("Main/DefaultProjectFolder", self.ui.le_main_def_folder.text())
  193. # ---------------------------------------
  194. settings.setValue("Canvas/Theme", self.ui.cb_canvas_theme.currentText())
  195. settings.setValue("Canvas/AutoHideGroups", self.ui.cb_canvas_hide_groups.isChecked())
  196. settings.setValue("Canvas/UseBezierLines", self.ui.cb_canvas_bezier_lines.isChecked())
  197. settings.setValue("Canvas/UseOpenGL", self.ui.cb_canvas_use_opengl.isChecked())
  198. settings.setValue("Canvas/HighQualityAntialiasing", self.ui.cb_canvas_render_hq_aa.isChecked())
  199. # 0, 1, 2 match their enum variants
  200. settings.setValue("Canvas/EyeCandy", self.ui.cb_canvas_eyecandy.checkState())
  201. settings.setValue("Canvas/Antialiasing", self.ui.cb_canvas_render_aa.checkState())
  202. # --------------------------------------------
  203. audioDriver = self.ui.cb_engine_audio_driver.currentText()
  204. settings.setValue("Engine/AudioDriver", audioDriver)
  205. if audioDriver == "JACK":
  206. settings.setValue("Engine/ProcessMode", self.ui.cb_engine_process_mode_jack.currentIndex())
  207. else:
  208. settings.setValue("Engine/ProcessMode", self.ui.cb_engine_process_mode_other.currentIndex()+PROCESS_MODE_NON_JACK_PADDING)
  209. settings.setValue("Engine/MaxParameters", self.ui.sb_engine_max_params.value())
  210. settings.setValue("Engine/PreferUiBridges", self.ui.ch_engine_prefer_ui_bridges.isChecked())
  211. settings.setValue("Engine/OscUiTimeout", self.ui.sb_engine_oscgui_timeout.value())
  212. settings.setValue("Engine/DisableChecks", self.ui.ch_engine_disable_checks.isChecked())
  213. settings.setValue("Engine/UseDssiVstChunks", self.ui.ch_engine_dssi_chunks.isChecked())
  214. settings.setValue("Engine/PreferPluginBridges", self.ui.ch_engine_prefer_plugin_bridges.isChecked())
  215. settings.setValue("Engine/ForceStereo", self.ui.ch_engine_force_stereo.isChecked())
  216. # --------------------------------------------
  217. # FIXME - find a cleaner way to do this, *.items() ?
  218. ladspas = []
  219. dssis = []
  220. lv2s = []
  221. vsts = []
  222. gigs = []
  223. sf2s = []
  224. sfzs = []
  225. for i in range(self.ui.lw_ladspa.count()):
  226. ladspas.append(self.ui.lw_ladspa.item(i).text())
  227. for i in range(self.ui.lw_dssi.count()):
  228. dssis.append(self.ui.lw_dssi.item(i).text())
  229. for i in range(self.ui.lw_lv2.count()):
  230. lv2s.append(self.ui.lw_lv2.item(i).text())
  231. for i in range(self.ui.lw_vst.count()):
  232. vsts.append(self.ui.lw_vst.item(i).text())
  233. for i in range(self.ui.lw_gig.count()):
  234. gigs.append(self.ui.lw_gig.item(i).text())
  235. for i in range(self.ui.lw_sf2.count()):
  236. sf2s.append(self.ui.lw_sf2.item(i).text())
  237. for i in range(self.ui.lw_sfz.count()):
  238. sfzs.append(self.ui.lw_sfz.item(i).text())
  239. settings.setValue("Paths/LADSPA", ladspas)
  240. settings.setValue("Paths/DSSI", dssis)
  241. settings.setValue("Paths/LV2", lv2s)
  242. settings.setValue("Paths/VST", vsts)
  243. settings.setValue("Paths/GIG", gigs)
  244. settings.setValue("Paths/SF2", sf2s)
  245. settings.setValue("Paths/SFZ", sfzs)
  246. @pyqtSlot()
  247. def slot_resetSettings(self):
  248. if self.ui.lw_page.currentRow() == TAB_INDEX_MAIN:
  249. self.ui.le_main_def_folder.setText(HOME)
  250. self.ui.sb_gui_refresh.setValue(50)
  251. elif self.ui.lw_page.currentRow() == TAB_INDEX_CANVAS:
  252. self.ui.cb_canvas_theme.setCurrentIndex(0)
  253. self.ui.cb_canvas_hide_groups.setChecked(False)
  254. self.ui.cb_canvas_bezier_lines.setChecked(True)
  255. self.ui.cb_canvas_eyecandy.setCheckState(Qt.PartiallyChecked)
  256. self.ui.cb_canvas_use_opengl.setChecked(False)
  257. self.ui.cb_canvas_render_aa.setCheckState(Qt.PartiallyChecked)
  258. self.ui.cb_canvas_render_hq_aa.setChecked(False)
  259. elif self.ui.lw_page.currentRow() == TAB_INDEX_CARLA_ENGINE:
  260. self.ui.cb_engine_audio_driver.setCurrentIndex(0)
  261. self.ui.sb_engine_max_params.setValue(CARLA_DEFAULT_MAX_PARAMETERS)
  262. self.ui.ch_engine_prefer_ui_bridges.setChecked(CARLA_DEFAULT_PREFER_UI_BRIDGES)
  263. self.ui.sb_engine_oscgui_timeout.setValue(CARLA_DEFAULT_OSC_UI_TIMEOUT)
  264. self.ui.ch_engine_disable_checks.setChecked(CARLA_DEFAULT_DISABLE_CHECKS)
  265. self.ui.ch_engine_dssi_chunks.setChecked(CARLA_DEFAULT_USE_DSSI_VST_CHUNKS)
  266. self.ui.ch_engine_prefer_plugin_bridges.setChecked(CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES)
  267. self.ui.ch_engine_force_stereo.setChecked(CARLA_DEFAULT_FORCE_STEREO)
  268. if self.ui.cb_engine_audio_driver.currentText() == "JACK":
  269. self.ui.cb_engine_process_mode_jack.setCurrentIndex(PROCESS_MODE_MULTIPLE_CLIENTS)
  270. self.ui.sw_engine_process_mode.setCurrentIndex(0)
  271. else:
  272. self.ui.cb_engine_process_mode_other.setCurrentIndex(PROCESS_MODE_CONTINUOUS_RACK-PROCESS_MODE_NON_JACK_PADDING)
  273. self.ui.sw_engine_process_mode.setCurrentIndex(1)
  274. elif self.ui.lw_page.currentRow() == TAB_INDEX_CARLA_PATHS:
  275. if self.ui.tw_paths.currentIndex() == 0:
  276. Carla.LADSPA_PATH.sort()
  277. self.ui.lw_ladspa.clear()
  278. for ladspa in Carla.LADSPA_PATH:
  279. self.ui.lw_ladspa.addItem(ladspa)
  280. elif self.ui.tw_paths.currentIndex() == 1:
  281. Carla.DSSI_PATH.sort()
  282. self.ui.lw_dssi.clear()
  283. for dssi in Carla.DSSI_PATH:
  284. self.ui.lw_dssi.addItem(dssi)
  285. elif self.ui.tw_paths.currentIndex() == 2:
  286. Carla.LV2_PATH.sort()
  287. self.ui.lw_lv2.clear()
  288. for lv2 in Carla.LV2_PATH:
  289. self.ui.lw_lv2.addItem(lv2)
  290. elif self.ui.tw_paths.currentIndex() == 3:
  291. Carla.VST_PATH.sort()
  292. self.ui.lw_vst.clear()
  293. for vst in Carla.VST_PATH:
  294. self.ui.lw_vst.addItem(vst)
  295. elif self.ui.tw_paths.currentIndex() == 4:
  296. Carla.GIG_PATH.sort()
  297. self.ui.lw_gig.clear()
  298. for gig in Carla.GIG_PATH:
  299. self.ui.lw_gig.addItem(gig)
  300. elif self.ui.tw_paths.currentIndex() == 5:
  301. Carla.SF2_PATH.sort()
  302. self.ui.lw_sf2.clear()
  303. for sf2 in Carla.SF2_PATH:
  304. self.ui.lw_sf2.addItem(sf2)
  305. elif self.ui.tw_paths.currentIndex() == 6:
  306. Carla.SFZ_PATH.sort()
  307. self.ui.lw_sfz.clear()
  308. for sfz in Carla.SFZ_PATH:
  309. self.ui.lw_sfz.addItem(sfz)
  310. @pyqtSlot()
  311. def slot_getAndSetProjectPath(self):
  312. getAndSetPath(self, self.ui.le_main_def_folder.text(), self.ui.le_main_def_folder)
  313. @pyqtSlot()
  314. def slot_engineAudioDriverChanged(self):
  315. if self.ui.cb_engine_audio_driver.currentText() == "JACK":
  316. self.ui.sw_engine_process_mode.setCurrentIndex(0)
  317. else:
  318. self.ui.sw_engine_process_mode.setCurrentIndex(1)
  319. @pyqtSlot()
  320. def slot_addPluginPath(self):
  321. newPath = QFileDialog.getExistingDirectory(self, self.tr("Add Path"), "", QFileDialog.ShowDirsOnly)
  322. if newPath:
  323. if self.ui.tw_paths.currentIndex() == 0:
  324. self.ui.lw_ladspa.addItem(newPath)
  325. elif self.ui.tw_paths.currentIndex() == 1:
  326. self.ui.lw_dssi.addItem(newPath)
  327. elif self.ui.tw_paths.currentIndex() == 2:
  328. self.ui.lw_lv2.addItem(newPath)
  329. elif self.ui.tw_paths.currentIndex() == 3:
  330. self.ui.lw_vst.addItem(newPath)
  331. elif self.ui.tw_paths.currentIndex() == 4:
  332. self.ui.lw_gig.addItem(newPath)
  333. elif self.ui.tw_paths.currentIndex() == 5:
  334. self.ui.lw_sf2.addItem(newPath)
  335. elif self.ui.tw_paths.currentIndex() == 6:
  336. self.ui.lw_sfz.addItem(newPath)
  337. @pyqtSlot()
  338. def slot_removePluginPath(self):
  339. if self.ui.tw_paths.currentIndex() == 0:
  340. self.ui.lw_ladspa.takeItem(self.ui.lw_ladspa.currentRow())
  341. elif self.ui.tw_paths.currentIndex() == 1:
  342. self.ui.lw_dssi.takeItem(self.ui.lw_dssi.currentRow())
  343. elif self.ui.tw_paths.currentIndex() == 2:
  344. self.ui.lw_lv2.takeItem(self.ui.lw_lv2.currentRow())
  345. elif self.ui.tw_paths.currentIndex() == 3:
  346. self.ui.lw_vst.takeItem(self.ui.lw_vst.currentRow())
  347. elif self.ui.tw_paths.currentIndex() == 4:
  348. self.ui.lw_gig.takeItem(self.ui.lw_gig.currentRow())
  349. elif self.ui.tw_paths.currentIndex() == 5:
  350. self.ui.lw_sf2.takeItem(self.ui.lw_sf2.currentRow())
  351. elif self.ui.tw_paths.currentIndex() == 6:
  352. self.ui.lw_sfz.takeItem(self.ui.lw_sfz.currentRow())
  353. @pyqtSlot()
  354. def slot_changePluginPath(self):
  355. if self.ui.tw_paths.currentIndex() == 0:
  356. currentPath = self.ui.lw_ladspa.currentItem().text()
  357. elif self.ui.tw_paths.currentIndex() == 1:
  358. currentPath = self.ui.lw_dssi.currentItem().text()
  359. elif self.ui.tw_paths.currentIndex() == 2:
  360. currentPath = self.ui.lw_lv2.currentItem().text()
  361. elif self.ui.tw_paths.currentIndex() == 3:
  362. currentPath = self.ui.lw_vst.currentItem().text()
  363. elif self.ui.tw_paths.currentIndex() == 4:
  364. currentPath = self.ui.lw_gig.currentItem().text()
  365. elif self.ui.tw_paths.currentIndex() == 5:
  366. currentPath = self.ui.lw_sf2.currentItem().text()
  367. elif self.ui.tw_paths.currentIndex() == 6:
  368. currentPath = self.ui.lw_sfz.currentItem().text()
  369. else:
  370. currentPath = ""
  371. newPath = QFileDialog.getExistingDirectory(self, self.tr("Add Path"), currentPath, QFileDialog.ShowDirsOnly)
  372. if newPath:
  373. if self.ui.tw_paths.currentIndex() == 0:
  374. self.ui.lw_ladspa.currentItem().setText(newPath)
  375. elif self.ui.tw_paths.currentIndex() == 1:
  376. self.ui.lw_dssi.currentItem().setText(newPath)
  377. elif self.ui.tw_paths.currentIndex() == 2:
  378. self.ui.lw_lv2.currentItem().setText(newPath)
  379. elif self.ui.tw_paths.currentIndex() == 3:
  380. self.ui.lw_vst.currentItem().setText(newPath)
  381. elif self.ui.tw_paths.currentIndex() == 4:
  382. self.ui.lw_gig.currentItem().setText(newPath)
  383. elif self.ui.tw_paths.currentIndex() == 5:
  384. self.ui.lw_sf2.currentItem().setText(newPath)
  385. elif self.ui.tw_paths.currentIndex() == 6:
  386. self.ui.lw_sfz.currentItem().setText(newPath)
  387. @pyqtSlot(int)
  388. def slot_pluginPathTabChanged(self, index):
  389. if index == 0:
  390. row = self.ui.lw_ladspa.currentRow()
  391. elif index == 1:
  392. row = self.ui.lw_dssi.currentRow()
  393. elif index == 2:
  394. row = self.ui.lw_lv2.currentRow()
  395. elif index == 3:
  396. row = self.ui.lw_vst.currentRow()
  397. elif index == 4:
  398. row = self.ui.lw_gig.currentRow()
  399. elif index == 5:
  400. row = self.ui.lw_sf2.currentRow()
  401. elif index == 6:
  402. row = self.ui.lw_sfz.currentRow()
  403. else:
  404. row = -1
  405. check = bool(row >= 0)
  406. self.ui.b_paths_remove.setEnabled(check)
  407. self.ui.b_paths_change.setEnabled(check)
  408. @pyqtSlot(int)
  409. def slot_pluginPathRowChanged(self, row):
  410. check = bool(row >= 0)
  411. self.ui.b_paths_remove.setEnabled(check)
  412. self.ui.b_paths_change.setEnabled(check)
  413. def done(self, r):
  414. QDialog.done(self, r)
  415. self.close()
  416. # ------------------------------------------------------------------------------------------------------------
  417. # Main Window
  418. class CarlaMainW(QMainWindow):
  419. def __init__(self, parent=None):
  420. QMainWindow.__init__(self, parent)
  421. self.ui = ui_carla.Ui_CarlaMainW()
  422. self.ui.setupUi(self)
  423. # -------------------------------------------------------------
  424. # Load Settings
  425. self.loadSettings(True)
  426. self.loadRDFs()
  427. self.setStyleSheet("""
  428. QWidget#w_plugins {
  429. background-color: qlineargradient(spread:pad,
  430. x1:0.0, y1:0.0,
  431. x2:0.2, y2:1.0,
  432. stop:0 rgb( 7, 7, 7),
  433. stop:1 rgb(28, 28, 28)
  434. );
  435. }
  436. """)
  437. # -------------------------------------------------------------
  438. # Internal stuff
  439. self.fEngineStarted = False
  440. self.fFirstEngineInit = False
  441. self.fProjectFilename = None
  442. self.fProjectLoading = False
  443. self.fPluginCount = 0
  444. self.fPluginList = []
  445. self.fIdleTimerFast = 0
  446. self.fIdleTimerSlow = 0
  447. self.fLastLoadedPluginId = -1
  448. #self._nsmAnnounce2str = ""
  449. #self._nsmOpen1str = ""
  450. #self._nsmOpen2str = ""
  451. #self.nsm_server = None
  452. #self.nsm_url = None
  453. # -------------------------------------------------------------
  454. # Set-up GUI stuff
  455. self.fDirModel = QFileSystemModel(self)
  456. self.fDirModel.setNameFilters(cString(Carla.host.get_supported_file_types()).split(";"))
  457. self.fDirModel.setRootPath(HOME)
  458. self.ui.fileTreeView.setModel(self.fDirModel)
  459. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
  460. self.ui.fileTreeView.setColumnHidden(1, True)
  461. self.ui.fileTreeView.setColumnHidden(2, True)
  462. self.ui.fileTreeView.setColumnHidden(3, True)
  463. self.ui.fileTreeView.setHeaderHidden(True)
  464. self.ui.act_engine_start.setEnabled(False)
  465. self.ui.act_engine_stop.setEnabled(False)
  466. self.ui.act_plugin_remove_all.setEnabled(False)
  467. # FIXME: Qt4 needs this so it properly create & resize the canvas
  468. self.ui.tabMain.setCurrentIndex(1)
  469. self.ui.tabMain.setCurrentIndex(0)
  470. # -------------------------------------------------------------
  471. # Set-up Canvas
  472. self.scene = patchcanvas.PatchScene(self, self.ui.graphicsView)
  473. self.ui.graphicsView.setScene(self.scene)
  474. self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, bool(self.fSavedSettings["Canvas/Antialiasing"] == patchcanvas.ANTIALIASING_FULL))
  475. if self.fSavedSettings["Canvas/UseOpenGL"] and hasGL:
  476. self.ui.graphicsView.setViewport(QGLWidget(self.ui.graphicsView))
  477. self.ui.graphicsView.setRenderHint(QPainter.HighQualityAntialiasing, self.fSavedSettings["Canvas/HighQualityAntialiasing"])
  478. pOptions = patchcanvas.options_t()
  479. pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  480. pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  481. pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  482. pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  483. pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  484. pFeatures = patchcanvas.features_t()
  485. pFeatures.group_info = False
  486. pFeatures.group_rename = False
  487. pFeatures.port_info = False
  488. pFeatures.port_rename = False
  489. pFeatures.handle_group_pos = True
  490. patchcanvas.setOptions(pOptions)
  491. patchcanvas.setFeatures(pFeatures)
  492. patchcanvas.init("Carla", self.scene, canvasCallback, False)
  493. patchcanvas.setCanvasSize(0, 0, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT)
  494. patchcanvas.setInitialPos(DEFAULT_CANVAS_WIDTH / 2, DEFAULT_CANVAS_HEIGHT / 2)
  495. self.ui.graphicsView.setSceneRect(0, 0, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT)
  496. # -------------------------------------------------------------
  497. # Set-up Canvas Preview
  498. self.ui.miniCanvasPreview.setRealParent(self)
  499. self.ui.miniCanvasPreview.setViewTheme(patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color())
  500. self.ui.miniCanvasPreview.init(self.scene, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT)
  501. QTimer.singleShot(100, self, SLOT("slot_miniCanvasInit()"))
  502. #self.m_fakeEdit = PluginEdit(self, -1)
  503. #self.m_curEdit = self.m_fakeEdit
  504. #self.w_edit.layout().addWidget(self.m_curEdit)
  505. #self.w_edit.layout().addStretch()
  506. # -------------------------------------------------------------
  507. # Connect actions to functions
  508. self.connect(self.ui.act_file_new, SIGNAL("triggered()"), SLOT("slot_fileNew()"))
  509. self.connect(self.ui.act_file_open, SIGNAL("triggered()"), SLOT("slot_fileOpen()"))
  510. self.connect(self.ui.act_file_save, SIGNAL("triggered()"), SLOT("slot_fileSave()"))
  511. self.connect(self.ui.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_fileSaveAs()"))
  512. self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_engineStart()"))
  513. self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_engineStop()"))
  514. self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_pluginAdd()"))
  515. self.connect(self.ui.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_pluginRemoveAll()"))
  516. self.ui.act_canvas_arrange.setEnabled(False) # TODO, later
  517. self.connect(self.ui.act_canvas_arrange, SIGNAL("triggered()"), SLOT("slot_canvasArrange()"))
  518. self.connect(self.ui.act_canvas_refresh, SIGNAL("triggered()"), SLOT("slot_canvasRefresh()"))
  519. self.connect(self.ui.act_canvas_zoom_fit, SIGNAL("triggered()"), SLOT("slot_canvasZoomFit()"))
  520. self.connect(self.ui.act_canvas_zoom_in, SIGNAL("triggered()"), SLOT("slot_canvasZoomIn()"))
  521. self.connect(self.ui.act_canvas_zoom_out, SIGNAL("triggered()"), SLOT("slot_canvasZoomOut()"))
  522. self.connect(self.ui.act_canvas_zoom_100, SIGNAL("triggered()"), SLOT("slot_canvasZoomReset()"))
  523. self.connect(self.ui.act_canvas_print, SIGNAL("triggered()"), SLOT("slot_canvasPrint()"))
  524. self.connect(self.ui.act_canvas_save_image, SIGNAL("triggered()"), SLOT("slot_canvasSaveImage()"))
  525. self.connect(self.ui.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()"))
  526. self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()"))
  527. self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  528. self.connect(self.ui.ch_transport, SIGNAL("currentIndexChanged(int)"), SLOT("slot_transportModeChanged(int)"))
  529. self.connect(self.ui.b_transport_play, SIGNAL("clicked(bool)"), SLOT("slot_transportPlayPause(bool)"))
  530. self.connect(self.ui.b_transport_stop, SIGNAL("clicked()"), SLOT("slot_transportStop()"))
  531. self.connect(self.ui.fileTreeView, SIGNAL("doubleClicked(QModelIndex)"), SLOT("slot_fileTreeDoubleClicked(QModelIndex)"))
  532. self.connect(self.ui.miniCanvasPreview, SIGNAL("miniCanvasMoved(double, double)"), SLOT("slot_miniCanvasMoved(double, double)"))
  533. self.connect(self.ui.graphicsView.horizontalScrollBar(), SIGNAL("valueChanged(int)"), SLOT("slot_horizontalScrollBarChanged(int)"))
  534. self.connect(self.ui.graphicsView.verticalScrollBar(), SIGNAL("valueChanged(int)"), SLOT("slot_verticalScrollBarChanged(int)"))
  535. self.connect(self.scene, SIGNAL("sceneGroupMoved(int, int, QPointF)"), SLOT("slot_canvasItemMoved(int, int, QPointF)"))
  536. self.connect(self.scene, SIGNAL("scaleChanged(double)"), SLOT("slot_canvasScaleChanged(double)"))
  537. self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()"))
  538. self.connect(self, SIGNAL("SIGTERM()"), SLOT("slot_handleSIGTERM()"))
  539. self.connect(self, SIGNAL("DebugCallback(int, int, int, double, QString)"), SLOT("slot_handleDebugCallback(int, int, int, double, QString)"))
  540. self.connect(self, SIGNAL("PluginAddedCallback(int)"), SLOT("slot_handlePluginAddedCallback(int)"))
  541. self.connect(self, SIGNAL("PluginRemovedCallback(int)"), SLOT("slot_handlePluginRemovedCallback(int)"))
  542. self.connect(self, SIGNAL("PluginRenamedCallback(int, QString)"), SLOT("slot_handlePluginRenamedCallback(int, QString)"))
  543. self.connect(self, SIGNAL("ParameterValueChangedCallback(int, int, double)"), SLOT("slot_handleParameterValueChangedCallback(int, int, double)"))
  544. self.connect(self, SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), SLOT("slot_handleParameterDefaultChangedCallback(int, int, double)"))
  545. self.connect(self, SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiChannelChangedCallback(int, int, int)"))
  546. self.connect(self, SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiCcChangedCallback(int, int, int)"))
  547. self.connect(self, SIGNAL("ProgramChangedCallback(int, int)"), SLOT("slot_handleProgramChangedCallback(int, int)"))
  548. self.connect(self, SIGNAL("MidiProgramChangedCallback(int, int)"), SLOT("slot_handleMidiProgramChangedCallback(int, int)"))
  549. self.connect(self, SIGNAL("NoteOnCallback(int, int, int, int)"), SLOT("slot_handleNoteOnCallback(int, int, int, int)"))
  550. self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), SLOT("slot_handleNoteOffCallback(int, int, int)"))
  551. self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), SLOT("slot_handleShowGuiCallback(int, int)"))
  552. self.connect(self, SIGNAL("UpdateCallback(int)"), SLOT("slot_handleUpdateCallback(int)"))
  553. self.connect(self, SIGNAL("ReloadInfoCallback(int)"), SLOT("slot_handleReloadInfoCallback(int)"))
  554. self.connect(self, SIGNAL("ReloadParametersCallback(int)"), SLOT("slot_handleReloadParametersCallback(int)"))
  555. self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), SLOT("slot_handleReloadProgramsCallback(int)"))
  556. self.connect(self, SIGNAL("ReloadAllCallback(int)"), SLOT("slot_handleReloadAllCallback(int)"))
  557. self.connect(self, SIGNAL("PatchbayClientAddedCallback(int, QString)"), SLOT("slot_handlePatchbayClientAddedCallback(int, QString)"))
  558. self.connect(self, SIGNAL("PatchbayClientRemovedCallback(int)"), SLOT("slot_handlePatchbayClientRemovedCallback(int)"))
  559. self.connect(self, SIGNAL("PatchbayClientRenamedCallback(int, QString)"), SLOT("slot_handlePatchbayClientRenamedCallback(int, QString)"))
  560. self.connect(self, SIGNAL("PatchbayPortAddedCallback(int, int, int, QString)"), SLOT("slot_handlePatchbayPortAddedCallback(int, int, int, QString)"))
  561. self.connect(self, SIGNAL("PatchbayPortRemovedCallback(int)"), SLOT("slot_handlePatchbayPortRemovedCallback(int)"))
  562. self.connect(self, SIGNAL("PatchbayPortRenamedCallback(int, QString)"), SLOT("slot_handlePatchbayPortRenamedCallback(int, QString)"))
  563. self.connect(self, SIGNAL("PatchbayConnectionAddedCallback(int, int, int)"), SLOT("slot_handlePatchbayConnectionAddedCallback(int, int, int)"))
  564. self.connect(self, SIGNAL("PatchbayConnectionRemovedCallback(int)"), SLOT("slot_handlePatchbayConnectionRemovedCallback(int)"))
  565. #self.connect(self, SIGNAL("NSM_AnnounceCallback()"), SLOT("slot_handleNSM_AnnounceCallback()"))
  566. #self.connect(self, SIGNAL("NSM_Open1Callback()"), SLOT("slot_handleNSM_Open1Callback()"))
  567. #self.connect(self, SIGNAL("NSM_Open2Callback()"), SLOT("slot_handleNSM_Open2Callback()"))
  568. #self.connect(self, SIGNAL("NSM_SaveCallback()"), SLOT("slot_handleNSM_SaveCallback()"))
  569. self.connect(self, SIGNAL("ErrorCallback(QString)"), SLOT("slot_handleErrorCallback(QString)"))
  570. self.connect(self, SIGNAL("QuitCallback()"), SLOT("slot_handleQuitCallback()"))
  571. #NSM_URL = os.getenv("NSM_URL")
  572. #if NSM_URL:
  573. #Carla.host.nsm_announce(NSM_URL, os.getpid())
  574. #else:
  575. QTimer.singleShot(0, self, SLOT("slot_engineStart()"))
  576. @pyqtSlot()
  577. def slot_canvasArrange(self):
  578. patchcanvas.arrange()
  579. @pyqtSlot()
  580. def slot_canvasRefresh(self):
  581. patchcanvas.clear()
  582. Carla.host.patchbay_refresh()
  583. @pyqtSlot()
  584. def slot_canvasZoomFit(self):
  585. self.scene.zoom_fit()
  586. @pyqtSlot()
  587. def slot_canvasZoomIn(self):
  588. self.scene.zoom_in()
  589. @pyqtSlot()
  590. def slot_canvasZoomOut(self):
  591. self.scene.zoom_out()
  592. @pyqtSlot()
  593. def slot_canvasZoomReset(self):
  594. self.scene.zoom_reset()
  595. @pyqtSlot()
  596. def slot_canvasPrint(self):
  597. self.scene.clearSelection()
  598. self.fExportPrinter = QPrinter()
  599. dialog = QPrintDialog(self.fExportPrinter, self)
  600. if dialog.exec_():
  601. painter = QPainter(self.fExportPrinter)
  602. painter.setRenderHint(QPainter.Antialiasing)
  603. painter.setRenderHint(QPainter.TextAntialiasing)
  604. self.scene.render(painter)
  605. @pyqtSlot()
  606. def slot_canvasSaveImage(self):
  607. newPath = QFileDialog.getSaveFileName(self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)"))
  608. if newPath:
  609. self.scene.clearSelection()
  610. # FIXME - must be a better way...
  611. if newPath.endswith((".jpg", ".jpG", ".jPG", ".JPG", ".JPg", ".Jpg")):
  612. imgFormat = "JPG"
  613. elif newPath.endswith((".png", ".pnG", ".pNG", ".PNG", ".PNg", ".Png")):
  614. imgFormat = "PNG"
  615. else:
  616. # File-dialog may not auto-add the extension
  617. imgFormat = "PNG"
  618. newPath += ".png"
  619. self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32)
  620. painter = QPainter(self.fExportImage)
  621. painter.setRenderHint(QPainter.Antialiasing) # TODO - set true, cleanup this
  622. painter.setRenderHint(QPainter.TextAntialiasing)
  623. self.scene.render(painter)
  624. self.fExportImage.save(newPath, imgFormat, 100)
  625. def showLastError(self, isProject):
  626. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  627. self.tr("Failed to load %s" % (self.tr("project") if isProject else self.tr("plugin"))),
  628. cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  629. @pyqtSlot(QModelIndex)
  630. def slot_fileTreeDoubleClicked(self, modelIndex):
  631. filename = self.fDirModel.filePath(modelIndex)
  632. print(filename)
  633. if not os.path.exists(filename):
  634. return
  635. if not os.path.isfile(filename):
  636. return
  637. basename = os.path.basename(filename)
  638. extension = filename.rsplit(".", 1)[-1].lower()
  639. if extension == "carxp":
  640. if not Carla.host.load_project(filename):
  641. self.showLastError(True)
  642. elif extension == "gig":
  643. if not Carla.host.add_plugin(BINARY_NATIVE, PLUGIN_GIG, filename, None, basename, None):
  644. self.showLastError(False)
  645. elif extension == "sf2":
  646. if not Carla.host.add_plugin(BINARY_NATIVE, PLUGIN_SF2, filename, None, basename, None):
  647. self.showLastError(False)
  648. elif extension == "sfz":
  649. if not Carla.host.add_plugin(BINARY_NATIVE, PLUGIN_SFZ, filename, None, basename, None):
  650. self.showLastError(False)
  651. elif extension in ("aac", "flac", "oga", "ogg", "mp3", "wav"):
  652. self.fLastLoadedPluginId = -2
  653. if Carla.host.add_plugin(BINARY_NATIVE, PLUGIN_INTERNAL, None, basename, "audiofile", None):
  654. while (self.fLastLoadedPluginId == -2): sleep(0.2)
  655. idx = self.fLastLoadedPluginId
  656. self.fLastLoadedPluginId = -1
  657. Carla.host.set_custom_data(idx, CUSTOM_DATA_STRING, "file00", filename)
  658. else:
  659. self.fLastLoadedPluginId = -1
  660. self.showLastError(False)
  661. elif extension in ("mid", "midi"):
  662. self.fLastLoadedPluginId = -2
  663. if Carla.host.add_plugin(BINARY_NATIVE, PLUGIN_INTERNAL, None, basename, "midifile", None):
  664. while (self.fLastLoadedPluginId == -2): sleep(0.2)
  665. idx = self.fLastLoadedPluginId
  666. self.fLastLoadedPluginId = -1
  667. Carla.host.set_custom_data(idx, CUSTOM_DATA_STRING, "file", filename)
  668. else:
  669. self.fLastLoadedPluginId = -1
  670. self.showLastError(False)
  671. @pyqtSlot(float)
  672. def slot_canvasScaleChanged(self, scale):
  673. self.ui.miniCanvasPreview.setViewScale(scale)
  674. @pyqtSlot(int, int, QPointF)
  675. def slot_canvasItemMoved(self, group_id, split_mode, pos):
  676. self.ui.miniCanvasPreview.update()
  677. @pyqtSlot(int)
  678. def slot_horizontalScrollBarChanged(self, value):
  679. maximum = self.ui.graphicsView.horizontalScrollBar().maximum()
  680. if maximum == 0:
  681. xp = 0
  682. else:
  683. xp = float(value) / maximum
  684. self.ui.miniCanvasPreview.setViewPosX(xp)
  685. @pyqtSlot(int)
  686. def slot_verticalScrollBarChanged(self, value):
  687. maximum = self.ui.graphicsView.verticalScrollBar().maximum()
  688. if maximum == 0:
  689. yp = 0
  690. else:
  691. yp = float(value) / maximum
  692. self.ui.miniCanvasPreview.setViewPosY(yp)
  693. @pyqtSlot()
  694. def slot_miniCanvasInit(self):
  695. settings = QSettings()
  696. self.ui.graphicsView.horizontalScrollBar().setValue(settings.value("HorizontalScrollBarValue", DEFAULT_CANVAS_WIDTH / 3, type=int))
  697. self.ui.graphicsView.verticalScrollBar().setValue(settings.value("VerticalScrollBarValue", DEFAULT_CANVAS_HEIGHT * 3 / 8, type=int))
  698. @pyqtSlot(float, float)
  699. def slot_miniCanvasMoved(self, xp, yp):
  700. self.ui.graphicsView.horizontalScrollBar().setValue(xp * DEFAULT_CANVAS_WIDTH)
  701. self.ui.graphicsView.verticalScrollBar().setValue(yp * DEFAULT_CANVAS_HEIGHT)
  702. @pyqtSlot()
  703. def slot_miniCanvasCheckAll(self):
  704. self.slot_miniCanvasCheckSize()
  705. self.slot_horizontalScrollBarChanged(self.ui.graphicsView.horizontalScrollBar().value())
  706. self.slot_verticalScrollBarChanged(self.ui.graphicsView.verticalScrollBar().value())
  707. @pyqtSlot()
  708. def slot_miniCanvasCheckSize(self):
  709. self.ui.miniCanvasPreview.setViewSize(float(self.ui.graphicsView.width()) / DEFAULT_CANVAS_WIDTH, float(self.ui.graphicsView.height()) / DEFAULT_CANVAS_HEIGHT)
  710. def startEngine(self, clientName = "Carla"):
  711. # ---------------------------------------------
  712. # Engine settings
  713. settings = QSettings()
  714. if LINUX:
  715. defaultMode = PROCESS_MODE_MULTIPLE_CLIENTS
  716. else:
  717. defaultMode = PROCESS_MODE_CONTINUOUS_RACK
  718. Carla.processMode = settings.value("Engine/ProcessMode", defaultMode, type=int)
  719. Carla.maxParameters = settings.value("Engine/MaxParameters", MAX_DEFAULT_PARAMETERS, type=int)
  720. transportMode = settings.value("Engine/TransportMode", TRANSPORT_MODE_JACK, type=int)
  721. forceStereo = settings.value("Engine/ForceStereo", False, type=bool)
  722. preferPluginBridges = settings.value("Engine/PreferPluginBridges", False, type=bool)
  723. preferUiBridges = settings.value("Engine/PreferUiBridges", True, type=bool)
  724. useDssiVstChunks = settings.value("Engine/UseDssiVstChunks", False, type=bool)
  725. oscUiTimeout = settings.value("Engine/OscUiTimeout", 40, type=int)
  726. preferredBufferSize = settings.value("Engine/PreferredBufferSize", 512, type=int)
  727. preferredSampleRate = settings.value("Engine/PreferredSampleRate", 44100, type=int)
  728. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  729. forceStereo = True
  730. elif Carla.processMode == PROCESS_MODE_MULTIPLE_CLIENTS and os.getenv("LADISH_APP_NAME"):
  731. print("LADISH detected but using multiple clients (not allowed), forcing single client now")
  732. Carla.processMode = PROCESS_MODE_SINGLE_CLIENT
  733. Carla.host.set_engine_option(OPTION_PROCESS_MODE, Carla.processMode, "")
  734. Carla.host.set_engine_option(OPTION_MAX_PARAMETERS, Carla.maxParameters, "")
  735. Carla.host.set_engine_option(OPTION_FORCE_STEREO, forceStereo, "")
  736. Carla.host.set_engine_option(OPTION_PREFER_PLUGIN_BRIDGES, preferPluginBridges, "")
  737. Carla.host.set_engine_option(OPTION_PREFER_UI_BRIDGES, preferUiBridges, "")
  738. Carla.host.set_engine_option(OPTION_USE_DSSI_VST_CHUNKS, useDssiVstChunks, "")
  739. Carla.host.set_engine_option(OPTION_OSC_UI_TIMEOUT, oscUiTimeout, "")
  740. Carla.host.set_engine_option(OPTION_PREFERRED_BUFFER_SIZE, preferredBufferSize, "")
  741. Carla.host.set_engine_option(OPTION_PREFERRED_SAMPLE_RATE, preferredSampleRate, "")
  742. # ---------------------------------------------
  743. # Start
  744. if WINDOWS:
  745. defaultDriver = "DirectSound"
  746. elif MACOS:
  747. defaultDriver = "CoreAudio"
  748. else:
  749. defaultDriver = "JACK"
  750. audioDriver = settings.value("Engine/AudioDriver", defaultDriver, type=str)
  751. if not Carla.host.engine_init(audioDriver, clientName):
  752. if self.fFirstEngineInit:
  753. self.fFirstEngineInit = False
  754. return
  755. audioError = cString(Carla.host.get_last_error())
  756. if audioError:
  757. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
  758. else:
  759. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s'" % audioDriver))
  760. return
  761. self.fEngineStarted = True
  762. self.fFirstEngineInit = False
  763. self.fPluginCount = 0
  764. self.fPluginList = []
  765. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  766. maxCount = MAX_RACK_PLUGINS
  767. elif Carla.processMode == PROCESS_MODE_PATCHBAY:
  768. maxCount = MAX_PATCHBAY_PLUGINS
  769. else:
  770. maxCount = MAX_DEFAULT_PLUGINS
  771. self.ui.ch_transport.blockSignals(True)
  772. self.ui.ch_transport.addItem(self.tr("Internal"))
  773. if audioDriver == "JACK":
  774. self.ui.ch_transport.addItem(self.tr("JACK"))
  775. elif transportMode == TRANSPORT_MODE_JACK:
  776. transportMode = TRANSPORT_MODE_INTERNAL
  777. self.ui.ch_transport.setCurrentIndex(transportMode)
  778. self.ui.ch_transport.blockSignals(False)
  779. for x in range(maxCount):
  780. self.fPluginList.append(None)
  781. Carla.host.set_engine_option(OPTION_TRANSPORT_MODE, transportMode, "")
  782. # Peaks
  783. self.fIdleTimerFast = self.startTimer(self.fSavedSettings["Main/RefreshInterval"])
  784. # LEDs and edit dialog parameters
  785. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings["Main/RefreshInterval"]*2)
  786. def stopEngine(self):
  787. if self.fPluginCount > 0:
  788. 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"
  789. "Do you want to do this now?"),
  790. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  791. if ask != QMessageBox.Yes:
  792. return
  793. self.removeAllPlugins()
  794. if Carla.host.is_engine_running() and not Carla.host.engine_close():
  795. print(cString(Carla.host.get_last_error()))
  796. self.fEngineStarted = False
  797. self.fPluginCount = 0
  798. self.fPluginList = []
  799. self.killTimer(self.fIdleTimerFast)
  800. self.killTimer(self.fIdleTimerSlow)
  801. settings = QSettings()
  802. settings.setValue("Engine/TransportMode", self.ui.ch_transport.currentIndex())
  803. self.ui.ch_transport.blockSignals(True)
  804. self.ui.ch_transport.clear()
  805. self.ui.ch_transport.blockSignals(False)
  806. patchcanvas.clear()
  807. def loadProject(self, filename):
  808. self.fProjectFilename = filename
  809. self.setWindowTitle("Carla - %s" % os.path.basename(filename))
  810. self.fProjectLoading = True
  811. Carla.host.load_project(filename)
  812. self.fProjectLoading = False
  813. def loadProjectLater(self, filename):
  814. self.fProjectFilename = filename
  815. self.setWindowTitle("Carla - %s" % os.path.basename(filename))
  816. QTimer.singleShot(0, self, SLOT("slot_loadProjectLater()"))
  817. def saveProject(self, filename):
  818. self.fProjectFilename = filename
  819. self.setWindowTitle("Carla - %s" % os.path.basename(filename))
  820. Carla.host.save_project(filename)
  821. def addPlugin(self, btype, ptype, filename, name, label, extraStuff):
  822. if not self.fEngineStarted:
  823. QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
  824. return False
  825. if not Carla.host.add_plugin(btype, ptype, filename, name, label, extraStuff):
  826. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  827. return False
  828. return True
  829. def removeAllPlugins(self):
  830. while (self.ui.w_plugins.layout().takeAt(0)):
  831. pass
  832. self.ui.act_plugin_remove_all.setEnabled(False)
  833. for i in range(self.fPluginCount):
  834. pwidget = self.fPluginList[i]
  835. if pwidget is None:
  836. break
  837. self.fPluginList[i] = None
  838. pwidget.ui.edit_dialog.close()
  839. pwidget.close()
  840. pwidget.deleteLater()
  841. del pwidget
  842. self.fPluginCount = 0
  843. Carla.host.remove_all_plugins()
  844. @pyqtSlot()
  845. def slot_fileNew(self):
  846. self.removeAllPlugins()
  847. self.fProjectFilename = None
  848. self.fProjectLoading = False
  849. self.setWindowTitle("Carla")
  850. @pyqtSlot()
  851. def slot_fileOpen(self):
  852. fileFilter = self.tr("Carla Project File (*.carxp)")
  853. filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  854. if filenameTry:
  855. # FIXME - show dialog to user
  856. self.removeAllPlugins()
  857. self.loadProject(filenameTry)
  858. @pyqtSlot()
  859. def slot_fileSave(self, saveAs=False):
  860. if self.fProjectFilename and not saveAs:
  861. return self.saveProject(self.fProjectFilename)
  862. fileFilter = self.tr("Carla Project File (*.carxp)")
  863. filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  864. if filenameTry:
  865. if not filenameTry.endswith(".carxp"):
  866. filenameTry += ".carxp"
  867. self.saveProject(filenameTry)
  868. @pyqtSlot()
  869. def slot_fileSaveAs(self):
  870. self.slot_fileSave(True)
  871. @pyqtSlot()
  872. def slot_loadProjectLater(self):
  873. self.fProjectLoading = True
  874. Carla.host.load_project(self.fProjectFilename)
  875. self.fProjectLoading = False
  876. @pyqtSlot()
  877. def slot_engineStart(self):
  878. self.startEngine()
  879. check = Carla.host.is_engine_running()
  880. self.ui.act_file_open.setEnabled(check)
  881. self.ui.act_engine_start.setEnabled(not check)
  882. self.ui.act_engine_stop.setEnabled(check)
  883. self.ui.act_engine_configure.setEnabled(not check)
  884. self.ui.frame_transport.setEnabled(check)
  885. @pyqtSlot()
  886. def slot_engineStop(self):
  887. self.stopEngine()
  888. check = Carla.host.is_engine_running()
  889. self.ui.act_file_open.setEnabled(check)
  890. self.ui.act_engine_start.setEnabled(not check)
  891. self.ui.act_engine_stop.setEnabled(check)
  892. self.ui.frame_transport.setEnabled(check)
  893. @pyqtSlot()
  894. def slot_pluginAdd(self):
  895. dialog = PluginDatabaseW(self)
  896. if dialog.exec_():
  897. btype = dialog.fRetPlugin['build']
  898. ptype = dialog.fRetPlugin['type']
  899. filename = dialog.fRetPlugin['binary']
  900. label = dialog.fRetPlugin['label']
  901. extraStuff = self.getExtraStuff(dialog.fRetPlugin)
  902. self.addPlugin(btype, ptype, filename, None, label, extraStuff)
  903. @pyqtSlot()
  904. def slot_pluginRemoveAll(self):
  905. self.removeAllPlugins()
  906. self.connect(self.ui.ch_transport, SIGNAL("currentIndexChanged(int)"), SLOT("(int)"))
  907. self.connect(self.ui.b_transport_play, SIGNAL("clicked(bool)"), SLOT("(bool)"))
  908. self.connect(self.ui.b_transport_stop, SIGNAL("clicked()"), SLOT("()"))
  909. @pyqtSlot(int)
  910. def slot_transportModeChanged(self, newMode):
  911. Carla.host.set_engine_option(OPTION_TRANSPORT_MODE, newMode, "")
  912. @pyqtSlot(bool)
  913. def slot_transportPlayPause(self, toggled):
  914. if toggled:
  915. Carla.host.transport_play()
  916. else:
  917. Carla.host.transport_pause()
  918. @pyqtSlot()
  919. def slot_transportStop(self):
  920. Carla.host.transport_pause()
  921. Carla.host.transport_relocate(0)
  922. @pyqtSlot()
  923. def slot_aboutCarla(self):
  924. CarlaAboutW(self).exec_()
  925. @pyqtSlot()
  926. def slot_configureCarla(self):
  927. dialog = CarlaSettingsW(self)
  928. if dialog.exec_():
  929. self.loadSettings(False)
  930. patchcanvas.clear()
  931. pOptions = patchcanvas.options_t()
  932. pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  933. pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  934. pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  935. pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  936. pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  937. pFeatures = patchcanvas.features_t()
  938. pFeatures.group_info = False
  939. pFeatures.group_rename = False
  940. pFeatures.port_info = False
  941. pFeatures.port_rename = False
  942. pFeatures.handle_group_pos = True
  943. patchcanvas.setOptions(pOptions)
  944. patchcanvas.setFeatures(pFeatures)
  945. patchcanvas.init("Carla", self.scene, canvasCallback, False)
  946. if self.fEngineStarted:
  947. Carla.host.patchbay_refresh()
  948. for pwidget in self.fPluginList:
  949. if pwidget is None:
  950. return
  951. pwidget.setRefreshRate(self.fSavedSettings["Main/RefreshInterval"])
  952. @pyqtSlot()
  953. def slot_handleSIGUSR1(self):
  954. print("Got SIGUSR1 -> Saving project now")
  955. QTimer.singleShot(0, self, SLOT("slot_fileSave()"))
  956. @pyqtSlot()
  957. def slot_handleSIGTERM(self):
  958. print("Got SIGTERM -> Closing now")
  959. self.close()
  960. @pyqtSlot(int, int, int, float, str)
  961. def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
  962. print("DEBUG :: %i, %i, %i, %f, \"%s\")" % (pluginId, value1, value2, value3, valueStr))
  963. @pyqtSlot(int)
  964. def slot_handlePluginAddedCallback(self, pluginId):
  965. pwidget = PluginWidget(self, pluginId)
  966. pwidget.setRefreshRate(self.fSavedSettings["Main/RefreshInterval"])
  967. self.ui.w_plugins.layout().addWidget(pwidget)
  968. self.fPluginCount += 1
  969. self.fPluginList[pluginId] = pwidget
  970. if not self.fProjectLoading:
  971. pwidget.setActive(True, True, True)
  972. if self.fPluginCount == 1:
  973. self.ui.act_plugin_remove_all.setEnabled(True)
  974. if self.fLastLoadedPluginId == -2:
  975. self.fLastLoadedPluginId = pluginId
  976. @pyqtSlot(int)
  977. def slot_handlePluginRemovedCallback(self, pluginId):
  978. if pluginId >= self.fPluginCount:
  979. return
  980. pwidget = self.fPluginList[pluginId]
  981. if pwidget is None:
  982. return
  983. self.fPluginList[pluginId] = None
  984. self.fPluginCount -= 1
  985. self.ui.w_plugins.layout().removeWidget(pwidget)
  986. pwidget.ui.edit_dialog.close()
  987. pwidget.close()
  988. pwidget.deleteLater()
  989. del pwidget
  990. # push all plugins 1 slot back
  991. for i in range(pluginId, self.fPluginCount):
  992. self.fPluginList[i] = self.fPluginList[i+1]
  993. self.fPluginList[i].setId(i)
  994. if self.fPluginCount == 0:
  995. self.ui.act_plugin_remove_all.setEnabled(False)
  996. @pyqtSlot(int, str)
  997. def slot_handlePluginRenamedCallback(self, pluginId, newName):
  998. if pluginId >= self.fPluginCount:
  999. return
  1000. pwidget = self.fPluginList[pluginId]
  1001. if pwidget is None:
  1002. return
  1003. pwidget.ui.label_name.setText(newName)
  1004. @pyqtSlot(int, int, float)
  1005. def slot_handleParameterValueChangedCallback(self, pluginId, parameterId, value):
  1006. if pluginId >= self.fPluginCount:
  1007. return
  1008. pwidget = self.fPluginList[pluginId]
  1009. if pwidget is None:
  1010. return
  1011. pwidget.setParameterValue(parameterId, value)
  1012. @pyqtSlot(int, int, float)
  1013. def slot_handleParameterDefaultChangedCallback(self, pluginId, parameterId, value):
  1014. if pluginId >= self.fPluginCount:
  1015. return
  1016. pwidget = self.fPluginList[pluginId]
  1017. if pwidget is None:
  1018. return
  1019. pwidget.setParameterDefault(parameterId, value)
  1020. @pyqtSlot(int, int, int)
  1021. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, parameterId, channel):
  1022. if pluginId >= self.fPluginCount:
  1023. return
  1024. pwidget = self.fPluginList[pluginId]
  1025. if pwidget is None:
  1026. return
  1027. pwidget.setParameterMidiChannel(parameterId, channel)
  1028. @pyqtSlot(int, int, int)
  1029. def slot_handleParameterMidiCcChangedCallback(self, pluginId, parameterId, cc):
  1030. if pluginId >= self.fPluginCount:
  1031. return
  1032. pwidget = self.fPluginList[pluginId]
  1033. if pwidget is None:
  1034. return
  1035. pwidget.setParameterMidiControl(parameterId, cc)
  1036. @pyqtSlot(int, int)
  1037. def slot_handleProgramChangedCallback(self, pluginId, programId):
  1038. if pluginId >= self.fPluginCount:
  1039. return
  1040. pwidget = self.fPluginList[pluginId]
  1041. if pwidget is None:
  1042. return
  1043. pwidget.setProgram(programId)
  1044. @pyqtSlot(int, int)
  1045. def slot_handleMidiProgramChangedCallback(self, pluginId, midiProgramId):
  1046. if pluginId >= self.fPluginCount:
  1047. return
  1048. pwidget = self.fPluginList[pluginId]
  1049. if pwidget is None:
  1050. return
  1051. pwidget.setMidiProgram(midiProgramId)
  1052. @pyqtSlot(int, int, int, int)
  1053. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  1054. if pluginId >= self.fPluginCount:
  1055. return
  1056. pwidget = self.fPluginList[pluginId]
  1057. if pwidget is None:
  1058. return
  1059. pwidget.sendNoteOn(channel, note)
  1060. @pyqtSlot(int, int, int)
  1061. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  1062. if pluginId >= self.fPluginCount:
  1063. return
  1064. pwidget = self.fPluginList[pluginId]
  1065. if pwidget is None:
  1066. return
  1067. pwidget.sendNoteOff(channel, note)
  1068. @pyqtSlot(int, int)
  1069. def slot_handleShowGuiCallback(self, pluginId, show):
  1070. if pluginId >= self.fPluginCount:
  1071. return
  1072. pwidget = self.fPluginList[pluginId]
  1073. if pwidget is None:
  1074. return
  1075. if show == 0:
  1076. pwidget.ui.b_gui.setChecked(False)
  1077. pwidget.ui.b_gui.setEnabled(True)
  1078. elif show == 1:
  1079. pwidget.ui.b_gui.setChecked(True)
  1080. pwidget.ui.b_gui.setEnabled(True)
  1081. elif show == -1:
  1082. pwidget.ui.b_gui.setChecked(False)
  1083. pwidget.ui.b_gui.setEnabled(False)
  1084. @pyqtSlot(int)
  1085. def slot_handleUpdateCallback(self, pluginId):
  1086. if pluginId >= self.fPluginCount:
  1087. return
  1088. pwidget = self.fPluginList[pluginId]
  1089. if pwidget is None:
  1090. return
  1091. pwidget.ui.edit_dialog.updateInfo()
  1092. @pyqtSlot(int)
  1093. def slot_handleReloadInfoCallback(self, pluginId):
  1094. if pluginId >= self.fPluginCount:
  1095. return
  1096. pwidget = self.fPluginList[pluginId]
  1097. if pwidget is None:
  1098. return
  1099. pwidget.ui.edit_dialog.reloadInfo()
  1100. @pyqtSlot(int)
  1101. def slot_handleReloadParametersCallback(self, pluginId):
  1102. if pluginId >= self.fPluginCount:
  1103. return
  1104. pwidget = self.fPluginList[pluginId]
  1105. if pwidget is None:
  1106. return
  1107. pwidget.ui.edit_dialog.reloadParameters()
  1108. @pyqtSlot(int)
  1109. def slot_handleReloadProgramsCallback(self, pluginId):
  1110. if pluginId >= self.fPluginCount:
  1111. return
  1112. pwidget = self.fPluginList[pluginId]
  1113. if pwidget is None:
  1114. return
  1115. pwidget.ui.edit_dialog.reloadPrograms()
  1116. @pyqtSlot(int)
  1117. def slot_handleReloadAllCallback(self, pluginId):
  1118. if pluginId >= self.fPluginCount:
  1119. return
  1120. pwidget = self.fPluginList[pluginId]
  1121. if pwidget is None:
  1122. return
  1123. pwidget.ui.edit_dialog.reloadAll()
  1124. @pyqtSlot(int, str)
  1125. def slot_handlePatchbayClientAddedCallback(self, clientId, clientName):
  1126. patchcanvas.addGroup(clientId, clientName)
  1127. @pyqtSlot(int)
  1128. def slot_handlePatchbayClientRemovedCallback(self, clientId):
  1129. patchcanvas.removeGroup(clientId)
  1130. @pyqtSlot(int, str)
  1131. def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
  1132. patchcanvas.renameGroup(clientId, newClientName)
  1133. @pyqtSlot(int, int, int, str)
  1134. def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName):
  1135. if (portFlags & PATCHBAY_PORT_IS_INPUT):
  1136. portMode = patchcanvas.PORT_MODE_INPUT
  1137. elif (portFlags & PATCHBAY_PORT_IS_OUTPUT):
  1138. portMode = patchcanvas.PORT_MODE_OUTPUT
  1139. else:
  1140. portMode = patchcanvas.PORT_MODE_NULL
  1141. if (portFlags & PATCHBAY_PORT_IS_AUDIO):
  1142. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  1143. elif (portFlags & PATCHBAY_PORT_IS_MIDI):
  1144. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  1145. else:
  1146. portType = patchcanvas.PORT_TYPE_NULL
  1147. patchcanvas.addPort(clientId, portId, portName, portMode, portType)
  1148. @pyqtSlot(int)
  1149. def slot_handlePatchbayPortRemovedCallback(self, portId):
  1150. patchcanvas.removePort(portId)
  1151. @pyqtSlot(int, str)
  1152. def slot_handlePatchbayPortRenamedCallback(self, portId, newPortName):
  1153. patchcanvas.renamePort(portId, newPortName)
  1154. @pyqtSlot(int, int, int)
  1155. def slot_handlePatchbayConnectionAddedCallback(self, connectionId, portOutId, portInId):
  1156. patchcanvas.connectPorts(connectionId, portOutId, portInId)
  1157. @pyqtSlot(int)
  1158. def slot_handlePatchbayConnectionRemovedCallback(self, connectionId):
  1159. patchcanvas.disconnectPorts(connectionId)
  1160. @pyqtSlot(str)
  1161. def slot_handleErrorCallback(self, error):
  1162. QMessageBox.critical(self, self.tr("Error"), error)
  1163. @pyqtSlot()
  1164. def slot_handleQuitCallback(self):
  1165. CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"),
  1166. self.tr("Engine has been stopped or crashed.\nPlease restart Carla"),
  1167. self.tr("You may want to save your session now..."), QMessageBox.Ok, QMessageBox.Ok)
  1168. def getExtraStuff(self, plugin):
  1169. ptype = plugin['type']
  1170. if ptype == PLUGIN_LADSPA:
  1171. uniqueId = plugin['uniqueId']
  1172. for rdfItem in self.fLadspaRdfList:
  1173. if rdfItem.UniqueID == uniqueId:
  1174. return pointer(rdfItem)
  1175. elif ptype == PLUGIN_DSSI:
  1176. if (plugin['hints'] & PLUGIN_HAS_GUI):
  1177. gui = findDSSIGUI(plugin['binary'], plugin['name'], plugin['label'])
  1178. if gui:
  1179. return gui.encode("utf-8")
  1180. elif ptype == PLUGIN_SF2:
  1181. if plugin['name'].endswith(" (16 outputs)"):
  1182. # return a dummy non-null pointer
  1183. INTPOINTER = POINTER(c_int)
  1184. ptr = c_int(0x1)
  1185. addr = addressof(ptr)
  1186. return cast(addr, INTPOINTER)
  1187. return c_nullptr
  1188. def loadRDFs(self):
  1189. # Save RDF info for later
  1190. self.fLadspaRdfList = []
  1191. if not haveLRDF:
  1192. return
  1193. settingsDir = os.path.join(HOME, ".config", "falkTX")
  1194. frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
  1195. if os.path.exists(frLadspaFile):
  1196. frLadspa = open(frLadspaFile, 'r')
  1197. try:
  1198. self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
  1199. except:
  1200. pass
  1201. frLadspa.close()
  1202. def saveSettings(self):
  1203. settings = QSettings()
  1204. settings.setValue("Geometry", self.saveGeometry())
  1205. settings.setValue("SplitterState", self.ui.splitter.saveState())
  1206. settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  1207. #settings.setValue("ShowTransport", self.ui.frame_transport.isVisible())
  1208. settings.setValue("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().value())
  1209. settings.setValue("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().value())
  1210. def loadSettings(self, geometry):
  1211. settings = QSettings()
  1212. if geometry:
  1213. self.restoreGeometry(settings.value("Geometry", ""))
  1214. showToolbar = settings.value("ShowToolbar", True, type=bool)
  1215. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  1216. self.ui.toolBar.setVisible(showToolbar)
  1217. #showTransport = settings.value("ShowTransport", True, type=bool)
  1218. #self.ui.act_settings_show_transport.setChecked(showTransport)
  1219. #self.ui.frame_transport.setVisible(showTransport)
  1220. self.ui.frame_transport.setVisible(False)
  1221. if settings.contains("SplitterState"):
  1222. self.ui.splitter.restoreState(settings.value("SplitterState", ""))
  1223. else:
  1224. self.ui.splitter.setSizes([99999, 210])
  1225. self.fSavedSettings = {
  1226. "Main/DefaultProjectFolder": settings.value("Main/DefaultProjectFolder", HOME, type=str),
  1227. "Main/RefreshInterval": settings.value("Main/RefreshInterval", 50, type=int),
  1228. "Canvas/Theme": settings.value("Canvas/Theme", patchcanvas.getDefaultThemeName(), type=str),
  1229. "Canvas/AutoHideGroups": settings.value("Canvas/AutoHideGroups", False, type=bool),
  1230. "Canvas/UseBezierLines": settings.value("Canvas/UseBezierLines", True, type=bool),
  1231. "Canvas/EyeCandy": settings.value("Canvas/EyeCandy", patchcanvas.EYECANDY_SMALL, type=int),
  1232. "Canvas/UseOpenGL": settings.value("Canvas/UseOpenGL", False, type=bool),
  1233. "Canvas/Antialiasing": settings.value("Canvas/Antialiasing", patchcanvas.ANTIALIASING_SMALL, type=int),
  1234. "Canvas/HighQualityAntialiasing": settings.value("Canvas/HighQualityAntialiasing", False, type=bool)
  1235. }
  1236. # ---------------------------------------------
  1237. # plugin checks
  1238. if settings.value("Engine/DisableChecks", False, type=bool):
  1239. os.environ["CARLA_DISCOVERY_NO_PROCESSING_CHECKS"] = "true"
  1240. elif os.getenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS"):
  1241. os.environ.pop("CARLA_DISCOVERY_NO_PROCESSING_CHECKS")
  1242. # ---------------------------------------------
  1243. # plugin paths
  1244. Carla.LADSPA_PATH = toList(settings.value("Paths/LADSPA", Carla.LADSPA_PATH))
  1245. Carla.DSSI_PATH = toList(settings.value("Paths/DSSI", Carla.DSSI_PATH))
  1246. Carla.LV2_PATH = toList(settings.value("Paths/LV2", Carla.LV2_PATH))
  1247. Carla.VST_PATH = toList(settings.value("Paths/VST", Carla.VST_PATH))
  1248. Carla.GIG_PATH = toList(settings.value("Paths/GIG", Carla.GIG_PATH))
  1249. Carla.SF2_PATH = toList(settings.value("Paths/SF2", Carla.SF2_PATH))
  1250. Carla.SFZ_PATH = toList(settings.value("Paths/SFZ", Carla.SFZ_PATH))
  1251. os.environ["LADSPA_PATH"] = splitter.join(Carla.LADSPA_PATH)
  1252. os.environ["DSSI_PATH"] = splitter.join(Carla.DSSI_PATH)
  1253. os.environ["LV2_PATH"] = splitter.join(Carla.LV2_PATH)
  1254. os.environ["VST_PATH"] = splitter.join(Carla.VST_PATH)
  1255. os.environ["GIG_PATH"] = splitter.join(Carla.GIG_PATH)
  1256. os.environ["SF2_PATH"] = splitter.join(Carla.SF2_PATH)
  1257. os.environ["SFZ_PATH"] = splitter.join(Carla.SFZ_PATH)
  1258. def resizeEvent(self, event):
  1259. if self.ui.tabMain.currentIndex() == 0:
  1260. # Force update of 2nd tab
  1261. width = self.ui.tab_plugins.width()-4
  1262. height = self.ui.tab_plugins.height()-4
  1263. self.ui.miniCanvasPreview.setViewSize(float(width) / DEFAULT_CANVAS_WIDTH, float(height) / DEFAULT_CANVAS_HEIGHT)
  1264. else:
  1265. QTimer.singleShot(0, self, SLOT("slot_miniCanvasCheckSize()"))
  1266. QMainWindow.resizeEvent(self, event)
  1267. def timerEvent(self, event):
  1268. if event.timerId() == self.fIdleTimerFast:
  1269. Carla.host.engine_idle()
  1270. for pwidget in self.fPluginList:
  1271. if pwidget is None:
  1272. break
  1273. pwidget.idleFast()
  1274. elif event.timerId() == self.fIdleTimerSlow:
  1275. for pwidget in self.fPluginList:
  1276. if pwidget is None:
  1277. break
  1278. pwidget.idleSlow()
  1279. QMainWindow.timerEvent(self, event)
  1280. def closeEvent(self, event):
  1281. #if self.nsm_server:
  1282. #self.nsm_server.stop()
  1283. self.saveSettings()
  1284. if Carla.host.is_engine_running():
  1285. Carla.host.set_engine_about_to_close()
  1286. self.removeAllPlugins()
  1287. self.stopEngine()
  1288. QMainWindow.closeEvent(self, event)
  1289. # ------------------------------------------------------------------------------------------------
  1290. def canvasCallback(action, value1, value2, valueStr):
  1291. if action == patchcanvas.ACTION_GROUP_INFO:
  1292. pass
  1293. elif action == patchcanvas.ACTION_GROUP_RENAME:
  1294. pass
  1295. elif action == patchcanvas.ACTION_GROUP_SPLIT:
  1296. groupId = value1
  1297. patchcanvas.splitGroup(groupId)
  1298. elif action == patchcanvas.ACTION_GROUP_JOIN:
  1299. groupId = value1
  1300. patchcanvas.joinGroup(groupId)
  1301. elif action == patchcanvas.ACTION_PORT_INFO:
  1302. pass
  1303. elif action == patchcanvas.ACTION_PORT_RENAME:
  1304. pass
  1305. elif action == patchcanvas.ACTION_PORTS_CONNECT:
  1306. portIdA = value1
  1307. portIdB = value2
  1308. Carla.host.patchbay_connect(portIdA, portIdB)
  1309. elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
  1310. connectionId = value1
  1311. Carla.host.patchbay_disconnect(connectionId)
  1312. def engineCallback(ptr, action, pluginId, value1, value2, value3, valueStr):
  1313. if pluginId < 0 or not Carla.gui:
  1314. return
  1315. if action == CALLBACK_DEBUG:
  1316. Carla.gui.emit(SIGNAL("DebugCallback(int, int, int, double, QString)"), pluginId, value1, value2, value3, cString(valueStr))
  1317. elif action == CALLBACK_PLUGIN_ADDED:
  1318. Carla.gui.emit(SIGNAL("PluginAddedCallback(int)"), pluginId)
  1319. elif action == CALLBACK_PLUGIN_REMOVED:
  1320. Carla.gui.emit(SIGNAL("PluginRemovedCallback(int)"), pluginId)
  1321. elif action == CALLBACK_PLUGIN_RENAMED:
  1322. Carla.gui.emit(SIGNAL("PluginRenamedCallback(int, QString)"), pluginId, valueStr)
  1323. elif action == CALLBACK_PARAMETER_VALUE_CHANGED:
  1324. Carla.gui.emit(SIGNAL("ParameterValueChangedCallback(int, int, double)"), pluginId, value1, value3)
  1325. elif action == CALLBACK_PARAMETER_DEFAULT_CHANGED:
  1326. Carla.gui.emit(SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), pluginId, value1, value3)
  1327. elif action == CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  1328. Carla.gui.emit(SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), pluginId, value1, value2)
  1329. elif action == CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  1330. Carla.gui.emit(SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), pluginId, value1, value2)
  1331. elif action == CALLBACK_PROGRAM_CHANGED:
  1332. Carla.gui.emit(SIGNAL("ProgramChangedCallback(int, int)"), pluginId, value1)
  1333. elif action == CALLBACK_MIDI_PROGRAM_CHANGED:
  1334. Carla.gui.emit(SIGNAL("MidiProgramChangedCallback(int, int)"), pluginId, value1)
  1335. elif action == CALLBACK_NOTE_ON:
  1336. Carla.gui.emit(SIGNAL("NoteOnCallback(int, int, int, int)"), pluginId, value1, value2, value3)
  1337. elif action == CALLBACK_NOTE_OFF:
  1338. Carla.gui.emit(SIGNAL("NoteOffCallback(int, int, int)"), pluginId, value1, value2)
  1339. elif action == CALLBACK_SHOW_GUI:
  1340. Carla.gui.emit(SIGNAL("ShowGuiCallback(int, int)"), pluginId, value1)
  1341. elif action == CALLBACK_UPDATE:
  1342. Carla.gui.emit(SIGNAL("UpdateCallback(int)"), pluginId)
  1343. elif action == CALLBACK_RELOAD_INFO:
  1344. Carla.gui.emit(SIGNAL("ReloadInfoCallback(int)"), pluginId)
  1345. elif action == CALLBACK_RELOAD_PARAMETERS:
  1346. Carla.gui.emit(SIGNAL("ReloadParametersCallback(int)"), pluginId)
  1347. elif action == CALLBACK_RELOAD_PROGRAMS:
  1348. Carla.gui.emit(SIGNAL("ReloadProgramsCallback(int)"), pluginId)
  1349. elif action == CALLBACK_RELOAD_ALL:
  1350. Carla.gui.emit(SIGNAL("ReloadAllCallback(int)"), pluginId)
  1351. elif action == CALLBACK_PATCHBAY_CLIENT_ADDED:
  1352. Carla.gui.emit(SIGNAL("PatchbayClientAddedCallback(int, QString)"), value1, cString(valueStr))
  1353. elif action == CALLBACK_PATCHBAY_CLIENT_REMOVED:
  1354. Carla.gui.emit(SIGNAL("PatchbayClientRemovedCallback(int)"), value1)
  1355. elif action == CALLBACK_PATCHBAY_CLIENT_RENAMED:
  1356. Carla.gui.emit(SIGNAL("PatchbayClientRenamedCallback(int, QString)"), value1, cString(valueStr))
  1357. elif action == CALLBACK_PATCHBAY_PORT_ADDED:
  1358. Carla.gui.emit(SIGNAL("PatchbayPortAddedCallback(int, int, int, QString)"), value1, value2, value3, cString(valueStr))
  1359. elif action == CALLBACK_PATCHBAY_PORT_REMOVED:
  1360. Carla.gui.emit(SIGNAL("PatchbayPortRemovedCallback(int)"), value1)
  1361. elif action == CALLBACK_PATCHBAY_PORT_RENAMED:
  1362. Carla.gui.emit(SIGNAL("PatchbayPortRenamedCallback(int, QString)"), value1, cString(valueStr))
  1363. elif action == CALLBACK_PATCHBAY_CONNECTION_ADDED:
  1364. Carla.gui.emit(SIGNAL("PatchbayConnectionAddedCallback(int, int, int)"), value1, value2, value3)
  1365. elif action == CALLBACK_PATCHBAY_CONNECTION_REMOVED:
  1366. Carla.gui.emit(SIGNAL("PatchbayConnectionRemovedCallback(int)"), value1)
  1367. #elif action == CALLBACK_NSM_ANNOUNCE:
  1368. #Carla.gui._nsmAnnounce2str = cString(Carla.host.get_last_error())
  1369. #Carla.gui.emit(SIGNAL("NSM_AnnounceCallback()"))
  1370. #elif action == CALLBACK_NSM_OPEN1:
  1371. #Carla.gui._nsmOpen1str = cString(valueStr)
  1372. #Carla.gui.emit(SIGNAL("NSM_Open1Callback()"))
  1373. #elif action == CALLBACK_NSM_OPEN2:
  1374. #Carla.gui._nsmOpen2str = cString(valueStr)
  1375. #Carla.gui.emit(SIGNAL("NSM_Open2Callback()"))
  1376. #elif action == CALLBACK_NSM_SAVE:
  1377. #Carla.gui.emit(SIGNAL("NSM_SaveCallback()"))
  1378. elif action == CALLBACK_ERROR:
  1379. Carla.gui.emit(SIGNAL("ErrorCallback(QString)"), cString(valueStr))
  1380. elif action == CALLBACK_QUIT:
  1381. Carla.gui.emit(SIGNAL("QuitCallback()"))
  1382. #--------------- main ------------------
  1383. if __name__ == '__main__':
  1384. # App initialization
  1385. app = QApplication(sys.argv)
  1386. app.setApplicationName("Carla")
  1387. app.setApplicationVersion(VERSION)
  1388. app.setOrganizationName("falkTX")
  1389. app.setWindowIcon(QIcon(":/scalable/carla.svg"))
  1390. libPrefix = None
  1391. projectFilename = None
  1392. for i in range(len(app.arguments())):
  1393. if i == 0: continue
  1394. argument = app.arguments()[i]
  1395. if argument.startswith("--with-libprefix="):
  1396. libPrefix = argument.replace("--with-libprefix=", "")
  1397. elif os.path.exists(argument):
  1398. projectFilename = argument
  1399. # Init backend
  1400. Carla.host = Host(libPrefix)
  1401. Carla.host.set_engine_callback(engineCallback)
  1402. Carla.host.set_engine_option(OPTION_PROCESS_NAME, 0, "carla")
  1403. # Set bridge paths
  1404. if carla_bridge_native:
  1405. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_NATIVE, 0, carla_bridge_native)
  1406. if carla_bridge_posix32:
  1407. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_POSIX32, 0, carla_bridge_posix32)
  1408. if carla_bridge_posix64:
  1409. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_POSIX64, 0, carla_bridge_posix64)
  1410. if carla_bridge_win32:
  1411. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_WIN32, 0, carla_bridge_win32)
  1412. if carla_bridge_win64:
  1413. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_WIN64, 0, carla_bridge_win64)
  1414. if WINDOWS:
  1415. if carla_bridge_lv2_windows:
  1416. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_WINDOWS, 0, carla_bridge_lv2_windows)
  1417. if carla_bridge_vst_hwnd:
  1418. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_HWND, 0, carla_bridge_vst_hwnd)
  1419. elif MACOS:
  1420. if carla_bridge_lv2_cocoa:
  1421. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_COCOA, 0, carla_bridge_lv2_cocoa)
  1422. if carla_bridge_vst_cocoa:
  1423. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_COCOA, 0, carla_bridge_vst_cocoa)
  1424. else:
  1425. if carla_bridge_lv2_gtk2:
  1426. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_GTK2, 0, carla_bridge_lv2_gtk2)
  1427. if carla_bridge_lv2_gtk3:
  1428. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_GTK3, 0, carla_bridge_lv2_gtk3)
  1429. if carla_bridge_lv2_qt4:
  1430. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_QT4, 0, carla_bridge_lv2_qt4)
  1431. if carla_bridge_lv2_qt5:
  1432. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_QT5, 0, carla_bridge_lv2_qt5)
  1433. if carla_bridge_lv2_x11:
  1434. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_X11, 0, carla_bridge_lv2_x11)
  1435. if carla_bridge_vst_x11:
  1436. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_X11, 0, carla_bridge_vst_x11)
  1437. # Create GUI and start engine
  1438. Carla.gui = CarlaMainW()
  1439. # Set-up custom signal handling
  1440. setUpSignals()
  1441. # Show GUI
  1442. Carla.gui.show()
  1443. # Load project file if set
  1444. if projectFilename:
  1445. Carla.gui.loadProjectLater(projectFilename)
  1446. # App-Loop
  1447. ret = app.exec_()
  1448. # Exit properly
  1449. sys.exit(ret)