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.

721 lines
29KB

  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 PyQt4.QtCore import QSize
  20. from PyQt4.QtGui import QApplication, QListWidgetItem, QMainWindow
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Custom Stuff)
  23. import ui_carla
  24. from carla_backend import *
  25. from carla_shared import *
  26. # FIXME, remove later
  27. #from shared_settings import *
  28. # ------------------------------------------------------------------------------------------------------------
  29. # Main Window
  30. class CarlaMainW(QMainWindow):
  31. def __init__(self, parent=None):
  32. QMainWindow.__init__(self, parent)
  33. self.ui = ui_carla.Ui_CarlaMainW()
  34. self.ui.setupUi(self)
  35. # -------------------------------------------------------------
  36. # Load Settings
  37. self.loadSettings(True)
  38. self.loadRDFs()
  39. self.setStyleSheet("""
  40. QWidget#centralwidget {
  41. background-color: qlineargradient(spread:pad,
  42. x1:0.0, y1:0.0,
  43. x2:0.2, y2:1.0,
  44. stop:0 rgb( 7, 7, 7),
  45. stop:1 rgb(28, 28, 28)
  46. );
  47. }
  48. """)
  49. # -------------------------------------------------------------
  50. # Internal stuff
  51. self.fEngineStarted = False
  52. self.fFirstEngineInit = False
  53. self.fPluginCount = 0
  54. self.fPluginList = []
  55. self.fIdleTimerFast = 0
  56. self.fIdleTimerSlow = 0
  57. #self.m_project_filename = None
  58. #self._nsmAnnounce2str = ""
  59. #self._nsmOpen1str = ""
  60. #self._nsmOpen2str = ""
  61. #self.nsm_server = None
  62. #self.nsm_url = None
  63. # -------------------------------------------------------------
  64. # Set-up GUI stuff
  65. self.ui.act_engine_start.setEnabled(False)
  66. self.ui.act_engine_stop.setEnabled(False)
  67. self.ui.act_plugin_remove_all.setEnabled(False)
  68. self.resize(self.width(), 0)
  69. #self.m_fakeEdit = PluginEdit(self, -1)
  70. #self.m_curEdit = self.m_fakeEdit
  71. #self.w_edit.layout().addWidget(self.m_curEdit)
  72. #self.w_edit.layout().addStretch()
  73. # -------------------------------------------------------------
  74. # Connect actions to functions
  75. #self.connect(self.act_file_new, SIGNAL("triggered()"), SLOT("slot_file_new()"))
  76. #self.connect(self.act_file_open, SIGNAL("triggered()"), SLOT("slot_file_open()"))
  77. #self.connect(self.act_file_save, SIGNAL("triggered()"), SLOT("slot_file_save()"))
  78. #self.connect(self.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_file_save_as()"))
  79. self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_startEngine()"))
  80. self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_stopEngine()"))
  81. self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_addPlugin()"))
  82. #self.connect(self.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_remove_all()"))
  83. #self.connect(self.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()"))
  84. self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()"))
  85. self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  86. #self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()"))
  87. self.connect(self, SIGNAL("DebugCallback(int, int, int, double, QString)"), SLOT("slot_handleDebugCallback(int, int, int, double, QString)"))
  88. self.connect(self, SIGNAL("PluginAddedCallback(int)"), SLOT("slot_handlePluginAddedCallback(int)"))
  89. self.connect(self, SIGNAL("PluginRemovedCallback(int)"), SLOT("slot_handlePluginRemovedCallback(int)"))
  90. self.connect(self, SIGNAL("ParameterValueChangedCallback(int, int, double)"), SLOT("slot_handleParameterValueChangedCallback(int, int, double)"))
  91. self.connect(self, SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), SLOT("slot_handleParameterDefaultChangedCallback(int, int, double)"))
  92. self.connect(self, SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiChannelChangedCallback(int, int, int)"))
  93. self.connect(self, SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiCcChangedCallback(int, int, int)"))
  94. self.connect(self, SIGNAL("ProgramChangedCallback(int, int)"), SLOT("slot_handleProgramChangedCallback(int, int)"))
  95. self.connect(self, SIGNAL("MidiProgramChangedCallback(int, int)"), SLOT("slot_handleMidiProgramChangedCallback(int, int)"))
  96. self.connect(self, SIGNAL("NoteOnCallback(int, int, int, int)"), SLOT("slot_handleNoteOnCallback(int, int, int, int)"))
  97. self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), SLOT("slot_handleNoteOffCallback(int, int, int)"))
  98. self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), SLOT("slot_handleShowGuiCallback(int, int)"))
  99. self.connect(self, SIGNAL("UpdateCallback(int)"), SLOT("slot_handleUpdateCallback(int)"))
  100. self.connect(self, SIGNAL("ReloadInfoCallback(int)"), SLOT("slot_handleReloadInfoCallback(int)"))
  101. self.connect(self, SIGNAL("ReloadParametersCallback(int)"), SLOT("slot_handleReloadParametersCallback(int)"))
  102. self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), SLOT("slot_handleReloadProgramsCallback(int)"))
  103. self.connect(self, SIGNAL("ReloadAllCallback(int)"), SLOT("slot_handleReloadAllCallback(int)"))
  104. #self.connect(self, SIGNAL("NSM_AnnounceCallback()"), SLOT("slot_handleNSM_AnnounceCallback()"))
  105. #self.connect(self, SIGNAL("NSM_Open1Callback()"), SLOT("slot_handleNSM_Open1Callback()"))
  106. #self.connect(self, SIGNAL("NSM_Open2Callback()"), SLOT("slot_handleNSM_Open2Callback()"))
  107. #self.connect(self, SIGNAL("NSM_SaveCallback()"), SLOT("slot_handleNSM_SaveCallback()"))
  108. self.connect(self, SIGNAL("ErrorCallback(QString)"), SLOT("slot_handleErrorCallback(QString)"))
  109. self.connect(self, SIGNAL("QuitCallback()"), SLOT("slot_handleQuitCallback()"))
  110. #NSM_URL = os.getenv("NSM_URL")
  111. #if NSM_URL:
  112. #Carla.host.nsm_announce(NSM_URL, os.getpid())
  113. #else:
  114. QTimer.singleShot(0, self, SLOT("slot_startEngine()"))
  115. def startEngine(self, clientName = "Carla"):
  116. # ---------------------------------------------
  117. # Engine settings
  118. settings = QSettings()
  119. Carla.processMode = settings.value("Engine/ProcessMode", PROCESS_MODE_MULTIPLE_CLIENTS, type=int)
  120. Carla.maxParameters = settings.value("Engine/MaxParameters", MAX_DEFAULT_PARAMETERS, type=int)
  121. forceStereo = settings.value("Engine/ForceStereo", False, type=bool)
  122. preferPluginBridges = settings.value("Engine/PreferPluginBridges", False, type=bool)
  123. preferUiBridges = settings.value("Engine/PreferUiBridges", True, type=bool)
  124. useDssiVstChunks = settings.value("Engine/UseDssiVstChunks", False, type=bool)
  125. oscUiTimeout = settings.value("Engine/OscUiTimeout", 40, type=int)
  126. preferredBufferSize = settings.value("Engine/PreferredBufferSize", 512, type=int)
  127. preferredSampleRate = settings.value("Engine/PreferredSampleRate", 44100, type=int)
  128. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  129. forceStereo = True
  130. elif Carla.processMode == PROCESS_MODE_MULTIPLE_CLIENTS and os.getenv("LADISH_APP_NAME"):
  131. print("LADISH detected but using multiple clients (not allowed), forcing single client now")
  132. Carla.processMode = PROCESS_MODE_SINGLE_CLIENT
  133. Carla.host.set_option(OPTION_PROCESS_MODE, Carla.processMode, "")
  134. Carla.host.set_option(OPTION_MAX_PARAMETERS, Carla.maxParameters, "")
  135. Carla.host.set_option(OPTION_FORCE_STEREO, forceStereo, "")
  136. Carla.host.set_option(OPTION_PREFER_PLUGIN_BRIDGES, preferPluginBridges, "")
  137. Carla.host.set_option(OPTION_PREFER_UI_BRIDGES, preferUiBridges, "")
  138. Carla.host.set_option(OPTION_USE_DSSI_VST_CHUNKS, useDssiVstChunks, "")
  139. Carla.host.set_option(OPTION_OSC_UI_TIMEOUT, oscUiTimeout, "")
  140. Carla.host.set_option(OPTION_PREFERRED_BUFFER_SIZE, preferredBufferSize, "")
  141. Carla.host.set_option(OPTION_PREFERRED_SAMPLE_RATE, preferredSampleRate, "")
  142. # ---------------------------------------------
  143. # start
  144. audioDriver = settings.value("Engine/AudioDriver", "JACK", type=str)
  145. if not Carla.host.engine_init(audioDriver, clientName):
  146. if self.fFirstEngineInit:
  147. self.fFirstEngineInit = False
  148. return
  149. self.ui.act_engine_start.setEnabled(True)
  150. self.ui.act_engine_stop.setEnabled(False)
  151. audioError = cString(Carla.host.get_last_error())
  152. if audioError:
  153. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s', possible reasons: %s" % (audioDriver, audioError)))
  154. else:
  155. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s'" % audioDriver))
  156. return
  157. self.fEngineStarted = True
  158. self.fFirstEngineInit = False
  159. self.fPluginCount = 0
  160. self.fPluginList = []
  161. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  162. maxCount = MAX_RACK_PLUGINS
  163. elif Carla.processMode == PROCESS_MODE_PATCHBAY:
  164. maxCount = MAX_PATCHBAY_PLUGINS
  165. else:
  166. maxCount = MAX_DEFAULT_PLUGINS
  167. for x in range(maxCount):
  168. self.fPluginList.append(None)
  169. # Peaks
  170. self.fIdleTimerFast = self.startTimer(self.fSavedSettings["Main/RefreshInterval"])
  171. # LEDs and edit dialog parameters
  172. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings["Main/RefreshInterval"]*2)
  173. def stopEngine(self):
  174. if self.fPluginCount > 0:
  175. 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"
  176. "Do you want to do this now?"),
  177. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  178. if ask != QMessageBox.Yes:
  179. return
  180. #self.slot_remove_all()
  181. if Carla.host.is_engine_running() and not Carla.host.engine_close():
  182. print(cString(Carla.host.get_last_error()))
  183. self.fEngineStarted = False
  184. self.fPluginCount = 0
  185. self.fPluginList = []
  186. self.killTimer(self.fIdleTimerFast)
  187. self.killTimer(self.fIdleTimerSlow)
  188. def addPlugin(self, btype, ptype, filename, name, label, extraStuff):
  189. if not self.fEngineStarted:
  190. QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
  191. return False
  192. if not Carla.host.add_plugin(btype, ptype, filename, name, label, extraStuff):
  193. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  194. return False
  195. return True
  196. @pyqtSlot()
  197. def slot_startEngine(self):
  198. self.startEngine()
  199. check = Carla.host.is_engine_running()
  200. self.ui.act_file_open.setEnabled(check)
  201. self.ui.act_engine_start.setEnabled(not check)
  202. self.ui.act_engine_stop.setEnabled(check)
  203. @pyqtSlot()
  204. def slot_stopEngine(self):
  205. self.stopEngine()
  206. check = Carla.host.is_engine_running()
  207. self.ui.act_file_open.setEnabled(check)
  208. self.ui.act_engine_start.setEnabled(not check)
  209. self.ui.act_engine_stop.setEnabled(check)
  210. @pyqtSlot()
  211. def slot_handleSIGUSR1(self):
  212. print("Got SIGUSR1 -> Saving project now")
  213. #QTimer.singleShot(0, self, SLOT("slot_file_save()"))
  214. @pyqtSlot(int, int, int, float, str)
  215. def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
  216. print("DEBUG :: %i, %i, %i, %f, \"%s\")" % (pluginId, value1, value2, value3, valueStr))
  217. @pyqtSlot(int)
  218. def slot_handlePluginAddedCallback(self, pluginId, pluginName="todo"):
  219. pwidgetItem = QListWidgetItem(self.ui.listWidget)
  220. pwidgetItem.setSizeHint(QSize(pwidgetItem.sizeHint().width(), 48))
  221. pwidget = PluginWidget(self, pwidgetItem, pluginId)
  222. pwidget.setRefreshRate(self.fSavedSettings["Main/RefreshInterval"])
  223. self.ui.listWidget.setItemWidget(pwidgetItem, pwidget)
  224. self.fPluginCount += 1
  225. self.fPluginList[pluginId] = pwidget
  226. if self.fPluginCount == 1:
  227. self.ui.act_plugin_remove_all.setEnabled(True)
  228. @pyqtSlot(int)
  229. def slot_handlePluginRemovedCallback(self, pluginId):
  230. pwidget = self.fPluginList[pluginId]
  231. if pwidget is None:
  232. return
  233. self.fPluginList[pluginId] = None
  234. self.fPluginCount -= 1
  235. pwidget.ui.edit_dialog.close()
  236. pwidget.close()
  237. del pwidget
  238. self.ui.listWidget.takeItem(pluginId)
  239. #self.ui.listWidget.removeItemWidget(pwidget.getListWidgetItem())
  240. if self.fPluginCount == 0:
  241. self.ui.act_plugin_remove_all.setEnabled(False)
  242. @pyqtSlot(int, int, float)
  243. def slot_handleParameterValueChangedCallback(self, pluginId, parameterId, value):
  244. pwidget = self.fPluginList[pluginId]
  245. if pwidget is None:
  246. return
  247. pwidget.setParameterValue(value, True, False)
  248. @pyqtSlot(int, int, float)
  249. def slot_handleParameterDefaultChangedCallback(self, pluginId, parameterId, value):
  250. pwidget = self.fPluginList[pluginId]
  251. if pwidget is None:
  252. return
  253. pwidget.setParameterDefault(value, True, False)
  254. @pyqtSlot(int, int, int)
  255. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, parameterId, channel):
  256. pwidget = self.fPluginList[pluginId]
  257. if pwidget is None:
  258. return
  259. pwidget.setParameterMidiChannel(parameterId, channel, True)
  260. @pyqtSlot(int, int, int)
  261. def slot_handleParameterMidiCcChangedCallback(self, pluginId, parameterId, cc):
  262. pwidget = self.fPluginList[pluginId]
  263. if pwidget is None:
  264. return
  265. pwidget.setParameterMidiControl(parameterId, cc, True)
  266. @pyqtSlot(int, int)
  267. def slot_handleProgramChangedCallback(self, pluginId, programId):
  268. pwidget = self.fPluginList[pluginId]
  269. if pwidget is None:
  270. return
  271. pwidget.setProgram(programId)
  272. @pyqtSlot(int, int)
  273. def slot_handleMidiProgramChangedCallback(self, pluginId, midiProgramId):
  274. pwidget = self.fPluginList[pluginId]
  275. if pwidget is None:
  276. return
  277. pwidget.setMidiProgram(midiProgramId)
  278. @pyqtSlot(int, int, int, int)
  279. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  280. pwidget = self.fPluginList[pluginId]
  281. if pwidget is None:
  282. return
  283. pwidget.ui.edit_dialog.keyboard.sendNoteOn(note, False)
  284. @pyqtSlot(int, int, int)
  285. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  286. pwidget = self.fPluginList[pluginId]
  287. if pwidget is None:
  288. return
  289. pwidget.ui.edit_dialog.keyboard.sendNoteOff(note, False)
  290. @pyqtSlot(int, int)
  291. def slot_handleShowGuiCallback(self, pluginId, show):
  292. pwidget = self.fPluginList[pluginId]
  293. if pwidget is None:
  294. return
  295. if show == 0:
  296. pwidget.ui.b_gui.setChecked(False)
  297. pwidget.ui.b_gui.setEnabled(True)
  298. elif show == 1:
  299. pwidget.ui.b_gui.setChecked(True)
  300. pwidget.ui.b_gui.setEnabled(True)
  301. elif show == -1:
  302. pwidget.ui.b_gui.setChecked(False)
  303. pwidget.ui.b_gui.setEnabled(False)
  304. @pyqtSlot(int)
  305. def slot_handleUpdateCallback(self, pluginId):
  306. pwidget = self.fPluginList[pluginId]
  307. if pwidget is None:
  308. return
  309. pwidget.ui.edit_dialog.do_update()
  310. @pyqtSlot(int)
  311. def slot_handleReloadInfoCallback(self, pluginId):
  312. pwidget = self.fPluginList[pluginId]
  313. if pwidget is None:
  314. return
  315. pwidget.ui.edit_dialog.reloadInfo()
  316. @pyqtSlot(int)
  317. def slot_handleReloadParametersCallback(self, pluginId):
  318. pwidget = self.fPluginList[pluginId]
  319. if pwidget is None:
  320. return
  321. pwidget.ui.edit_dialog.reloadParameters()
  322. @pyqtSlot(int)
  323. def slot_handleReloadProgramsCallback(self, pluginId):
  324. pwidget = self.fPluginList[pluginId]
  325. if pwidget is None:
  326. return
  327. pwidget.ui.edit_dialog.reloadPrograms()
  328. @pyqtSlot(int)
  329. def slot_handleReloadAllCallback(self, pluginId):
  330. pwidget = self.fPluginList[pluginId]
  331. if pwidget is None:
  332. return
  333. pwidget.ui.edit_dialog.reloadAll()
  334. @pyqtSlot(str)
  335. def slot_handleErrorCallback(self, error):
  336. QMessageBox.critical(self, self.tr("Error"), error)
  337. @pyqtSlot()
  338. def slot_handleQuitCallback(self):
  339. CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"),
  340. self.tr("Engine has been stopped or crashed.\nPlease restart Carla"),
  341. self.tr("You may want to save your session now..."), QMessageBox.Ok, QMessageBox.Ok)
  342. @pyqtSlot()
  343. def slot_addPlugin(self):
  344. dialog = PluginDatabaseW(self)
  345. if dialog.exec_():
  346. btype = dialog.fRetPlugin['build']
  347. ptype = dialog.fRetPlugin['type']
  348. filename = dialog.fRetPlugin['binary']
  349. label = dialog.fRetPlugin['label']
  350. extraStuff = self.getExtraStuff(dialog.fRetPlugin)
  351. self.addPlugin(btype, ptype, filename, None, label, extraStuff)
  352. @pyqtSlot()
  353. def slot_aboutCarla(self):
  354. CarlaAboutW(self).exec_()
  355. def getExtraStuff(self, plugin):
  356. ptype = plugin['type']
  357. if ptype == PLUGIN_LADSPA:
  358. uniqueId = plugin['uniqueId']
  359. for rdfItem in self.fLadspaRdfList:
  360. if rdfItem.UniqueID == uniqueId:
  361. return pointer(rdfItem)
  362. elif ptype == PLUGIN_DSSI:
  363. if (plugin['hints'] & PLUGIN_HAS_GUI):
  364. gui = findDSSIGUI(plugin['binary'], plugin['name'], plugin['label'])
  365. if gui:
  366. return gui.encode("utf-8")
  367. return c_nullptr
  368. def loadRDFs(self):
  369. # Save RDF info for later
  370. self.fLadspaRdfList = []
  371. if haveLRDF:
  372. settingsDir = os.path.join(HOME, ".config", "Cadence")
  373. frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
  374. if os.path.exists(frLadspaFile):
  375. frLadspa = open(frLadspaFile, 'r')
  376. try:
  377. self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
  378. except:
  379. pass
  380. frLadspa.close()
  381. def saveSettings(self):
  382. settings = QSettings()
  383. settings.setValue("Geometry", self.saveGeometry())
  384. settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  385. def loadSettings(self, geometry):
  386. settings = QSettings()
  387. if geometry:
  388. self.restoreGeometry(settings.value("Geometry", ""))
  389. showToolbar = settings.value("ShowToolbar", True, type=bool)
  390. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  391. self.ui.toolBar.setVisible(showToolbar)
  392. self.fSavedSettings = {
  393. "Main/DefaultProjectFolder": settings.value("Main/DefaultProjectFolder", HOME, type=str),
  394. "Main/RefreshInterval": settings.value("Main/RefreshInterval", 50, type=int)
  395. }
  396. # ---------------------------------------------
  397. # plugin checks
  398. if settings.value("Engine/DisableChecks", False, type=bool):
  399. os.environ["CARLA_DISCOVERY_NO_PROCESSING_CHECKS"] = "true"
  400. elif os.getenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS"):
  401. os.environ.pop("CARLA_DISCOVERY_NO_PROCESSING_CHECKS")
  402. # ---------------------------------------------
  403. # plugin paths
  404. global LADSPA_PATH, DSSI_PATH, LV2_PATH, VST_PATH, GIG_PATH, SF2_PATH, SFZ_PATH
  405. LADSPA_PATH = toList(settings.value("Paths/LADSPA", LADSPA_PATH))
  406. DSSI_PATH = toList(settings.value("Paths/DSSI", DSSI_PATH))
  407. LV2_PATH = toList(settings.value("Paths/LV2", LV2_PATH))
  408. VST_PATH = toList(settings.value("Paths/VST", VST_PATH))
  409. GIG_PATH = toList(settings.value("Paths/GIG", GIG_PATH))
  410. SF2_PATH = toList(settings.value("Paths/SF2", SF2_PATH))
  411. SFZ_PATH = toList(settings.value("Paths/SFZ", SFZ_PATH))
  412. os.environ["LADSPA_PATH"] = splitter.join(LADSPA_PATH)
  413. os.environ["DSSI_PATH"] = splitter.join(DSSI_PATH)
  414. os.environ["LV2_PATH"] = splitter.join(LV2_PATH)
  415. os.environ["VST_PATH"] = splitter.join(VST_PATH)
  416. os.environ["GIG_PATH"] = splitter.join(GIG_PATH)
  417. os.environ["SF2_PATH"] = splitter.join(SF2_PATH)
  418. os.environ["SFZ_PATH"] = splitter.join(SFZ_PATH)
  419. def timerEvent(self, event):
  420. if event.timerId() == self.fIdleTimerFast:
  421. Carla.host.engine_idle()
  422. for pwidget in self.fPluginList:
  423. if pwidget is not None:
  424. pwidget.idleFast()
  425. elif event.timerId() == self.fIdleTimerSlow:
  426. for pwidget in self.fPluginList:
  427. if pwidget is not None:
  428. pwidget.idleSlow()
  429. QMainWindow.timerEvent(self, event)
  430. def closeEvent(self, event):
  431. #if self.nsm_server:
  432. #self.nsm_server.stop()
  433. self.saveSettings()
  434. #self.slot_remove_all()
  435. self.stopEngine()
  436. QMainWindow.closeEvent(self, event)
  437. # ------------------------------------------------------------------------------------------------
  438. def callbackFunction(ptr, action, pluginId, value1, value2, value3, valueStr):
  439. if pluginId < 0 or not Carla.gui:
  440. return
  441. if action == CALLBACK_DEBUG:
  442. return Carla.gui.emit(SIGNAL("DebugCallback(int, int, int, double, QString)"), pluginId, value1, value2, value3, cString(valueStr))
  443. elif action == CALLBACK_PLUGIN_ADDED:
  444. return Carla.gui.emit(SIGNAL("PluginAddedCallback(int)"), pluginId)
  445. elif action == CALLBACK_PLUGIN_REMOVED:
  446. return Carla.gui.emit(SIGNAL("PluginRemovedCallback(int)"), pluginId)
  447. elif action == CALLBACK_PARAMETER_VALUE_CHANGED:
  448. return Carla.gui.emit(SIGNAL("ParameterValueChangedCallback(int, int, double)"), pluginId, value1, value3)
  449. elif action == CALLBACK_PARAMETER_DEFAULT_CHANGED:
  450. return Carla.gui.emit(SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), pluginId, value1, value3)
  451. elif action == CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  452. return Carla.gui.emit(SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), pluginId, value1, value2)
  453. elif action == CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  454. return Carla.gui.emit(SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), pluginId, value1, value2)
  455. elif action == CALLBACK_PROGRAM_CHANGED:
  456. return Carla.gui.emit(SIGNAL("ProgramChangedCallback(int, int)"), pluginId, value1)
  457. elif action == CALLBACK_MIDI_PROGRAM_CHANGED:
  458. return Carla.gui.emit(SIGNAL("MidiProgramChangedCallback(int, int)"), pluginId, value1)
  459. elif action == CALLBACK_NOTE_ON:
  460. return Carla.gui.emit(SIGNAL("NoteOnCallback(int, int, int, int)"), pluginId, value1, value2, value3)
  461. elif action == CALLBACK_NOTE_OFF:
  462. return Carla.gui.emit(SIGNAL("NoteOffCallback(int, int, int)"), pluginId, value1, value2)
  463. elif action == CALLBACK_SHOW_GUI:
  464. return Carla.gui.emit(SIGNAL("ShowGuiCallback(int, int)"), pluginId, value1)
  465. elif action == CALLBACK_UPDATE:
  466. return Carla.gui.emit(SIGNAL("UpdateCallback(int)"), pluginId)
  467. elif action == CALLBACK_RELOAD_INFO:
  468. return Carla.gui.emit(SIGNAL("ReloadInfoCallback(int)"), pluginId)
  469. elif action == CALLBACK_RELOAD_PARAMETERS:
  470. return Carla.gui.emit(SIGNAL("ReloadParametersCallback(int)"), pluginId)
  471. elif action == CALLBACK_RELOAD_PROGRAMS:
  472. return Carla.gui.emit(SIGNAL("ReloadProgramsCallback(int)"), pluginId)
  473. elif action == CALLBACK_RELOAD_ALL:
  474. return Carla.gui.emit(SIGNAL("ReloadAllCallback(int)"), pluginId)
  475. #elif action == CALLBACK_NSM_ANNOUNCE:
  476. #Carla.gui._nsmAnnounce2str = cString(Carla.host.get_last_error())
  477. #Carla.gui.emit(SIGNAL("NSM_AnnounceCallback()"))
  478. #return
  479. #elif action == CALLBACK_NSM_OPEN1:
  480. #Carla.gui._nsmOpen1str = cString(valueStr)
  481. #Carla.gui.emit(SIGNAL("NSM_Open1Callback()"))
  482. #return
  483. #elif action == CALLBACK_NSM_OPEN2:
  484. #Carla.gui._nsmOpen2str = cString(valueStr)
  485. #Carla.gui.emit(SIGNAL("NSM_Open2Callback()"))
  486. #return
  487. #elif action == CALLBACK_NSM_SAVE:
  488. #return Carla.gui.emit(SIGNAL("NSM_SaveCallback()"))
  489. elif action == CALLBACK_ERROR:
  490. return Carla.gui.emit(SIGNAL("ErrorCallback(QString)"), valueStr)
  491. elif action == CALLBACK_QUIT:
  492. return Carla.gui.emit(SIGNAL("QuitCallback()"))
  493. #--------------- main ------------------
  494. if __name__ == '__main__':
  495. # App initialization
  496. app = QApplication(sys.argv)
  497. app.setApplicationName("Carla")
  498. app.setApplicationVersion(VERSION)
  499. app.setOrganizationName("Cadence")
  500. app.setWindowIcon(QIcon(":/scalable/carla.svg"))
  501. libPrefix = None
  502. projectFilename = None
  503. for i in range(len(app.arguments())):
  504. if i == 0: continue
  505. argument = app.arguments()[i]
  506. if argument.startswith("--with-libprefix="):
  507. libPrefix = argument.replace("--with-libprefix=", "")
  508. elif os.path.exists(argument):
  509. projectFilename = argument
  510. # Init backend
  511. Carla.host = Host(libPrefix)
  512. Carla.host.set_callback_function(callbackFunction)
  513. Carla.host.set_option(OPTION_PROCESS_NAME, 0, "carla")
  514. # Set bridge paths
  515. if carla_bridge_native:
  516. Carla.host.set_option(OPTION_PATH_BRIDGE_NATIVE, 0, carla_bridge_native)
  517. if carla_bridge_posix32:
  518. Carla.host.set_option(OPTION_PATH_BRIDGE_POSIX32, 0, carla_bridge_posix32)
  519. if carla_bridge_posix64:
  520. Carla.host.set_option(OPTION_PATH_BRIDGE_POSIX64, 0, carla_bridge_posix64)
  521. if carla_bridge_win32:
  522. Carla.host.set_option(OPTION_PATH_BRIDGE_WIN32, 0, carla_bridge_win32)
  523. if carla_bridge_win64:
  524. Carla.host.set_option(OPTION_PATH_BRIDGE_WIN64, 0, carla_bridge_win64)
  525. if WINDOWS:
  526. if carla_bridge_lv2_windows:
  527. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_WINDOWS, 0, carla_bridge_lv2_windows)
  528. if carla_bridge_vst_hwnd:
  529. Carla.host.set_option(OPTION_PATH_BRIDGE_VST_HWND, 0, carla_bridge_vst_hwnd)
  530. elif MACOS:
  531. if carla_bridge_lv2_cocoa:
  532. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_COCOA, 0, carla_bridge_lv2_cocoa)
  533. if carla_bridge_vst_cocoa:
  534. Carla.host.set_option(OPTION_PATH_BRIDGE_VST_COCOA, 0, carla_bridge_vst_cocoa)
  535. else:
  536. if carla_bridge_lv2_gtk2:
  537. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_GTK2, 0, carla_bridge_lv2_gtk2)
  538. if carla_bridge_lv2_gtk3:
  539. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_GTK3, 0, carla_bridge_lv2_gtk3)
  540. if carla_bridge_lv2_qt4:
  541. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_QT4, 0, carla_bridge_lv2_qt4)
  542. if carla_bridge_lv2_qt5:
  543. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_QT5, 0, carla_bridge_lv2_qt5)
  544. if carla_bridge_lv2_x11:
  545. Carla.host.set_option(OPTION_PATH_BRIDGE_LV2_X11, 0, carla_bridge_lv2_x11)
  546. if carla_bridge_vst_x11:
  547. Carla.host.set_option(OPTION_PATH_BRIDGE_VST_X11, 0, carla_bridge_vst_x11)
  548. # Create GUI and start engine
  549. Carla.gui = CarlaMainW()
  550. # Set-up custom signal handling
  551. #setUpSignals(Carla.gui)
  552. # Show GUI
  553. Carla.gui.show()
  554. # Load project file if set
  555. #if projectFilename:
  556. #Carla.gui.m_project_filename = projectFilename
  557. #Carla.gui.loadProjectLater()
  558. #Carla.gui.setWindowTitle("Carla - %s" % os.path.basename(projectFilename)) # FIXME - put in loadProject
  559. # App-Loop
  560. ret = app.exec_()
  561. # Exit properly
  562. sys.exit(ret)