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.

1365 lines
52KB

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