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.

1451 lines
55KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # JACK Patchbay
  4. # Copyright (C) 2010-2013 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 PyQt4.QtGui import QApplication
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Custom Stuff)
  22. import ui_catia
  23. from shared_canvasjack import *
  24. from shared_settings import *
  25. # ------------------------------------------------------------------------------------------------------------
  26. # Try Import DBus
  27. try:
  28. import dbus
  29. from dbus.mainloop.qt import DBusQtMainLoop
  30. haveDBus = True
  31. except:
  32. haveDBus = False
  33. # ------------------------------------------------------------------------------------------------------------
  34. # Try Import OpenGL
  35. try:
  36. from PyQt4.QtOpenGL import QGLWidget
  37. hasGL = True
  38. except:
  39. hasGL = False
  40. # ------------------------------------------------------------------------------------------------------------
  41. # Check for ALSA-MIDI
  42. haveALSA = False
  43. if LINUX:
  44. for iPATH in PATH:
  45. if not os.path.exists(os.path.join(iPATH, "aconnect")):
  46. continue
  47. haveALSA = True
  48. if sys.version_info >= (3, 0):
  49. from subprocess import getoutput
  50. else:
  51. from commands import getoutput
  52. if DEBUG:
  53. print("Using experimental ALSA-MIDI support")
  54. break
  55. # ------------------------------------------------------------------------------------------------------------
  56. # Global Variables
  57. global gA2JClientName
  58. gA2JClientName = None
  59. # ------------------------------------------------------------------------------------------------------------
  60. # Static Variables
  61. GROUP_TYPE_NULL = 0
  62. GROUP_TYPE_ALSA = 1
  63. GROUP_TYPE_JACK = 2
  64. iGroupId = 0
  65. iGroupName = 1
  66. iGroupType = 2
  67. iPortId = 0
  68. iPortName = 1
  69. iPortNameR = 2
  70. iPortGroupName = 3
  71. iConnId = 0
  72. iConnOutput = 1
  73. iConnInput = 2
  74. URI_CANVAS_ICON = "http://kxstudio.sf.net/ns/canvas/icon"
  75. # ------------------------------------------------------------------------------------------------------------
  76. # Catia Main Window
  77. class CatiaMainW(AbstractCanvasJackClass):
  78. def __init__(self, parent=None):
  79. AbstractCanvasJackClass.__init__(self, "Catia", ui_catia.Ui_CatiaMainW, parent)
  80. self.fGroupList = []
  81. self.fGroupSplitList = []
  82. self.fPortList = []
  83. self.fConnectionList = []
  84. self.fLastGroupId = 1
  85. self.fLastPortId = 1
  86. self.fLastConnectionId = 1
  87. self.loadSettings(True)
  88. # -------------------------------------------------------------
  89. # Set-up GUI
  90. setIcons(self, ["canvas", "jack", "transport", "misc"])
  91. self.ui.act_quit.setIcon(getIcon("application-exit"))
  92. self.ui.act_configure.setIcon(getIcon("configure"))
  93. self.ui.cb_buffer_size.clear()
  94. self.ui.cb_sample_rate.clear()
  95. for bufferSize in BUFFER_SIZE_LIST:
  96. self.ui.cb_buffer_size.addItem(str(bufferSize))
  97. for sampleRate in SAMPLE_RATE_LIST:
  98. self.ui.cb_sample_rate.addItem(str(sampleRate))
  99. self.ui.act_jack_bf_list = (self.ui.act_jack_bf_16, self.ui.act_jack_bf_32, self.ui.act_jack_bf_64, self.ui.act_jack_bf_128,
  100. self.ui.act_jack_bf_256, self.ui.act_jack_bf_512, self.ui.act_jack_bf_1024, self.ui.act_jack_bf_2048,
  101. self.ui.act_jack_bf_4096, self.ui.act_jack_bf_8192)
  102. if not haveALSA:
  103. self.ui.act_settings_show_alsa.setChecked(False)
  104. self.ui.act_settings_show_alsa.setEnabled(False)
  105. # -------------------------------------------------------------
  106. # Set-up Canvas
  107. self.scene = patchcanvas.PatchScene(self, self.ui.graphicsView)
  108. self.ui.graphicsView.setScene(self.scene)
  109. self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, bool(self.fSavedSettings["Canvas/Antialiasing"] == patchcanvas.ANTIALIASING_FULL))
  110. if self.fSavedSettings["Canvas/UseOpenGL"] and hasGL:
  111. self.ui.graphicsView.setViewport(QGLWidget(self.ui.graphicsView))
  112. self.ui.graphicsView.setRenderHint(QPainter.HighQualityAntialiasing, self.fSavedSettings["Canvas/HighQualityAntialiasing"])
  113. pOptions = patchcanvas.options_t()
  114. pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  115. pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  116. pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  117. pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  118. pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  119. pFeatures = patchcanvas.features_t()
  120. pFeatures.group_info = False
  121. pFeatures.group_rename = False
  122. pFeatures.port_info = True
  123. pFeatures.port_rename = bool(self.fSavedSettings["Main/JackPortAlias"] > 0)
  124. pFeatures.handle_group_pos = True
  125. patchcanvas.setOptions(pOptions)
  126. patchcanvas.setFeatures(pFeatures)
  127. patchcanvas.init("Catia", self.scene, self.canvasCallback, DEBUG)
  128. # -------------------------------------------------------------
  129. # Try to connect to jack
  130. if self.jackStarted():
  131. self.init_alsaPorts()
  132. # -------------------------------------------------------------
  133. # Check DBus
  134. if haveDBus:
  135. if gDBus.jack:
  136. pass
  137. else:
  138. self.ui.act_tools_jack_start.setEnabled(False)
  139. self.ui.act_tools_jack_stop.setEnabled(False)
  140. self.ui.act_jack_configure.setEnabled(False)
  141. self.ui.b_jack_configure.setEnabled(False)
  142. if gDBus.a2j:
  143. if gDBus.a2j.is_started():
  144. self.a2jStarted()
  145. else:
  146. self.a2jStopped()
  147. else:
  148. self.ui.act_tools_a2j_start.setEnabled(False)
  149. self.ui.act_tools_a2j_stop.setEnabled(False)
  150. self.ui.act_tools_a2j_export_hw.setEnabled(False)
  151. self.ui.menu_A2J_Bridge.setEnabled(False)
  152. else:
  153. # No DBus
  154. self.ui.act_tools_jack_start.setEnabled(False)
  155. self.ui.act_tools_jack_stop.setEnabled(False)
  156. self.ui.act_jack_configure.setEnabled(False)
  157. self.ui.b_jack_configure.setEnabled(False)
  158. self.ui.act_tools_a2j_start.setEnabled(False)
  159. self.ui.act_tools_a2j_stop.setEnabled(False)
  160. self.ui.act_tools_a2j_export_hw.setEnabled(False)
  161. self.ui.menu_A2J_Bridge.setEnabled(False)
  162. # -------------------------------------------------------------
  163. # Set-up Timers
  164. self.fTimer120 = self.startTimer(self.fSavedSettings["Main/RefreshInterval"])
  165. self.fTimer600 = self.startTimer(self.fSavedSettings["Main/RefreshInterval"] * 5)
  166. # -------------------------------------------------------------
  167. # Set-up Connections
  168. self.setCanvasConnections()
  169. self.setJackConnections(["jack", "buffer-size", "transport", "misc"])
  170. self.connect(self.ui.act_tools_jack_start, SIGNAL("triggered()"), SLOT("slot_JackServerStart()"))
  171. self.connect(self.ui.act_tools_jack_stop, SIGNAL("triggered()"), SLOT("slot_JackServerStop()"))
  172. self.connect(self.ui.act_tools_a2j_start, SIGNAL("triggered()"), SLOT("slot_A2JBridgeStart()"))
  173. self.connect(self.ui.act_tools_a2j_stop, SIGNAL("triggered()"), SLOT("slot_A2JBridgeStop()"))
  174. self.connect(self.ui.act_tools_a2j_export_hw, SIGNAL("triggered()"), SLOT("slot_A2JBridgeExportHW()"))
  175. self.connect(self.ui.act_settings_show_alsa, SIGNAL("triggered(bool)"), SLOT("slot_showAlsaMIDI(bool)"))
  176. self.connect(self.ui.act_configure, SIGNAL("triggered()"), SLOT("slot_configureCatia()"))
  177. self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCatia()"))
  178. self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  179. self.connect(self, SIGNAL("XRunCallback()"), SLOT("slot_XRunCallback()"))
  180. self.connect(self, SIGNAL("BufferSizeCallback(int)"), SLOT("slot_BufferSizeCallback(int)"))
  181. self.connect(self, SIGNAL("SampleRateCallback(int)"), SLOT("slot_SampleRateCallback(int)"))
  182. self.connect(self, SIGNAL("ClientRenameCallback(QString, QString)"), SLOT("slot_ClientRenameCallback(QString, QString)"))
  183. self.connect(self, SIGNAL("PortRegistrationCallback(int, bool)"), SLOT("slot_PortRegistrationCallback(int, bool)"))
  184. self.connect(self, SIGNAL("PortConnectCallback(int, int, bool)"), SLOT("slot_PortConnectCallback(int, int, bool)"))
  185. self.connect(self, SIGNAL("PortRenameCallback(int, QString, QString)"), SLOT("slot_PortRenameCallback(int, QString, QString)"))
  186. self.connect(self, SIGNAL("ShutdownCallback()"), SLOT("slot_ShutdownCallback()"))
  187. # -------------------------------------------------------------
  188. # Set-up DBus
  189. if gDBus.jack or gDBus.a2j:
  190. gDBus.bus.add_signal_receiver(self.DBusSignalReceiver, destination_keyword="dest", path_keyword="path",
  191. member_keyword="member", interface_keyword="interface", sender_keyword="sender")
  192. # -------------------------------------------------------------
  193. def canvasCallback(self, action, value1, value2, valueStr):
  194. if action == patchcanvas.ACTION_GROUP_INFO:
  195. pass
  196. elif action == patchcanvas.ACTION_GROUP_RENAME:
  197. pass
  198. elif action == patchcanvas.ACTION_GROUP_SPLIT:
  199. groupId = value1
  200. patchcanvas.splitGroup(groupId)
  201. elif action == patchcanvas.ACTION_GROUP_JOIN:
  202. groupId = value1
  203. patchcanvas.joinGroup(groupId)
  204. elif action == patchcanvas.ACTION_PORT_INFO:
  205. portId = value1
  206. for port in self.fPortList:
  207. if port[iPortId] == portId:
  208. portNameR = port[iPortNameR]
  209. portNameG = port[iPortGroupName]
  210. break
  211. else:
  212. return
  213. if portNameR.startswith("[ALSA-"):
  214. portId, portName = portNameR.split("] ", 1)[1].split(" ", 1)
  215. flags = []
  216. if portNameR.startswith("[ALSA-Input] "):
  217. flags.append(self.tr("Input"))
  218. elif portNameR.startswith("[ALSA-Output] "):
  219. flags.append(self.tr("Output"))
  220. flagsText = " | ".join(flags)
  221. typeText = self.tr("ALSA MIDI")
  222. info = self.tr(""
  223. "<table>"
  224. "<tr><td align='right'><b>Group Name:</b></td><td>&nbsp;%s</td></tr>"
  225. "<tr><td align='right'><b>Port Id:</b></td><td>&nbsp;%s</td></tr>"
  226. "<tr><td align='right'><b>Port Name:</b></td><td>&nbsp;%s</td></tr>"
  227. "<tr><td colspan='2'>&nbsp;</td></tr>"
  228. "<tr><td align='right'><b>Port Flags:</b></td><td>&nbsp;%s</td></tr>"
  229. "<tr><td align='right'><b>Port Type:</b></td><td>&nbsp;%s</td></tr>"
  230. "</table>" % (portNameG, portId, portName, flagsText, typeText))
  231. else:
  232. portPtr = jacklib.port_by_name(gJack.client, portNameR)
  233. portFlags = jacklib.port_flags(portPtr)
  234. groupName = portNameR.split(":", 1)[0]
  235. portShortName = str(jacklib.port_short_name(portPtr), encoding="utf-8")
  236. aliases = jacklib.port_get_aliases(portPtr)
  237. if aliases[0] == 1:
  238. alias1text = aliases[1]
  239. alias2text = "(none)"
  240. elif aliases[0] == 2:
  241. alias1text = aliases[1]
  242. alias2text = aliases[2]
  243. else:
  244. alias1text = "(none)"
  245. alias2text = "(none)"
  246. flags = []
  247. if portFlags & jacklib.JackPortIsInput:
  248. flags.append(self.tr("Input"))
  249. if portFlags & jacklib.JackPortIsOutput:
  250. flags.append(self.tr("Output"))
  251. if portFlags & jacklib.JackPortIsPhysical:
  252. flags.append(self.tr("Physical"))
  253. if portFlags & jacklib.JackPortCanMonitor:
  254. flags.append(self.tr("Can Monitor"))
  255. if portFlags & jacklib.JackPortIsTerminal:
  256. flags.append(self.tr("Terminal"))
  257. flagsText = " | ".join(flags)
  258. portTypeStr = str(jacklib.port_type(portPtr), encoding="utf-8")
  259. if portTypeStr == jacklib.JACK_DEFAULT_AUDIO_TYPE:
  260. typeText = self.tr("JACK Audio")
  261. elif portTypeStr == jacklib.JACK_DEFAULT_MIDI_TYPE:
  262. typeText = self.tr("JACK MIDI")
  263. else:
  264. typeText = self.tr("Unknown")
  265. portLatency = jacklib.port_get_latency(portPtr)
  266. portTotalLatency = jacklib.port_get_total_latency(gJack.client, portPtr)
  267. latencyText = self.tr("%.1f ms (%i frames)" % (portLatency * 1000 / int(self.fSampleRate), portLatency))
  268. latencyTotalText = self.tr("%.1f ms (%i frames)" % (portTotalLatency * 1000 / int(self.fSampleRate), portTotalLatency))
  269. info = self.tr(""
  270. "<table>"
  271. "<tr><td align='right'><b>Group Name:</b></td><td>&nbsp;%s</td></tr>"
  272. "<tr><td align='right'><b>Port Name:</b></td><td>&nbsp;%s</td></tr>"
  273. "<tr><td align='right'><b>Full Port Name:</b></td><td>&nbsp;%s</td></tr>"
  274. "<tr><td align='right'><b>Port Alias #1:</b></td><td>&nbsp;%s</td></tr>"
  275. "<tr><td align='right'><b>Port Alias #2:</b></td><td>&nbsp;%s</td></tr>"
  276. "<tr><td colspan='2'>&nbsp;</td></tr>"
  277. "<tr><td align='right'><b>Port Flags:</b></td><td>&nbsp;%s</td></tr>"
  278. "<tr><td align='right'><b>Port Type:</b></td><td>&nbsp;%s</td></tr>"
  279. "<tr><td colspan='2'>&nbsp;</td></tr>"
  280. "<tr><td align='right'><b>Port Latency:</b></td><td>&nbsp;%s</td></tr>"
  281. "<tr><td align='right'><b>Total Port Latency:</b></td><td>&nbsp;%s</td></tr>"
  282. "</table>" % (groupName, portShortName, portNameR, alias1text, alias2text, flagsText, typeText, latencyText, latencyTotalText))
  283. QMessageBox.information(self, self.tr("Port Information"), info)
  284. elif action == patchcanvas.ACTION_PORT_RENAME:
  285. global gA2JClientName
  286. portId = value1
  287. portShortName = asciiString(valueStr)
  288. for port in self.fPortList:
  289. if port[iPortId] == portId:
  290. portNameR = port[iPortNameR]
  291. if portNameR.startswith("[ALSA-"):
  292. QMessageBox.warning(self, self.tr("Cannot continue"), self.tr(""
  293. "Rename functions rely on JACK aliases and cannot be done in ALSA ports"))
  294. return
  295. if portNameR.split(":", 1)[0] == gA2JClientName:
  296. a2jSplit = portNameR.split(":", 3)
  297. portName = "%s:%s: %s" % (a2jSplit[0], a2jSplit[1], portShortName)
  298. else:
  299. portName = "%s:%s" % (port[iPortGroupName], portShortName)
  300. break
  301. else:
  302. return
  303. portPtr = jacklib.port_by_name(gJack.client, portNameR)
  304. aliases = jacklib.port_get_aliases(portPtr)
  305. if aliases[0] == 2:
  306. # JACK only allows 2 aliases, remove 2nd
  307. jacklib.port_unset_alias(portPtr, aliases[2])
  308. # If we're going for 1st alias, unset it too
  309. if self.fSavedSettings["Main/JackPortAlias"] == 1:
  310. jacklib.port_unset_alias(portPtr, aliases[1])
  311. elif aliases[0] == 1 and self.fSavedSettings["Main/JackPortAlias"] == 1:
  312. jacklib.port_unset_alias(portPtr, aliases[1])
  313. if aliases[0] == 0 and self.fSavedSettings["Main/JackPortAlias"] == 2:
  314. # If 2nd alias is enabled and port had no previous aliases, set the 1st alias now
  315. jacklib.port_set_alias(portPtr, portName)
  316. if jacklib.port_set_alias(portPtr, portName) == 0:
  317. patchcanvas.renamePort(portId, portShortName)
  318. elif action == patchcanvas.ACTION_PORTS_CONNECT:
  319. portIdA = value1
  320. portIdB = value2
  321. portRealNameA = ""
  322. portRealNameB = ""
  323. for port in self.fPortList:
  324. if port[iPortId] == portIdA:
  325. portRealNameA = port[iPortNameR]
  326. if port[iPortId] == portIdB:
  327. portRealNameB = port[iPortNameR]
  328. if portRealNameA.startswith("[ALSA-"):
  329. portIdAlsaA = portRealNameA.split(" ", 2)[1]
  330. portIdAlsaB = portRealNameB.split(" ", 2)[1]
  331. if os.system("aconnect %s %s" % (portIdAlsaA, portIdAlsaB)) == 0:
  332. self.canvas_connectPorts(portIdA, portIdB)
  333. elif portRealNameA and portRealNameB:
  334. jacklib.connect(gJack.client, portRealNameA, portRealNameB)
  335. elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
  336. connectionId = value1
  337. for connection in self.fConnectionList:
  338. if connection[iConnId] == connectionId:
  339. portIdA = connection[iConnOutput]
  340. portIdB = connection[iConnInput]
  341. break
  342. else:
  343. return
  344. portRealNameA = ""
  345. portRealNameB = ""
  346. for port in self.fPortList:
  347. if port[iPortId] == portIdA:
  348. portRealNameA = port[iPortNameR]
  349. if port[iPortId] == portIdB:
  350. portRealNameB = port[iPortNameR]
  351. if portRealNameA.startswith("[ALSA-"):
  352. portIdAlsaA = portRealNameA.split(" ", 2)[1]
  353. portIdAlsaB = portRealNameB.split(" ", 2)[1]
  354. if os.system("aconnect -d %s %s" % (portIdAlsaA, portIdAlsaB)) == 0:
  355. self.canvas_disconnectPorts(portIdA, portIdB)
  356. elif portRealNameA and portRealNameB:
  357. jacklib.disconnect(gJack.client, portRealNameA, portRealNameB)
  358. def init_ports(self):
  359. self.fGroupList = []
  360. self.fGroupSplitList = []
  361. self.fPortList = []
  362. self.fConnectionList = []
  363. self.fLastGroupId = 1
  364. self.fLastPortId = 1
  365. self.fLastConnectionId = 1
  366. self.init_jackPorts()
  367. self.init_alsaPorts()
  368. def init_jack(self):
  369. self.fXruns = 0
  370. self.fNextSampleRate = 0.0
  371. self.fLastBPM = None
  372. self.fLastTransportState = None
  373. bufferSize = int(jacklib.get_buffer_size(gJack.client))
  374. sampleRate = int(jacklib.get_sample_rate(gJack.client))
  375. realtime = bool(int(jacklib.is_realtime(gJack.client)))
  376. self.ui_setBufferSize(bufferSize)
  377. self.ui_setSampleRate(sampleRate)
  378. self.ui_setRealTime(realtime)
  379. self.ui_setXruns(0)
  380. self.refreshDSPLoad()
  381. self.refreshTransport()
  382. self.init_jackCallbacks()
  383. self.init_jackPorts()
  384. self.scene.zoom_fit()
  385. self.scene.zoom_reset()
  386. jacklib.activate(gJack.client)
  387. def init_jackCallbacks(self):
  388. jacklib.set_buffer_size_callback(gJack.client, self.JackBufferSizeCallback, None)
  389. jacklib.set_sample_rate_callback(gJack.client, self.JackSampleRateCallback, None)
  390. jacklib.set_xrun_callback(gJack.client, self.JackXRunCallback, None)
  391. jacklib.set_port_registration_callback(gJack.client, self.JackPortRegistrationCallback, None)
  392. jacklib.set_port_connect_callback(gJack.client, self.JackPortConnectCallback, None)
  393. jacklib.set_session_callback(gJack.client, self.JackSessionCallback, None)
  394. jacklib.on_shutdown(gJack.client, self.JackShutdownCallback, None)
  395. jacklib.set_client_rename_callback(gJack.client, self.JackClientRenameCallback, None)
  396. jacklib.set_port_rename_callback(gJack.client, self.JackPortRenameCallback, None)
  397. def init_jackPorts(self):
  398. if not gJack.client:
  399. return
  400. global gA2JClientName
  401. # Get all jack ports, put a2j ones to the bottom of the list
  402. a2jNameList = []
  403. portNameList = c_char_p_p_to_list(jacklib.get_ports(gJack.client, "", "", 0))
  404. h = 0
  405. for i in range(len(portNameList)):
  406. if portNameList[i - h].split(":")[0] == gA2JClientName:
  407. portName = portNameList.pop(i - h)
  408. a2jNameList.append(portName)
  409. h += 1
  410. for a2jName in a2jNameList:
  411. portNameList.append(a2jName)
  412. del a2jNameList
  413. # Add jack ports
  414. for portName in portNameList:
  415. portPtr = jacklib.port_by_name(gJack.client, portName)
  416. self.canvas_addJackPort(portPtr, portName)
  417. # Add jack connections
  418. for portName in portNameList:
  419. portPtr = jacklib.port_by_name(gJack.client, portName)
  420. # Only make connections from an output port
  421. if jacklib.port_flags(portPtr) & jacklib.JackPortIsInput:
  422. continue
  423. portConnectionNames = c_char_p_p_to_list(jacklib.port_get_all_connections(gJack.client, portPtr))
  424. for portConName in portConnectionNames:
  425. self.canvas_connectPortsByName(portName, portConName)
  426. def init_alsaPorts(self):
  427. if not (haveALSA and self.ui.act_settings_show_alsa.isChecked()):
  428. return
  429. # Get ALSA MIDI ports (outputs)
  430. output = getoutput("env LANG=C aconnect -i").split("\n")
  431. lastGroupId = -1
  432. lastGroupName = ""
  433. for line in output:
  434. # Make 'System' match JACK's 'system'
  435. if line == "client 0: 'System' [type=kernel]":
  436. line = "client 0: 'system' [type=kernel]"
  437. if line.startswith("client "):
  438. lineSplit = line.split(": ", 1)
  439. lineSplit2 = lineSplit[1].replace("'", "", 1).split("' [type=", 1)
  440. groupId = int(lineSplit[0].replace("client ", ""))
  441. groupName = lineSplit2[0]
  442. groupType = lineSplit2[1].rsplit("]", 1)[0]
  443. lastGroupId = self.canvas_getGroupId(groupName)
  444. if lastGroupId == -1:
  445. # Group doesn't exist yet
  446. lastGroupId = self.canvas_addAlsaGroup(groupId, groupName, bool(groupType == "kernel"))
  447. lastGroupName = groupName
  448. elif line.startswith(" ") and lastGroupId >= 0 and lastGroupName:
  449. lineSplit = line.split(" '", 1)
  450. portId = int(lineSplit[0].strip())
  451. portName = lineSplit[1].rsplit("'", 1)[0].strip()
  452. self.canvas_addAlsaPort(lastGroupId, lastGroupName, portName, "%i:%i %s" % (groupId, portId, portName), False)
  453. else:
  454. lastGroupId = -1
  455. lastGroupName = ""
  456. # Get ALSA MIDI ports (inputs)
  457. output = getoutput("env LANG=C aconnect -o").split("\n")
  458. lastGroupId = -1
  459. lastGroupName = ""
  460. for line in output:
  461. # Make 'System' match JACK's 'system'
  462. if line == "client 0: 'System' [type=kernel]":
  463. line = "client 0: 'system' [type=kernel]"
  464. if line.startswith("client "):
  465. lineSplit = line.split(": ", 1)
  466. lineSplit2 = lineSplit[1].replace("'", "", 1).split("' [type=", 1)
  467. groupId = int(lineSplit[0].replace("client ", ""))
  468. groupName = lineSplit2[0]
  469. groupType = lineSplit2[1].rsplit("]", 1)[0]
  470. lastGroupId = self.canvas_getGroupId(groupName)
  471. if lastGroupId == -1:
  472. # Group doesn't exist yet
  473. lastGroupId = self.canvas_addAlsaGroup(groupId, groupName, bool(groupType == "kernel"))
  474. lastGroupName = groupName
  475. elif line.startswith(" ") and lastGroupId >= 0 and lastGroupName:
  476. lineSplit = line.split(" '", 1)
  477. portId = int(lineSplit[0].strip())
  478. portName = lineSplit[1].rsplit("'", 1)[0].strip()
  479. self.canvas_addAlsaPort(lastGroupId, lastGroupName, portName, "%i:%i %s" % (groupId, portId, portName), True)
  480. else:
  481. lastGroupId = -1
  482. lastGroupName = ""
  483. # Get ALSA MIDI connections
  484. output = getoutput("env LANG=C aconnect -ol").split("\n")
  485. lastGroupId = -1
  486. lastPortId = -1
  487. for line in output:
  488. # Make 'System' match JACK's 'system'
  489. if line == "client 0: 'System' [type=kernel]":
  490. line = "client 0: 'system' [type=kernel]"
  491. if line.startswith("client "):
  492. lineSplit = line.split(": ", 1)
  493. lineSplit2 = lineSplit[1].replace("'", "", 1).split("' [type=", 1)
  494. groupId = int(lineSplit[0].replace("client ", ""))
  495. groupName = lineSplit2[0]
  496. lastGroupId = self.canvas_getGroupId(groupName)
  497. elif line.startswith(" ") and lastGroupId >= 0:
  498. lineSplit = line.split(" '", 1)
  499. portId = int(lineSplit[0].strip())
  500. portName = lineSplit[1].rsplit("'", 1)[0].strip()
  501. for port in self.fPortList:
  502. if port[iPortNameR] == "[ALSA-Input] %i:%i %s" % (groupId, portId, portName):
  503. lastPortId = port[iPortId]
  504. break
  505. else:
  506. lastPortId = -1
  507. elif line.startswith("\tConnect") and lastGroupId >= 0 and lastPortId >= 0:
  508. if line.startswith("\tConnected From"):
  509. lineSplit = line.split(": ", 1)[1]
  510. lineConns = lineSplit.split(", ")
  511. for lineConn in lineConns:
  512. lineConnSplit = lineConn.replace("'","").split(":", 1)
  513. alsaGroupId = int(lineConnSplit[0])
  514. alsaPortId = int(lineConnSplit[1])
  515. portNameRtest = "[ALSA-Output] %i:%i " % (alsaGroupId, alsaPortId)
  516. for port in self.fPortList:
  517. if port[iPortNameR].startswith(portNameRtest):
  518. self.canvas_connectPorts(port[iPortId], lastPortId)
  519. break
  520. else:
  521. lastGroupId = -1
  522. lastPortId = -1
  523. def canvas_getGroupId(self, groupName):
  524. for group in self.fGroupList:
  525. if group[iGroupName] == groupName:
  526. return group[iGroupId]
  527. return -1
  528. def canvas_addAlsaGroup(self, alsaGroupId, groupName, hwSplit):
  529. groupId = self.fLastGroupId
  530. if hwSplit:
  531. patchcanvas.addGroup(groupId, groupName, patchcanvas.SPLIT_YES, patchcanvas.ICON_HARDWARE)
  532. else:
  533. patchcanvas.addGroup(groupId, groupName)
  534. groupObj = [None, None, None]
  535. groupObj[iGroupId] = groupId
  536. groupObj[iGroupName] = groupName
  537. groupObj[iGroupType] = GROUP_TYPE_ALSA
  538. self.fGroupList.append(groupObj)
  539. self.fLastGroupId += 1
  540. return groupId
  541. def canvas_addJackGroup(self, groupName):
  542. ret, data, dataSize = jacklib.custom_get_data(gJack.client, groupName, URI_CANVAS_ICON)
  543. groupId = self.fLastGroupId
  544. groupSplit = patchcanvas.SPLIT_UNDEF
  545. groupIcon = patchcanvas.ICON_APPLICATION
  546. if ret == 0:
  547. iconName = voidptr2str(data)
  548. jacklib.free(data)
  549. if iconName == "hardware":
  550. groupSplit = patchcanvas.SPLIT_YES
  551. groupIcon = patchcanvas.ICON_HARDWARE
  552. #elif iconName =="carla":
  553. #groupIcon = patchcanvas.ICON_CARLA
  554. elif iconName =="distrho":
  555. groupIcon = patchcanvas.ICON_DISTRHO
  556. elif iconName =="file":
  557. groupIcon = patchcanvas.ICON_FILE
  558. elif iconName =="plugin":
  559. groupIcon = patchcanvas.ICON_PLUGIN
  560. patchcanvas.addGroup(groupId, groupName, groupSplit, groupIcon)
  561. groupObj = [None, None, None]
  562. groupObj[iGroupId] = groupId
  563. groupObj[iGroupName] = groupName
  564. groupObj[iGroupType] = GROUP_TYPE_JACK
  565. self.fGroupList.append(groupObj)
  566. self.fLastGroupId += 1
  567. return groupId
  568. def canvas_removeGroup(self, groupName):
  569. groupId = -1
  570. for group in self.fGroupList:
  571. if group[iGroupName] == groupName:
  572. groupId = group[iGroupId]
  573. self.fGroupList.remove(group)
  574. break
  575. else:
  576. print("Catia - remove group failed")
  577. return
  578. patchcanvas.removeGroup(groupId)
  579. def canvas_addAlsaPort(self, groupId, groupName, portName, portNameR, isPortInput):
  580. portId = self.fLastPortId
  581. portMode = patchcanvas.PORT_MODE_INPUT if isPortInput else patchcanvas.PORT_MODE_OUTPUT
  582. portType = patchcanvas.PORT_TYPE_MIDI_ALSA
  583. patchcanvas.addPort(groupId, portId, portName, portMode, portType)
  584. portObj = [None, None, None, None]
  585. portObj[iPortId] = portId
  586. portObj[iPortName] = portName
  587. portObj[iPortNameR] = "[ALSA-%s] %s" % ("Input" if isPortInput else "Output", portNameR)
  588. portObj[iPortGroupName] = groupName
  589. self.fPortList.append(portObj)
  590. self.fLastPortId += 1
  591. return portId
  592. def canvas_addJackPort(self, portPtr, portName):
  593. global gA2JClientName
  594. portId = self.fLastPortId
  595. groupId = -1
  596. portNameR = portName
  597. aliasN = self.fSavedSettings["Main/JackPortAlias"]
  598. if aliasN in (1, 2):
  599. aliases = jacklib.port_get_aliases(portPtr)
  600. if aliases[0] == 2 and aliasN == 2:
  601. portName = aliases[2]
  602. elif aliases[0] >= 1 and aliasN == 1:
  603. portName = aliases[1]
  604. portFlags = jacklib.port_flags(portPtr)
  605. groupName = portName.split(":", 1)[0]
  606. if portFlags & jacklib.JackPortIsInput:
  607. portMode = patchcanvas.PORT_MODE_INPUT
  608. elif portFlags & jacklib.JackPortIsOutput:
  609. portMode = patchcanvas.PORT_MODE_OUTPUT
  610. else:
  611. portMode = patchcanvas.PORT_MODE_NULL
  612. if groupName == gA2JClientName:
  613. portType = patchcanvas.PORT_TYPE_MIDI_A2J
  614. groupName = portName.replace("%s:" % gA2JClientName, "", 1).split(" [", 1)[0]
  615. portShortName = portName.split("): ", 1)[1]
  616. else:
  617. portShortName = portName.replace("%s:" % groupName, "", 1)
  618. portTypeStr = str(jacklib.port_type(portPtr), encoding="utf-8")
  619. if portTypeStr == jacklib.JACK_DEFAULT_AUDIO_TYPE:
  620. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  621. elif portTypeStr == jacklib.JACK_DEFAULT_MIDI_TYPE:
  622. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  623. else:
  624. portType = patchcanvas.PORT_TYPE_NULL
  625. for group in self.fGroupList:
  626. if group[iGroupName] == groupName:
  627. groupId = group[iGroupId]
  628. break
  629. else:
  630. # For ports with no group
  631. groupId = self.canvas_addJackGroup(groupName)
  632. patchcanvas.addPort(groupId, portId, portShortName, portMode, portType)
  633. portObj = [None, None, None, None]
  634. portObj[iPortId] = portId
  635. portObj[iPortName] = portName
  636. portObj[iPortNameR] = portNameR
  637. portObj[iPortGroupName] = groupName
  638. self.fPortList.append(portObj)
  639. self.fLastPortId += 1
  640. if groupId not in self.fGroupSplitList and (portFlags & jacklib.JackPortIsPhysical) > 0:
  641. patchcanvas.splitGroup(groupId)
  642. patchcanvas.setGroupIcon(groupId, patchcanvas.ICON_HARDWARE)
  643. self.fGroupSplitList.append(groupId)
  644. return portId
  645. def canvas_removeJackPort(self, portId):
  646. patchcanvas.removePort(portId)
  647. for port in self.fPortList:
  648. if port[iPortId] == portId:
  649. groupName = port[iPortGroupName]
  650. self.fPortList.remove(port)
  651. break
  652. else:
  653. return
  654. # Check if group has no more ports; if yes remove it
  655. for port in self.fPortList:
  656. if port[iPortGroupName] == groupName:
  657. break
  658. else:
  659. self.canvas_removeGroup(groupName)
  660. def canvas_renamePort(self, portId, portShortName):
  661. patchcanvas.renamePort(portId, portShortName)
  662. def canvas_connectPorts(self, portOutId, portInId):
  663. connectionId = self.fLastConnectionId
  664. patchcanvas.connectPorts(connectionId, portOutId, portInId)
  665. connObj = [None, None, None]
  666. connObj[iConnId] = connectionId
  667. connObj[iConnOutput] = portOutId
  668. connObj[iConnInput] = portInId
  669. self.fConnectionList.append(connObj)
  670. self.fLastConnectionId += 1
  671. return connectionId
  672. def canvas_connectPortsByName(self, portOutName, portInName):
  673. portOutId = -1
  674. portInId = -1
  675. for port in self.fPortList:
  676. if port[iPortNameR] == portOutName:
  677. portOutId = port[iPortId]
  678. elif port[iPortNameR] == portInName:
  679. portInId = port[iPortId]
  680. if portOutId >= 0 and portInId >= 0:
  681. break
  682. else:
  683. print("Catia - connect jack ports failed")
  684. return -1
  685. return self.canvas_connectPorts(portOutId, portInId)
  686. def canvas_disconnectPorts(self, portOutId, portInId):
  687. for connection in self.fConnectionList:
  688. if connection[iConnOutput] == portOutId and connection[iConnInput] == portInId:
  689. patchcanvas.disconnectPorts(connection[iConnId])
  690. self.fConnectionList.remove(connection)
  691. break
  692. def canvas_disconnectPortsByName(self, portOutName, portInName):
  693. portOutId = -1
  694. portInId = -1
  695. for port in self.fPortList:
  696. if port[iPortNameR] == portOutName:
  697. portOutId = port[iPortId]
  698. elif port[iPortNameR] == portInName:
  699. portInId = port[iPortId]
  700. if portOutId == -1 or portInId == -1:
  701. print("Catia - disconnect ports failed")
  702. return
  703. self.canvas_disconnectPorts(portOutId, portInId)
  704. def jackStarted(self):
  705. if not gJack.client:
  706. gJack.client = jacklib.client_open("catia", jacklib.JackNoStartServer | jacklib.JackSessionID, None)
  707. if not gJack.client:
  708. self.jackStopped()
  709. return False
  710. canRender = render.canRender()
  711. self.ui.act_jack_render.setEnabled(canRender)
  712. self.ui.b_jack_render.setEnabled(canRender)
  713. self.menuJackServer(True)
  714. self.menuJackTransport(True)
  715. self.ui.cb_buffer_size.setEnabled(True)
  716. self.ui.cb_sample_rate.setEnabled(bool(gDBus.jack)) # DBus.jack and jacksettings.getSampleRate() != -1
  717. self.ui.menu_Jack_Buffer_Size.setEnabled(True)
  718. self.ui.pb_dsp_load.setMaximum(100)
  719. self.ui.pb_dsp_load.setValue(0)
  720. self.ui.pb_dsp_load.update()
  721. self.init_jack()
  722. return True
  723. def jackStopped(self):
  724. if haveDBus:
  725. self.DBusReconnect()
  726. # client already closed
  727. gJack.client = None
  728. # refresh canvas (remove jack ports)
  729. patchcanvas.clear()
  730. self.init_ports()
  731. if self.fNextSampleRate:
  732. self.jack_setSampleRate(self.fNextSampleRate)
  733. if gDBus.jack:
  734. bufferSize = jacksettings.getBufferSize()
  735. sampleRate = jacksettings.getSampleRate()
  736. bufferSizeTest = bool(bufferSize != -1)
  737. sampleRateTest = bool(sampleRate != -1)
  738. if bufferSizeTest:
  739. self.ui_setBufferSize(bufferSize)
  740. if sampleRateTest:
  741. self.ui_setSampleRate(sampleRate)
  742. self.ui_setRealTime(jacksettings.isRealtime())
  743. self.ui.cb_buffer_size.setEnabled(bufferSizeTest)
  744. self.ui.cb_sample_rate.setEnabled(sampleRateTest)
  745. self.ui.menu_Jack_Buffer_Size.setEnabled(bufferSizeTest)
  746. else:
  747. self.ui.cb_buffer_size.setEnabled(False)
  748. self.ui.cb_sample_rate.setEnabled(False)
  749. self.ui.menu_Jack_Buffer_Size.setEnabled(False)
  750. self.ui.act_jack_render.setEnabled(False)
  751. self.ui.b_jack_render.setEnabled(False)
  752. self.menuJackServer(False)
  753. self.menuJackTransport(False)
  754. self.ui_setXruns(-1)
  755. if self.fCurTransportView == TRANSPORT_VIEW_HMS:
  756. self.ui.label_time.setText("00:00:00")
  757. elif self.fCurTransportView == TRANSPORT_VIEW_BBT:
  758. self.ui.label_time.setText("000|0|0000")
  759. elif self.fCurTransportView == TRANSPORT_VIEW_FRAMES:
  760. self.ui.label_time.setText("000'000'000")
  761. self.ui.pb_dsp_load.setValue(0)
  762. self.ui.pb_dsp_load.setMaximum(0)
  763. self.ui.pb_dsp_load.update()
  764. def a2jStarted(self):
  765. self.menuA2JBridge(True)
  766. def a2jStopped(self):
  767. self.menuA2JBridge(False)
  768. def menuJackServer(self, started):
  769. if gDBus.jack:
  770. self.ui.act_tools_jack_start.setEnabled(not started)
  771. self.ui.act_tools_jack_stop.setEnabled(started)
  772. self.menuA2JBridge(False)
  773. def menuJackTransport(self, enabled):
  774. self.ui.act_transport_play.setEnabled(enabled)
  775. self.ui.act_transport_stop.setEnabled(enabled)
  776. self.ui.act_transport_backwards.setEnabled(enabled)
  777. self.ui.act_transport_forwards.setEnabled(enabled)
  778. self.ui.menu_Transport.setEnabled(enabled)
  779. self.ui.group_transport.setEnabled(enabled)
  780. def menuA2JBridge(self, started):
  781. if gDBus.jack and not gDBus.jack.IsStarted():
  782. self.ui.act_tools_a2j_start.setEnabled(False)
  783. self.ui.act_tools_a2j_stop.setEnabled(False)
  784. self.ui.act_tools_a2j_export_hw.setEnabled(gDBus.a2j and not gDBus.a2j.is_started())
  785. else:
  786. self.ui.act_tools_a2j_start.setEnabled(not started)
  787. self.ui.act_tools_a2j_stop.setEnabled(started)
  788. self.ui.act_tools_a2j_export_hw.setEnabled(not started)
  789. def DBusSignalReceiver(self, *args, **kwds):
  790. if kwds["interface"] == "org.freedesktop.DBus" and kwds["path"] == "/org/freedesktop/DBus" and kwds["member"] == "NameOwnerChanged":
  791. appInterface, appId, newId = args
  792. if not newId:
  793. # Something crashed
  794. if appInterface == "org.gna.home.a2jmidid":
  795. QTimer.singleShot(0, self, SLOT("slot_handleCrash_a2j()"))
  796. elif appInterface == "org.jackaudio.service":
  797. QTimer.singleShot(0, self, SLOT("slot_handleCrash_jack()"))
  798. elif kwds['interface'] == "org.jackaudio.JackControl":
  799. if kwds['member'] == "ServerStarted":
  800. self.jackStarted()
  801. elif kwds['member'] == "ServerStopped":
  802. self.jackStopped()
  803. elif kwds['interface'] == "org.gna.home.a2jmidid.control":
  804. if kwds['member'] == "bridge_started":
  805. self.a2jStarted()
  806. elif kwds['member'] == "bridge_stopped":
  807. self.a2jStopped()
  808. def DBusReconnect(self):
  809. global gA2JClientName
  810. try:
  811. gDBus.jack = gDBus.bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller")
  812. jacksettings.initBus(gDBus.bus)
  813. except:
  814. gDBus.jack = None
  815. try:
  816. gDBus.a2j = dbus.Interface(gDBus.bus.get_object("org.gna.home.a2jmidid", "/"), "org.gna.home.a2jmidid.control")
  817. gA2JClientName = str(gDBus.a2j.get_jack_client_name())
  818. except:
  819. gDBus.a2j = None
  820. gA2JClientName = None
  821. def JackXRunCallback(self, arg):
  822. if DEBUG: print("JackXRunCallback()")
  823. self.emit(SIGNAL("XRunCallback()"))
  824. return 0
  825. def JackBufferSizeCallback(self, bufferSize, arg):
  826. if DEBUG: print("JackBufferSizeCallback(%i)" % bufferSize)
  827. self.emit(SIGNAL("BufferSizeCallback(int)"), bufferSize)
  828. return 0
  829. def JackSampleRateCallback(self, sampleRate, arg):
  830. if DEBUG: print("JackSampleRateCallback(%i)" % sampleRate)
  831. self.emit(SIGNAL("SampleRateCallback(int)"), sampleRate)
  832. return 0
  833. def JackClientRenameCallback(self, oldName, newName, arg):
  834. if DEBUG: print("JackClientRenameCallback(\"%s\", \"%s\")" % (oldName, newName))
  835. self.emit(SIGNAL("ClientRenameCallback(QString, QString)"), str(oldName, encoding="utf-8"), str(newName, encoding="utf-8"))
  836. return 0
  837. def JackPortRegistrationCallback(self, portId, registerYesNo, arg):
  838. if DEBUG: print("JackPortRegistrationCallback(%i, %i)" % (portId, registerYesNo))
  839. self.emit(SIGNAL("PortRegistrationCallback(int, bool)"), portId, bool(registerYesNo))
  840. return 0
  841. def JackPortConnectCallback(self, portA, portB, connectYesNo, arg):
  842. if DEBUG: print("JackPortConnectCallback(%i, %i, %i)" % (portA, portB, connectYesNo))
  843. self.emit(SIGNAL("PortConnectCallback(int, int, bool)"), portA, portB, bool(connectYesNo))
  844. return 0
  845. def JackPortRenameCallback(self, portId, oldName, newName, arg):
  846. if DEBUG: print("JackPortRenameCallback(%i, \"%s\", \"%s\")" % (portId, oldName, newName))
  847. self.emit(SIGNAL("PortRenameCallback(int, QString, QString)"), portId, str(oldName, encoding="utf-8"), str(newName, encoding="utf-8"))
  848. return 0
  849. def JackSessionCallback(self, event, arg):
  850. if WINDOWS:
  851. filepath = os.path.join(sys.argv[0])
  852. else:
  853. if sys.argv[0].startswith("/"):
  854. filepath = "catia"
  855. else:
  856. filepath = os.path.join(sys.path[0], "catia.py")
  857. event.command_line = str(filepath).encode("utf-8")
  858. jacklib.session_reply(gJack.client, event)
  859. if event.type == jacklib.JackSessionSaveAndQuit:
  860. app.quit()
  861. #jacklib.session_event_free(event)
  862. def JackShutdownCallback(self, arg):
  863. if DEBUG: print("JackShutdownCallback()")
  864. self.emit(SIGNAL("ShutdownCallback()"))
  865. return 0
  866. @pyqtSlot(bool)
  867. def slot_showAlsaMIDI(self, yesNo):
  868. # refresh canvas (remove jack ports)
  869. patchcanvas.clear()
  870. self.init_ports()
  871. @pyqtSlot()
  872. def slot_JackServerStart(self):
  873. ret = False
  874. if gDBus.jack:
  875. try:
  876. ret = bool(gDBus.jack.StartServer())
  877. except:
  878. QMessageBox.warning(self, self.tr("Warning"), self.tr("Failed to start JACK, please check the logs for more information."))
  879. #self.jackStopped()
  880. return ret
  881. @pyqtSlot()
  882. def slot_JackServerStop(self):
  883. ret = False
  884. if gDBus.jack:
  885. ret = bool(gDBus.jack.StopServer())
  886. return ret
  887. @pyqtSlot()
  888. def slot_JackClearXruns(self):
  889. if gJack.client:
  890. self.fXruns = 0
  891. self.ui_setXruns(0)
  892. @pyqtSlot()
  893. def slot_A2JBridgeStart(self):
  894. ret = False
  895. if gDBus.a2j:
  896. ret = bool(gDBus.a2j.start())
  897. return ret
  898. @pyqtSlot()
  899. def slot_A2JBridgeStop(self):
  900. ret = False
  901. if gDBus.a2j:
  902. ret = bool(gDBus.a2j.stop())
  903. return ret
  904. @pyqtSlot()
  905. def slot_A2JBridgeExportHW(self):
  906. if gDBus.a2j:
  907. ask = QMessageBox.question(self, self.tr("A2J Hardware Export"), self.tr("Enable Hardware Export on the A2J Bridge?"), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.No)
  908. if ask == QMessageBox.Yes:
  909. gDBus.a2j.set_hw_export(True)
  910. elif ask == QMessageBox.No:
  911. gDBus.a2j.set_hw_export(False)
  912. @pyqtSlot()
  913. def slot_XRunCallback(self):
  914. self.fXruns += 1
  915. self.ui_setXruns(self.fXruns)
  916. @pyqtSlot(int)
  917. def slot_BufferSizeCallback(self, bufferSize):
  918. self.ui_setBufferSize(bufferSize)
  919. @pyqtSlot(int)
  920. def slot_SampleRateCallback(self, sampleRate):
  921. self.ui_setSampleRate(sampleRate)
  922. self.ui_setRealTime(bool(int(jacklib.is_realtime(gJack.client))))
  923. self.ui_setXruns(0)
  924. @pyqtSlot(str, str)
  925. def slot_ClientRenameCallback(self, oldName, newName):
  926. pass # TODO
  927. @pyqtSlot(int, bool)
  928. def slot_PortRegistrationCallback(self, portIdJack, registerYesNo):
  929. portPtr = jacklib.port_by_id(gJack.client, portIdJack)
  930. portNameR = str(jacklib.port_name(portPtr), encoding="utf-8")
  931. if registerYesNo:
  932. self.canvas_addJackPort(portPtr, portNameR)
  933. else:
  934. for port in self.fPortList:
  935. if port[iPortNameR] == portNameR:
  936. portIdCanvas = port[iPortId]
  937. break
  938. else:
  939. return
  940. self.canvas_removeJackPort(portIdCanvas)
  941. @pyqtSlot(int, int, bool)
  942. def slot_PortConnectCallback(self, portIdJackA, portIdJackB, connectYesNo):
  943. portPtrA = jacklib.port_by_id(gJack.client, portIdJackA)
  944. portPtrB = jacklib.port_by_id(gJack.client, portIdJackB)
  945. portRealNameA = str(jacklib.port_name(portPtrA), encoding="utf-8")
  946. portRealNameB = str(jacklib.port_name(portPtrB), encoding="utf-8")
  947. if connectYesNo:
  948. self.canvas_connectPortsByName(portRealNameA, portRealNameB)
  949. else:
  950. self.canvas_disconnectPortsByName(portRealNameA, portRealNameB)
  951. @pyqtSlot(int, str, str)
  952. def slot_PortRenameCallback(self, portIdJack, oldName, newName):
  953. portPtr = jacklib.port_by_id(gJack.client, portIdJack)
  954. portShortName = str(jacklib.port_short_name(portPtr), encoding="utf-8")
  955. for port in self.fPortList:
  956. if port[iPortNameR] == oldName:
  957. portIdCanvas = port[iPortId]
  958. port[iPortNameR] = newName
  959. break
  960. else:
  961. return
  962. # Only set new name in canvas if no alias is active for this port
  963. aliases = jacklib.port_get_aliases(portPtr)
  964. if aliases[0] == 1 and self.fSavedSettings["Main/JackPortAlias"] == 1:
  965. pass
  966. elif aliases[0] == 2 and self.fSavedSettings["Main/JackPortAlias"] == 2:
  967. pass
  968. else:
  969. self.canvas_renamePort(portIdCanvas, portShortName)
  970. @pyqtSlot()
  971. def slot_ShutdownCallback(self):
  972. self.jackStopped()
  973. @pyqtSlot()
  974. def slot_handleCrash_a2j(self):
  975. global gA2JClientName
  976. try:
  977. gDBus.a2j = dbus.Interface(gDBus.bus.get_object("org.gna.home.a2jmidid", "/"), "org.gna.home.a2jmidid.control")
  978. gA2JClientName = str(gDBus.a2j.get_jack_client_name())
  979. except:
  980. gDBus.a2j = None
  981. gA2JClientName = None
  982. if gDBus.a2j:
  983. if gDBus.a2j.is_started():
  984. self.a2jStarted()
  985. else:
  986. self.a2jStopped()
  987. else:
  988. self.ui.act_tools_a2j_start.setEnabled(False)
  989. self.ui.act_tools_a2j_stop.setEnabled(False)
  990. self.ui.act_tools_a2j_export_hw.setEnabled(False)
  991. self.ui.menu_A2J_Bridge.setEnabled(False)
  992. @pyqtSlot()
  993. def slot_handleCrash_jack(self):
  994. self.DBusReconnect()
  995. if gDBus.jack:
  996. self.ui.act_jack_configure.setEnabled(True)
  997. self.ui.b_jack_configure.setEnabled(True)
  998. else:
  999. self.ui.act_tools_jack_start.setEnabled(False)
  1000. self.ui.act_tools_jack_stop.setEnabled(False)
  1001. self.ui.act_jack_configure.setEnabled(False)
  1002. self.ui.b_jack_configure.setEnabled(False)
  1003. if gDBus.a2j:
  1004. if gDBus.a2j.is_started():
  1005. self.a2jStarted()
  1006. else:
  1007. self.a2jStopped()
  1008. else:
  1009. self.ui.act_tools_a2j_start.setEnabled(False)
  1010. self.ui.act_tools_a2j_stop.setEnabled(False)
  1011. self.ui.act_tools_a2j_export_hw.setEnabled(False)
  1012. self.ui.menu_A2J_Bridge.setEnabled(False)
  1013. self.jackStopped()
  1014. @pyqtSlot()
  1015. def slot_configureCatia(self):
  1016. dialog = SettingsW(self, "catia", hasGL)
  1017. if dialog.exec_():
  1018. self.loadSettings(False)
  1019. patchcanvas.clear()
  1020. pOptions = patchcanvas.options_t()
  1021. pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  1022. pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  1023. pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  1024. pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  1025. pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  1026. pFeatures = patchcanvas.features_t()
  1027. pFeatures.group_info = False
  1028. pFeatures.group_rename = False
  1029. pFeatures.port_info = True
  1030. pFeatures.port_rename = bool(self.fSavedSettings["Main/JackPortAlias"] > 0)
  1031. pFeatures.handle_group_pos = True
  1032. patchcanvas.setOptions(pOptions)
  1033. patchcanvas.setFeatures(pFeatures)
  1034. patchcanvas.init("Catia", self.scene, self.canvasCallback, DEBUG)
  1035. self.init_ports()
  1036. @pyqtSlot()
  1037. def slot_aboutCatia(self):
  1038. QMessageBox.about(self, self.tr("About Catia"), self.tr("<h3>Catia</h3>"
  1039. "<br>Version %s"
  1040. "<br>Catia is a nice JACK Patchbay with A2J Bridge integration.<br>"
  1041. "<br>Copyright (C) 2010-2013 falkTX" % VERSION))
  1042. def saveSettings(self):
  1043. settings = QSettings()
  1044. settings.setValue("Geometry", self.saveGeometry())
  1045. settings.setValue("ShowAlsaMIDI", self.ui.act_settings_show_alsa.isChecked())
  1046. settings.setValue("ShowToolbar", self.ui.frame_toolbar.isVisible())
  1047. settings.setValue("ShowStatusbar", self.ui.frame_statusbar.isVisible())
  1048. settings.setValue("TransportView", self.fCurTransportView)
  1049. def loadSettings(self, geometry):
  1050. settings = QSettings()
  1051. if geometry:
  1052. self.restoreGeometry(settings.value("Geometry", ""))
  1053. showAlsaMidi = settings.value("ShowAlsaMIDI", False, type=bool)
  1054. self.ui.act_settings_show_alsa.setChecked(showAlsaMidi)
  1055. showToolbar = settings.value("ShowToolbar", True, type=bool)
  1056. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  1057. self.ui.frame_toolbar.setVisible(showToolbar)
  1058. showStatusbar = settings.value("ShowStatusbar", True, type=bool)
  1059. self.ui.act_settings_show_statusbar.setChecked(showStatusbar)
  1060. self.ui.frame_statusbar.setVisible(showStatusbar)
  1061. self.setTransportView(settings.value("TransportView", TRANSPORT_VIEW_HMS, type=int))
  1062. self.fSavedSettings = {
  1063. "Main/RefreshInterval": settings.value("Main/RefreshInterval", 120, type=int),
  1064. "Main/JackPortAlias": settings.value("Main/JackPortAlias", 2, type=int),
  1065. "Canvas/Theme": settings.value("Canvas/Theme", patchcanvas.getDefaultThemeName(), type=str),
  1066. "Canvas/AutoHideGroups": settings.value("Canvas/AutoHideGroups", False, type=bool),
  1067. "Canvas/UseBezierLines": settings.value("Canvas/UseBezierLines", True, type=bool),
  1068. "Canvas/EyeCandy": settings.value("Canvas/EyeCandy", patchcanvas.EYECANDY_SMALL, type=int),
  1069. "Canvas/UseOpenGL": settings.value("Canvas/UseOpenGL", False, type=bool),
  1070. "Canvas/Antialiasing": settings.value("Canvas/Antialiasing", patchcanvas.ANTIALIASING_SMALL, type=int),
  1071. "Canvas/HighQualityAntialiasing": settings.value("Canvas/HighQualityAntialiasing", False, type=bool)
  1072. }
  1073. def timerEvent(self, event):
  1074. if event.timerId() == self.fTimer120:
  1075. if gJack.client:
  1076. self.refreshTransport()
  1077. elif event.timerId() == self.fTimer600:
  1078. if gJack.client:
  1079. self.refreshDSPLoad()
  1080. QMainWindow.timerEvent(self, event)
  1081. def closeEvent(self, event):
  1082. self.saveSettings()
  1083. patchcanvas.clear()
  1084. QMainWindow.closeEvent(self, event)
  1085. # ------------------------------------------------------------------------------------------------------------
  1086. # Main
  1087. if __name__ == '__main__':
  1088. # App initialization
  1089. app = QApplication(sys.argv)
  1090. app.setApplicationName("Catia")
  1091. app.setApplicationVersion(VERSION)
  1092. app.setOrganizationName("Cadence")
  1093. app.setWindowIcon(QIcon(":/scalable/catia.svg"))
  1094. if jacklib is None:
  1095. QMessageBox.critical(None, app.translate("CatiaMainW", "Error"), app.translate("CatiaMainW",
  1096. "JACK is not available in this system, cannot use this application."))
  1097. sys.exit(1)
  1098. if haveDBus:
  1099. gDBus.loop = DBusQtMainLoop(set_as_default=True)
  1100. gDBus.bus = dbus.SessionBus(mainloop=gDBus.loop)
  1101. try:
  1102. gDBus.jack = gDBus.bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller")
  1103. jacksettings.initBus(gDBus.bus)
  1104. except:
  1105. gDBus.jack = None
  1106. try:
  1107. gDBus.a2j = dbus.Interface(gDBus.bus.get_object("org.gna.home.a2jmidid", "/"), "org.gna.home.a2jmidid.control")
  1108. gA2JClientName = str(gDBus.a2j.get_jack_client_name())
  1109. except:
  1110. gDBus.a2j = None
  1111. gA2JClientName = None
  1112. if DEBUG and (gDBus.jack or gDBus.a2j):
  1113. string = "Using DBus for "
  1114. if gDBus.jack:
  1115. string += "JACK"
  1116. if gDBus.a2j:
  1117. string += " and a2jmidid"
  1118. elif gDBus.a2j:
  1119. string += "a2jmidid"
  1120. print(string)
  1121. else:
  1122. gDBus.jack = None
  1123. gDBus.a2j = None
  1124. gA2JClientName = None
  1125. if DEBUG:
  1126. print("Not using DBus")
  1127. # Init GUI
  1128. gui = CatiaMainW()
  1129. # Set-up custom signal handling
  1130. setUpSignals(gui)
  1131. # Show GUI
  1132. gui.show()
  1133. # App-Loop
  1134. ret = app.exec_()
  1135. # Close Jack
  1136. if gJack.client:
  1137. jacklib.deactivate(gJack.client)
  1138. jacklib.client_close(gJack.client)
  1139. # Exit properly
  1140. sys.exit(ret)