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

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