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

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