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.

2057 lines
83KB

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