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

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