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.

2096 lines
85KB

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