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.

2443 lines
98KB

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