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 50KB

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