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.

949 lines
40KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # JACK Settings Dialog
  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 sys import platform, version_info
  20. if True:
  21. from PyQt5.QtCore import pyqtSlot, Qt, QSettings, QTimer
  22. from PyQt5.QtGui import QFontMetrics
  23. from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox
  24. else:
  25. from PyQt4.QtCore import pyqtSlot, Qt, QSettings, QTimer
  26. from PyQt4.QtGui import QFontMetrics
  27. from PyQt4.QtGui import QDialog, QDialogButtonBox, QMessageBox
  28. # ------------------------------------------------------------------------------------------------------------
  29. # Imports (Custom Stuff)
  30. import ui_settings_jack
  31. from shared_i18n import *
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Try Import DBus
  34. try:
  35. import dbus
  36. except:
  37. dbus = None
  38. # ------------------------------------------------------------------------------------------------------------
  39. # Global object
  40. global gJackctl, gResetNeeded
  41. gJackctl = None
  42. gResetNeeded = False
  43. # ------------------------------------------------------------------------------------------------------------
  44. # enum jack_timer_type_t
  45. JACK_TIMER_SYSTEM_CLOCK = 0
  46. JACK_TIMER_CYCLE_COUNTER = 1
  47. JACK_TIMER_HPET = 2
  48. # ------------------------------------------------------------------------------------------------------------
  49. # Set Platform
  50. if "linux" in platform:
  51. LINUX = True
  52. if version_info >= (3, 0):
  53. from subprocess import getoutput
  54. else:
  55. from commands import getoutput
  56. else:
  57. LINUX = False
  58. # ------------------------------------------------------------------------------------------------------------
  59. # Init DBus
  60. def initBus(bus):
  61. global gJackctl
  62. if not bus:
  63. gJackctl = None
  64. return 1
  65. try:
  66. gJackctl = dbus.Interface(bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller"), "org.jackaudio.Configure")
  67. return 0
  68. except:
  69. gJackctl = None
  70. return 1
  71. def needsInit():
  72. global gJackctl
  73. return bool(gJackctl is None)
  74. def setResetNeeded(yesNo):
  75. global gResetNeeded
  76. gResetNeeded = yesNo
  77. # ------------------------------------------------------------------------------------------------------------
  78. # Helper functions
  79. def getBufferSize():
  80. return getDriverParameter("period", -1)
  81. def getSampleRate():
  82. return getDriverParameter("rate", -1)
  83. def isRealtime():
  84. return getEngineParameter("realtime", False)
  85. def setBufferSize(bsize):
  86. return setDriverParameter("period", dbus.UInt32(bsize))
  87. def setSampleRate(srate):
  88. return setDriverParameter("rate", dbus.UInt32(srate))
  89. # ------------------------------------------------------------------------------------------------------------
  90. # Helper functions (engine)
  91. def engineHasFeature(feature):
  92. if gJackctl is None:
  93. return False
  94. try:
  95. featureList = gJackctl.ReadContainer(["engine"])[1]
  96. except:
  97. featureList = ()
  98. return bool(dbus.String(feature) in featureList)
  99. def getEngineParameter(parameter, fallback):
  100. if gJackctl is None or not engineHasFeature(parameter):
  101. return fallback
  102. else:
  103. try:
  104. return gJackctl.GetParameterValue(["engine", parameter])[2]
  105. except:
  106. return fallback
  107. def setEngineParameter(parameter, value, optional=True):
  108. if not engineHasFeature(parameter):
  109. return False
  110. elif optional:
  111. paramValueTry = gJackctl.GetParameterValue(["engine", parameter])
  112. if paramValueTry is None:
  113. return False
  114. paramValue = paramValueTry[2]
  115. if value != paramValue:
  116. return bool(gJackctl.SetParameterValue(["engine", parameter], value))
  117. else:
  118. return False
  119. else:
  120. return bool(gJackctl.SetParameterValue(["engine", parameter], value))
  121. # ------------------------------------------------------------------------------------------------------------
  122. # Helper functions (driver)
  123. def driverHasFeature(feature):
  124. if gJackctl is None:
  125. return False
  126. try:
  127. featureList = gJackctl.ReadContainer(["driver"])[1]
  128. except:
  129. featureList = ()
  130. return bool(dbus.String(feature) in featureList)
  131. def getDriverParameter(parameter, fallback):
  132. if gJackctl is None or not driverHasFeature(parameter):
  133. return fallback
  134. else:
  135. try:
  136. return gJackctl.GetParameterValue(["driver", parameter])[2]
  137. except:
  138. return fallback
  139. def setDriverParameter(parameter, value, optional=True):
  140. if not driverHasFeature(parameter):
  141. return False
  142. elif optional:
  143. if value != gJackctl.GetParameterValue(["driver", parameter])[2]:
  144. return bool(gJackctl.SetParameterValue(["driver", parameter], value))
  145. else:
  146. return False
  147. else:
  148. return bool(gJackctl.SetParameterValue(["driver", parameter], value))
  149. # ------------------------------------------------------------------------------------------------------------
  150. # JACK Settings Dialog
  151. class JackSettingsW(QDialog):
  152. def __init__(self, parent):
  153. QDialog.__init__(self, parent)
  154. self.ui = ui_settings_jack.Ui_JackSettingsW()
  155. self.ui.setupUi(self)
  156. # -------------------------------------------------------------
  157. # Check if we've got valid control interface
  158. global gJackctl
  159. if gJackctl is None:
  160. QTimer.singleShot(0, self, SLOT("slot_closeWithError()"))
  161. return
  162. # -------------------------------------------------------------
  163. # Align driver text and hide non available ones
  164. driverList = gJackctl.ReadContainer(["drivers"])[1]
  165. fontMetris = QFontMetrics(self.ui.obj_server_driver.font())
  166. maxWidth = 75
  167. for i in range(self.ui.obj_server_driver.rowCount()):
  168. item = self.ui.obj_server_driver.item(i, 0)
  169. item.setTextAlignment(Qt.AlignCenter)
  170. itexText = item.text()
  171. itemWidth = fontMetris.width(itexText)+25
  172. if itemWidth > maxWidth:
  173. maxWidth = itemWidth
  174. if dbus.String(itexText.lower()) not in driverList:
  175. self.ui.obj_server_driver.hideRow(i)
  176. self.ui.obj_server_driver.setMinimumWidth(maxWidth)
  177. self.ui.obj_server_driver.setMaximumWidth(maxWidth)
  178. # -------------------------------------------------------------
  179. # Set-up connections
  180. self.accepted.connect(self.slot_saveJackSettings)
  181. self.ui.buttonBox.button(QDialogButtonBox.Reset).clicked.connect(self.slot_resetJackSettings)
  182. self.ui.obj_driver_duplex.clicked.connect(self.slot_checkDuplexSelection)
  183. self.ui.obj_server_driver.currentCellChanged.connect(self.slot_checkDriverSelection)
  184. self.ui.obj_driver_capture.currentIndexChanged[int].connect(self.slot_checkALSASelection)
  185. self.ui.obj_driver_playback.currentIndexChanged[int].connect(self.slot_checkALSASelection)
  186. # -------------------------------------------------------------
  187. # Load initial settings
  188. self.fDriverName = ""
  189. self.fBrokenServerClockSource = False
  190. self.checkEngine()
  191. self.loadServerSettings()
  192. self.loadDriverSettings(True) # reset because we'll change it below
  193. # -------------------------------------------------------------
  194. # Load selected JACK driver
  195. self.fDriverName = str(gJackctl.GetParameterValue(["engine", "driver"])[2])
  196. for i in range(self.ui.obj_server_driver.rowCount()):
  197. if self.ui.obj_server_driver.item(i, 0).text().lower() == self.fDriverName:
  198. self.ui.obj_server_driver.setCurrentCell(i, 0)
  199. break
  200. # Special ALSA check
  201. self.slot_checkALSASelection()
  202. # -------------------------------------------------------------
  203. # Load last GUI settings
  204. self.loadSettings()
  205. # -----------------------------------------------------------------
  206. # Engine calls
  207. def checkEngine(self):
  208. self.ui.obj_server_realtime.setEnabled(engineHasFeature("realtime"))
  209. self.ui.obj_server_realtime_priority.setEnabled(engineHasFeature("realtime-priority"))
  210. self.ui.obj_server_temporary.setEnabled(engineHasFeature("temporary"))
  211. self.ui.obj_server_verbose.setEnabled(engineHasFeature("verbose"))
  212. self.ui.obj_server_alias.setEnabled(engineHasFeature("alias"))
  213. self.ui.obj_server_client_timeout.setEnabled(engineHasFeature("client-timeout"))
  214. self.ui.obj_server_clock_source.setEnabled(engineHasFeature("clock-source"))
  215. self.ui.obj_server_port_max.setEnabled(engineHasFeature("port-max"))
  216. self.ui.obj_server_replace_registry.setEnabled(engineHasFeature("replace-registry"))
  217. self.ui.obj_server_sync.setEnabled(engineHasFeature("sync"))
  218. self.ui.obj_server_self_connect_mode.setEnabled(engineHasFeature("self-connect-mode"))
  219. # Disable clock-source if not on Linux
  220. if not LINUX:
  221. self.ui.obj_server_clock_source.setEnabled(False)
  222. # -----------------------------------------------------------------
  223. # Server calls
  224. def saveServerSettings(self):
  225. # always reset server name
  226. if engineHasFeature("name"):
  227. setEngineParameter("name", "default", True)
  228. if self.ui.obj_server_realtime.isEnabled():
  229. value = dbus.Boolean(self.ui.obj_server_realtime.isChecked())
  230. setEngineParameter("realtime", value, True)
  231. if self.ui.obj_server_realtime_priority.isEnabled():
  232. value = dbus.Int32(self.ui.obj_server_realtime_priority.value())
  233. setEngineParameter("realtime-priority", value, True)
  234. if self.ui.obj_server_temporary.isEnabled():
  235. value = dbus.Boolean(self.ui.obj_server_temporary.isChecked())
  236. setEngineParameter("temporary", value, True)
  237. if self.ui.obj_server_verbose.isEnabled():
  238. value = dbus.Boolean(self.ui.obj_server_verbose.isChecked())
  239. setEngineParameter("verbose", value, True)
  240. if self.ui.obj_server_alias.isEnabled():
  241. value = dbus.Boolean(self.ui.obj_server_alias.isChecked())
  242. setEngineParameter("alias", value, True)
  243. if self.ui.obj_server_client_timeout.isEnabled():
  244. value = dbus.Int32(int(self.ui.obj_server_client_timeout.currentText()))
  245. setEngineParameter("client-timeout", value, True)
  246. if self.ui.obj_server_clock_source.isEnabled():
  247. if self.ui.obj_server_clock_source_system.isChecked():
  248. if self.fBrokenServerClockSource:
  249. value = dbus.UInt32(JACK_TIMER_SYSTEM_CLOCK)
  250. else:
  251. value = dbus.Byte("s".encode("utf-8"))
  252. elif self.ui.obj_server_clock_source_cycle.isChecked():
  253. if self.fBrokenServerClockSource:
  254. value = dbus.UInt32(JACK_TIMER_CYCLE_COUNTER)
  255. else:
  256. value = dbus.Byte("c".encode("utf-8"))
  257. elif self.ui.obj_server_clock_source_hpet.isChecked():
  258. if self.fBrokenServerClockSource:
  259. value = dbus.UInt32(JACK_TIMER_HPET)
  260. else:
  261. value = dbus.Byte("h".encode("utf-8"))
  262. else:
  263. value = None
  264. print("JackSettingsW::saveServerSettings() - Cannot save clock-source value")
  265. if value != None:
  266. setEngineParameter("clock-source", value, True)
  267. if self.ui.obj_server_port_max.isEnabled():
  268. value = dbus.UInt32(int(self.ui.obj_server_port_max.currentText()))
  269. setEngineParameter("port-max", value, True)
  270. if self.ui.obj_server_replace_registry.isEnabled():
  271. value = dbus.Boolean(self.ui.obj_server_replace_registry.isChecked())
  272. setEngineParameter("replace-registry", value, True)
  273. if self.ui.obj_server_sync.isEnabled():
  274. value = dbus.Boolean(self.ui.obj_server_sync.isChecked())
  275. setEngineParameter("sync", value, True)
  276. if self.ui.obj_server_self_connect_mode.isEnabled():
  277. if self.ui.obj_server_self_connect_mode_0.isChecked():
  278. value = dbus.Byte(" ".encode("utf-8"))
  279. elif self.ui.obj_server_self_connect_mode_1.isChecked():
  280. value = dbus.Byte("E".encode("utf-8"))
  281. elif self.ui.obj_server_self_connect_mode_2.isChecked():
  282. value = dbus.Byte("e".encode("utf-8"))
  283. elif self.ui.obj_server_self_connect_mode_3.isChecked():
  284. value = dbus.Byte("A".encode("utf-8"))
  285. elif self.ui.obj_server_self_connect_mode_4.isChecked():
  286. value = dbus.Byte("a".encode("utf-8"))
  287. else:
  288. value = None
  289. print("JackSettingsW::saveServerSettings() - Cannot save self-connect-mode value")
  290. if value != None:
  291. setEngineParameter("self-connect-mode", value, True)
  292. def loadServerSettings(self, reset=False, forceReset=False):
  293. global gJackctl
  294. settings = gJackctl.ReadContainer(["engine"])
  295. for i in range(len(settings[1])):
  296. attribute = str(settings[1][i])
  297. if reset:
  298. valueTry = gJackctl.GetParameterValue(["engine", attribute])
  299. if valueTry is None:
  300. continue
  301. else:
  302. value = valueTry[1]
  303. if forceReset and attribute != "driver":
  304. gJackctl.ResetParameterValue(["engine", attribute])
  305. else:
  306. valueTry = gJackctl.GetParameterValue(["engine", attribute])
  307. if valueTry is None:
  308. continue
  309. else:
  310. value = valueTry[2]
  311. if attribute == "name":
  312. pass # Don't allow to change this
  313. elif attribute == "realtime":
  314. self.ui.obj_server_realtime.setChecked(bool(value))
  315. elif attribute == "realtime-priority":
  316. self.ui.obj_server_realtime_priority.setValue(int(value))
  317. elif attribute == "temporary":
  318. self.ui.obj_server_temporary.setChecked(bool(value))
  319. elif attribute == "verbose":
  320. self.ui.obj_server_verbose.setChecked(bool(value))
  321. elif attribute == "alias":
  322. self.ui.obj_server_alias.setChecked(bool(value))
  323. elif attribute == "client-timeout":
  324. self.setComboBoxValue(self.ui.obj_server_client_timeout, str(value))
  325. elif attribute == "clock-source":
  326. value = str(value)
  327. if value == "c":
  328. self.ui.obj_server_clock_source_cycle.setChecked(True)
  329. elif value == "h":
  330. self.ui.obj_server_clock_source_hpet.setChecked(True)
  331. elif value == "s":
  332. self.ui.obj_server_clock_source_system.setChecked(True)
  333. else:
  334. self.fBrokenServerClockSource = True
  335. if value == str(JACK_TIMER_SYSTEM_CLOCK):
  336. self.ui.obj_server_clock_source_system.setChecked(True)
  337. elif value == str(JACK_TIMER_CYCLE_COUNTER):
  338. self.ui.obj_server_clock_source_cycle.setChecked(True)
  339. elif value == str(JACK_TIMER_HPET):
  340. self.ui.obj_server_clock_source_hpet.setChecked(True)
  341. else:
  342. self.ui.obj_server_clock_source.setEnabled(False)
  343. print("JackSettingsW::saveServerSettings() - Invalid clock-source value '%s'" % value)
  344. elif attribute == "port-max":
  345. self.setComboBoxValue(self.ui.obj_server_port_max, str(value))
  346. elif attribute == "replace-registry":
  347. self.ui.obj_server_replace_registry.setChecked(bool(value))
  348. elif attribute == "sync":
  349. self.ui.obj_server_sync.setChecked(bool(value))
  350. elif attribute == "self-connect-mode":
  351. value = str(value)
  352. if value == " ":
  353. self.ui.obj_server_self_connect_mode_0.setChecked(True)
  354. elif value == "E":
  355. self.ui.obj_server_self_connect_mode_1.setChecked(True)
  356. elif value == "e":
  357. self.ui.obj_server_self_connect_mode_2.setChecked(True)
  358. elif value == "A":
  359. self.ui.obj_server_self_connect_mode_3.setChecked(True)
  360. elif value == "a":
  361. self.ui.obj_server_self_connect_mode_4.setChecked(True)
  362. else:
  363. self.ui.obj_server_self_connect_mode.setEnabled(False)
  364. print("JackSettingsW::loadServerSettings() - Invalid self-connect-mode value '%s'" % value)
  365. elif attribute in ("driver", "slave-drivers"):
  366. pass
  367. else:
  368. print("JackSettingsW::loadServerSettings() - Unimplemented server attribute '%s', value: '%s'" % (attribute, str(value)))
  369. # -----------------------------------------------------------------
  370. # Driver calls
  371. # resetIfNeeded: fix alsa parameter re-order bug in JACK 1.9.8 (reset/remove non-used values)
  372. def saveDriverSettings(self, resetIfNeeded):
  373. global gJackctl, gResetNeeded
  374. if resetIfNeeded and not gResetNeeded:
  375. resetIfNeeded = False
  376. if self.ui.obj_driver_device.isEnabled():
  377. value = dbus.String(self.ui.obj_driver_device.currentText().split(" [")[0])
  378. if value != gJackctl.GetParameterValue(["driver", "device"])[2]:
  379. gJackctl.SetParameterValue(["driver", "device"], value)
  380. elif resetIfNeeded:
  381. gJackctl.ResetParameterValue(["driver", "device"])
  382. if self.ui.obj_driver_capture.isEnabled():
  383. if self.fDriverName == "alsa":
  384. value = dbus.String(self.ui.obj_driver_capture.currentText().split(" ")[0])
  385. elif self.fDriverName == "dummy":
  386. value = dbus.UInt32(int(self.ui.obj_driver_capture.currentText()))
  387. elif self.fDriverName == "firewire":
  388. value = dbus.Boolean(self.ui.obj_driver_capture.currentIndex() == 1)
  389. else:
  390. value = None
  391. print("JackSettingsW::saveDriverSettings() - Cannot save capture value")
  392. if value != None:
  393. setDriverParameter("capture", value, True)
  394. elif resetIfNeeded:
  395. gJackctl.ResetParameterValue(["driver", "capture"])
  396. if self.ui.obj_driver_playback.isEnabled():
  397. if self.fDriverName == "alsa":
  398. value = dbus.String(self.ui.obj_driver_playback.currentText().split(" ")[0])
  399. elif self.fDriverName == "dummy":
  400. value = dbus.UInt32(int(self.ui.obj_driver_playback.currentText()))
  401. elif self.fDriverName == "firewire":
  402. value = dbus.Boolean(self.ui.obj_driver_playback.currentIndex() == 1)
  403. else:
  404. value = None
  405. print("JackSettingsW::saveDriverSettings() - Cannot save playback value")
  406. if value != None:
  407. setDriverParameter("playback", value, True)
  408. elif resetIfNeeded:
  409. gJackctl.ResetParameterValue(["driver", "playback"])
  410. if self.ui.obj_driver_rate.isEnabled():
  411. value = dbus.UInt32(int(self.ui.obj_driver_rate.currentText()))
  412. setDriverParameter("rate", value, True)
  413. if self.ui.obj_driver_period.isEnabled():
  414. value = dbus.UInt32(int(self.ui.obj_driver_period.currentText()))
  415. setDriverParameter("period", value, True)
  416. if self.ui.obj_driver_nperiods.isEnabled():
  417. value = dbus.UInt32(self.ui.obj_driver_nperiods.value())
  418. setDriverParameter("nperiods", value, True)
  419. if self.ui.obj_driver_hwmon.isEnabled():
  420. value = dbus.Boolean(self.ui.obj_driver_hwmon.isChecked())
  421. setDriverParameter("hwmon", value, True)
  422. if self.ui.obj_driver_hwmeter.isEnabled():
  423. value = dbus.Boolean(self.ui.obj_driver_hwmeter.isChecked())
  424. setDriverParameter("hwmeter", value, True)
  425. if self.ui.obj_driver_duplex.isEnabled():
  426. value = dbus.Boolean(self.ui.obj_driver_duplex.isChecked())
  427. setDriverParameter("duplex", value, True)
  428. if self.ui.obj_driver_hw_alias.isEnabled():
  429. value = dbus.Boolean(self.ui.obj_driver_hw_alias.isChecked())
  430. setDriverParameter("hw-alias", value, True)
  431. if self.ui.obj_driver_softmode.isEnabled():
  432. value = dbus.Boolean(self.ui.obj_driver_softmode.isChecked())
  433. setDriverParameter("softmode", value, True)
  434. if self.ui.obj_driver_monitor.isEnabled():
  435. value = dbus.Boolean(self.ui.obj_driver_monitor.isChecked())
  436. setDriverParameter("monitor", value, True)
  437. if self.ui.obj_driver_dither.isEnabled():
  438. if self.ui.obj_driver_dither.currentIndex() == 0:
  439. value = dbus.Byte("n".encode("utf-8"))
  440. elif self.ui.obj_driver_dither.currentIndex() == 1:
  441. value = dbus.Byte("r".encode("utf-8"))
  442. elif self.ui.obj_driver_dither.currentIndex() == 2:
  443. value = dbus.Byte("s".encode("utf-8"))
  444. elif self.ui.obj_driver_dither.currentIndex() == 3:
  445. value = dbus.Byte("t".encode("utf-8"))
  446. else:
  447. value = None
  448. print("JackSettingsW::saveDriverSettings() - Cannot save dither value")
  449. if value != None:
  450. setDriverParameter("dither", value, True)
  451. if self.ui.obj_driver_inchannels.isEnabled():
  452. value = dbus.UInt32(self.ui.obj_driver_inchannels.value())
  453. setDriverParameter("inchannels", value, True)
  454. if self.ui.obj_driver_outchannels.isEnabled():
  455. value = dbus.UInt32(self.ui.obj_driver_outchannels.value())
  456. setDriverParameter("outchannels", value, True)
  457. if self.ui.obj_driver_shorts.isEnabled():
  458. value = dbus.Boolean(self.ui.obj_driver_shorts.isChecked())
  459. setDriverParameter("shorts", value, True)
  460. if self.ui.obj_driver_input_latency.isEnabled():
  461. value = dbus.UInt32(self.ui.obj_driver_input_latency.value())
  462. setDriverParameter("input-latency", value, True)
  463. if self.ui.obj_driver_output_latency.isEnabled():
  464. value = dbus.UInt32(self.ui.obj_driver_output_latency.value())
  465. setDriverParameter("output-latency", value, True)
  466. if self.ui.obj_driver_midi_driver.isEnabled():
  467. if self.ui.obj_driver_midi_driver.currentIndex() == 0:
  468. value = dbus.String("none")
  469. elif self.ui.obj_driver_midi_driver.currentIndex() == 1:
  470. value = dbus.String("seq")
  471. elif self.ui.obj_driver_midi_driver.currentIndex() == 2:
  472. value = dbus.String("raw")
  473. else:
  474. value = None
  475. print("JackSettingsW::saveDriverSettings() - Cannot save midi-driver value")
  476. if value != None:
  477. if driverHasFeature("midi"):
  478. setDriverParameter("midi", value, True)
  479. else:
  480. setDriverParameter("midi-driver", value, True)
  481. if self.ui.obj_driver_wait.isEnabled():
  482. value = dbus.UInt32(self.ui.obj_driver_wait.value())
  483. setDriverParameter("wait", value, True)
  484. if self.ui.obj_driver_verbose.isEnabled():
  485. value = dbus.UInt32(self.ui.obj_driver_verbose.value())
  486. setDriverParameter("verbose", value, True)
  487. if self.ui.obj_driver_snoop.isEnabled():
  488. value = dbus.Boolean(self.ui.obj_driver_snoop.isChecked())
  489. setDriverParameter("snoop", value, True)
  490. if self.ui.obj_driver_channels.isEnabled():
  491. value = dbus.Int32(self.ui.obj_driver_channels.value())
  492. setDriverParameter("channels", value, True)
  493. def loadDriverSettings(self, reset=False, forceReset=False):
  494. global gJackctl
  495. settings = gJackctl.ReadContainer(["driver"])
  496. for i in range(len(settings[1])):
  497. attribute = str(settings[1][i])
  498. if reset:
  499. value = gJackctl.GetParameterValue(["driver", attribute])[1]
  500. if forceReset:
  501. gJackctl.ResetParameterValue(["driver", attribute])
  502. else:
  503. value = gJackctl.GetParameterValue(["driver", attribute])[2]
  504. if attribute == "device":
  505. self.setComboBoxValue(self.ui.obj_driver_device, str(value), True)
  506. elif attribute == "capture":
  507. if self.fDriverName == "firewire":
  508. self.ui.obj_driver_capture.setCurrentIndex(1 if bool(value) else 0)
  509. else:
  510. self.setComboBoxValue(self.ui.obj_driver_capture, str(value), True)
  511. elif attribute == "playback":
  512. if self.fDriverName == "firewire":
  513. self.ui.obj_driver_playback.setCurrentIndex(1 if bool(value) else 0)
  514. else:
  515. self.setComboBoxValue(self.ui.obj_driver_playback, str(value), True)
  516. elif attribute == "rate":
  517. self.setComboBoxValue(self.ui.obj_driver_rate, str(value))
  518. elif attribute == "period":
  519. self.setComboBoxValue(self.ui.obj_driver_period, str(value))
  520. elif attribute == "nperiods":
  521. self.ui.obj_driver_nperiods.setValue(int(value))
  522. elif attribute == "hwmon":
  523. self.ui.obj_driver_hwmon.setChecked(bool(value))
  524. elif attribute == "hwmeter":
  525. self.ui.obj_driver_hwmeter.setChecked(bool(value))
  526. elif attribute == "duplex":
  527. self.ui.obj_driver_duplex.setChecked(bool(value))
  528. elif attribute == "hw-alias":
  529. self.ui.obj_driver_hw_alias.setChecked(bool(value))
  530. elif attribute == "softmode":
  531. self.ui.obj_driver_softmode.setChecked(bool(value))
  532. elif attribute == "monitor":
  533. self.ui.obj_driver_monitor.setChecked(bool(value))
  534. elif attribute == "dither":
  535. value = str(value)
  536. if value == "n":
  537. self.ui.obj_driver_dither.setCurrentIndex(0)
  538. elif value == "r":
  539. self.ui.obj_driver_dither.setCurrentIndex(1)
  540. elif value == "s":
  541. self.ui.obj_driver_dither.setCurrentIndex(2)
  542. elif value == "t":
  543. self.ui.obj_driver_dither.setCurrentIndex(3)
  544. else:
  545. self.ui.obj_driver_dither.setEnabled(False)
  546. print("JackSettingsW::loadDriverSettings() - Invalid dither value '%s'" % value)
  547. elif attribute == "inchannels":
  548. self.ui.obj_driver_inchannels.setValue(int(value))
  549. elif attribute == "outchannels":
  550. self.ui.obj_driver_outchannels.setValue(int(value))
  551. elif attribute == "shorts":
  552. self.ui.obj_driver_shorts.setChecked(bool(value))
  553. elif attribute == "input-latency":
  554. self.ui.obj_driver_input_latency.setValue(int(value))
  555. elif attribute == "output-latency":
  556. self.ui.obj_driver_output_latency.setValue(int(value))
  557. elif attribute in ("midi", "midi-driver"):
  558. value = str(value)
  559. if value == "none":
  560. self.ui.obj_driver_midi_driver.setCurrentIndex(0)
  561. elif value == "seq":
  562. self.ui.obj_driver_midi_driver.setCurrentIndex(1)
  563. elif value == "raw":
  564. self.ui.obj_driver_midi_driver.setCurrentIndex(2)
  565. else:
  566. self.ui.obj_driver_midi_driver.setEnabled(False)
  567. print("JackSettingsW::loadDriverSettings() - Invalid midi-driver value '%s'" % value)
  568. elif attribute == "wait":
  569. self.ui.obj_driver_wait.setValue(int(value))
  570. elif attribute == "verbose":
  571. self.ui.obj_driver_verbose.setValue(int(value))
  572. elif attribute == "snoop":
  573. self.ui.obj_driver_snoop.setChecked(bool(value))
  574. elif attribute == "channels":
  575. self.ui.obj_driver_channels.setValue(int(value))
  576. else:
  577. print("JackSettingsW::loadDriverSettings() - Unimplemented driver attribute '%s', value: '%s'" % (attribute, str(value)))
  578. # -----------------------------------------------------------------
  579. # Helper functions
  580. def getAlsaDeviceList(self):
  581. alsaDeviceList = []
  582. aplay_out = getoutput("env LANG=C LC_ALL=C aplay -l").split("\n")
  583. for line in aplay_out:
  584. line = line.strip()
  585. if line.startswith("card "):
  586. cardInfo = line.split(", ", 1)[0].split(": ")
  587. cardIndex = cardInfo[0].replace("card ", "")
  588. cardName = cardInfo[1].split(" [")[0]
  589. deviceInfo = line.split(", ", 1)[1].split(": ")
  590. deviceIndex = deviceInfo[0].replace("device ", "")
  591. deviceName = deviceInfo[1].split(" [")[0]
  592. if cardName != "Loopback":
  593. fullName = "hw:%s,%s [%s]" % (cardName, deviceIndex, deviceName)
  594. alsaDeviceList.append(fullName)
  595. return alsaDeviceList
  596. def setComboBoxValue(self, box, text, split=False):
  597. for i in range(box.count()):
  598. if box.itemText(i) == text or (box.itemText(i).split(" ")[0] == text and split):
  599. box.setCurrentIndex(i)
  600. break
  601. else:
  602. if text:
  603. box.addItem(text)
  604. box.setCurrentIndex(box.count() - 1)
  605. # -----------------------------------------------------------------
  606. # Qt SLOT calls
  607. @pyqtSlot(int)
  608. def slot_checkALSASelection(self, ignored=0):
  609. if self.fDriverName == "alsa":
  610. check = bool(self.ui.obj_driver_duplex.isChecked() and (self.ui.obj_driver_capture.currentIndex() > 0 or self.ui.obj_driver_playback.currentIndex() > 0))
  611. self.ui.obj_driver_device.setEnabled(not check)
  612. @pyqtSlot(bool)
  613. def slot_checkDuplexSelection(self, active):
  614. if driverHasFeature("duplex"):
  615. self.ui.obj_driver_capture.setEnabled(active)
  616. self.ui.obj_driver_capture_label.setEnabled(active)
  617. self.ui.obj_driver_playback.setEnabled(active)
  618. self.ui.obj_driver_playback_label.setEnabled(active)
  619. #self.ui.obj_driver_inchannels.setEnabled(active)
  620. #self.ui.obj_driver_inchannels_label.setEnabled(active)
  621. #self.ui.obj_driver_input_latency.setEnabled(active)
  622. #self.ui.obj_driver_input_latency_label.setEnabled(active)
  623. self.slot_checkALSASelection()
  624. @pyqtSlot(int)
  625. def slot_checkDriverSelection(self, row):
  626. global gJackctl
  627. # Save previous settings
  628. self.saveDriverSettings(False)
  629. # Set new Jack driver
  630. self.fDriverName = dbus.String(self.ui.obj_server_driver.item(row, 0).text().lower())
  631. gJackctl.SetParameterValue(["engine", "driver"], self.fDriverName)
  632. # Add device list
  633. self.ui.obj_driver_device.clear()
  634. if driverHasFeature("device"):
  635. if LINUX and self.fDriverName == "alsa":
  636. dev_list = self.getAlsaDeviceList()
  637. for dev in dev_list:
  638. self.ui.obj_driver_device.addItem(dev)
  639. else:
  640. dev_list = gJackctl.GetParameterConstraint(["driver", "device"])[3]
  641. for i in range(len(dev_list)):
  642. self.ui.obj_driver_device.addItem(dev_list[i][0] + " [%s]" % str(dev_list[i][1]))
  643. # Custom 'playback' and 'capture' values
  644. self.ui.obj_driver_capture.clear()
  645. self.ui.obj_driver_playback.clear()
  646. if self.fDriverName == "alsa":
  647. self.ui.obj_driver_capture.addItem("none")
  648. self.ui.obj_driver_playback.addItem("none")
  649. if LINUX:
  650. dev_list = self.getAlsaDeviceList()
  651. for dev in dev_list:
  652. self.ui.obj_driver_capture.addItem(dev)
  653. self.ui.obj_driver_playback.addItem(dev)
  654. else:
  655. dev_list = gJackctl.GetParameterConstraint(["driver", "device"])[3]
  656. for i in range(len(dev_list)):
  657. self.ui.obj_driver_capture.addItem(dev_list[i][0] + " [" + dev_list[i][1] + "]")
  658. self.ui.obj_driver_playback.addItem(dev_list[i][0] + " [" + dev_list[i][1] + "]")
  659. elif self.fDriverName == "dummy":
  660. for i in range(16):
  661. self.ui.obj_driver_capture.addItem("%i" % int((i * 2) + 2))
  662. self.ui.obj_driver_playback.addItem("%i" % int((i * 2) + 2))
  663. elif self.fDriverName == "firewire":
  664. self.ui.obj_driver_capture.addItem("no")
  665. self.ui.obj_driver_capture.addItem("yes")
  666. self.ui.obj_driver_playback.addItem("no")
  667. self.ui.obj_driver_playback.addItem("yes")
  668. elif driverHasFeature("playback") or driverHasFeature("capture"):
  669. print("JackSettingsW::slot_checkDriverSelection() - Custom playback/capture for driver '%s' not implemented yet" % self.fDriverName)
  670. # Load Driver Settings
  671. self.loadDriverSettings()
  672. # Enable widgets according to driver
  673. self.ui.obj_driver_capture.setEnabled(driverHasFeature("capture"))
  674. self.ui.obj_driver_capture_label.setEnabled(driverHasFeature("capture"))
  675. self.ui.obj_driver_playback.setEnabled(driverHasFeature("playback"))
  676. self.ui.obj_driver_playback_label.setEnabled(driverHasFeature("playback"))
  677. self.ui.obj_driver_device.setEnabled(driverHasFeature("device"))
  678. self.ui.obj_driver_device_label.setEnabled(driverHasFeature("device"))
  679. self.ui.obj_driver_rate.setEnabled(driverHasFeature("rate"))
  680. self.ui.obj_driver_rate_label.setEnabled(driverHasFeature("rate"))
  681. self.ui.obj_driver_period.setEnabled(driverHasFeature("period"))
  682. self.ui.obj_driver_period_label.setEnabled(driverHasFeature("period"))
  683. self.ui.obj_driver_nperiods.setEnabled(driverHasFeature("nperiods"))
  684. self.ui.obj_driver_nperiods_label.setEnabled(driverHasFeature("nperiods"))
  685. self.ui.obj_driver_hwmon.setEnabled(driverHasFeature("hwmon"))
  686. self.ui.obj_driver_hwmeter.setEnabled(driverHasFeature("hwmeter"))
  687. self.ui.obj_driver_duplex.setEnabled(driverHasFeature("duplex"))
  688. self.ui.obj_driver_hw_alias.setEnabled(driverHasFeature("hw-alias"))
  689. self.ui.obj_driver_softmode.setEnabled(driverHasFeature("softmode"))
  690. self.ui.obj_driver_monitor.setEnabled(driverHasFeature("monitor"))
  691. self.ui.obj_driver_dither.setEnabled(driverHasFeature("dither"))
  692. self.ui.obj_driver_dither_label.setEnabled(driverHasFeature("dither"))
  693. self.ui.obj_driver_inchannels.setEnabled(driverHasFeature("inchannels"))
  694. self.ui.obj_driver_inchannels_label.setEnabled(driverHasFeature("inchannels"))
  695. self.ui.obj_driver_outchannels.setEnabled(driverHasFeature("outchannels"))
  696. self.ui.obj_driver_outchannels_label.setEnabled(driverHasFeature("outchannels"))
  697. self.ui.obj_driver_shorts.setEnabled(driverHasFeature("shorts"))
  698. self.ui.obj_driver_input_latency.setEnabled(driverHasFeature("input-latency"))
  699. self.ui.obj_driver_input_latency_label.setEnabled(driverHasFeature("input-latency"))
  700. self.ui.obj_driver_output_latency.setEnabled(driverHasFeature("output-latency"))
  701. self.ui.obj_driver_output_latency_label.setEnabled(driverHasFeature("output-latency"))
  702. self.ui.obj_driver_midi_driver.setEnabled(driverHasFeature("midi") or driverHasFeature("midi-driver"))
  703. self.ui.obj_driver_midi_driver_label.setEnabled(driverHasFeature("midi") or driverHasFeature("midi-driver"))
  704. self.ui.obj_driver_wait.setEnabled(driverHasFeature("wait"))
  705. self.ui.obj_driver_wait_label.setEnabled(driverHasFeature("wait"))
  706. self.ui.obj_driver_verbose.setEnabled(driverHasFeature("verbose"))
  707. self.ui.obj_driver_verbose_label.setEnabled(driverHasFeature("verbose"))
  708. self.ui.obj_driver_snoop.setEnabled(driverHasFeature("snoop"))
  709. self.ui.obj_driver_channels.setEnabled(driverHasFeature("channels"))
  710. self.ui.obj_driver_channels_label.setEnabled(driverHasFeature("channels"))
  711. # Misc stuff
  712. if self.ui.obj_server_driver.item(row, 0).text() == "ALSA":
  713. self.ui.toolbox_driver_misc.setCurrentIndex(1)
  714. self.ui.obj_driver_capture_label.setText(self.tr("Input Device:"))
  715. self.ui.obj_driver_playback_label.setText(self.tr("Output Device:"))
  716. elif self.ui.obj_server_driver.item(row, 0).text() == "Dummy":
  717. self.ui.toolbox_driver_misc.setCurrentIndex(2)
  718. self.ui.obj_driver_capture_label.setText(self.tr("Input Ports:"))
  719. self.ui.obj_driver_playback_label.setText(self.tr("Output Ports:"))
  720. elif self.ui.obj_server_driver.item(row, 0).text() == "FireWire":
  721. self.ui.toolbox_driver_misc.setCurrentIndex(3)
  722. self.ui.obj_driver_capture_label.setText(self.tr("Capture Ports:"))
  723. self.ui.obj_driver_playback_label.setText(self.tr("Playback Ports:"))
  724. elif self.ui.obj_server_driver.item(row, 0).text() == "Loopback":
  725. self.ui.toolbox_driver_misc.setCurrentIndex(4)
  726. else:
  727. self.ui.toolbox_driver_misc.setCurrentIndex(0)
  728. self.slot_checkDuplexSelection(self.ui.obj_driver_duplex.isChecked())
  729. @pyqtSlot()
  730. def slot_saveJackSettings(self):
  731. self.saveServerSettings()
  732. self.saveDriverSettings(True)
  733. @pyqtSlot()
  734. def slot_resetJackSettings(self):
  735. if self.ui.tabWidget.currentIndex() == 0:
  736. self.loadServerSettings(True, True)
  737. elif self.ui.tabWidget.currentIndex() == 1:
  738. self.loadDriverSettings(True, True)
  739. @pyqtSlot()
  740. def slot_closeWithError(self):
  741. QMessageBox.critical(self, self.tr("Error"), self.tr("jackdbus is not available!\nIt's not possible to configure JACK at this point."))
  742. self.close()
  743. def saveSettings(self):
  744. settings = QSettings("Cadence", "JackSettings")
  745. settings.setValue("Geometry", self.saveGeometry())
  746. settings.setValue("CurrentTab", self.ui.tabWidget.currentIndex())
  747. def loadSettings(self):
  748. settings = QSettings("Cadence", "JackSettings")
  749. self.restoreGeometry(settings.value("Geometry", b""))
  750. self.ui.tabWidget.setCurrentIndex(settings.value("CurrentTab", 0, type=int))
  751. def closeEvent(self, event):
  752. self.saveSettings()
  753. QDialog.closeEvent(self, event)
  754. def done(self, r):
  755. QDialog.done(self, r)
  756. self.close()
  757. # ------------------------------------------------------------------------------------------------------------
  758. # Allow to use this as a standalone app
  759. if __name__ == '__main__':
  760. # Additional imports
  761. import resources_rc
  762. from sys import argv as sys_argv, exit as sys_exit
  763. from PyQt5.QtGui import QIcon
  764. from PyQt5.QtWidgets import QApplication
  765. # App initialization
  766. app = QApplication(sys_argv)
  767. setup_i18n()
  768. # Connect to DBus
  769. if dbus:
  770. if initBus(dbus.SessionBus()):
  771. QMessageBox.critical(None, app.translate("JackSettingsW", "Error"), app.translate("JackSettingsW",
  772. "jackdbus is not available!\n"
  773. "Is not possible to configure JACK at this point."))
  774. sys_exit(1)
  775. else:
  776. QMessageBox.critical(None, app.translate("JackSettingsW", "Error"),
  777. app.translate("JackSettingsW", "DBus is not available, cannot continue."))
  778. sys_exit(1)
  779. # Show GUI
  780. gui = JackSettingsW(None)
  781. gui.setWindowIcon(QIcon(":/scalable/jack.svg"))
  782. gui.show()
  783. # App-Loop
  784. sys_exit(app.exec_())