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

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