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.

carla_host.py 46KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla host code
  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 doc/GPL.txt file.
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from PyQt4.QtCore import qCritical, QModelIndex, QTimer
  20. from PyQt4.QtGui import QApplication, QFileSystemModel, QMainWindow, QPalette
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Custom)
  23. import ui_carla_host
  24. from carla_database import *
  25. from carla_settings import *
  26. from carla_style import *
  27. from carla_widgets import *
  28. # ------------------------------------------------------------------------------------------------------------
  29. # PatchCanvas defines
  30. CANVAS_ANTIALIASING_SMALL = 1
  31. CANVAS_EYECANDY_SMALL = 1
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Session Management support
  34. LADISH_APP_NAME = os.getenv("LADISH_APP_NAME")
  35. NSM_URL = os.getenv("NSM_URL")
  36. # ------------------------------------------------------------------------------------------------------------
  37. # Dummy widget
  38. class CarlaDummyW(object):
  39. def __init__(self, parent):
  40. object.__init__(self)
  41. # -----------------------------------------------------------------
  42. def getPluginCount(self):
  43. return 0
  44. # -----------------------------------------------------------------
  45. def addPlugin(self, pluginId, isProjectLoading):
  46. pass
  47. def removePlugin(self, pluginId):
  48. pass
  49. def renamePlugin(self, pluginId, newName):
  50. pass
  51. def disablePlugin(self, pluginId, errorMsg):
  52. pass
  53. def removeAllPlugins(self):
  54. pass
  55. # -----------------------------------------------------------------
  56. def engineStarted(self):
  57. pass
  58. def engineStopped(self):
  59. pass
  60. def engineChanged(self):
  61. pass
  62. # -----------------------------------------------------------------
  63. def idleFast(self):
  64. pass
  65. def idleSlow(self):
  66. pass
  67. # -----------------------------------------------------------------
  68. def saveSettings(self, settings):
  69. pass
  70. # ------------------------------------------------------------------------------------------------------------
  71. # Host Window
  72. class HostWindow(QMainWindow):
  73. # signals
  74. DebugCallback = pyqtSignal(int, int, int, float, str)
  75. PluginAddedCallback = pyqtSignal(int, str)
  76. PluginRemovedCallback = pyqtSignal(int)
  77. PluginRenamedCallback = pyqtSignal(int, str)
  78. PluginUnavailableCallback = pyqtSignal(int, str)
  79. ParameterValueChangedCallback = pyqtSignal(int, int, float)
  80. ParameterDefaultChangedCallback = pyqtSignal(int, int, float)
  81. ParameterMidiCcChangedCallback = pyqtSignal(int, int, int)
  82. ParameterMidiChannelChangedCallback = pyqtSignal(int, int, int)
  83. ProgramChangedCallback = pyqtSignal(int, int)
  84. MidiProgramChangedCallback = pyqtSignal(int, int)
  85. UiStateChangedCallback = pyqtSignal(int, int)
  86. NoteOnCallback = pyqtSignal(int, int, int, int)
  87. NoteOffCallback = pyqtSignal(int, int, int)
  88. UpdateCallback = pyqtSignal(int)
  89. ReloadInfoCallback = pyqtSignal(int)
  90. ReloadParametersCallback = pyqtSignal(int)
  91. ReloadProgramsCallback = pyqtSignal(int)
  92. ReloadAllCallback = pyqtSignal(int)
  93. PatchbayClientAddedCallback = pyqtSignal(int, int, str)
  94. PatchbayClientRemovedCallback = pyqtSignal(int)
  95. PatchbayClientRenamedCallback = pyqtSignal(int, str)
  96. PatchbayClientIconChangedCallback = pyqtSignal(int, int)
  97. PatchbayPortAddedCallback = pyqtSignal(int, int, int, str)
  98. PatchbayPortRemovedCallback = pyqtSignal(int, int)
  99. PatchbayPortRenamedCallback = pyqtSignal(int, int, str)
  100. PatchbayConnectionAddedCallback = pyqtSignal(int, int, int)
  101. PatchbayConnectionRemovedCallback = pyqtSignal(int, int, int)
  102. EngineStartedCallback = pyqtSignal(int, int, str)
  103. EngineStoppedCallback = pyqtSignal()
  104. ProcessModeChangedCallback = pyqtSignal(int)
  105. TransportModeChangedCallback = pyqtSignal(int)
  106. BufferSizeChangedCallback = pyqtSignal(int)
  107. SampleRateChangedCallback = pyqtSignal(float)
  108. InfoCallback = pyqtSignal(str)
  109. ErrorCallback = pyqtSignal(str)
  110. QuitCallback = pyqtSignal()
  111. SIGTERM = pyqtSignal()
  112. SIGUSR1 = pyqtSignal()
  113. def __init__(self, parent):
  114. QMainWindow.__init__(self, parent)
  115. self.ui = ui_carla_host.Ui_CarlaHostW()
  116. self.ui.setupUi(self)
  117. if False:
  118. Carla.gui = self
  119. self.fContainer = CarlaDummyW(self)
  120. # -------------------------------------------------------------
  121. # Set callback, TODO put somewhere else
  122. if Carla.host is not None:
  123. Carla.host.set_engine_callback(engineCallback)
  124. # -------------------------------------------------------------
  125. # Internal stuff
  126. self.fIdleTimerFast = 0
  127. self.fIdleTimerSlow = 0
  128. self.fIsProjectLoading = False
  129. self.fProjectFilename = ""
  130. self.fLadspaRdfNeedsUpdate = True
  131. self.fLadspaRdfList = []
  132. self.fLastTransportFrame = 0
  133. self.fLastTransportState = False
  134. self.fTransportText = ""
  135. # when true, call engineChanged() asap
  136. self.fEngineChanged = False
  137. # first attempt of auto-start engine doesn't show an error
  138. self.fFirstEngineInit = True
  139. self.fSavedSettings = {}
  140. if LADISH_APP_NAME:
  141. self.fClientName = LADISH_APP_NAME
  142. self.fSessionManagerName = "LADISH"
  143. elif NSM_URL:
  144. self.fClientName = "Carla.tmp"
  145. self.fSessionManagerName = "Non Session Manager"
  146. else:
  147. self.fClientName = "Carla"
  148. self.fSessionManagerName = ""
  149. # -------------------------------------------------------------
  150. # Load Settings
  151. self.loadSettings(True)
  152. # -------------------------------------------------------------
  153. # Set up GUI (engine stopped)
  154. if Carla.isPlugin:
  155. self.ui.act_file_new.setEnabled(False)
  156. self.ui.act_file_open.setEnabled(False)
  157. self.ui.act_engine_start.setEnabled(False)
  158. self.ui.menu_Engine.setEnabled(False)
  159. else:
  160. self.ui.act_engine_start.setEnabled(True)
  161. self.ui.act_file_save.setEnabled(False)
  162. self.ui.act_file_save_as.setEnabled(False)
  163. self.ui.act_engine_stop.setEnabled(False)
  164. self.ui.act_plugin_remove_all.setEnabled(False)
  165. self.ui.menu_PluginMacros.setEnabled(False)
  166. self.ui.menu_Canvas.setEnabled(False)
  167. self.setTransportMenuEnabled(False)
  168. # -------------------------------------------------------------
  169. # Set up GUI (right panel)
  170. self.fDirModel = QFileSystemModel(self)
  171. self.fDirModel.setRootPath(HOME)
  172. if Carla.host is not None:
  173. self.fDirModel.setNameFilters(Carla.host.get_supported_file_extensions().split(";"))
  174. self.ui.fileTreeView.setModel(self.fDirModel)
  175. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
  176. self.ui.fileTreeView.setColumnHidden(1, True)
  177. self.ui.fileTreeView.setColumnHidden(2, True)
  178. self.ui.fileTreeView.setColumnHidden(3, True)
  179. self.ui.fileTreeView.setHeaderHidden(True)
  180. self.setProperWindowTitle()
  181. # -------------------------------------------------------------
  182. # Connect actions to functions
  183. self.ui.act_file_new.triggered.connect(self.slot_fileNew)
  184. self.ui.act_file_open.triggered.connect(self.slot_fileOpen)
  185. self.ui.act_file_save.triggered.connect(self.slot_fileSave)
  186. self.ui.act_file_save_as.triggered.connect(self.slot_fileSaveAs)
  187. self.ui.act_engine_start.triggered.connect(self.slot_engineStart)
  188. self.ui.act_engine_stop.triggered.connect(self.slot_engineStop)
  189. self.ui.act_plugin_add.triggered.connect(self.slot_pluginAdd)
  190. self.ui.act_plugin_add2.triggered.connect(self.slot_pluginAdd)
  191. self.ui.act_plugin_remove_all.triggered.connect(self.slot_pluginRemoveAll)
  192. self.ui.act_transport_play.triggered.connect(self.slot_transportPlayPause)
  193. self.ui.act_transport_stop.triggered.connect(self.slot_transportStop)
  194. self.ui.act_transport_backwards.triggered.connect(self.slot_transportBackwards)
  195. self.ui.act_transport_forwards.triggered.connect(self.slot_transportForwards)
  196. self.ui.act_help_about.triggered.connect(self.slot_aboutCarla)
  197. self.ui.act_help_about_qt.triggered.connect(self.slot_aboutQt)
  198. self.ui.cb_disk.currentIndexChanged.connect(self.slot_diskFolderChanged)
  199. self.ui.b_disk_add.clicked.connect(self.slot_diskFolderAdd)
  200. self.ui.b_disk_remove.clicked.connect(self.slot_diskFolderRemove)
  201. self.ui.fileTreeView.doubleClicked.connect(self.slot_fileTreeDoubleClicked)
  202. self.DebugCallback.connect(self.slot_handleDebugCallback)
  203. self.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback)
  204. self.PluginRemovedCallback.connect(self.slot_handlePluginRemovedCallback)
  205. self.PluginRenamedCallback.connect(self.slot_handlePluginRenamedCallback)
  206. self.PluginUnavailableCallback.connect(self.slot_handlePluginUnavailableCallback)
  207. # parameter (rack, patchbay)
  208. # program, midi-program, ui-state (rack, patchbay)
  209. # note on, off (rack, patchbay)
  210. # update, reload (rack, patchbay)
  211. # patchbay
  212. self.EngineStartedCallback.connect(self.slot_handleEngineStartedCallback)
  213. self.EngineStoppedCallback.connect(self.slot_handleEngineStoppedCallback)
  214. self.ProcessModeChangedCallback.connect(self.slot_handleProcessModeChangedCallback)
  215. self.TransportModeChangedCallback.connect(self.slot_handleTransportModeChangedCallback)
  216. self.BufferSizeChangedCallback.connect(self.slot_handleBufferSizeChangedCallback)
  217. self.SampleRateChangedCallback.connect(self.slot_handleSampleRateChangedCallback)
  218. self.InfoCallback.connect(self.slot_handleInfoCallback)
  219. self.ErrorCallback.connect(self.slot_handleErrorCallback)
  220. self.QuitCallback.connect(self.slot_handleQuitCallback)
  221. self.SIGUSR1.connect(self.slot_handleSIGUSR1)
  222. self.SIGTERM.connect(self.slot_handleSIGTERM)
  223. # -------------------------------------------------------------
  224. # Final setup
  225. QTimer.singleShot(0, self.slot_engineStart)
  226. # -----------------------------------------------------------------
  227. # Called by containers
  228. def openSettingsWindow(self, hasCanvas, hasCanvasGL):
  229. dialog = CarlaSettingsW(self, hasCanvas, hasCanvasGL)
  230. return dialog.exec_()
  231. def setupContainer(self, showMiniCanvas, canvasThemeData = []):
  232. if showMiniCanvas:
  233. canvasWidth, canvasHeight, canvasBg, canvasBrush, canvasPen = canvasThemeData
  234. self.ui.miniCanvasPreview.setViewTheme(canvasBg, canvasBrush, canvasPen)
  235. self.ui.miniCanvasPreview.init(self.fContainer.scene, canvasWidth, canvasHeight, self.fSavedSettings["UseCustomMiniCanvasPaint"])
  236. else:
  237. self.ui.miniCanvasPreview.hide()
  238. self.ui.splitter.insertWidget(1, self.fContainer)
  239. def updateContainer(self, canvasThemeData):
  240. canvasWidth, canvasHeight, canvasBg, canvasBrush, canvasPen = canvasThemeData
  241. self.ui.miniCanvasPreview.setViewTheme(canvasBg, canvasBrush, canvasPen)
  242. self.ui.miniCanvasPreview.init(self.fContainer.scene, canvasWidth, canvasHeight, self.fSavedSettings["UseCustomMiniCanvasPaint"])
  243. # -----------------------------------------------------------------
  244. # Internal stuff (files)
  245. def loadProjectNow(self):
  246. if not self.fProjectFilename:
  247. return qCritical("ERROR: loading project without filename set")
  248. self.fIsProjectLoading = True
  249. Carla.host.load_project(self.fProjectFilename)
  250. self.fIsProjectLoading = False
  251. @pyqtSlot()
  252. def slot_loadProjectNow(self):
  253. self.loadProjectNow()
  254. def loadProjectLater(self, filename):
  255. self.fProjectFilename = filename
  256. self.setProperWindowTitle()
  257. QTimer.singleShot(0, self.slot_loadProjectNow)
  258. def saveProjectNow(self):
  259. if not self.fProjectFilename:
  260. return qCritical("ERROR: saving project without filename set")
  261. Carla.host.save_project(self.fProjectFilename)
  262. # -----------------------------------------------------------------
  263. # Internal stuff (engine)
  264. def setEngineSettings(self, settings = None):
  265. if Carla.isPlugin:
  266. return "Plugin"
  267. if settings is None: settings = QSettings()
  268. # -------------------------------------------------------------
  269. # read settings
  270. # bool values
  271. try:
  272. forceStereo = settings.value(CARLA_KEY_ENGINE_FORCE_STEREO, CARLA_DEFAULT_FORCE_STEREO, type=bool)
  273. except:
  274. forceStereo = CARLA_DEFAULT_FORCE_STEREO
  275. try:
  276. preferPluginBridges = settings.value(CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES, CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, type=bool)
  277. except:
  278. preferPluginBridges = CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES
  279. try:
  280. preferUiBridges = settings.value(CARLA_KEY_ENGINE_PREFER_UI_BRIDGES, CARLA_DEFAULT_PREFER_UI_BRIDGES, type=bool)
  281. except:
  282. preferUiBridges = CARLA_DEFAULT_PREFER_UI_BRIDGES
  283. try:
  284. uisAlwaysOnTop = settings.value(CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP, CARLA_DEFAULT_UIS_ALWAYS_ON_TOP, type=bool)
  285. except:
  286. uisAlwaysOnTop = CARLA_DEFAULT_UIS_ALWAYS_ON_TOP
  287. # int values
  288. try:
  289. maxParameters = settings.value(CARLA_KEY_ENGINE_MAX_PARAMETERS, CARLA_DEFAULT_MAX_PARAMETERS, type=int)
  290. except:
  291. maxParameters = CARLA_DEFAULT_MAX_PARAMETERS
  292. try:
  293. uiBridgesTimeout = settings.value(CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT, CARLA_DEFAULT_UI_BRIDGES_TIMEOUT, type=int)
  294. except:
  295. uiBridgesTimeout = CARLA_DEFAULT_UI_BRIDGES_TIMEOUT
  296. # enums
  297. try:
  298. processMode = settings.value(CARLA_KEY_ENGINE_PROCESS_MODE, CARLA_DEFAULT_PROCESS_MODE, type=int)
  299. except:
  300. processMode = CARLA_DEFAULT_PROCESS_MODE
  301. try:
  302. transportMode = settings.value(CARLA_KEY_ENGINE_TRANSPORT_MODE, CARLA_DEFAULT_TRANSPORT_MODE, type=int)
  303. except:
  304. transportMode = CARLA_DEFAULT_TRANSPORT_MODE
  305. # driver name
  306. try:
  307. audioDriver = settings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, type=str)
  308. except:
  309. audioDriver = CARLA_DEFAULT_AUDIO_DRIVER
  310. # driver options
  311. try:
  312. audioDevice = settings.value("%s%s/Device" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), "", type=str)
  313. except:
  314. audioDevice = ""
  315. try:
  316. audioNumPeriods = settings.value("%s%s/NumPeriods" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), CARLA_DEFAULT_AUDIO_NUM_PERIODS, type=int)
  317. except:
  318. audioNumPeriods = CARLA_DEFAULT_AUDIO_NUM_PERIODS
  319. try:
  320. audioBufferSize = settings.value("%s%s/BufferSize" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), CARLA_DEFAULT_AUDIO_BUFFER_SIZE, type=int)
  321. except:
  322. audioBufferSize = CARLA_DEFAULT_AUDIO_BUFFER_SIZE
  323. try:
  324. audioSampleRate = settings.value("%s%s/SampleRate" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), CARLA_DEFAULT_AUDIO_SAMPLE_RATE, type=int)
  325. except:
  326. audioSampleRate = CARLA_DEFAULT_AUDIO_SAMPLE_RATE
  327. # -------------------------------------------------------------
  328. # fix things if needed
  329. if processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
  330. forceStereo = True
  331. elif processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS and LADISH_APP_NAME:
  332. print("LADISH detected but using multiple clients (not allowed), forcing single client now")
  333. processMode = ENGINE_PROCESS_MODE_SINGLE_CLIENT
  334. if audioDriver != "JACK" and transportMode == ENGINE_TRANSPORT_MODE_JACK:
  335. transportMode = ENGINE_TRANSPORT_MODE_INTERNAL
  336. # -------------------------------------------------------------
  337. # apply to engine
  338. Carla.host.set_engine_option(ENGINE_OPTION_FORCE_STEREO, forceStereo, "")
  339. Carla.host.set_engine_option(ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, preferPluginBridges, "")
  340. Carla.host.set_engine_option(ENGINE_OPTION_PREFER_UI_BRIDGES, preferUiBridges, "")
  341. Carla.host.set_engine_option(ENGINE_OPTION_UIS_ALWAYS_ON_TOP, uisAlwaysOnTop, "")
  342. Carla.host.set_engine_option(ENGINE_OPTION_MAX_PARAMETERS, maxParameters, "")
  343. Carla.host.set_engine_option(ENGINE_OPTION_UI_BRIDGES_TIMEOUT, uiBridgesTimeout, "")
  344. Carla.host.set_engine_option(ENGINE_OPTION_PROCESS_MODE, processMode, "")
  345. Carla.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, transportMode, "")
  346. Carla.host.set_engine_option(ENGINE_OPTION_AUDIO_NUM_PERIODS, audioNumPeriods, "")
  347. Carla.host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, audioBufferSize, "")
  348. Carla.host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, audioSampleRate, "")
  349. Carla.host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, audioDevice)
  350. # save this for later
  351. Carla.maxParameters = maxParameters
  352. # return selected driver name
  353. return audioDriver
  354. def startEngine(self):
  355. audioDriver = self.setEngineSettings()
  356. if not Carla.host.engine_init(audioDriver, self.fClientName):
  357. if self.fFirstEngineInit:
  358. self.fFirstEngineInit = False
  359. return
  360. audioError = Carla.host.get_last_error()
  361. if audioError:
  362. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
  363. else:
  364. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s'" % audioDriver))
  365. return
  366. self.fFirstEngineInit = False
  367. def stopEngine(self):
  368. if self.fContainer.getPluginCount() > 0:
  369. 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"
  370. "Do you want to do this now?"),
  371. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  372. if ask != QMessageBox.Yes:
  373. return
  374. self.ui.act_plugin_remove_all.setEnabled(False)
  375. self.fContainer.removeAllPlugins()
  376. if Carla.host.is_engine_running() and not Carla.host.engine_close():
  377. print(Carla.host.get_last_error())
  378. # -----------------------------------------------------------------
  379. # Internal stuff (plugins)
  380. def getExtraPtr(self, plugin):
  381. ptype = plugin['type']
  382. if ptype == PLUGIN_LADSPA:
  383. uniqueId = plugin['uniqueId']
  384. self.maybeLoadRDFs()
  385. for rdfItem in self.fLadspaRdfList:
  386. if rdfItem.UniqueID == uniqueId:
  387. return pointer(rdfItem)
  388. elif ptype in (PLUGIN_FILE_GIG, PLUGIN_FILE_SF2):
  389. if plugin['name'].lower().endswith(" (16 outputs)"):
  390. return c_char_p("true".encode("utf-8"))
  391. return None
  392. def maybeLoadRDFs(self):
  393. if not self.fLadspaRdfNeedsUpdate:
  394. return
  395. self.fLadspaRdfNeedsUpdate = False
  396. self.fLadspaRdfList = []
  397. if not haveLRDF:
  398. return
  399. settingsDir = os.path.join(HOME, ".config", "falkTX")
  400. frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
  401. if os.path.exists(frLadspaFile):
  402. frLadspa = open(frLadspaFile, 'r')
  403. try:
  404. self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
  405. except:
  406. pass
  407. frLadspa.close()
  408. def setLoadRDFsNeeded(self):
  409. self.fLadspaRdfNeedsUpdate = True
  410. # -----------------------------------------------------------------
  411. # Internal stuff (transport)
  412. def refreshTransport(self, forced = False):
  413. if Carla.sampleRate == 0.0 or not Carla.host.is_engine_running():
  414. return
  415. timeInfo = Carla.host.get_transport_info()
  416. playing = bool(timeInfo['playing'])
  417. frame = int(timeInfo['frame'])
  418. if playing != self.fLastTransportState or forced:
  419. if playing:
  420. icon = getIcon("media-playback-pause")
  421. self.ui.act_transport_play.setChecked(True)
  422. self.ui.act_transport_play.setIcon(icon)
  423. self.ui.act_transport_play.setText(self.tr("&Pause"))
  424. else:
  425. icon = getIcon("media-playback-start")
  426. self.ui.act_transport_play.setChecked(False)
  427. self.ui.act_transport_play.setIcon(icon)
  428. self.ui.act_transport_play.setText(self.tr("&Play"))
  429. self.fLastTransportState = playing
  430. if frame != self.fLastTransportFrame or forced:
  431. time = frame / Carla.sampleRate
  432. secs = time % 60
  433. mins = (time / 60) % 60
  434. hrs = (time / 3600) % 60
  435. self.fTextTransport = "Transport %s, at %02i:%02i:%02i" % ("playing" if playing else "stopped", hrs, mins, secs)
  436. self.fLastTransportFrame = frame
  437. def setTransportMenuEnabled(self, enabled):
  438. self.ui.act_transport_play.setEnabled(enabled)
  439. self.ui.act_transport_stop.setEnabled(enabled)
  440. self.ui.act_transport_backwards.setEnabled(enabled)
  441. self.ui.act_transport_forwards.setEnabled(enabled)
  442. self.ui.menu_Transport.setEnabled(enabled)
  443. # -----------------------------------------------------------------
  444. # Internal stuff (settings)
  445. def loadSettings(self, firstTime):
  446. settings = QSettings()
  447. if firstTime:
  448. self.restoreGeometry(settings.value("Geometry", ""))
  449. showToolbar = settings.value("ShowToolbar", True, type=bool)
  450. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  451. self.ui.toolBar.setVisible(showToolbar)
  452. #if settings.contains("SplitterState"):
  453. #self.ui.splitter.restoreState(settings.value("SplitterState", ""))
  454. #else:
  455. self.ui.splitter.setSizes([99999, 210])
  456. diskFolders = toList(settings.value("DiskFolders", [HOME]))
  457. self.ui.cb_disk.setItemData(0, HOME)
  458. for i in range(len(diskFolders)):
  459. if i == 0: continue
  460. folder = diskFolders[i]
  461. self.ui.cb_disk.addItem(os.path.basename(folder), folder)
  462. if MACOS and not settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, type=bool):
  463. self.setUnifiedTitleAndToolBarOnMac(True)
  464. # ---------------------------------------------
  465. # plugin checks
  466. if settings.value("Engine/DisableChecks", False, type=bool):
  467. os.environ["CARLA_DISCOVERY_NO_PROCESSING_CHECKS"] = "true"
  468. elif os.getenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS"):
  469. os.environ.pop("CARLA_DISCOVERY_NO_PROCESSING_CHECKS")
  470. # ---------------------------------------------
  471. if not Carla.isPlugin:
  472. # engine
  473. self.setEngineSettings(settings)
  474. # plugin paths
  475. LADSPA_PATH = toList(settings.value("Paths/LADSPA", Carla.DEFAULT_LADSPA_PATH))
  476. DSSI_PATH = toList(settings.value("Paths/DSSI", Carla.DEFAULT_DSSI_PATH))
  477. LV2_PATH = toList(settings.value("Paths/LV2", Carla.DEFAULT_LV2_PATH))
  478. VST_PATH = toList(settings.value("Paths/VST", Carla.DEFAULT_VST_PATH))
  479. AU_PATH = toList(settings.value("Paths/AU", Carla.DEFAULT_AU_PATH))
  480. CSOUND_PATH = toList(settings.value("Paths/CSOUND", Carla.DEFAULT_CSOUND_PATH))
  481. GIG_PATH = toList(settings.value("Paths/GIG", Carla.DEFAULT_GIG_PATH))
  482. SF2_PATH = toList(settings.value("Paths/SF2", Carla.DEFAULT_SF2_PATH))
  483. SFZ_PATH = toList(settings.value("Paths/SFZ", Carla.DEFAULT_SFZ_PATH))
  484. os.environ["LADSPA_PATH"] = splitter.join(LADSPA_PATH)
  485. os.environ["DSSI_PATH"] = splitter.join(DSSI_PATH)
  486. os.environ["LV2_PATH"] = splitter.join(LV2_PATH)
  487. os.environ["VST_PATH"] = splitter.join(VST_PATH)
  488. os.environ["AU_PATH"] = splitter.join(AU_PATH)
  489. os.environ["CSOUND_PATH"] = splitter.join(CSOUND_PATH)
  490. os.environ["GIG_PATH"] = splitter.join(GIG_PATH)
  491. os.environ["SF2_PATH"] = splitter.join(SF2_PATH)
  492. os.environ["SFZ_PATH"] = splitter.join(SFZ_PATH)
  493. # ---------------------------------------------
  494. # TODO
  495. self.fSavedSettings = {
  496. CARLA_KEY_MAIN_PROJECT_FOLDER: settings.value(CARLA_KEY_MAIN_PROJECT_FOLDER, CARLA_DEFAULT_MAIN_PROJECT_FOLDER, type=str),
  497. CARLA_KEY_MAIN_REFRESH_INTERVAL: settings.value(CARLA_KEY_MAIN_REFRESH_INTERVAL, CARLA_DEFAULT_MAIN_REFRESH_INTERVAL, type=int),
  498. CARLA_KEY_CANVAS_THEME: settings.value(CARLA_KEY_CANVAS_THEME, CARLA_DEFAULT_CANVAS_THEME, type=str),
  499. CARLA_KEY_CANVAS_SIZE: settings.value(CARLA_KEY_CANVAS_SIZE, CARLA_DEFAULT_CANVAS_SIZE, type=str),
  500. CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS: settings.value(CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS, CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS, type=bool),
  501. CARLA_KEY_CANVAS_USE_BEZIER_LINES: settings.value(CARLA_KEY_CANVAS_USE_BEZIER_LINES, CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES, type=bool),
  502. CARLA_KEY_CANVAS_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_EYE_CANDY, CARLA_DEFAULT_CANVAS_EYE_CANDY, type=int),
  503. CARLA_KEY_CANVAS_USE_OPENGL: settings.value(CARLA_KEY_CANVAS_USE_OPENGL, CARLA_DEFAULT_CANVAS_USE_OPENGL, type=bool),
  504. CARLA_KEY_CANVAS_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_ANTIALIASING, CARLA_DEFAULT_CANVAS_ANTIALIASING, type=int),
  505. CARLA_KEY_CANVAS_HQ_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_HQ_ANTIALIASING, CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING, type=bool),
  506. "UseCustomMiniCanvasPaint": (settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, type=bool) and
  507. settings.value(CARLA_KEY_MAIN_PRO_THEME_COLOR, "Black", type=str).lower() == "black")
  508. }
  509. # ---------------------------------------------
  510. if self.fIdleTimerFast != 0:
  511. self.killTimer(self.fIdleTimerFast)
  512. self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
  513. if self.fIdleTimerSlow != 0:
  514. self.killTimer(self.fIdleTimerSlow)
  515. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*2)
  516. def saveSettings(self):
  517. settings = QSettings()
  518. settings.setValue("Geometry", self.saveGeometry())
  519. settings.setValue("SplitterState", self.ui.splitter.saveState())
  520. settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  521. diskFolders = []
  522. for i in range(self.ui.cb_disk.count()):
  523. diskFolders.append(self.ui.cb_disk.itemData(i))
  524. settings.setValue("DiskFolders", diskFolders)
  525. self.fContainer.saveSettings(settings)
  526. # -----------------------------------------------------------------
  527. # Internal stuff (gui)
  528. def setProperWindowTitle(self):
  529. title = self.fClientName
  530. if self.fProjectFilename:
  531. title += " - %s" % os.path.basename(self.fProjectFilename)
  532. if self.fSessionManagerName:
  533. title += " (%s)" % self.fSessionManagerName
  534. self.setWindowTitle(title)
  535. # -----------------------------------------------------------------
  536. @pyqtSlot()
  537. def slot_fileNew(self):
  538. self.fContainer.removeAllPlugins()
  539. self.fProjectFilename = ""
  540. self.setProperWindowTitle()
  541. @pyqtSlot()
  542. def slot_fileOpen(self):
  543. fileFilter = self.tr("Carla Project File (*.carxp)")
  544. filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
  545. if not filenameTry:
  546. return
  547. filename = filenameTry if isinstance(filenameTry, str) else filenameTry[0]
  548. newFile = True
  549. if self.fContainer.getPluginCount() > 0:
  550. ask = QMessageBox.question(self, self.tr("Question"), self.tr("There are some plugins loaded, do you want to remove them now?"),
  551. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  552. newFile = (ask == QMessageBox.Yes)
  553. if newFile:
  554. self.fContainer.removeAllPlugins()
  555. self.fProjectFilename = filename
  556. self.setProperWindowTitle()
  557. self.loadProjectNow()
  558. else:
  559. filenameOld = self.fProjectFilename
  560. self.fProjectFilename = filename
  561. self.loadProjectNow()
  562. self.fProjectFilename = filenameOld
  563. @pyqtSlot()
  564. def slot_fileSave(self, saveAs=False):
  565. if self.fProjectFilename and not saveAs:
  566. return self.saveProjectNow()
  567. fileFilter = self.tr("Carla Project File (*.carxp)")
  568. filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
  569. if not filenameTry:
  570. return
  571. filename = filenameTry if isinstance(filenameTry, str) else filenameTry[0]
  572. if not filename.endswith(".carxp"):
  573. filename += ".carxp"
  574. if self.fProjectFilename != filename:
  575. self.fProjectFilename = filename
  576. self.setProperWindowTitle()
  577. self.saveProjectNow()
  578. @pyqtSlot()
  579. def slot_fileSaveAs(self):
  580. self.slot_fileSave(True)
  581. # -----------------------------------------------------------------
  582. @pyqtSlot()
  583. def slot_engineStart(self, doStart = True):
  584. if doStart: self.startEngine()
  585. check = Carla.host.is_engine_running()
  586. self.ui.menu_PluginMacros.setEnabled(check)
  587. self.ui.menu_Canvas.setEnabled(check)
  588. if not Carla.isPlugin:
  589. self.ui.act_file_save.setEnabled(check)
  590. self.ui.act_engine_start.setEnabled(not check)
  591. self.ui.act_engine_stop.setEnabled(check)
  592. if self.fSessionManagerName != "Non Session Manager":
  593. self.ui.act_file_open.setEnabled(check)
  594. self.ui.act_file_save_as.setEnabled(check)
  595. self.setTransportMenuEnabled(check)
  596. if check:
  597. if not Carla.isPlugin:
  598. self.refreshTransport(True)
  599. self.fContainer.engineStarted()
  600. @pyqtSlot()
  601. def slot_engineStop(self, doStop = True):
  602. if doStop: self.stopEngine()
  603. # FIXME?
  604. if self.fContainer.getPluginCount() > 0:
  605. self.ui.act_plugin_remove_all.setEnabled(False)
  606. self.fContainer.removeAllPlugins()
  607. check = Carla.host.is_engine_running()
  608. self.ui.menu_PluginMacros.setEnabled(check)
  609. self.ui.menu_Canvas.setEnabled(check)
  610. if not Carla.isPlugin:
  611. self.ui.act_file_save.setEnabled(check)
  612. self.ui.act_engine_start.setEnabled(not check)
  613. self.ui.act_engine_stop.setEnabled(check)
  614. if self.fSessionManagerName != "Non Session Manager":
  615. self.ui.act_file_open.setEnabled(check)
  616. self.ui.act_file_save_as.setEnabled(check)
  617. self.setTransportMenuEnabled(check)
  618. if not check:
  619. self.fTextTransport = ""
  620. self.fContainer.engineStopped()
  621. # -----------------------------------------------------------------
  622. @pyqtSlot()
  623. def slot_pluginAdd(self):
  624. dialog = PluginDatabaseW(self)
  625. if not dialog.exec_():
  626. return
  627. if not Carla.host.is_engine_running():
  628. QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
  629. return
  630. btype = dialog.fRetPlugin['build']
  631. ptype = dialog.fRetPlugin['type']
  632. filename = dialog.fRetPlugin['filename']
  633. label = dialog.fRetPlugin['label']
  634. extraPtr = self.getExtraPtr(dialog.fRetPlugin)
  635. if not Carla.host.add_plugin(btype, ptype, filename, None, label, extraPtr):
  636. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), charPtrToString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  637. return
  638. @pyqtSlot()
  639. def slot_pluginRemoveAll(self):
  640. self.ui.act_plugin_remove_all.setEnabled(False)
  641. self.fContainer.removeAllPlugins()
  642. Carla.host.remove_all_plugins()
  643. # -----------------------------------------------------------------
  644. @pyqtSlot(bool)
  645. def slot_transportPlayPause(self, toggled):
  646. if not Carla.host.is_engine_running():
  647. return
  648. if toggled:
  649. Carla.host.transport_play()
  650. else:
  651. Carla.host.transport_pause()
  652. self.refreshTransport()
  653. @pyqtSlot()
  654. def slot_transportStop(self):
  655. if not Carla.host.is_engine_running():
  656. return
  657. Carla.host.transport_pause()
  658. Carla.host.transport_relocate(0)
  659. self.refreshTransport()
  660. @pyqtSlot()
  661. def slot_transportBackwards(self):
  662. if not Carla.host.is_engine_running():
  663. return
  664. newFrame = Carla.host.get_current_transport_frame() - 100000
  665. if newFrame < 0:
  666. newFrame = 0
  667. Carla.host.transport_relocate(newFrame)
  668. @pyqtSlot()
  669. def slot_transportForwards(self):
  670. if not Carla.host.is_engine_running():
  671. return
  672. newFrame = Carla.host.get_current_transport_frame() + 100000
  673. Carla.host.transport_relocate(newFrame)
  674. # -----------------------------------------------------------------
  675. @pyqtSlot()
  676. def slot_aboutCarla(self):
  677. CarlaAboutW(self).exec_()
  678. @pyqtSlot()
  679. def slot_aboutQt(self):
  680. QApplication.instance().aboutQt()
  681. # -----------------------------------------------------------------
  682. @pyqtSlot(int)
  683. def slot_diskFolderChanged(self, index):
  684. if index < 0:
  685. return
  686. elif index == 0:
  687. filename = HOME
  688. self.ui.b_disk_remove.setEnabled(False)
  689. else:
  690. filename = self.ui.cb_disk.itemData(index)
  691. self.ui.b_disk_remove.setEnabled(True)
  692. self.fDirModel.setRootPath(filename)
  693. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(filename))
  694. @pyqtSlot()
  695. def slot_diskFolderAdd(self):
  696. newPath = QFileDialog.getExistingDirectory(self, self.tr("New Folder"), "", QFileDialog.ShowDirsOnly)
  697. if newPath:
  698. if newPath[-1] == os.sep:
  699. newPath = newPath[:-1]
  700. self.ui.cb_disk.addItem(os.path.basename(newPath), newPath)
  701. self.ui.cb_disk.setCurrentIndex(self.ui.cb_disk.count()-1)
  702. self.ui.b_disk_remove.setEnabled(True)
  703. @pyqtSlot()
  704. def slot_diskFolderRemove(self):
  705. index = self.ui.cb_disk.currentIndex()
  706. if index <= 0:
  707. return
  708. self.ui.cb_disk.removeItem(index)
  709. if self.ui.cb_disk.currentIndex() == 0:
  710. self.ui.b_disk_remove.setEnabled(False)
  711. @pyqtSlot(QModelIndex)
  712. def slot_fileTreeDoubleClicked(self, modelIndex):
  713. filename = self.fDirModel.filePath(modelIndex)
  714. if not Carla.host.load_file(filename):
  715. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  716. self.tr("Failed to load file"),
  717. Carla.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  718. # -----------------------------------------------------------------
  719. @pyqtSlot(int, int, int, float, str)
  720. def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
  721. print("DEBUG:", pluginId, value1, value2, value3, valueStr)
  722. #self.ui.pte_log.appendPlainText(valueStr.replace("", "DEBUG: ").replace("", "ERROR: ").replace("", "").replace("\n", ""))
  723. # -----------------------------------------------------------------
  724. @pyqtSlot(int, str)
  725. def slot_handlePluginAddedCallback(self, pluginId, pluginName):
  726. self.fContainer.addPlugin(pluginId, self.fIsProjectLoading)
  727. if self.fContainer.getPluginCount() == 1:
  728. self.ui.act_plugin_remove_all.setEnabled(True)
  729. @pyqtSlot(int)
  730. def slot_handlePluginRemovedCallback(self, pluginId):
  731. self.fContainer.removePlugin(pluginId)
  732. if self.fContainer.getPluginCount() == 0:
  733. self.ui.act_plugin_remove_all.setEnabled(False)
  734. @pyqtSlot(int, str)
  735. def slot_handlePluginRenamedCallback(self, pluginId, newName):
  736. self.fContainer.renamePlugin(pluginId, newName)
  737. @pyqtSlot(int, str)
  738. def slot_handlePluginUnavailableCallback(self, pluginId, errorMsg):
  739. self.fContainer.disablePlugin(pluginId, errorMsg)
  740. # -----------------------------------------------------------------
  741. @pyqtSlot(str)
  742. def slot_handleEngineStartedCallback(self, processMode, transportMode, driverName):
  743. Carla.processMode = processMode
  744. Carla.transportMode = transportMode
  745. Carla.bufferSize = Carla.host.get_buffer_size()
  746. Carla.sampleRate = Carla.host.get_sample_rate()
  747. self.slot_engineStart(False)
  748. if self.fIdleTimerFast == 0:
  749. self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
  750. if self.fIdleTimerSlow == 0:
  751. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*2)
  752. @pyqtSlot()
  753. def slot_handleEngineStoppedCallback(self):
  754. if self.fIdleTimerFast != 0:
  755. self.killTimer(self.fIdleTimerFast)
  756. self.fIdleTimerFast = 0
  757. if self.fIdleTimerSlow != 0:
  758. self.killTimer(self.fIdleTimerSlow)
  759. self.fIdleTimerSlow = 0
  760. self.slot_engineStop(False)
  761. Carla.bufferSize = 0
  762. Carla.sampleRate = 0.0
  763. # -----------------------------------------------------------------
  764. @pyqtSlot(int)
  765. def slot_handleProcessModeChangedCallback(self, newProcessMode):
  766. self.fEngineChanged = True
  767. @pyqtSlot(int)
  768. def slot_handleTransportModeChangedCallback(self, newTransportMode):
  769. self.fEngineChanged = True
  770. # -----------------------------------------------------------------
  771. @pyqtSlot(int)
  772. def slot_handleBufferSizeChangedCallback(self, newBufferSize):
  773. self.fEngineChanged = True
  774. @pyqtSlot(float)
  775. def slot_handleSampleRateChangedCallback(self, newSampleRate):
  776. self.fEngineChanged = True
  777. # -----------------------------------------------------------------
  778. @pyqtSlot(str)
  779. def slot_handleInfoCallback(self, info):
  780. pass
  781. @pyqtSlot(str)
  782. def slot_handleErrorCallback(self, error):
  783. pass
  784. @pyqtSlot()
  785. def slot_handleQuitCallback(self):
  786. pass
  787. # -----------------------------------------------------------------
  788. @pyqtSlot()
  789. def slot_handleSIGUSR1(self):
  790. print("Got SIGUSR1 -> Saving project now")
  791. QTimer.singleShot(0, self.slot_fileSave)
  792. @pyqtSlot()
  793. def slot_handleSIGTERM(self):
  794. print("Got SIGTERM -> Closing now")
  795. self.close()
  796. # -----------------------------------------------------------------
  797. def timerEvent(self, event):
  798. if event.timerId() == self.fIdleTimerFast:
  799. #if not Carla.isPlugin:
  800. Carla.host.engine_idle()
  801. self.refreshTransport()
  802. self.fContainer.idleFast()
  803. elif event.timerId() == self.fIdleTimerSlow:
  804. if self.fEngineChanged:
  805. self.fContainer.engineChanged()
  806. self.fEngineChanged = False
  807. self.fContainer.idleSlow()
  808. QMainWindow.timerEvent(self, event)
  809. def closeEvent(self, event):
  810. if self.fIdleTimerFast != 0:
  811. self.killTimer(self.fIdleTimerFast)
  812. self.fIdleTimerFast = 0
  813. if self.fIdleTimerSlow != 0:
  814. self.killTimer(self.fIdleTimerSlow)
  815. self.fIdleTimerSlow = 0
  816. self.saveSettings()
  817. if Carla.host.is_engine_running():
  818. Carla.host.set_engine_about_to_close()
  819. self.ui.act_plugin_remove_all.setEnabled(False)
  820. self.fContainer.removeAllPlugins()
  821. self.stopEngine()
  822. QMainWindow.closeEvent(self, event)
  823. # ------------------------------------------------------------------------------------------------------------
  824. # Engine callback
  825. def engineCallback(ptr, action, pluginId, value1, value2, value3, valueStr):
  826. if action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
  827. Carla.processMode = value1
  828. if Carla.gui is not None:
  829. Carla.gui.ProcessModeChangedCallback.emit(value1)
  830. return
  831. if action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
  832. Carla.transportMode = value1
  833. if Carla.gui is not None:
  834. Carla.gui.TransportModeChangedCallback.emit(value1)
  835. return
  836. if action == ENGINE_CALLBACK_BUFFER_SIZE_CHANGED:
  837. Carla.bufferSize = value1
  838. if Carla.gui is not None:
  839. Carla.gui.BufferSizeChangedCallback.emit(value1)
  840. return
  841. if action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED:
  842. Carla.sampleRate = value1
  843. if Carla.gui is not None:
  844. Carla.gui.SampleRateChangedCallback.emit(value3)
  845. return
  846. if Carla.gui is None:
  847. print("WARNING: Got engine callback but UI is not ready : ", pluginId, value1, value2, value3, valueStr)
  848. return
  849. valueStr = charPtrToString(valueStr)
  850. if action == ENGINE_CALLBACK_DEBUG:
  851. Carla.gui.DebugCallback.emit(pluginId, value1, value2, value3, valueStr)
  852. elif action == ENGINE_CALLBACK_PLUGIN_ADDED:
  853. Carla.gui.PluginAddedCallback.emit(pluginId, valueStr)
  854. elif action == ENGINE_CALLBACK_PLUGIN_REMOVED:
  855. Carla.gui.PluginRemovedCallback.emit(pluginId)
  856. elif action == ENGINE_CALLBACK_PLUGIN_RENAMED:
  857. Carla.gui.PluginRenamedCallback.emit(pluginId, valueStr)
  858. elif action == ENGINE_CALLBACK_PLUGIN_UNAVAILABLE:
  859. Carla.gui.PluginUnavailableCallback.emit(pluginId, valueStr)
  860. elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
  861. Carla.gui.ParameterValueChangedCallback.emit(pluginId, value1, value3)
  862. elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
  863. Carla.gui.ParameterDefaultChangedCallback.emit(pluginId, value1, value3)
  864. elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  865. Carla.gui.ParameterMidiCcChangedCallback.emit(pluginId, value1, value2)
  866. elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  867. Carla.gui.ParameterMidiChannelChangedCallback.emit(pluginId, value1, value2)
  868. elif action == ENGINE_CALLBACK_PROGRAM_CHANGED:
  869. Carla.gui.ProgramChangedCallback.emit(pluginId, value1)
  870. elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED:
  871. Carla.gui.MidiProgramChangedCallback.emit(pluginId, value1)
  872. elif action == ENGINE_CALLBACK_UI_STATE_CHANGED:
  873. Carla.gui.UiStateChangedCallback.emit(pluginId, value1)
  874. elif action == ENGINE_CALLBACK_NOTE_ON:
  875. Carla.gui.NoteOnCallback.emit(pluginId, value1, value2, int(value3))
  876. elif action == ENGINE_CALLBACK_NOTE_OFF:
  877. Carla.gui.NoteOffCallback.emit(pluginId, value1, value2)
  878. elif action == ENGINE_CALLBACK_UPDATE:
  879. Carla.gui.UpdateCallback.emit(pluginId)
  880. elif action == ENGINE_CALLBACK_RELOAD_INFO:
  881. Carla.gui.ReloadInfoCallback.emit(pluginId)
  882. elif action == ENGINE_CALLBACK_RELOAD_PARAMETERS:
  883. Carla.gui.ReloadParametersCallback.emit(pluginId)
  884. elif action == ENGINE_CALLBACK_RELOAD_PROGRAMS:
  885. Carla.gui.ReloadProgramsCallback.emit(pluginId)
  886. elif action == ENGINE_CALLBACK_RELOAD_ALL:
  887. Carla.gui.ReloadAllCallback.emit(pluginId)
  888. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
  889. Carla.gui.PatchbayClientAddedCallback.emit(pluginId, value1, valueStr)
  890. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED:
  891. Carla.gui.PatchbayClientRemovedCallback.emit(pluginId)
  892. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_RENAMED:
  893. Carla.gui.PatchbayClientRenamedCallback.emit(pluginId, valueStr)
  894. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_ICON_CHANGED:
  895. Carla.gui.PatchbayClientIconChangedCallback.emit(pluginId, value1)
  896. elif action == ENGINE_CALLBACK_PATCHBAY_PORT_ADDED:
  897. Carla.gui.PatchbayPortAddedCallback.emit(pluginId, value1, value2, valueStr)
  898. elif action == ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED:
  899. Carla.gui.PatchbayPortRemovedCallback.emit(pluginId, value1)
  900. elif action == ENGINE_CALLBACK_PATCHBAY_PORT_RENAMED:
  901. Carla.gui.PatchbayPortRenamedCallback.emit(pluginId, value1, valueStr)
  902. elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED:
  903. Carla.gui.PatchbayConnectionAddedCallback.emit(pluginId, value1, value2)
  904. elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED:
  905. Carla.gui.PatchbayConnectionRemovedCallback.emit(pluginId, value1, value2)
  906. elif action == ENGINE_CALLBACK_ENGINE_STARTED:
  907. Carla.gui.EngineStartedCallback.emit(value1, value2, valueStr)
  908. elif action == ENGINE_CALLBACK_ENGINE_STOPPED:
  909. Carla.gui.EngineStoppedCallback.emit()
  910. elif action == ENGINE_CALLBACK_INFO:
  911. Carla.gui.InfoCallback.emit(valueStr)
  912. elif action == ENGINE_CALLBACK_ERROR:
  913. Carla.gui.ErrorCallback.emit(valueStr)
  914. elif action == ENGINE_CALLBACK_QUIT:
  915. Carla.gui.QuitCallback.emit()