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.

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