Collection of tools useful for audio production
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.

2408 lines
93KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Cadence, JACK utilities
  4. # Copyright (C) 2010-2018 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # 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 COPYING file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from platform import architecture
  20. if True:
  21. from PyQt5.QtCore import QFileSystemWatcher, QThread, QSemaphore
  22. from PyQt5.QtWidgets import QApplication, QDialogButtonBox, QLabel, QMainWindow, QSizePolicy
  23. else:
  24. from PyQt4.QtCore import QFileSystemWatcher, QThread, QSemaphore
  25. from PyQt4.QtGui import QApplication, QDialogButtonBox, QLabel, QMainWindow, QSizePolicy
  26. # ------------------------------------------------------------------------------------------------------------
  27. # Imports (Custom Stuff)
  28. import systray
  29. import ui_cadence
  30. import ui_cadence_tb_jack
  31. import ui_cadence_tb_alsa
  32. import ui_cadence_tb_a2j
  33. import ui_cadence_tb_pa
  34. import ui_cadence_rwait
  35. from shared_cadence import *
  36. from shared_canvasjack import *
  37. from shared_settings import *
  38. # ------------------------------------------------------------------------------------------------------------
  39. # Import getoutput
  40. from subprocess import getoutput
  41. # ------------------------------------------------------------------------------------------------------------
  42. # Try Import DBus
  43. try:
  44. import dbus
  45. from dbus.mainloop.pyqt5 import DBusQtMainLoop
  46. haveDBus = True
  47. except:
  48. kxstudioWorkaround = "/opt/kxstudio/python3/dist-packages/dbus/mainloop"
  49. if os.path.exists(kxstudioWorkaround):
  50. try:
  51. sys.path.insert(0, kxstudioWorkaround)
  52. from pyqt5 import DBusQtMainLoop
  53. haveDBus = True
  54. except:
  55. haveDBus = False
  56. else:
  57. haveDBus = False
  58. # ------------------------------------------------------------------------------------------------------------
  59. # Check for PulseAudio and Wine
  60. havePulseAudio = os.path.exists("/usr/bin/pulseaudio")
  61. haveWine = os.path.exists("/usr/bin/regedit")
  62. if haveWine:
  63. WINEPREFIX = os.getenv("WINEPREFIX")
  64. if not WINEPREFIX:
  65. WINEPREFIX = os.path.join(HOME, ".wine")
  66. # ---------------------------------------------------------------------
  67. DESKTOP_X_IMAGE = [
  68. "eog.desktop",
  69. "kde4/digikam.desktop",
  70. "kde4/gwenview.desktop",
  71. "org.kde.digikam.desktop",
  72. "org.kde.gwenview.desktop",
  73. ]
  74. DESKTOP_X_MUSIC = [
  75. "audacious.desktop",
  76. "clementine.desktop",
  77. "smplayer.desktop",
  78. "vlc.desktop",
  79. "kde4/amarok.desktop",
  80. "org.kde.amarok.desktop",
  81. ]
  82. DESKTOP_X_VIDEO = [
  83. "smplayer.desktop",
  84. "vlc.desktop",
  85. ]
  86. DESKTOP_X_TEXT = [
  87. "gedit.desktop",
  88. "kde4/kate.desktop",
  89. "kde4/kwrite.desktop",
  90. "org.kde.kate.desktop",
  91. "org.kde.kwrite.desktop",
  92. ]
  93. DESKTOP_X_BROWSER = [
  94. "chrome.desktop",
  95. "firefox.desktop",
  96. "iceweasel.desktop",
  97. "kde4/konqbrowser.desktop",
  98. "org.kde.konqbrowser.desktop",
  99. ]
  100. XDG_APPLICATIONS_PATH = [
  101. "/usr/share/applications",
  102. "/usr/local/share/applications"
  103. ]
  104. WINEASIO_PREFIX = "HKEY_CURRENT_USER\Software\Wine\WineASIO"
  105. # ---------------------------------------------------------------------
  106. global jackClientIdALSA, jackClientIdPulse
  107. jackClientIdALSA = -1
  108. jackClientIdPulse = -1
  109. # jackdbus indexes
  110. iGraphVersion = 0
  111. iJackClientId = 1
  112. iJackClientName = 2
  113. iJackPortId = 3
  114. iJackPortName = 4
  115. iJackPortNewName = 5
  116. iJackPortFlags = 5
  117. iJackPortType = 6
  118. asoundrc_aloop = (""
  119. "# ------------------------------------------------------\n"
  120. "# Custom asoundrc file for use with snd-aloop and JACK\n"
  121. "#\n"
  122. "# use it like this:\n"
  123. "# env JACK_SAMPLE_RATE=44100 JACK_PERIOD_SIZE=1024 alsa_in (...)\n"
  124. "#\n"
  125. "\n"
  126. "# ------------------------------------------------------\n"
  127. "# playback device\n"
  128. "pcm.aloopPlayback {\n"
  129. " type dmix\n"
  130. " ipc_key 1\n"
  131. " ipc_key_add_uid true\n"
  132. " slave {\n"
  133. " pcm \"hw:Loopback,0,0\"\n"
  134. " format S32_LE\n"
  135. " rate {\n"
  136. " @func igetenv\n"
  137. " vars [ JACK_SAMPLE_RATE ]\n"
  138. " default 44100\n"
  139. " }\n"
  140. " period_size {\n"
  141. " @func igetenv\n"
  142. " vars [ JACK_PERIOD_SIZE ]\n"
  143. " default 1024\n"
  144. " }\n"
  145. " buffer_size 4096\n"
  146. " }\n"
  147. "}\n"
  148. "\n"
  149. "# capture device\n"
  150. "pcm.aloopCapture {\n"
  151. " type dsnoop\n"
  152. " ipc_key 2\n"
  153. " ipc_key_add_uid true\n"
  154. " slave {\n"
  155. " pcm \"hw:Loopback,0,1\"\n"
  156. " format S32_LE\n"
  157. " rate {\n"
  158. " @func igetenv\n"
  159. " vars [ JACK_SAMPLE_RATE ]\n"
  160. " default 44100\n"
  161. " }\n"
  162. " period_size {\n"
  163. " @func igetenv\n"
  164. " vars [ JACK_PERIOD_SIZE ]\n"
  165. " default 1024\n"
  166. " }\n"
  167. " buffer_size 4096\n"
  168. " }\n"
  169. "}\n"
  170. "\n"
  171. "# duplex device\n"
  172. "pcm.aloopDuplex {\n"
  173. " type asym\n"
  174. " playback.pcm \"aloopPlayback\"\n"
  175. " capture.pcm \"aloopCapture\"\n"
  176. "}\n"
  177. "\n"
  178. "# ------------------------------------------------------\n"
  179. "# default device\n"
  180. "pcm.!default {\n"
  181. " type plug\n"
  182. " slave.pcm \"aloopDuplex\"\n"
  183. "}\n"
  184. "\n"
  185. "# ------------------------------------------------------\n"
  186. "# alsa_in -j alsa_in -dcloop -q 1\n"
  187. "pcm.cloop {\n"
  188. " type dsnoop\n"
  189. " ipc_key 3\n"
  190. " ipc_key_add_uid true\n"
  191. " slave {\n"
  192. " pcm \"hw:Loopback,1,0\"\n"
  193. " channels 2\n"
  194. " format S32_LE\n"
  195. " rate {\n"
  196. " @func igetenv\n"
  197. " vars [ JACK_SAMPLE_RATE ]\n"
  198. " default 44100\n"
  199. " }\n"
  200. " period_size {\n"
  201. " @func igetenv\n"
  202. " vars [ JACK_PERIOD_SIZE ]\n"
  203. " default 1024\n"
  204. " }\n"
  205. " buffer_size 32768\n"
  206. " }\n"
  207. "}\n"
  208. "\n"
  209. "# ------------------------------------------------------\n"
  210. "# alsa_out -j alsa_out -dploop -q 1\n"
  211. "pcm.ploop {\n"
  212. " type plug\n"
  213. " slave.pcm \"hw:Loopback,1,1\"\n"
  214. "}")
  215. asoundrc_aloop_check = asoundrc_aloop.split("pcm.aloopPlayback", 1)[0]
  216. asoundrc_jack = (""
  217. "pcm.!default {\n"
  218. " type plug\n"
  219. " slave { pcm \"jack\" }\n"
  220. "}\n"
  221. "\n"
  222. "pcm.jack {\n"
  223. " type jack\n"
  224. " playback_ports {\n"
  225. " 0 system:playback_1\n"
  226. " 1 system:playback_2\n"
  227. " }\n"
  228. " capture_ports {\n"
  229. " 0 system:capture_1\n"
  230. " 1 system:capture_2\n"
  231. " }\n"
  232. "}\n"
  233. "\n"
  234. "ctl.mixer0 {\n"
  235. " type hw\n"
  236. " card 0\n"
  237. "}")
  238. asoundrc_pulse = (""
  239. "pcm.!default {\n"
  240. " type plug\n"
  241. " slave { pcm \"pulse\" }\n"
  242. "}\n"
  243. "\n"
  244. "pcm.pulse {\n"
  245. " type pulse\n"
  246. "}\n"
  247. "\n"
  248. "ctl.mixer0 {\n"
  249. " type hw\n"
  250. " card 0\n"
  251. "}")
  252. # ---------------------------------------------------------------------
  253. def get_architecture():
  254. return architecture()[0]
  255. def get_haiku_information():
  256. # TODO
  257. return ("Haiku OS", "Unknown")
  258. def get_linux_information():
  259. if os.path.exists("/etc/lsb-release"):
  260. distro = getoutput(". /etc/lsb-release && echo $DISTRIB_DESCRIPTION")
  261. elif os.path.exists("/etc/arch-release"):
  262. distro = "ArchLinux"
  263. else:
  264. distro = os.uname()[0]
  265. kernel = os.uname()[2]
  266. return (distro, kernel)
  267. def get_mac_information():
  268. # TODO
  269. return ("Mac OS", "Unknown")
  270. def get_windows_information():
  271. major = sys.getwindowsversion()[0]
  272. minor = sys.getwindowsversion()[1]
  273. servp = sys.getwindowsversion()[4]
  274. os = "Windows"
  275. version = servp
  276. if major == 4 and minor == 0:
  277. os = "Windows 95"
  278. version = "RTM"
  279. elif major == 4 and minor == 10:
  280. os = "Windows 98"
  281. version = "Second Edition"
  282. elif major == 5 and minor == 0:
  283. os = "Windows 2000"
  284. elif major == 5 and minor == 1:
  285. os = "Windows XP"
  286. elif major == 5 and minor == 2:
  287. os = "Windows Server 2003"
  288. elif major == 6 and minor == 0:
  289. os = "Windows Vista"
  290. elif major == 6 and minor == 1:
  291. os = "Windows 7"
  292. elif major == 6 and minor == 2:
  293. os = "Windows 8"
  294. return (os, version)
  295. # ---------------------------------------------------------------------
  296. def isAlsaAudioBridged():
  297. global jackClientIdALSA
  298. return bool(jackClientIdALSA != -1)
  299. def isPulseAudioStarted():
  300. return bool("pulseaudio" in getProcList())
  301. def isPulseAudioBridged():
  302. global jackClientIdPulse
  303. return bool(jackClientIdPulse != -1)
  304. def isDesktopFileInstalled(desktop):
  305. for X_PATH in XDG_APPLICATIONS_PATH:
  306. if os.path.exists(os.path.join(X_PATH, desktop)):
  307. return True
  308. return False
  309. def getDesktopFileContents(desktop):
  310. for X_PATH in XDG_APPLICATIONS_PATH:
  311. if os.path.exists(os.path.join(X_PATH, desktop)):
  312. fd = open(os.path.join(X_PATH, desktop), "r")
  313. contents = fd.read()
  314. fd.close()
  315. return contents
  316. return None
  317. def getXdgProperty(fileRead, key):
  318. fileReadSplit = fileRead.split(key, 1)
  319. if len(fileReadSplit) > 1:
  320. fileReadLine = fileReadSplit[1].split("\n",1)[0]
  321. fileReadLineStripped = fileReadLine.rsplit(";",1)[0].strip()
  322. value = fileReadLineStripped.replace("=","",1)
  323. return value
  324. return None
  325. def getWineAsioKeyValue(key, default):
  326. wineFile = os.path.join(WINEPREFIX, "user.reg")
  327. if not os.path.exists(wineFile):
  328. return default
  329. wineDumpF = open(wineFile, "r")
  330. wineDump = wineDumpF.read()
  331. wineDumpF.close()
  332. wineDumpSplit = wineDump.split("[Software\\\\Wine\\\\WineASIO]")
  333. if len(wineDumpSplit) <= 1:
  334. return default
  335. wineDumpSmall = wineDumpSplit[1].split("[")[0]
  336. keyDumpSplit = wineDumpSmall.split('"%s"' % key)
  337. if len(keyDumpSplit) <= 1:
  338. return default
  339. keyDumpSmall = keyDumpSplit[1].split(":")[1].split("\n")[0]
  340. return keyDumpSmall
  341. def searchAndSetComboBoxValue(comboBox, value):
  342. for i in range(comboBox.count()):
  343. if comboBox.itemText(i).replace("/","-") == value:
  344. comboBox.setCurrentIndex(i)
  345. comboBox.setEnabled(True)
  346. return True
  347. return False
  348. def smartHex(value, length):
  349. hexStr = hex(value).replace("0x","")
  350. if len(hexStr) < length:
  351. zeroCount = length - len(hexStr)
  352. hexStr = "%s%s" % ("0"*zeroCount, hexStr)
  353. return hexStr
  354. # ---------------------------------------------------------------------
  355. cadenceSystemChecks = []
  356. class CadenceSystemCheck(object):
  357. ICON_ERROR = 0
  358. ICON_WARN = 1
  359. ICON_OK = 2
  360. def __init__(self):
  361. object.__init__(self)
  362. self.name = self.tr("check")
  363. self.icon = self.ICON_OK
  364. self.result = self.tr("yes")
  365. self.moreInfo = self.tr("nothing to report")
  366. def tr(self, text):
  367. return app.translate("CadenceSystemCheck", text)
  368. class CadenceSystemCheck_audioGroup(CadenceSystemCheck):
  369. def __init__(self):
  370. CadenceSystemCheck.__init__(self)
  371. self.name = self.tr("User in audio group")
  372. user = getoutput("whoami").strip()
  373. groups = getoutput("groups").strip().split(" ")
  374. if "audio" in groups:
  375. self.icon = self.ICON_OK
  376. self.result = self.tr("Yes")
  377. self.moreInfo = None
  378. else:
  379. fd = open("/etc/group", "r")
  380. groupRead = fd.read().strip().split("\n")
  381. fd.close()
  382. onAudioGroup = False
  383. for lineRead in groupRead:
  384. if lineRead.startswith("audio:"):
  385. groups = lineRead.split(":")[-1].split(",")
  386. if user in groups:
  387. onAudioGroup = True
  388. break
  389. if onAudioGroup:
  390. self.icon = self.ICON_WARN
  391. self.result = self.tr("Yes, but needs relogin")
  392. self.moreInfo = None
  393. else:
  394. self.icon = self.ICON_ERROR
  395. self.result = self.tr("No")
  396. self.moreInfo = None
  397. class CadenceSystemCheck_kernel(CadenceSystemCheck):
  398. def __init__(self):
  399. CadenceSystemCheck.__init__(self)
  400. self.name = self.tr("Current kernel")
  401. uname3 = os.uname()[2]
  402. versionInt = []
  403. versionStr = uname3.split("-",1)[0]
  404. versionSplit = versionStr.split(".")
  405. for split in versionSplit:
  406. if split.isdigit():
  407. versionInt.append(int(split))
  408. else:
  409. versionInt = [0, 0, 0]
  410. break
  411. self.result = versionStr + " "
  412. if "-" not in uname3:
  413. self.icon = self.ICON_WARN
  414. self.result += self.tr("Vanilla")
  415. self.moreInfo = None
  416. else:
  417. if uname3.endswith("-pae"):
  418. kernelType = uname3.split("-")[-2].lower()
  419. self.result += kernelType.title() + " (PAE)"
  420. else:
  421. kernelType = uname3.split("-")[-1].lower()
  422. self.result += kernelType.title()
  423. if kernelType in ("rt", "realtime") or (kernelType == "lowlatency" and versionInt >= [2, 6, 39]):
  424. self.icon = self.ICON_OK
  425. self.moreInfo = None
  426. elif versionInt >= [2, 6, 39]:
  427. self.icon = self.ICON_WARN
  428. self.moreInfo = None
  429. else:
  430. self.icon = self.ICON_ERROR
  431. self.moreInfo = None
  432. def initSystemChecks():
  433. if LINUX:
  434. cadenceSystemChecks.append(CadenceSystemCheck_kernel())
  435. cadenceSystemChecks.append(CadenceSystemCheck_audioGroup())
  436. # ---------------------------------------------------------------------
  437. # Wait while JACK restarts
  438. class ForceRestartThread(QThread):
  439. progressChanged = pyqtSignal(int)
  440. def __init__(self, parent):
  441. QThread.__init__(self, parent)
  442. self.m_wasStarted = False
  443. self.m_a2jExportHW = False
  444. def wasJackStarted(self):
  445. return self.m_wasStarted
  446. def startA2J(self):
  447. gDBus.a2j.set_hw_export(self.m_a2jExportHW)
  448. gDBus.a2j.start()
  449. def run(self):
  450. # Not started yet
  451. self.m_wasStarted = False
  452. self.progressChanged.emit(0)
  453. # Stop JACK safely first, if possible
  454. runFunctionInMainThread(tryCloseJackDBus)
  455. self.progressChanged.emit(20)
  456. # Kill All
  457. stopAllAudioProcesses(False)
  458. self.progressChanged.emit(30)
  459. # Connect to jackdbus
  460. runFunctionInMainThread(self.parent().DBusReconnect)
  461. if not gDBus.jack:
  462. return
  463. for x in range(30):
  464. self.progressChanged.emit(30+x*2)
  465. procsList = getProcList()
  466. if "jackdbus" in procsList:
  467. break
  468. else:
  469. sleep(0.1)
  470. self.progressChanged.emit(90)
  471. # Start it
  472. runFunctionInMainThread(gDBus.jack.StartServer)
  473. self.progressChanged.emit(93)
  474. # If we made it this far, then JACK is started
  475. self.m_wasStarted = True
  476. # Start bridges according to user settings
  477. # ALSA-Audio
  478. if GlobalSettings.value("ALSA-Audio/BridgeIndexType", iAlsaFileNone, type=int) == iAlsaFileLoop:
  479. startAlsaAudioLoopBridge()
  480. sleep(0.5)
  481. self.progressChanged.emit(94)
  482. # ALSA-MIDI
  483. if GlobalSettings.value("A2J/AutoStart", True, type=bool) and gDBus.a2j and not bool(gDBus.a2j.is_started()):
  484. self.m_a2jExportHW = GlobalSettings.value("A2J/ExportHW", True, type=bool)
  485. runFunctionInMainThread(self.startA2J)
  486. self.progressChanged.emit(96)
  487. # PulseAudio
  488. if GlobalSettings.value("Pulse2JACK/AutoStart", True, type=bool) and not isPulseAudioBridged():
  489. if GlobalSettings.value("Pulse2JACK/PlaybackModeOnly", False, type=bool):
  490. os.system("cadence-pulse2jack -p")
  491. else:
  492. os.system("cadence-pulse2jack")
  493. self.progressChanged.emit(100)
  494. # Force Restart Dialog
  495. class ForceWaitDialog(QDialog, ui_cadence_rwait.Ui_Dialog):
  496. def __init__(self, parent):
  497. QDialog.__init__(self, parent)
  498. self.setupUi(self)
  499. self.setWindowFlags(Qt.Dialog|Qt.WindowCloseButtonHint)
  500. self.rThread = ForceRestartThread(self)
  501. self.rThread.start()
  502. self.rThread.progressChanged.connect(self.progressBar.setValue)
  503. self.rThread.finished.connect(self.slot_rThreadFinished)
  504. def DBusReconnect(self):
  505. self.parent().DBusReconnect()
  506. @pyqtSlot()
  507. def slot_rThreadFinished(self):
  508. self.close()
  509. if self.rThread.wasJackStarted():
  510. QMessageBox.information(self, self.tr("Info"), self.tr("JACK was re-started sucessfully"))
  511. else:
  512. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not start JACK!"))
  513. def done(self, r):
  514. QDialog.done(self, r)
  515. self.close()
  516. # Additional JACK options
  517. class ToolBarJackDialog(QDialog, ui_cadence_tb_jack.Ui_Dialog):
  518. def __init__(self, parent):
  519. QDialog.__init__(self, parent)
  520. self.setupUi(self)
  521. self.m_ladishLoaded = False
  522. if haveDBus:
  523. if GlobalSettings.value("JACK/AutoLoadLadishStudio", False, type=bool):
  524. self.rb_ladish.setChecked(True)
  525. self.m_ladishLoaded = True
  526. elif "org.ladish" in gDBus.bus.list_names():
  527. self.m_ladishLoaded = True
  528. else:
  529. self.rb_ladish.setEnabled(False)
  530. self.rb_jack.setChecked(True)
  531. if self.m_ladishLoaded:
  532. self.fillStudioNames()
  533. self.accepted.connect(self.slot_setOptions)
  534. self.rb_ladish.clicked.connect(self.slot_maybeFillStudioNames)
  535. def fillStudioNames(self):
  536. gDBus.ladish_control = gDBus.bus.get_object("org.ladish", "/org/ladish/Control")
  537. ladishStudioName = dbus.String(GlobalSettings.value("JACK/LadishStudioName", "", type=str))
  538. ladishStudioListDump = gDBus.ladish_control.GetStudioList()
  539. if len(ladishStudioListDump) == 0:
  540. self.rb_ladish.setEnabled(False)
  541. self.rb_jack.setChecked(True)
  542. else:
  543. i=0
  544. for thisStudioName, thisStudioDict in ladishStudioListDump:
  545. self.cb_studio_name.addItem(thisStudioName)
  546. if ladishStudioName and thisStudioName == ladishStudioName:
  547. self.cb_studio_name.setCurrentIndex(i)
  548. i += 1
  549. @pyqtSlot()
  550. def slot_maybeFillStudioNames(self):
  551. if not self.m_ladishLoaded:
  552. self.fillStudioNames()
  553. self.m_ladishLoaded = True
  554. @pyqtSlot()
  555. def slot_setOptions(self):
  556. GlobalSettings.setValue("JACK/AutoLoadLadishStudio", self.rb_ladish.isChecked())
  557. GlobalSettings.setValue("JACK/LadishStudioName", self.cb_studio_name.currentText())
  558. def done(self, r):
  559. QDialog.done(self, r)
  560. self.close()
  561. # Additional ALSA Audio options
  562. class ToolBarAlsaAudioDialog(QDialog, ui_cadence_tb_alsa.Ui_Dialog):
  563. def __init__(self, parent, customMode):
  564. QDialog.__init__(self, parent)
  565. self.setupUi(self)
  566. self.asoundrcFile = os.path.join(HOME, ".asoundrc")
  567. self.fCustomMode = customMode
  568. if customMode:
  569. asoundrcFd = open(self.asoundrcFile, "r")
  570. asoundrcRead = asoundrcFd.read().strip()
  571. asoundrcFd.close()
  572. self.textBrowser.setPlainText(asoundrcRead)
  573. self.stackedWidget.setCurrentIndex(0)
  574. self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
  575. else:
  576. self.textBrowser.hide()
  577. self.stackedWidget.setCurrentIndex(1)
  578. self.adjustSize()
  579. self.spinBox.setValue(GlobalSettings.value("ALSA-Audio/BridgeChannels", 2, type=int))
  580. if GlobalSettings.value("ALSA-Audio/BridgeTool", "alsa_in", type=str) == "zita":
  581. self.comboBox.setCurrentIndex(1)
  582. else:
  583. self.comboBox.setCurrentIndex(0)
  584. self.accepted.connect(self.slot_setOptions)
  585. @pyqtSlot()
  586. def slot_setOptions(self):
  587. channels = self.spinBox.value()
  588. GlobalSettings.setValue("ALSA-Audio/BridgeChannels", channels)
  589. GlobalSettings.setValue("ALSA-Audio/BridgeTool", "zita" if (self.comboBox.currentIndex() == 1) else "alsa_in")
  590. asoundrcFd = open(self.asoundrcFile, "w")
  591. asoundrcFd.write(asoundrc_aloop.replace("channels 2\n", "channels %i\n" % channels) + "\n")
  592. asoundrcFd.close()
  593. def done(self, r):
  594. QDialog.done(self, r)
  595. self.close()
  596. # Additional ALSA MIDI options
  597. class ToolBarA2JDialog(QDialog, ui_cadence_tb_a2j.Ui_Dialog):
  598. def __init__(self, parent):
  599. QDialog.__init__(self, parent)
  600. self.setupUi(self)
  601. self.cb_export_hw.setChecked(GlobalSettings.value("A2J/ExportHW", True, type=bool))
  602. self.accepted.connect(self.slot_setOptions)
  603. @pyqtSlot()
  604. def slot_setOptions(self):
  605. GlobalSettings.setValue("A2J/ExportHW", self.cb_export_hw.isChecked())
  606. def done(self, r):
  607. QDialog.done(self, r)
  608. self.close()
  609. # Additional PulseAudio options
  610. class ToolBarPADialog(QDialog, ui_cadence_tb_pa.Ui_Dialog):
  611. def __init__(self, parent):
  612. QDialog.__init__(self, parent)
  613. self.setupUi(self)
  614. self.cb_playback_only.setChecked(GlobalSettings.value("Pulse2JACK/PlaybackModeOnly", False, type=bool))
  615. self.accepted.connect(self.slot_setOptions)
  616. @pyqtSlot()
  617. def slot_setOptions(self):
  618. GlobalSettings.setValue("Pulse2JACK/PlaybackModeOnly", self.cb_playback_only.isChecked())
  619. def done(self, r):
  620. QDialog.done(self, r)
  621. self.close()
  622. # Main Window
  623. class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW):
  624. DBusJackServerStartedCallback = pyqtSignal()
  625. DBusJackServerStoppedCallback = pyqtSignal()
  626. DBusJackClientAppearedCallback = pyqtSignal(int, str)
  627. DBusJackClientDisappearedCallback = pyqtSignal(int)
  628. DBusA2JBridgeStartedCallback = pyqtSignal()
  629. DBusA2JBridgeStoppedCallback = pyqtSignal()
  630. SIGTERM = pyqtSignal()
  631. SIGUSR1 = pyqtSignal()
  632. SIGUSR2 = pyqtSignal()
  633. def __init__(self, parent=None):
  634. QMainWindow.__init__(self, parent)
  635. self.setupUi(self)
  636. self.settings = QSettings("Cadence", "Cadence")
  637. self.loadSettings(True)
  638. self.pix_apply = QIcon(getIcon("dialog-ok-apply", 16)).pixmap(16, 16)
  639. self.pix_cancel = QIcon(getIcon("dialog-cancel", 16)).pixmap(16, 16)
  640. self.pix_error = QIcon(getIcon("dialog-error", 16)).pixmap(16, 16)
  641. self.pix_warning = QIcon(getIcon("dialog-warning", 16)).pixmap(16, 16)
  642. self.m_lastAlsaIndexType = -2 # invalid
  643. if jacklib and not jacklib.JACK2:
  644. self.b_jack_switchmaster.setEnabled(False)
  645. # -------------------------------------------------------------
  646. # Set-up GUI (System Information)
  647. if HAIKU:
  648. info = get_haiku_information()
  649. elif LINUX:
  650. info = get_linux_information()
  651. elif MACOS:
  652. info = get_mac_information()
  653. elif WINDOWS:
  654. info = get_windows_information()
  655. else:
  656. info = ("Unknown", "Unknown")
  657. self.label_info_os.setText(info[0])
  658. self.label_info_version.setText(info[1])
  659. self.label_info_arch.setText(get_architecture())
  660. # -------------------------------------------------------------
  661. # Set-up GUI (System Status)
  662. self.m_availGovPath = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors"
  663. self.m_curGovPath = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
  664. self.m_curGovPaths = []
  665. self.m_curGovCPUs = []
  666. try:
  667. fBus = dbus.SystemBus(mainloop=gDBus.loop)
  668. fProxy = fBus.get_object("com.ubuntu.IndicatorCpufreqSelector", "/Selector", introspect=False)
  669. haveFreqSelector = True
  670. except:
  671. haveFreqSelector = False
  672. if haveFreqSelector and os.path.exists(self.m_availGovPath) and os.path.exists(self.m_curGovPath):
  673. self.m_govWatcher = QFileSystemWatcher(self)
  674. self.m_govWatcher.addPath(self.m_curGovPath)
  675. self.m_govWatcher.fileChanged.connect(self.slot_governorFileChanged)
  676. QTimer.singleShot(0, self.slot_governorFileChanged)
  677. availGovFd = open(self.m_availGovPath, "r")
  678. availGovRead = availGovFd.read().strip()
  679. availGovFd.close()
  680. self.m_availGovList = availGovRead.split(" ")
  681. for availGov in self.m_availGovList:
  682. self.cb_cpufreq.addItem(availGov)
  683. for root, dirs, files in os.walk("/sys/devices/system/cpu/"):
  684. for dir_ in [dir_ for dir_ in dirs if dir_.startswith("cpu")]:
  685. if not dir_.replace("cpu", "", 1).isdigit():
  686. continue
  687. cpuGovPath = os.path.join(root, dir_, "cpufreq", "scaling_governor")
  688. if os.path.exists(cpuGovPath):
  689. self.m_curGovPaths.append(cpuGovPath)
  690. self.m_curGovCPUs.append(int(dir_.replace("cpu", "", 1)))
  691. self.cb_cpufreq.setCurrentIndex(-1)
  692. else:
  693. self.m_govWatcher = None
  694. self.cb_cpufreq.setEnabled(False)
  695. self.label_cpufreq.setEnabled(False)
  696. # -------------------------------------------------------------
  697. # Set-up GUI (System Checks)
  698. #self.label_check_helper1.setVisible(False)
  699. #self.label_check_helper2.setVisible(False)
  700. #self.label_check_helper3.setVisible(False)
  701. index = 2
  702. checksLayout = self.groupBox_checks.layout()
  703. for check in cadenceSystemChecks:
  704. widgetName = QLabel("%s:" % check.name)
  705. widgetIcon = QLabel("")
  706. widgetResult = QLabel(check.result)
  707. if check.moreInfo:
  708. widgetName.setToolTip(check.moreInfo)
  709. widgetIcon.setToolTip(check.moreInfo)
  710. widgetResult.setToolTip(check.moreInfo)
  711. #widgetName.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
  712. #widgetIcon.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
  713. #widgetIcon.setMinimumSize(16, 16)
  714. #widgetIcon.setMaximumSize(16, 16)
  715. #widgetResult.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
  716. if check.icon == check.ICON_ERROR:
  717. widgetIcon.setPixmap(self.pix_error)
  718. elif check.icon == check.ICON_WARN:
  719. widgetIcon.setPixmap(self.pix_warning)
  720. elif check.icon == check.ICON_OK:
  721. widgetIcon.setPixmap(self.pix_apply)
  722. else:
  723. widgetIcon.setPixmap(self.pix_cancel)
  724. checksLayout.addWidget(widgetName, index, 0, Qt.AlignRight)
  725. checksLayout.addWidget(widgetIcon, index, 1, Qt.AlignHCenter)
  726. checksLayout.addWidget(widgetResult, index, 2, Qt.AlignLeft)
  727. index += 1
  728. # -------------------------------------------------------------
  729. # Set-up GUI (JACK Bridges)
  730. if not havePulseAudio:
  731. self.toolBox_pulseaudio.setEnabled(False)
  732. self.label_bridge_pulse.setText(self.tr("PulseAudio is not installed"))
  733. # Not available in cxfreeze builds
  734. if sys.argv[0].endswith("/cadence"):
  735. self.groupBox_bridges.setEnabled(False)
  736. self.cb_jack_autostart.setEnabled(False)
  737. self.tb_jack_options.setEnabled(False)
  738. # -------------------------------------------------------------
  739. # Set-up GUI (Tweaks)
  740. self.settings_changed_types = []
  741. self.frame_tweaks_settings.setVisible(False)
  742. for i in range(self.tw_tweaks.rowCount()):
  743. self.tw_tweaks.item(i, 0).setTextAlignment(Qt.AlignCenter)
  744. self.tw_tweaks.setCurrentCell(0, 0)
  745. # -------------------------------------------------------------
  746. # Set-up GUI (Tweaks, Audio Plugins PATH)
  747. self.b_tweak_plugins_change.setEnabled(False)
  748. self.b_tweak_plugins_remove.setEnabled(False)
  749. for iPath in DEFAULT_LADSPA_PATH:
  750. self.list_LADSPA.addItem(iPath)
  751. for iPath in DEFAULT_DSSI_PATH:
  752. self.list_DSSI.addItem(iPath)
  753. for iPath in DEFAULT_LV2_PATH:
  754. self.list_LV2.addItem(iPath)
  755. for iPath in DEFAULT_VST_PATH:
  756. self.list_VST.addItem(iPath)
  757. EXTRA_LADSPA_DIRS = GlobalSettings.value("AudioPlugins/EXTRA_LADSPA_PATH", "", type=str)
  758. EXTRA_DSSI_DIRS = GlobalSettings.value("AudioPlugins/EXTRA_DSSI_PATH", "", type=str)
  759. EXTRA_LV2_DIRS = GlobalSettings.value("AudioPlugins/EXTRA_LV2_PATH", "", type=str)
  760. EXTRA_VST_DIRS = GlobalSettings.value("AudioPlugins/EXTRA_VST_PATH", "", type=str)
  761. for iPath in EXTRA_LADSPA_DIRS.split(":"):
  762. if os.path.exists(iPath):
  763. self.list_LADSPA.addItem(iPath)
  764. for iPath in EXTRA_DSSI_DIRS.split(":"):
  765. if os.path.exists(iPath):
  766. self.list_DSSI.addItem(iPath)
  767. for iPath in EXTRA_LV2_DIRS.split(":"):
  768. if os.path.exists(iPath):
  769. self.list_LV2.addItem(iPath)
  770. for iPath in EXTRA_VST_DIRS.split(":"):
  771. if os.path.exists(iPath):
  772. self.list_VST.addItem(iPath)
  773. self.list_LADSPA.sortItems(Qt.AscendingOrder)
  774. self.list_DSSI.sortItems(Qt.AscendingOrder)
  775. self.list_LV2.sortItems(Qt.AscendingOrder)
  776. self.list_VST.sortItems(Qt.AscendingOrder)
  777. self.list_LADSPA.setCurrentRow(0)
  778. self.list_DSSI.setCurrentRow(0)
  779. self.list_LV2.setCurrentRow(0)
  780. self.list_VST.setCurrentRow(0)
  781. # -------------------------------------------------------------
  782. # Set-up GUI (Tweaks, Default Applications)
  783. for desktop in DESKTOP_X_IMAGE:
  784. if isDesktopFileInstalled(desktop):
  785. self.cb_app_image.addItem(desktop)
  786. for desktop in DESKTOP_X_MUSIC:
  787. if isDesktopFileInstalled(desktop):
  788. self.cb_app_music.addItem(desktop)
  789. for desktop in DESKTOP_X_VIDEO:
  790. if isDesktopFileInstalled(desktop):
  791. self.cb_app_video.addItem(desktop)
  792. for desktop in DESKTOP_X_TEXT:
  793. if isDesktopFileInstalled(desktop):
  794. self.cb_app_text.addItem(desktop)
  795. for desktop in DESKTOP_X_BROWSER:
  796. if isDesktopFileInstalled(desktop):
  797. self.cb_app_browser.addItem(desktop)
  798. if self.cb_app_image.count() == 0:
  799. self.ch_app_image.setEnabled(False)
  800. if self.cb_app_music.count() == 0:
  801. self.ch_app_music.setEnabled(False)
  802. if self.cb_app_video.count() == 0:
  803. self.ch_app_video.setEnabled(False)
  804. if self.cb_app_text.count() == 0:
  805. self.ch_app_text.setEnabled(False)
  806. if self.cb_app_browser.count() == 0:
  807. self.ch_app_browser.setEnabled(False)
  808. mimeappsPath = os.path.join(HOME, ".local", "share", "applications", "mimeapps.list")
  809. if os.path.exists(mimeappsPath):
  810. fd = open(mimeappsPath, "r")
  811. mimeappsRead = fd.read()
  812. fd.close()
  813. x_image = getXdgProperty(mimeappsRead, "image/bmp")
  814. x_music = getXdgProperty(mimeappsRead, "audio/wav")
  815. x_video = getXdgProperty(mimeappsRead, "video/webm")
  816. x_text = getXdgProperty(mimeappsRead, "text/plain")
  817. x_browser = getXdgProperty(mimeappsRead, "text/html")
  818. if x_image and searchAndSetComboBoxValue(self.cb_app_image, x_image):
  819. self.ch_app_image.setChecked(True)
  820. if x_music and searchAndSetComboBoxValue(self.cb_app_music, x_music):
  821. self.ch_app_music.setChecked(True)
  822. if x_video and searchAndSetComboBoxValue(self.cb_app_video, x_video):
  823. self.ch_app_video.setChecked(True)
  824. if x_text and searchAndSetComboBoxValue(self.cb_app_text, x_text):
  825. self.ch_app_text.setChecked(True)
  826. if x_browser and searchAndSetComboBoxValue(self.cb_app_browser, x_browser):
  827. self.ch_app_browser.setChecked(True)
  828. else: # ~/.local/share/applications/mimeapps.list doesn't exist
  829. if not os.path.exists(os.path.join(HOME, ".local")):
  830. os.mkdir(os.path.join(HOME, ".local"))
  831. elif not os.path.exists(os.path.join(HOME, ".local", "share")):
  832. os.mkdir(os.path.join(HOME, ".local", "share"))
  833. elif not os.path.exists(os.path.join(HOME, ".local", "share", "applications")):
  834. os.mkdir(os.path.join(HOME, ".local", "share", "applications"))
  835. # -------------------------------------------------------------
  836. # Set-up GUI (Tweaks, WineASIO)
  837. if haveWine:
  838. ins = int(getWineAsioKeyValue("Number of inputs", "00000010"), 16)
  839. outs = int(getWineAsioKeyValue("Number of outputs", "00000010"), 16)
  840. hw = bool(int(getWineAsioKeyValue("Connect to hardware", "00000001"), 10))
  841. autostart = bool(int(getWineAsioKeyValue("Autostart server", "00000000"), 10))
  842. fixed_bsize = bool(int(getWineAsioKeyValue("Fixed buffersize", "00000001"), 10))
  843. prefer_bsize = int(getWineAsioKeyValue("Preferred buffersize", "00000400"), 16)
  844. for bsize in BUFFER_SIZE_LIST:
  845. self.cb_wineasio_bsizes.addItem(str(bsize))
  846. if bsize == prefer_bsize:
  847. self.cb_wineasio_bsizes.setCurrentIndex(self.cb_wineasio_bsizes.count()-1)
  848. self.sb_wineasio_ins.setValue(ins)
  849. self.sb_wineasio_outs.setValue(outs)
  850. self.cb_wineasio_hw.setChecked(hw)
  851. self.cb_wineasio_autostart.setChecked(autostart)
  852. self.cb_wineasio_fixed_bsize.setChecked(fixed_bsize)
  853. else:
  854. # No Wine
  855. self.tw_tweaks.hideRow(2)
  856. # -------------------------------------------------------------
  857. # Set-up systray
  858. self.systray = systray.GlobalSysTray(self, "Cadence", "cadence")
  859. if haveDBus:
  860. self.systray.addAction("jack_start", self.tr("Start JACK"))
  861. self.systray.addAction("jack_stop", self.tr("Stop JACK"))
  862. self.systray.addAction("jack_configure", self.tr("Configure JACK"))
  863. self.systray.addSeparator("sep1")
  864. self.systray.addMenu("alsa", self.tr("ALSA Audio Bridge"))
  865. self.systray.addMenuAction("alsa", "alsa_start", self.tr("Start"))
  866. self.systray.addMenuAction("alsa", "alsa_stop", self.tr("Stop"))
  867. self.systray.addMenu("a2j", self.tr("ALSA MIDI Bridge"))
  868. self.systray.addMenuAction("a2j", "a2j_start", self.tr("Start"))
  869. self.systray.addMenuAction("a2j", "a2j_stop", self.tr("Stop"))
  870. self.systray.addMenuAction("a2j", "a2j_export_hw", self.tr("Export Hardware Ports..."))
  871. self.systray.addMenu("pulse", self.tr("PulseAudio Bridge"))
  872. self.systray.addMenuAction("pulse", "pulse_start", self.tr("Start"))
  873. self.systray.addMenuAction("pulse", "pulse_stop", self.tr("Stop"))
  874. self.systray.setActionIcon("jack_start", "media-playback-start")
  875. self.systray.setActionIcon("jack_stop", "media-playback-stop")
  876. self.systray.setActionIcon("jack_configure", "configure")
  877. self.systray.setActionIcon("alsa_start", "media-playback-start")
  878. self.systray.setActionIcon("alsa_stop", "media-playback-stop")
  879. self.systray.setActionIcon("a2j_start", "media-playback-start")
  880. self.systray.setActionIcon("a2j_stop", "media-playback-stop")
  881. self.systray.setActionIcon("pulse_start", "media-playback-start")
  882. self.systray.setActionIcon("pulse_stop", "media-playback-stop")
  883. self.systray.connect("jack_start", self.slot_JackServerStart)
  884. self.systray.connect("jack_stop", self.slot_JackServerStop)
  885. self.systray.connect("jack_configure", self.slot_JackServerConfigure)
  886. self.systray.connect("alsa_start", self.slot_AlsaBridgeStart)
  887. self.systray.connect("alsa_stop", self.slot_AlsaBridgeStop)
  888. self.systray.connect("a2j_start", self.slot_A2JBridgeStart)
  889. self.systray.connect("a2j_stop", self.slot_A2JBridgeStop)
  890. self.systray.connect("a2j_export_hw", self.slot_A2JBridgeExportHW)
  891. self.systray.connect("pulse_start", self.slot_PulseAudioBridgeStart)
  892. self.systray.connect("pulse_stop", self.slot_PulseAudioBridgeStop)
  893. self.systray.addMenu("tools", self.tr("Tools"))
  894. self.systray.addMenuAction("tools", "app_catarina", "Catarina")
  895. self.systray.addMenuAction("tools", "app_catia", "Catia")
  896. self.systray.addMenuAction("tools", "app_claudia", "Claudia")
  897. self.systray.addMenuSeparator("tools", "tools_sep")
  898. self.systray.addMenuAction("tools", "app_logs", "Logs")
  899. self.systray.addMenuAction("tools", "app_meter_in", "Meter (Inputs)")
  900. self.systray.addMenuAction("tools", "app_meter_out", "Meter (Output)")
  901. self.systray.addMenuAction("tools", "app_render", "Render")
  902. self.systray.addMenuAction("tools", "app_xy-controller", "XY-Controller")
  903. self.systray.addSeparator("sep2")
  904. self.systray.connect("app_catarina", self.func_start_catarina)
  905. self.systray.connect("app_catia", self.func_start_catia)
  906. self.systray.connect("app_claudia", self.func_start_claudia)
  907. self.systray.connect("app_logs", self.func_start_logs)
  908. self.systray.connect("app_meter_in", self.func_start_jackmeter_in)
  909. self.systray.connect("app_meter_out", self.func_start_jackmeter)
  910. self.systray.connect("app_render", self.func_start_render)
  911. self.systray.connect("app_xy-controller", self.func_start_xycontroller)
  912. self.systray.setToolTip("Cadence")
  913. self.systray.show()
  914. # -------------------------------------------------------------
  915. # Set-up connections
  916. self.b_jack_start.clicked.connect(self.slot_JackServerStart)
  917. self.b_jack_stop.clicked.connect(self.slot_JackServerStop)
  918. self.b_jack_restart.clicked.connect(self.slot_JackServerForceRestart)
  919. self.b_jack_configure.clicked.connect(self.slot_JackServerConfigure)
  920. self.b_jack_switchmaster.clicked.connect(self.slot_JackServerSwitchMaster)
  921. self.tb_jack_options.clicked.connect(self.slot_JackOptions)
  922. self.b_alsa_start.clicked.connect(self.slot_AlsaBridgeStart)
  923. self.b_alsa_stop.clicked.connect(self.slot_AlsaBridgeStop)
  924. self.cb_alsa_type.currentIndexChanged[int].connect(self.slot_AlsaBridgeChanged)
  925. self.tb_alsa_options.clicked.connect(self.slot_AlsaAudioBridgeOptions)
  926. self.b_a2j_start.clicked.connect(self.slot_A2JBridgeStart)
  927. self.b_a2j_stop.clicked.connect(self.slot_A2JBridgeStop)
  928. self.b_a2j_export_hw.clicked.connect(self.slot_A2JBridgeExportHW)
  929. self.tb_a2j_options.clicked.connect(self.slot_A2JBridgeOptions)
  930. self.b_pulse_start.clicked.connect(self.slot_PulseAudioBridgeStart)
  931. self.b_pulse_stop.clicked.connect(self.slot_PulseAudioBridgeStop)
  932. self.tb_pulse_options.clicked.connect(self.slot_PulseAudioBridgeOptions)
  933. self.pic_catia.clicked.connect(self.func_start_catia)
  934. self.pic_claudia.clicked.connect(self.func_start_claudia)
  935. self.pic_meter_in.clicked.connect(self.func_start_jackmeter_in)
  936. self.pic_meter_out.clicked.connect(self.func_start_jackmeter)
  937. self.pic_logs.clicked.connect(self.func_start_logs)
  938. self.pic_render.clicked.connect(self.func_start_render)
  939. self.pic_xycontroller.clicked.connect(self.func_start_xycontroller)
  940. self.b_tweaks_apply_now.clicked.connect(self.slot_tweaksApply)
  941. self.b_tweak_plugins_add.clicked.connect(self.slot_tweakPluginAdd)
  942. self.b_tweak_plugins_change.clicked.connect(self.slot_tweakPluginChange)
  943. self.b_tweak_plugins_remove.clicked.connect(self.slot_tweakPluginRemove)
  944. self.b_tweak_plugins_reset.clicked.connect(self.slot_tweakPluginReset)
  945. self.tb_tweak_plugins.currentChanged.connect(self.slot_tweakPluginTypeChanged)
  946. self.list_LADSPA.currentRowChanged.connect(self.slot_tweakPluginsLadspaRowChanged)
  947. self.list_DSSI.currentRowChanged.connect(self.slot_tweakPluginsDssiRowChanged)
  948. self.list_LV2.currentRowChanged.connect(self.slot_tweakPluginsLv2RowChanged)
  949. self.list_VST.currentRowChanged.connect(self.slot_tweakPluginsVstRowChanged)
  950. self.ch_app_image.clicked.connect(self.slot_tweaksSettingsChanged_apps)
  951. self.cb_app_image.highlighted.connect(self.slot_tweakAppImageHighlighted)
  952. self.cb_app_image.currentIndexChanged[int].connect(self.slot_tweakAppImageChanged)
  953. self.ch_app_music.clicked.connect(self.slot_tweaksSettingsChanged_apps)
  954. self.cb_app_music.highlighted.connect(self.slot_tweakAppMusicHighlighted)
  955. self.cb_app_music.currentIndexChanged[int].connect(self.slot_tweakAppMusicChanged)
  956. self.ch_app_video.clicked.connect(self.slot_tweaksSettingsChanged_apps)
  957. self.cb_app_video.highlighted.connect(self.slot_tweakAppVideoHighlighted)
  958. self.cb_app_video.currentIndexChanged[int].connect(self.slot_tweakAppVideoChanged)
  959. self.ch_app_text.clicked.connect(self.slot_tweaksSettingsChanged_apps)
  960. self.cb_app_text.highlighted.connect(self.slot_tweakAppTextHighlighted)
  961. self.cb_app_text.currentIndexChanged[int].connect(self.slot_tweakAppTextChanged)
  962. self.ch_app_browser.clicked.connect(self.slot_tweaksSettingsChanged_apps)
  963. self.cb_app_browser.highlighted.connect(self.slot_tweakAppBrowserHighlighted)
  964. self.cb_app_browser.currentIndexChanged[int].connect(self.slot_tweakAppBrowserChanged)
  965. self.sb_wineasio_ins.valueChanged.connect(self.slot_tweaksSettingsChanged_wineasio)
  966. self.sb_wineasio_outs.valueChanged.connect(self.slot_tweaksSettingsChanged_wineasio)
  967. self.cb_wineasio_hw.clicked.connect(self.slot_tweaksSettingsChanged_wineasio)
  968. self.cb_wineasio_autostart.clicked.connect(self.slot_tweaksSettingsChanged_wineasio)
  969. self.cb_wineasio_fixed_bsize.clicked.connect(self.slot_tweaksSettingsChanged_wineasio)
  970. self.cb_wineasio_bsizes.currentIndexChanged[int].connect(self.slot_tweaksSettingsChanged_wineasio)
  971. # org.jackaudio.JackControl
  972. self.DBusJackServerStartedCallback.connect(self.slot_DBusJackServerStartedCallback)
  973. self.DBusJackServerStoppedCallback.connect(self.slot_DBusJackServerStoppedCallback)
  974. # org.jackaudio.JackPatchbay
  975. self.DBusJackClientAppearedCallback.connect(self.slot_DBusJackClientAppearedCallback)
  976. self.DBusJackClientDisappearedCallback.connect(self.slot_DBusJackClientDisappearedCallback)
  977. # org.gna.home.a2jmidid.control
  978. self.DBusA2JBridgeStartedCallback.connect(self.slot_DBusA2JBridgeStartedCallback)
  979. self.DBusA2JBridgeStoppedCallback.connect(self.slot_DBusA2JBridgeStoppedCallback)
  980. # -------------------------------------------------------------
  981. self.m_last_dsp_load = None
  982. self.m_last_xruns = None
  983. self.m_last_buffer_size = None
  984. self.m_timer500 = None
  985. self.m_timer2000 = self.startTimer(2000)
  986. self.DBusReconnect()
  987. if haveDBus:
  988. gDBus.bus.add_signal_receiver(self.DBusSignalReceiver, destination_keyword='dest', path_keyword='path',
  989. member_keyword='member', interface_keyword='interface', sender_keyword='sender', )
  990. def DBusReconnect(self):
  991. if haveDBus:
  992. try:
  993. gDBus.jack = gDBus.bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller")
  994. gDBus.patchbay = dbus.Interface(gDBus.jack, "org.jackaudio.JackPatchbay")
  995. jacksettings.initBus(gDBus.bus)
  996. except:
  997. gDBus.jack = None
  998. gDBus.patchbay = None
  999. try:
  1000. gDBus.a2j = dbus.Interface(gDBus.bus.get_object("org.gna.home.a2jmidid", "/"), "org.gna.home.a2jmidid.control")
  1001. except:
  1002. gDBus.a2j = None
  1003. if gDBus.jack:
  1004. if gDBus.jack.IsStarted():
  1005. # Check for pulseaudio in jack graph
  1006. try:
  1007. version, groups, conns = gDBus.patchbay.GetGraph(0)
  1008. except:
  1009. version, groups, conns = (list(), list(), list())
  1010. for group_id, group_name, ports in groups:
  1011. if group_name == "alsa2jack":
  1012. global jackClientIdALSA
  1013. jackClientIdALSA = group_id
  1014. elif group_name == "PulseAudio JACK Sink":
  1015. global jackClientIdPulse
  1016. jackClientIdPulse = group_id
  1017. self.jackStarted()
  1018. else:
  1019. self.jackStopped()
  1020. self.label_jack_realtime.setText("Yes" if jacksettings.isRealtime() else "No")
  1021. else:
  1022. self.jackStopped()
  1023. self.label_jack_status.setText("Unavailable")
  1024. self.label_jack_status_ico.setPixmap(self.pix_error)
  1025. self.label_jack_realtime.setText("Unknown")
  1026. self.label_jack_realtime_ico.setPixmap(self.pix_error)
  1027. self.groupBox_jack.setEnabled(False)
  1028. self.groupBox_jack.setTitle("-- jackdbus is not available --")
  1029. self.b_jack_start.setEnabled(False)
  1030. self.b_jack_stop.setEnabled(False)
  1031. self.b_jack_restart.setEnabled(False)
  1032. self.b_jack_configure.setEnabled(False)
  1033. self.b_jack_switchmaster.setEnabled(False)
  1034. self.groupBox_bridges.setEnabled(False)
  1035. if gDBus.a2j:
  1036. try:
  1037. started = gDBus.a2j.is_started()
  1038. except:
  1039. started = False
  1040. if started:
  1041. self.a2jStarted()
  1042. else:
  1043. self.a2jStopped()
  1044. else:
  1045. self.toolBox_alsamidi.setEnabled(False)
  1046. self.cb_a2j_autostart.setChecked(False)
  1047. self.label_bridge_a2j.setText("ALSA MIDI Bridge is not installed")
  1048. self.settings.setValue("A2J/AutoStart", False)
  1049. self.updateSystrayTooltip()
  1050. def DBusSignalReceiver(self, *args, **kwds):
  1051. if kwds['interface'] == "org.freedesktop.DBus" and kwds['path'] == "/org/freedesktop/DBus" and kwds['member'] == "NameOwnerChanged":
  1052. appInterface, appId, newId = args
  1053. if not newId:
  1054. # Something crashed
  1055. if appInterface == "org.jackaudio.service":
  1056. QTimer.singleShot(0, self.slot_handleCrash_jack)
  1057. elif appInterface == "org.gna.home.a2jmidid":
  1058. QTimer.singleShot(0, self.slot_handleCrash_a2j)
  1059. elif kwds['interface'] == "org.jackaudio.JackControl":
  1060. if DEBUG: print("org.jackaudio.JackControl", kwds['member'])
  1061. if kwds['member'] == "ServerStarted":
  1062. self.DBusJackServerStartedCallback.emit()
  1063. elif kwds['member'] == "ServerStopped":
  1064. self.DBusJackServerStoppedCallback.emit()
  1065. elif kwds['interface'] == "org.jackaudio.JackPatchbay":
  1066. if gDBus.patchbay and kwds['path'] == gDBus.patchbay.object_path:
  1067. if DEBUG: print("org.jackaudio.JackPatchbay,", kwds['member'])
  1068. if kwds['member'] == "ClientAppeared":
  1069. self.DBusJackClientAppearedCallback.emit(args[iJackClientId], args[iJackClientName])
  1070. elif kwds['member'] == "ClientDisappeared":
  1071. self.DBusJackClientDisappearedCallback.emit(args[iJackClientId])
  1072. elif kwds['interface'] == "org.gna.home.a2jmidid.control":
  1073. if DEBUG: print("org.gna.home.a2jmidid.control", kwds['member'])
  1074. if kwds['member'] == "bridge_started":
  1075. self.DBusA2JBridgeStartedCallback.emit()
  1076. elif kwds['member'] == "bridge_stopped":
  1077. self.DBusA2JBridgeStoppedCallback.emit()
  1078. def jackStarted(self):
  1079. self.m_last_dsp_load = gDBus.jack.GetLoad()
  1080. self.m_last_xruns = gDBus.jack.GetXruns()
  1081. self.m_last_buffer_size = gDBus.jack.GetBufferSize()
  1082. self.b_jack_start.setEnabled(False)
  1083. self.b_jack_stop.setEnabled(True)
  1084. self.b_jack_switchmaster.setEnabled(True)
  1085. self.systray.setActionEnabled("jack_start", False)
  1086. self.systray.setActionEnabled("jack_stop", True)
  1087. self.label_jack_status.setText("Started")
  1088. self.label_jack_status_ico.setPixmap(self.pix_apply)
  1089. if gDBus.jack.IsRealtime():
  1090. self.label_jack_realtime.setText("Yes")
  1091. self.label_jack_realtime_ico.setPixmap(self.pix_apply)
  1092. else:
  1093. self.label_jack_realtime.setText("No")
  1094. self.label_jack_realtime_ico.setPixmap(self.pix_cancel)
  1095. self.label_jack_dsp.setText("%.2f%%" % self.m_last_dsp_load)
  1096. self.label_jack_xruns.setText(str(self.m_last_xruns))
  1097. self.label_jack_bfsize.setText("%i samples" % self.m_last_buffer_size)
  1098. self.label_jack_srate.setText("%i Hz" % gDBus.jack.GetSampleRate())
  1099. self.label_jack_latency.setText("%.1f ms" % gDBus.jack.GetLatency())
  1100. self.m_timer500 = self.startTimer(500)
  1101. if gDBus.a2j and not gDBus.a2j.is_started():
  1102. self.b_a2j_start.setEnabled(True)
  1103. self.systray.setActionEnabled("a2j_start", True)
  1104. self.checkAlsaAudio()
  1105. self.checkPulseAudio()
  1106. def jackStopped(self):
  1107. if self.m_timer500:
  1108. self.killTimer(self.m_timer500)
  1109. self.m_timer500 = None
  1110. self.m_last_dsp_load = None
  1111. self.m_last_xruns = None
  1112. self.m_last_buffer_size = None
  1113. self.b_jack_start.setEnabled(True)
  1114. self.b_jack_stop.setEnabled(False)
  1115. self.b_jack_switchmaster.setEnabled(False)
  1116. if haveDBus:
  1117. self.systray.setActionEnabled("jack_start", True)
  1118. self.systray.setActionEnabled("jack_stop", False)
  1119. self.label_jack_status.setText("Stopped")
  1120. self.label_jack_status_ico.setPixmap(self.pix_cancel)
  1121. self.label_jack_dsp.setText("---")
  1122. self.label_jack_xruns.setText("---")
  1123. self.label_jack_bfsize.setText("---")
  1124. self.label_jack_srate.setText("---")
  1125. self.label_jack_latency.setText("---")
  1126. if gDBus.a2j:
  1127. self.b_a2j_start.setEnabled(False)
  1128. self.systray.setActionEnabled("a2j_start", False)
  1129. global jackClientIdALSA, jackClientIdPulse
  1130. jackClientIdALSA = -1
  1131. jackClientIdPulse = -1
  1132. if haveDBus:
  1133. self.checkAlsaAudio()
  1134. self.checkPulseAudio()
  1135. def a2jStarted(self):
  1136. self.b_a2j_start.setEnabled(False)
  1137. self.b_a2j_stop.setEnabled(True)
  1138. self.b_a2j_export_hw.setEnabled(False)
  1139. self.systray.setActionEnabled("a2j_start", False)
  1140. self.systray.setActionEnabled("a2j_stop", True)
  1141. self.systray.setActionEnabled("a2j_export_hw", False)
  1142. self.label_bridge_a2j.setText(self.tr("ALSA MIDI Bridge is running"))
  1143. def a2jStopped(self):
  1144. jackRunning = bool(gDBus.jack and gDBus.jack.IsStarted())
  1145. self.b_a2j_start.setEnabled(jackRunning)
  1146. self.b_a2j_stop.setEnabled(False)
  1147. self.b_a2j_export_hw.setEnabled(True)
  1148. self.systray.setActionEnabled("a2j_start", jackRunning)
  1149. self.systray.setActionEnabled("a2j_stop", False)
  1150. self.systray.setActionEnabled("a2j_export_hw", True)
  1151. self.label_bridge_a2j.setText(self.tr("ALSA MIDI Bridge is stopped"))
  1152. def checkAlsaAudio(self):
  1153. asoundrcFile = os.path.join(HOME, ".asoundrc")
  1154. if not os.path.exists(asoundrcFile):
  1155. self.b_alsa_start.setEnabled(False)
  1156. self.b_alsa_stop.setEnabled(False)
  1157. self.cb_alsa_type.setCurrentIndex(iAlsaFileNone)
  1158. self.tb_alsa_options.setEnabled(False)
  1159. self.label_bridge_alsa.setText(self.tr("No bridge in use"))
  1160. self.m_lastAlsaIndexType = -1 # null
  1161. return
  1162. asoundrcFd = open(asoundrcFile, "r")
  1163. asoundrcRead = asoundrcFd.read().strip()
  1164. asoundrcFd.close()
  1165. if asoundrcRead.startswith(asoundrc_aloop_check):
  1166. if isAlsaAudioBridged():
  1167. self.b_alsa_start.setEnabled(False)
  1168. self.b_alsa_stop.setEnabled(True)
  1169. self.systray.setActionEnabled("alsa_start", False)
  1170. self.systray.setActionEnabled("alsa_stop", True)
  1171. self.label_bridge_alsa.setText(self.tr("Using Cadence snd-aloop daemon, started"))
  1172. else:
  1173. try:
  1174. jackRunning = bool(gDBus.jack and gDBus.jack.IsStarted())
  1175. except:
  1176. jackRunning = False
  1177. self.b_alsa_start.setEnabled(jackRunning)
  1178. self.b_alsa_stop.setEnabled(False)
  1179. self.systray.setActionEnabled("alsa_start", jackRunning)
  1180. self.systray.setActionEnabled("alsa_stop", False)
  1181. self.label_bridge_alsa.setText(self.tr("Using Cadence snd-aloop daemon, stopped"))
  1182. self.cb_alsa_type.setCurrentIndex(iAlsaFileLoop)
  1183. self.tb_alsa_options.setEnabled(True)
  1184. elif asoundrcRead == asoundrc_jack:
  1185. self.b_alsa_start.setEnabled(False)
  1186. self.b_alsa_stop.setEnabled(False)
  1187. self.systray.setActionEnabled("alsa_start", False)
  1188. self.systray.setActionEnabled("alsa_stop", False)
  1189. self.cb_alsa_type.setCurrentIndex(iAlsaFileJACK)
  1190. self.tb_alsa_options.setEnabled(False)
  1191. self.label_bridge_alsa.setText(self.tr("Using JACK plugin bridge (Always on)"))
  1192. elif asoundrcRead == asoundrc_pulse:
  1193. self.b_alsa_start.setEnabled(False)
  1194. self.b_alsa_stop.setEnabled(False)
  1195. self.systray.setActionEnabled("alsa_start", False)
  1196. self.systray.setActionEnabled("alsa_stop", False)
  1197. self.cb_alsa_type.setCurrentIndex(iAlsaFilePulse)
  1198. self.tb_alsa_options.setEnabled(False)
  1199. self.label_bridge_alsa.setText(self.tr("Using PulseAudio plugin bridge (Always on)"))
  1200. else:
  1201. self.b_alsa_start.setEnabled(False)
  1202. self.b_alsa_stop.setEnabled(False)
  1203. self.systray.setActionEnabled("alsa_start", False)
  1204. self.systray.setActionEnabled("alsa_stop", False)
  1205. self.cb_alsa_type.addItem(self.tr("Custom"))
  1206. self.cb_alsa_type.setCurrentIndex(iAlsaFileMax)
  1207. self.tb_alsa_options.setEnabled(True)
  1208. self.label_bridge_alsa.setText(self.tr("Using custom asoundrc, not managed by Cadence"))
  1209. self.m_lastAlsaIndexType = self.cb_alsa_type.currentIndex()
  1210. def checkPulseAudio(self):
  1211. if not havePulseAudio:
  1212. self.systray.setActionEnabled("pulse_start", False)
  1213. self.systray.setActionEnabled("pulse_stop", False)
  1214. return
  1215. if isPulseAudioStarted():
  1216. if isPulseAudioBridged():
  1217. self.b_pulse_start.setEnabled(False)
  1218. self.b_pulse_stop.setEnabled(True)
  1219. self.systray.setActionEnabled("pulse_start", False)
  1220. self.systray.setActionEnabled("pulse_stop", True)
  1221. self.label_bridge_pulse.setText(self.tr("PulseAudio is started and bridged to JACK"))
  1222. else:
  1223. jackRunning = bool(gDBus.jack and gDBus.jack.IsStarted())
  1224. self.b_pulse_start.setEnabled(jackRunning)
  1225. self.b_pulse_stop.setEnabled(False)
  1226. self.systray.setActionEnabled("pulse_start", jackRunning)
  1227. self.systray.setActionEnabled("pulse_stop", False)
  1228. self.label_bridge_pulse.setText(self.tr("PulseAudio is started but not bridged"))
  1229. else:
  1230. jackRunning = bool(gDBus.jack and gDBus.jack.IsStarted())
  1231. self.b_pulse_start.setEnabled(jackRunning)
  1232. self.b_pulse_stop.setEnabled(False)
  1233. self.systray.setActionEnabled("pulse_start", jackRunning)
  1234. self.systray.setActionEnabled("pulse_stop", False)
  1235. self.label_bridge_pulse.setText(self.tr("PulseAudio is not started"))
  1236. def setAppDetails(self, desktop):
  1237. appContents = getDesktopFileContents(desktop)
  1238. name = getXdgProperty(appContents, "Name")
  1239. icon = getXdgProperty(appContents, "Icon")
  1240. comment = getXdgProperty(appContents, "Comment")
  1241. if not name:
  1242. name = self.cb_app_image.currentText().replace(".desktop","").title()
  1243. if not icon:
  1244. icon = ""
  1245. if not comment:
  1246. comment = ""
  1247. self.ico_app.setPixmap(getIcon(icon, 48).pixmap(48, 48))
  1248. self.label_app_name.setText(name)
  1249. self.label_app_comment.setText(comment)
  1250. def updateSystrayTooltip(self):
  1251. systrayText = "Cadence<br/>"
  1252. systrayText += "<font size=\"-1\">"
  1253. systrayText += "<b>%s:</b>&nbsp;%s<br/>" % (self.tr("JACK Status"), self.label_jack_status.text())
  1254. systrayText += "<b>%s:</b>&nbsp;%s<br/>" % (self.tr("Realtime"), self.label_jack_realtime.text())
  1255. systrayText += "<b>%s:</b>&nbsp;%s<br/>" % (self.tr("DSP Load"), self.label_jack_dsp.text())
  1256. systrayText += "<b>%s:</b>&nbsp;%s<br/>" % (self.tr("Xruns"), self.label_jack_xruns.text())
  1257. systrayText += "<b>%s:</b>&nbsp;%s<br/>" % (self.tr("Buffer Size"), self.label_jack_bfsize.text())
  1258. systrayText += "<b>%s:</b>&nbsp;%s<br/>" % (self.tr("Sample Rate"), self.label_jack_srate.text())
  1259. systrayText += "<b>%s:</b>&nbsp;%s" % (self.tr("Block Latency"), self.label_jack_latency.text())
  1260. systrayText += "</font><font size=\"-2\"><br/></font>"
  1261. self.systray.setToolTip(systrayText)
  1262. @pyqtSlot()
  1263. def func_start_catarina(self):
  1264. self.func_start_tool("catarina")
  1265. @pyqtSlot()
  1266. def func_start_catia(self):
  1267. self.func_start_tool("catia")
  1268. @pyqtSlot()
  1269. def func_start_claudia(self):
  1270. self.func_start_tool("claudia")
  1271. @pyqtSlot()
  1272. def func_start_logs(self):
  1273. self.func_start_tool("cadence-logs")
  1274. @pyqtSlot()
  1275. def func_start_jackmeter(self):
  1276. self.func_start_tool("cadence-jackmeter")
  1277. @pyqtSlot()
  1278. def func_start_jackmeter_in(self):
  1279. self.func_start_tool("cadence-jackmeter -in")
  1280. @pyqtSlot()
  1281. def func_start_render(self):
  1282. self.func_start_tool("cadence-render")
  1283. @pyqtSlot()
  1284. def func_start_xycontroller(self):
  1285. self.func_start_tool("cadence-xycontroller")
  1286. def func_start_tool(self, tool):
  1287. if sys.argv[0].endswith(".py"):
  1288. if tool == "cadence-logs":
  1289. tool = "logs"
  1290. elif tool == "cadence-render":
  1291. tool = "render"
  1292. stool = tool.split(" ", 1)[0]
  1293. if stool in ("cadence-jackmeter", "cadence-xycontroller"):
  1294. python = ""
  1295. localPath = os.path.join(sys.path[0], "..", "c++", stool.replace("cadence-", ""))
  1296. if os.path.exists(os.path.join(localPath, stool)):
  1297. base = localPath + os.sep
  1298. else:
  1299. base = ""
  1300. else:
  1301. python = sys.executable
  1302. tool += ".py"
  1303. base = sys.argv[0].rsplit("cadence.py", 1)[0]
  1304. if python:
  1305. python += " "
  1306. cmd = "%s%s%s &" % (python, base, tool)
  1307. print(cmd)
  1308. os.system(cmd)
  1309. elif sys.argv[0].endswith("/cadence"):
  1310. base = sys.argv[0].rsplit("/cadence", 1)[0]
  1311. os.system("%s/%s &" % (base, tool))
  1312. else:
  1313. os.system("%s &" % tool)
  1314. def func_settings_changed(self, stype):
  1315. if stype not in self.settings_changed_types:
  1316. self.settings_changed_types.append(stype)
  1317. self.frame_tweaks_settings.setVisible(True)
  1318. @pyqtSlot()
  1319. def slot_DBusJackServerStartedCallback(self):
  1320. self.jackStarted()
  1321. @pyqtSlot()
  1322. def slot_DBusJackServerStoppedCallback(self):
  1323. self.jackStopped()
  1324. @pyqtSlot(int, str)
  1325. def slot_DBusJackClientAppearedCallback(self, group_id, group_name):
  1326. if group_name == "alsa2jack":
  1327. global jackClientIdALSA
  1328. jackClientIdALSA = group_id
  1329. self.checkAlsaAudio()
  1330. elif group_name == "PulseAudio JACK Sink":
  1331. global jackClientIdPulse
  1332. jackClientIdPulse = group_id
  1333. self.checkPulseAudio()
  1334. @pyqtSlot(int)
  1335. def slot_DBusJackClientDisappearedCallback(self, group_id):
  1336. global jackClientIdALSA, jackClientIdPulse
  1337. if group_id == jackClientIdALSA:
  1338. jackClientIdALSA = -1
  1339. self.checkAlsaAudio()
  1340. elif group_id == jackClientIdPulse:
  1341. jackClientIdPulse = -1
  1342. self.checkPulseAudio()
  1343. @pyqtSlot()
  1344. def slot_DBusA2JBridgeStartedCallback(self):
  1345. self.a2jStarted()
  1346. @pyqtSlot()
  1347. def slot_DBusA2JBridgeStoppedCallback(self):
  1348. self.a2jStopped()
  1349. @pyqtSlot()
  1350. def slot_JackServerStart(self):
  1351. self.saveSettings()
  1352. try:
  1353. gDBus.jack.StartServer()
  1354. except:
  1355. QMessageBox.warning(self, self.tr("Warning"), self.tr("Failed to start JACK, please check the logs for more information."))
  1356. @pyqtSlot()
  1357. def slot_JackServerStop(self):
  1358. try:
  1359. gDBus.jack.StopServer()
  1360. except:
  1361. QMessageBox.warning(self, self.tr("Warning"), self.tr("Failed to stop JACK, please check the logs for more information."))
  1362. @pyqtSlot()
  1363. def slot_JackServerForceRestart(self):
  1364. if gDBus.jack.IsStarted():
  1365. ask = CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"),
  1366. self.tr("This will force kill all JACK applications!<br>Make sure to save your projects before continue."),
  1367. self.tr("Are you sure you want to force the restart of JACK?"))
  1368. if ask != QMessageBox.Yes:
  1369. return
  1370. if self.m_timer500:
  1371. self.killTimer(self.m_timer500)
  1372. self.m_timer500 = None
  1373. self.saveSettings()
  1374. ForceWaitDialog(self).exec_()
  1375. @pyqtSlot()
  1376. def slot_JackServerConfigure(self):
  1377. jacksettingsW = jacksettings.JackSettingsW(self)
  1378. jacksettingsW.exec_()
  1379. del jacksettingsW
  1380. @pyqtSlot()
  1381. def slot_JackServerSwitchMaster(self):
  1382. try:
  1383. gDBus.jack.SwitchMaster()
  1384. except:
  1385. QMessageBox.warning(self, self.tr("Warning"), self.tr("Failed to switch JACK master, please check the logs for more information."))
  1386. return
  1387. self.jackStarted()
  1388. @pyqtSlot()
  1389. def slot_JackOptions(self):
  1390. ToolBarJackDialog(self).exec_()
  1391. @pyqtSlot()
  1392. def slot_JackClearXruns(self):
  1393. if gDBus.jack:
  1394. gDBus.jack.ResetXruns()
  1395. @pyqtSlot()
  1396. def slot_AlsaBridgeStart(self):
  1397. self.slot_AlsaBridgeStop()
  1398. startAlsaAudioLoopBridge()
  1399. @pyqtSlot()
  1400. def slot_AlsaBridgeStop(self):
  1401. checkFile = "/tmp/.cadence-aloop-daemon.x"
  1402. if os.path.exists(checkFile):
  1403. os.remove(checkFile)
  1404. @pyqtSlot(int)
  1405. def slot_AlsaBridgeChanged(self, index):
  1406. if self.m_lastAlsaIndexType == -2 or self.m_lastAlsaIndexType == index:
  1407. return
  1408. if self.m_lastAlsaIndexType == iAlsaFileMax:
  1409. ask = CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"),
  1410. self.tr(""
  1411. "You're using a custom ~/.asoundrc file not managed by Cadence.<br/>"
  1412. "By choosing to use a Cadence ALSA-Audio bridge, <b>the file will be replaced</b>."
  1413. ""),
  1414. self.tr("Are you sure you want to do this?"))
  1415. if ask == QMessageBox.Yes:
  1416. self.cb_alsa_type.blockSignals(True)
  1417. self.cb_alsa_type.removeItem(iAlsaFileMax)
  1418. self.cb_alsa_type.setCurrentIndex(index)
  1419. self.cb_alsa_type.blockSignals(False)
  1420. else:
  1421. self.cb_alsa_type.blockSignals(True)
  1422. self.cb_alsa_type.setCurrentIndex(iAlsaFileMax)
  1423. self.cb_alsa_type.blockSignals(False)
  1424. return
  1425. asoundrcFile = os.path.join(HOME, ".asoundrc")
  1426. if index == iAlsaFileNone:
  1427. os.remove(asoundrcFile)
  1428. elif index == iAlsaFileLoop:
  1429. asoundrcFd = open(asoundrcFile, "w")
  1430. asoundrcFd.write(asoundrc_aloop+"\n")
  1431. asoundrcFd.close()
  1432. elif index == iAlsaFileJACK:
  1433. asoundrcFd = open(asoundrcFile, "w")
  1434. asoundrcFd.write(asoundrc_jack+"\n")
  1435. asoundrcFd.close()
  1436. elif index == iAlsaFilePulse:
  1437. asoundrcFd = open(asoundrcFile, "w")
  1438. asoundrcFd.write(asoundrc_pulse+"\n")
  1439. asoundrcFd.close()
  1440. else:
  1441. print("Cadence::AlsaBridgeChanged(%i) - invalid index" % index)
  1442. self.checkAlsaAudio()
  1443. @pyqtSlot()
  1444. def slot_AlsaAudioBridgeOptions(self):
  1445. ToolBarAlsaAudioDialog(self, (self.cb_alsa_type.currentIndex() != iAlsaFileLoop)).exec_()
  1446. @pyqtSlot()
  1447. def slot_A2JBridgeStart(self):
  1448. gDBus.a2j.start()
  1449. @pyqtSlot()
  1450. def slot_A2JBridgeStop(self):
  1451. gDBus.a2j.stop()
  1452. @pyqtSlot()
  1453. def slot_A2JBridgeExportHW(self):
  1454. ask = QMessageBox.question(self, self.tr("ALSA MIDI Hardware Export"), self.tr("Enable Hardware Export on the ALSA MIDI Bridge?"), QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel, QMessageBox.Yes)
  1455. if ask == QMessageBox.Yes:
  1456. gDBus.a2j.set_hw_export(True)
  1457. elif ask == QMessageBox.No:
  1458. gDBus.a2j.set_hw_export(False)
  1459. @pyqtSlot()
  1460. def slot_A2JBridgeOptions(self):
  1461. ToolBarA2JDialog(self).exec_()
  1462. @pyqtSlot()
  1463. def slot_PulseAudioBridgeStart(self):
  1464. if GlobalSettings.value("Pulse2JACK/PlaybackModeOnly", False, type=bool):
  1465. os.system("cadence-pulse2jack -p")
  1466. else:
  1467. os.system("cadence-pulse2jack")
  1468. @pyqtSlot()
  1469. def slot_PulseAudioBridgeStop(self):
  1470. os.system("pulseaudio -k")
  1471. @pyqtSlot()
  1472. def slot_PulseAudioBridgeOptions(self):
  1473. ToolBarPADialog(self).exec_()
  1474. @pyqtSlot()
  1475. def slot_handleCrash_jack(self):
  1476. self.DBusReconnect()
  1477. @pyqtSlot()
  1478. def slot_handleCrash_a2j(self):
  1479. pass
  1480. @pyqtSlot(str)
  1481. def slot_changeGovernorMode(self, newMode):
  1482. bus = dbus.SystemBus(mainloop=gDBus.loop)
  1483. #proxy = bus.get_object("org.cadence.CpufreqSelector", "/Selector", introspect=False)
  1484. #print(proxy.hello())
  1485. proxy = bus.get_object("com.ubuntu.IndicatorCpufreqSelector", "/Selector", introspect=False)
  1486. proxy.SetGovernor(self.m_curGovCPUs, newMode, dbus_interface="com.ubuntu.IndicatorCpufreqSelector")
  1487. @pyqtSlot()
  1488. def slot_governorFileChanged(self):
  1489. curGovFd = open(self.m_curGovPath, "r")
  1490. curGovRead = curGovFd.read().strip()
  1491. curGovFd.close()
  1492. customTr = self.tr("Custom")
  1493. if self.cb_cpufreq.currentIndex() == -1:
  1494. # First init
  1495. self.cb_cpufreq.currentIndexChanged[str].connect(self.slot_changeGovernorMode)
  1496. self.cb_cpufreq.blockSignals(True)
  1497. if curGovRead in self.m_availGovList:
  1498. self.cb_cpufreq.setCurrentIndex(self.m_availGovList.index(curGovRead))
  1499. if customTr in self.m_availGovList:
  1500. self.m_availGovList.remove(customTr)
  1501. else:
  1502. if customTr not in self.m_availGovList:
  1503. self.cb_cpufreq.addItem(customTr)
  1504. self.m_availGovList.append(customTr)
  1505. self.cb_cpufreq.setCurrentIndex(len(self.m_availGovList)-1)
  1506. self.cb_cpufreq.blockSignals(False)
  1507. @pyqtSlot()
  1508. def slot_tweaksApply(self):
  1509. if "plugins" in self.settings_changed_types:
  1510. EXTRA_LADSPA_DIRS = []
  1511. EXTRA_DSSI_DIRS = []
  1512. EXTRA_LV2_DIRS = []
  1513. EXTRA_VST_DIRS = []
  1514. for i in range(self.list_LADSPA.count()):
  1515. iPath = self.list_LADSPA.item(i).text()
  1516. if iPath not in DEFAULT_LADSPA_PATH and iPath not in EXTRA_LADSPA_DIRS:
  1517. EXTRA_LADSPA_DIRS.append(iPath)
  1518. for i in range(self.list_DSSI.count()):
  1519. iPath = self.list_DSSI.item(i).text()
  1520. if iPath not in DEFAULT_DSSI_PATH and iPath not in EXTRA_DSSI_DIRS:
  1521. EXTRA_DSSI_DIRS.append(iPath)
  1522. for i in range(self.list_LV2.count()):
  1523. iPath = self.list_LV2.item(i).text()
  1524. if iPath not in DEFAULT_LV2_PATH and iPath not in EXTRA_LV2_DIRS:
  1525. EXTRA_LV2_DIRS.append(iPath)
  1526. for i in range(self.list_VST.count()):
  1527. iPath = self.list_VST.item(i).text()
  1528. if iPath not in DEFAULT_VST_PATH and iPath not in EXTRA_VST_DIRS:
  1529. EXTRA_VST_DIRS.append(iPath)
  1530. GlobalSettings.setValue("AudioPlugins/EXTRA_LADSPA_PATH", ":".join(EXTRA_LADSPA_DIRS))
  1531. GlobalSettings.setValue("AudioPlugins/EXTRA_DSSI_PATH", ":".join(EXTRA_DSSI_DIRS))
  1532. GlobalSettings.setValue("AudioPlugins/EXTRA_LV2_PATH", ":".join(EXTRA_LV2_DIRS))
  1533. GlobalSettings.setValue("AudioPlugins/EXTRA_VST_PATH", ":".join(EXTRA_VST_DIRS))
  1534. if "apps" in self.settings_changed_types:
  1535. mimeFileContent = ""
  1536. # Fix common mime errors
  1537. mimeFileContent += "application/x-designer=designer-qt4.desktop;\n"
  1538. mimeFileContent += "application/x-ms-dos-executable=wine.desktop;\n"
  1539. mimeFileContent += "audio/x-minipsf=audacious.desktop;\n"
  1540. mimeFileContent += "audio/x-psf=audacious.desktop;\n"
  1541. if self.ch_app_image.isChecked():
  1542. imageApp = self.cb_app_image.currentText().replace("/","-")
  1543. mimeFileContent += "image/bmp=%s;\n" % imageApp
  1544. mimeFileContent += "image/gif=%s;\n" % imageApp
  1545. mimeFileContent += "image/jp2=%s;\n" % imageApp
  1546. mimeFileContent += "image/jpeg=%s;\n" % imageApp
  1547. mimeFileContent += "image/png=%s;\n" % imageApp
  1548. mimeFileContent += "image/svg+xml=%s;\n" % imageApp
  1549. mimeFileContent += "image/svg+xml-compressed=%s;\n" % imageApp
  1550. mimeFileContent += "image/tiff=%s;\n" % imageApp
  1551. mimeFileContent += "image/x-canon-cr2=%s;\n" % imageApp
  1552. mimeFileContent += "image/x-canon-crw=%s;\n" % imageApp
  1553. mimeFileContent += "image/x-eps=%s;\n" % imageApp
  1554. mimeFileContent += "image/x-kodak-dcr=%s;\n" % imageApp
  1555. mimeFileContent += "image/x-kodak-k25=%s;\n" % imageApp
  1556. mimeFileContent += "image/x-kodak-kdc=%s;\n" % imageApp
  1557. mimeFileContent += "image/x-nikon-nef=%s;\n" % imageApp
  1558. mimeFileContent += "image/x-olympus-orf=%s;\n" % imageApp
  1559. mimeFileContent += "image/x-panasonic-raw=%s;\n" % imageApp
  1560. mimeFileContent += "image/x-pcx=%s;\n" % imageApp
  1561. mimeFileContent += "image/x-pentax-pef=%s;\n" % imageApp
  1562. mimeFileContent += "image/x-portable-anymap=%s;\n" % imageApp
  1563. mimeFileContent += "image/x-portable-bitmap=%s;\n" % imageApp
  1564. mimeFileContent += "image/x-portable-graymap=%s;\n" % imageApp
  1565. mimeFileContent += "image/x-portable-pixmap=%s;\n" % imageApp
  1566. mimeFileContent += "image/x-sony-arw=%s;\n" % imageApp
  1567. mimeFileContent += "image/x-sony-sr2=%s;\n" % imageApp
  1568. mimeFileContent += "image/x-sony-srf=%s;\n" % imageApp
  1569. mimeFileContent += "image/x-tga=%s;\n" % imageApp
  1570. mimeFileContent += "image/x-xbitmap=%s;\n" % imageApp
  1571. mimeFileContent += "image/x-xpixmap=%s;\n" % imageApp
  1572. if self.ch_app_music.isChecked():
  1573. musicApp = self.cb_app_music.currentText().replace("/","-")
  1574. mimeFileContent += "application/vnd.apple.mpegurl=%s;\n" % musicApp
  1575. mimeFileContent += "application/xspf+xml=%s;\n" % musicApp
  1576. mimeFileContent += "application/x-smaf=%s;\n" % musicApp
  1577. mimeFileContent += "audio/AMR=%s;\n" % musicApp
  1578. mimeFileContent += "audio/AMR-WB=%s;\n" % musicApp
  1579. mimeFileContent += "audio/aac=%s;\n" % musicApp
  1580. mimeFileContent += "audio/ac3=%s;\n" % musicApp
  1581. mimeFileContent += "audio/basic=%s;\n" % musicApp
  1582. mimeFileContent += "audio/flac=%s;\n" % musicApp
  1583. mimeFileContent += "audio/m3u=%s;\n" % musicApp
  1584. mimeFileContent += "audio/mp2=%s;\n" % musicApp
  1585. mimeFileContent += "audio/mp4=%s;\n" % musicApp
  1586. mimeFileContent += "audio/mpeg=%s;\n" % musicApp
  1587. mimeFileContent += "audio/ogg=%s;\n" % musicApp
  1588. mimeFileContent += "audio/vnd.rn-realaudio=%s;\n" % musicApp
  1589. mimeFileContent += "audio/vorbis=%s;\n" % musicApp
  1590. mimeFileContent += "audio/webm=%s;\n" % musicApp
  1591. mimeFileContent += "audio/wav=%s;\n" % musicApp
  1592. mimeFileContent += "audio/x-adpcm=%s;\n" % musicApp
  1593. mimeFileContent += "audio/x-aifc=%s;\n" % musicApp
  1594. mimeFileContent += "audio/x-aiff=%s;\n" % musicApp
  1595. mimeFileContent += "audio/x-aiffc=%s;\n" % musicApp
  1596. mimeFileContent += "audio/x-ape=%s;\n" % musicApp
  1597. mimeFileContent += "audio/x-cda=%s;\n" % musicApp
  1598. mimeFileContent += "audio/x-flac=%s;\n" % musicApp
  1599. mimeFileContent += "audio/x-flac+ogg=%s;\n" % musicApp
  1600. mimeFileContent += "audio/x-gsm=%s;\n" % musicApp
  1601. mimeFileContent += "audio/x-m4b=%s;\n" % musicApp
  1602. mimeFileContent += "audio/x-matroska=%s;\n" % musicApp
  1603. mimeFileContent += "audio/x-mp2=%s;\n" % musicApp
  1604. mimeFileContent += "audio/x-mpegurl=%s;\n" % musicApp
  1605. mimeFileContent += "audio/x-ms-asx=%s;\n" % musicApp
  1606. mimeFileContent += "audio/x-ms-wma=%s;\n" % musicApp
  1607. mimeFileContent += "audio/x-musepack=%s;\n" % musicApp
  1608. mimeFileContent += "audio/x-ogg=%s;\n" % musicApp
  1609. mimeFileContent += "audio/x-oggflac=%s;\n" % musicApp
  1610. mimeFileContent += "audio/x-pn-realaudio-plugin=%s;\n" % musicApp
  1611. mimeFileContent += "audio/x-riff=%s;\n" % musicApp
  1612. mimeFileContent += "audio/x-scpls=%s;\n" % musicApp
  1613. mimeFileContent += "audio/x-speex=%s;\n" % musicApp
  1614. mimeFileContent += "audio/x-speex+ogg=%s;\n" % musicApp
  1615. mimeFileContent += "audio/x-tta=%s;\n" % musicApp
  1616. mimeFileContent += "audio/x-vorbis+ogg=%s;\n" % musicApp
  1617. mimeFileContent += "audio/x-wav=%s;\n" % musicApp
  1618. mimeFileContent += "audio/x-wavpack=%s;\n" % musicApp
  1619. if self.ch_app_video.isChecked():
  1620. videoApp = self.cb_app_video.currentText().replace("/","-")
  1621. mimeFileContent +="application/mxf=%s;\n" % videoApp
  1622. mimeFileContent +="application/ogg=%s;\n" % videoApp
  1623. mimeFileContent +="application/ram=%s;\n" % videoApp
  1624. mimeFileContent +="application/vnd.ms-asf=%s;\n" % videoApp
  1625. mimeFileContent +="application/vnd.ms-wpl=%s;\n" % videoApp
  1626. mimeFileContent +="application/vnd.rn-realmedia=%s;\n" % videoApp
  1627. mimeFileContent +="application/x-ms-wmp=%s;\n" % videoApp
  1628. mimeFileContent +="application/x-ms-wms=%s;\n" % videoApp
  1629. mimeFileContent +="application/x-netshow-channel=%s;\n" % videoApp
  1630. mimeFileContent +="application/x-ogg=%s;\n" % videoApp
  1631. mimeFileContent +="application/x-quicktime-media-link=%s;\n" % videoApp
  1632. mimeFileContent +="video/3gpp=%s;\n" % videoApp
  1633. mimeFileContent +="video/3gpp2=%s;\n" % videoApp
  1634. mimeFileContent +="video/divx=%s;\n" % videoApp
  1635. mimeFileContent +="video/dv=%s;\n" % videoApp
  1636. mimeFileContent +="video/flv=%s;\n" % videoApp
  1637. mimeFileContent +="video/mp2t=%s;\n" % videoApp
  1638. mimeFileContent +="video/mp4=%s;\n" % videoApp
  1639. mimeFileContent +="video/mpeg=%s;\n" % videoApp
  1640. mimeFileContent +="video/ogg=%s;\n" % videoApp
  1641. mimeFileContent +="video/quicktime=%s;\n" % videoApp
  1642. mimeFileContent +="video/vivo=%s;\n" % videoApp
  1643. mimeFileContent +="video/vnd.rn-realvideo=%s;\n" % videoApp
  1644. mimeFileContent +="video/webm=%s;\n" % videoApp
  1645. mimeFileContent +="video/x-anim=%s;\n" % videoApp
  1646. mimeFileContent +="video/x-flic=%s;\n" % videoApp
  1647. mimeFileContent +="video/x-flv=%s;\n" % videoApp
  1648. mimeFileContent +="video/x-m4v=%s;\n" % videoApp
  1649. mimeFileContent +="video/x-matroska=%s;\n" % videoApp
  1650. mimeFileContent +="video/x-ms-asf=%s;\n" % videoApp
  1651. mimeFileContent +="video/x-ms-wm=%s;\n" % videoApp
  1652. mimeFileContent +="video/x-ms-wmp=%s;\n" % videoApp
  1653. mimeFileContent +="video/x-ms-wmv=%s;\n" % videoApp
  1654. mimeFileContent +="video/x-ms-wvx=%s;\n" % videoApp
  1655. mimeFileContent +="video/x-msvideo=%s;\n" % videoApp
  1656. mimeFileContent +="video/x-nsv=%s;\n" % videoApp
  1657. mimeFileContent +="video/x-ogg=%s;\n" % videoApp
  1658. mimeFileContent +="video/x-ogm=%s;\n" % videoApp
  1659. mimeFileContent +="video/x-ogm+ogg=%s;\n" % videoApp
  1660. mimeFileContent +="video/x-theora=%s;\n" % videoApp
  1661. mimeFileContent +="video/x-theora+ogg=%s;\n" % videoApp
  1662. mimeFileContent +="video/x-wmv=%s;\n" % videoApp
  1663. if self.ch_app_text.isChecked():
  1664. # TODO - more mimetypes
  1665. textApp = self.cb_app_text.currentText().replace("/","-")
  1666. mimeFileContent +="application/rdf+xml=%s;\n" % textApp
  1667. mimeFileContent +="application/xml=%s;\n" % textApp
  1668. mimeFileContent +="application/xml-dtd=%s;\n" % textApp
  1669. mimeFileContent +="application/xml-external-parsed-entity=%s;\n" % textApp
  1670. mimeFileContent +="application/xsd=%s;\n" % textApp
  1671. mimeFileContent +="application/xslt+xml=%s;\n" % textApp
  1672. mimeFileContent +="application/x-trash=%s;\n" % textApp
  1673. mimeFileContent +="application/x-wine-extension-inf=%s;\n" % textApp
  1674. mimeFileContent +="application/x-wine-extension-ini=%s;\n" % textApp
  1675. mimeFileContent +="application/x-zerosize=%s;\n" % textApp
  1676. mimeFileContent +="text/css=%s;\n" % textApp
  1677. mimeFileContent +="text/plain=%s;\n" % textApp
  1678. mimeFileContent +="text/x-authors=%s;\n" % textApp
  1679. mimeFileContent +="text/x-c++-hdr=%s;\n" % textApp
  1680. mimeFileContent +="text/x-c++-src=%s;\n" % textApp
  1681. mimeFileContent +="text/x-changelog=%s;\n" % textApp
  1682. mimeFileContent +="text/x-chdr=%s;\n" % textApp
  1683. mimeFileContent +="text/x-cmake=%s;\n" % textApp
  1684. mimeFileContent +="text/x-copying=%s;\n" % textApp
  1685. mimeFileContent +="text/x-credits=%s;\n" % textApp
  1686. mimeFileContent +="text/x-csharp=%s;\n" % textApp
  1687. mimeFileContent +="text/x-csrc=%s;\n" % textApp
  1688. mimeFileContent +="text/x-install=%s;\n" % textApp
  1689. mimeFileContent +="text/x-log=%s;\n" % textApp
  1690. mimeFileContent +="text/x-lua=%s;\n" % textApp
  1691. mimeFileContent +="text/x-makefile=%s;\n" % textApp
  1692. mimeFileContent +="text/x-ms-regedit=%s;\n" % textApp
  1693. mimeFileContent +="text/x-nfo=%s;\n" % textApp
  1694. mimeFileContent +="text/x-objchdr=%s;\n" % textApp
  1695. mimeFileContent +="text/x-objcsrc=%s;\n" % textApp
  1696. mimeFileContent +="text/x-pascal=%s;\n" % textApp
  1697. mimeFileContent +="text/x-patch=%s;\n" % textApp
  1698. mimeFileContent +="text/x-python=%s;\n" % textApp
  1699. mimeFileContent +="text/x-readme=%s;\n" % textApp
  1700. mimeFileContent +="text/x-vhdl=%s;\n" % textApp
  1701. if self.ch_app_browser.isChecked():
  1702. # TODO - needs something else for default browser
  1703. browserApp = self.cb_app_browser.currentText().replace("/","-")
  1704. mimeFileContent +="application/atom+xml=%s;\n" % browserApp
  1705. mimeFileContent +="application/rss+xml=%s;\n" % browserApp
  1706. mimeFileContent +="application/vnd.mozilla.xul+xml=%s;\n" % browserApp
  1707. mimeFileContent +="application/x-mozilla-bookmarks=%s;\n" % browserApp
  1708. mimeFileContent +="application/x-mswinurl=%s;\n" % browserApp
  1709. mimeFileContent +="application/x-xbel=%s;\n" % browserApp
  1710. mimeFileContent +="application/xhtml+xml=%s;\n" % browserApp
  1711. mimeFileContent +="text/html=%s;\n" % browserApp
  1712. mimeFileContent +="text/opml+xml=%s;\n" % browserApp
  1713. realMimeFileContent ="[Default Applications]\n"
  1714. realMimeFileContent += mimeFileContent
  1715. realMimeFileContent +="\n"
  1716. realMimeFileContent +="[Added Associations]\n"
  1717. realMimeFileContent += mimeFileContent
  1718. realMimeFileContent +="\n"
  1719. local_xdg_defaults = os.path.join(HOME, ".local", "share", "applications", "defaults.list")
  1720. local_xdg_mimeapps = os.path.join(HOME, ".local", "share", "applications", "mimeapps.list")
  1721. writeFile = open(local_xdg_defaults, "w")
  1722. writeFile.write(realMimeFileContent)
  1723. writeFile.close()
  1724. writeFile = open(local_xdg_mimeapps, "w")
  1725. writeFile.write(realMimeFileContent)
  1726. writeFile.close()
  1727. if "wineasio" in self.settings_changed_types:
  1728. REGFILE = 'REGEDIT4\n'
  1729. REGFILE += '\n'
  1730. REGFILE += '[HKEY_CURRENT_USER\Software\Wine\WineASIO]\n'
  1731. REGFILE += '"Autostart server"=dword:0000000%i\n' % int(1 if self.cb_wineasio_autostart.isChecked() else 0)
  1732. REGFILE += '"Connect to hardware"=dword:0000000%i\n' % int(1 if self.cb_wineasio_hw.isChecked() else 0)
  1733. REGFILE += '"Fixed buffersize"=dword:0000000%i\n' % int(1 if self.cb_wineasio_fixed_bsize.isChecked() else 0)
  1734. REGFILE += '"Number of inputs"=dword:000000%s\n' % smartHex(self.sb_wineasio_ins.value(), 2)
  1735. REGFILE += '"Number of outputs"=dword:000000%s\n' % smartHex(self.sb_wineasio_outs.value(), 2)
  1736. REGFILE += '"Preferred buffersize"=dword:0000%s\n' % smartHex(int(self.cb_wineasio_bsizes.currentText()), 4)
  1737. writeFile = open("/tmp/cadence-wineasio.reg", "w")
  1738. writeFile.write(REGFILE)
  1739. writeFile.close()
  1740. os.system("regedit /tmp/cadence-wineasio.reg")
  1741. self.settings_changed_types = []
  1742. self.frame_tweaks_settings.setVisible(False)
  1743. @pyqtSlot()
  1744. def slot_tweaksSettingsChanged_apps(self):
  1745. self.func_settings_changed("apps")
  1746. @pyqtSlot()
  1747. def slot_tweaksSettingsChanged_wineasio(self):
  1748. self.func_settings_changed("wineasio")
  1749. @pyqtSlot(int)
  1750. def slot_tweakAppImageHighlighted(self, index):
  1751. self.setAppDetails(self.cb_app_image.itemText(index))
  1752. @pyqtSlot(int)
  1753. def slot_tweakAppImageChanged(self, ignored):
  1754. self.setAppDetails(self.cb_app_image.currentText())
  1755. self.func_settings_changed("apps")
  1756. @pyqtSlot(int)
  1757. def slot_tweakAppMusicHighlighted(self, index):
  1758. self.setAppDetails(self.cb_app_music.itemText(index))
  1759. @pyqtSlot(int)
  1760. def slot_tweakAppMusicChanged(self, ignored):
  1761. self.setAppDetails(self.cb_app_music.currentText())
  1762. self.func_settings_changed("apps")
  1763. @pyqtSlot(int)
  1764. def slot_tweakAppVideoHighlighted(self, index):
  1765. self.setAppDetails(self.cb_app_video.itemText(index))
  1766. @pyqtSlot(int)
  1767. def slot_tweakAppVideoChanged(self, ignored):
  1768. self.setAppDetails(self.cb_app_video.currentText())
  1769. self.func_settings_changed("apps")
  1770. @pyqtSlot(int)
  1771. def slot_tweakAppTextHighlighted(self, index):
  1772. self.setAppDetails(self.cb_app_text.itemText(index))
  1773. @pyqtSlot(int)
  1774. def slot_tweakAppTextChanged(self, ignored):
  1775. self.setAppDetails(self.cb_app_text.currentText())
  1776. self.func_settings_changed("apps")
  1777. @pyqtSlot(int)
  1778. def slot_tweakAppBrowserHighlighted(self, index):
  1779. self.setAppDetails(self.cb_app_browser.itemText(index))
  1780. @pyqtSlot(int)
  1781. def slot_tweakAppBrowserChanged(self, ignored):
  1782. self.setAppDetails(self.cb_app_browser.currentText())
  1783. self.func_settings_changed("apps")
  1784. @pyqtSlot()
  1785. def slot_tweakPluginAdd(self):
  1786. newPath = QFileDialog.getExistingDirectory(self, self.tr("Add Path"), "", QFileDialog.ShowDirsOnly)
  1787. if not newPath:
  1788. return
  1789. if self.tb_tweak_plugins.currentIndex() == 0:
  1790. self.list_LADSPA.addItem(newPath)
  1791. elif self.tb_tweak_plugins.currentIndex() == 1:
  1792. self.list_DSSI.addItem(newPath)
  1793. elif self.tb_tweak_plugins.currentIndex() == 2:
  1794. self.list_LV2.addItem(newPath)
  1795. elif self.tb_tweak_plugins.currentIndex() == 3:
  1796. self.list_VST.addItem(newPath)
  1797. self.func_settings_changed("plugins")
  1798. @pyqtSlot()
  1799. def slot_tweakPluginChange(self):
  1800. if self.tb_tweak_plugins.currentIndex() == 0:
  1801. curPath = self.list_LADSPA.item(self.list_LADSPA.currentRow()).text()
  1802. elif self.tb_tweak_plugins.currentIndex() == 1:
  1803. curPath = self.list_DSSI.item(self.list_DSSI.currentRow()).text()
  1804. elif self.tb_tweak_plugins.currentIndex() == 2:
  1805. curPath = self.list_LV2.item(self.list_LV2.currentRow()).text()
  1806. elif self.tb_tweak_plugins.currentIndex() == 3:
  1807. curPath = self.list_VST.item(self.list_VST.currentRow()).text()
  1808. else:
  1809. curPath = ""
  1810. newPath = QFileDialog.getExistingDirectory(self, self.tr("Change Path"), curPath, QFileDialog.ShowDirsOnly)
  1811. if not newPath:
  1812. return
  1813. if self.tb_tweak_plugins.currentIndex() == 0:
  1814. self.list_LADSPA.item(self.list_LADSPA.currentRow()).setText(newPath)
  1815. elif self.tb_tweak_plugins.currentIndex() == 1:
  1816. self.list_DSSI.item(self.list_DSSI.currentRow()).setText(newPath)
  1817. elif self.tb_tweak_plugins.currentIndex() == 2:
  1818. self.list_LV2.item(self.list_LV2.currentRow()).setText(newPath)
  1819. elif self.tb_tweak_plugins.currentIndex() == 3:
  1820. self.list_VST.item(self.list_VST.currentRow()).setText(newPath)
  1821. self.func_settings_changed("plugins")
  1822. @pyqtSlot()
  1823. def slot_tweakPluginRemove(self):
  1824. if self.tb_tweak_plugins.currentIndex() == 0:
  1825. self.list_LADSPA.takeItem(self.list_LADSPA.currentRow())
  1826. elif self.tb_tweak_plugins.currentIndex() == 1:
  1827. self.list_DSSI.takeItem(self.list_DSSI.currentRow())
  1828. elif self.tb_tweak_plugins.currentIndex() == 2:
  1829. self.list_LV2.takeItem(self.list_LV2.currentRow())
  1830. elif self.tb_tweak_plugins.currentIndex() == 3:
  1831. self.list_VST.takeItem(self.list_VST.currentRow())
  1832. self.func_settings_changed("plugins")
  1833. @pyqtSlot()
  1834. def slot_tweakPluginReset(self):
  1835. if self.tb_tweak_plugins.currentIndex() == 0:
  1836. self.list_LADSPA.clear()
  1837. for iPath in DEFAULT_LADSPA_PATH:
  1838. self.list_LADSPA.addItem(iPath)
  1839. elif self.tb_tweak_plugins.currentIndex() == 1:
  1840. self.list_DSSI.clear()
  1841. for iPath in DEFAULT_DSSI_PATH:
  1842. self.list_DSSI.addItem(iPath)
  1843. elif self.tb_tweak_plugins.currentIndex() == 2:
  1844. self.list_LV2.clear()
  1845. for iPath in DEFAULT_LV2_PATH:
  1846. self.list_LV2.addItem(iPath)
  1847. elif self.tb_tweak_plugins.currentIndex() == 3:
  1848. self.list_VST.clear()
  1849. for iPath in DEFAULT_VST_PATH:
  1850. self.list_VST.addItem(iPath)
  1851. self.func_settings_changed("plugins")
  1852. @pyqtSlot(int)
  1853. def slot_tweakPluginTypeChanged(self, index):
  1854. # Force row change
  1855. if index == 0:
  1856. self.list_LADSPA.setCurrentRow(-1)
  1857. self.list_LADSPA.setCurrentRow(0)
  1858. elif index == 1:
  1859. self.list_DSSI.setCurrentRow(-1)
  1860. self.list_DSSI.setCurrentRow(0)
  1861. elif index == 2:
  1862. self.list_LV2.setCurrentRow(-1)
  1863. self.list_LV2.setCurrentRow(0)
  1864. elif index == 3:
  1865. self.list_VST.setCurrentRow(-1)
  1866. self.list_VST.setCurrentRow(0)
  1867. @pyqtSlot(int)
  1868. def slot_tweakPluginsLadspaRowChanged(self, index):
  1869. nonRemovable = (index >= 0 and self.list_LADSPA.item(index).text() not in DEFAULT_LADSPA_PATH)
  1870. self.b_tweak_plugins_change.setEnabled(nonRemovable)
  1871. self.b_tweak_plugins_remove.setEnabled(nonRemovable)
  1872. @pyqtSlot(int)
  1873. def slot_tweakPluginsDssiRowChanged(self, index):
  1874. nonRemovable = (index >= 0 and self.list_DSSI.item(index).text() not in DEFAULT_DSSI_PATH)
  1875. self.b_tweak_plugins_change.setEnabled(nonRemovable)
  1876. self.b_tweak_plugins_remove.setEnabled(nonRemovable)
  1877. @pyqtSlot(int)
  1878. def slot_tweakPluginsLv2RowChanged(self, index):
  1879. nonRemovable = (index >= 0 and self.list_LV2.item(index).text() not in DEFAULT_LV2_PATH)
  1880. self.b_tweak_plugins_change.setEnabled(nonRemovable)
  1881. self.b_tweak_plugins_remove.setEnabled(nonRemovable)
  1882. @pyqtSlot(int)
  1883. def slot_tweakPluginsVstRowChanged(self, index):
  1884. nonRemovable = (index >= 0 and self.list_VST.item(index).text() not in DEFAULT_VST_PATH)
  1885. self.b_tweak_plugins_change.setEnabled(nonRemovable)
  1886. self.b_tweak_plugins_remove.setEnabled(nonRemovable)
  1887. def saveSettings(self):
  1888. self.settings.setValue("Geometry", self.saveGeometry())
  1889. GlobalSettings.setValue("JACK/AutoStart", self.cb_jack_autostart.isChecked())
  1890. GlobalSettings.setValue("ALSA-Audio/BridgeIndexType", self.cb_alsa_type.currentIndex())
  1891. GlobalSettings.setValue("A2J/AutoStart", self.cb_a2j_autostart.isChecked())
  1892. GlobalSettings.setValue("Pulse2JACK/AutoStart", (havePulseAudio and self.cb_pulse_autostart.isChecked()))
  1893. def loadSettings(self, geometry):
  1894. if geometry:
  1895. self.restoreGeometry(self.settings.value("Geometry", b""))
  1896. usingAlsaLoop = bool(GlobalSettings.value("ALSA-Audio/BridgeIndexType", iAlsaFileNone, type=int) == iAlsaFileLoop)
  1897. self.cb_jack_autostart.setChecked(GlobalSettings.value("JACK/AutoStart", wantJackStart, type=bool))
  1898. self.cb_a2j_autostart.setChecked(GlobalSettings.value("A2J/AutoStart", True, type=bool))
  1899. self.cb_pulse_autostart.setChecked(GlobalSettings.value("Pulse2JACK/AutoStart", havePulseAudio and not usingAlsaLoop, type=bool))
  1900. def timerEvent(self, event):
  1901. if event.timerId() == self.m_timer500:
  1902. if gDBus.jack and self.m_last_dsp_load != None:
  1903. next_dsp_load = gDBus.jack.GetLoad()
  1904. next_xruns = gDBus.jack.GetXruns()
  1905. needUpdateTip = False
  1906. if self.m_last_dsp_load != next_dsp_load:
  1907. self.m_last_dsp_load = next_dsp_load
  1908. self.label_jack_dsp.setText("%.2f%%" % self.m_last_dsp_load)
  1909. needUpdateTip = True
  1910. if self.m_last_xruns != next_xruns:
  1911. self.m_last_xruns = next_xruns
  1912. self.label_jack_xruns.setText(str(self.m_last_xruns))
  1913. needUpdateTip = True
  1914. if needUpdateTip:
  1915. self.updateSystrayTooltip()
  1916. elif event.timerId() == self.m_timer2000:
  1917. if gDBus.jack and self.m_last_buffer_size != None:
  1918. next_buffer_size = gDBus.jack.GetBufferSize()
  1919. if self.m_last_buffer_size != next_buffer_size:
  1920. self.m_last_buffer_size = next_buffer_size
  1921. self.label_jack_bfsize.setText("%i samples" % self.m_last_buffer_size)
  1922. self.label_jack_latency.setText("%.1f ms" % gDBus.jack.GetLatency())
  1923. else:
  1924. self.update()
  1925. QMainWindow.timerEvent(self, event)
  1926. def closeEvent(self, event):
  1927. self.saveSettings()
  1928. self.systray.handleQtCloseEvent(event)
  1929. # ------------------------------------------------------------------------------------------------------------
  1930. def runFunctionInMainThread(task):
  1931. waiter = QSemaphore(1)
  1932. def taskInMainThread():
  1933. task()
  1934. waiter.release()
  1935. QTimer.singleShot(0, taskInMainThread)
  1936. waiter.tryAcquire()
  1937. #--------------- main ------------------
  1938. if __name__ == '__main__':
  1939. # App initialization
  1940. app = QApplication(sys.argv)
  1941. app.setApplicationName("Cadence")
  1942. app.setApplicationVersion(VERSION)
  1943. app.setOrganizationName("Cadence")
  1944. app.setWindowIcon(QIcon(":/scalable/cadence.svg"))
  1945. if haveDBus:
  1946. gDBus.loop = DBusQtMainLoop(set_as_default=True)
  1947. gDBus.bus = dbus.SessionBus(mainloop=gDBus.loop)
  1948. initSystemChecks()
  1949. # Show GUI
  1950. gui = CadenceMainW()
  1951. # Set-up custom signal handling
  1952. setUpSignals(gui)
  1953. if "--minimized" in app.arguments():
  1954. gui.hide()
  1955. gui.systray.setActionText("show", gui.tr("Restore"))
  1956. app.setQuitOnLastWindowClosed(False)
  1957. else:
  1958. gui.show()
  1959. # Exit properly
  1960. sys.exit(gui.systray.exec_(app))