From 618f9e0b84eff1479947cdeb639dd18d4f6e4dba Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 3 Oct 2012 14:04:11 +0100 Subject: [PATCH] Cadence: Implement pulseaudio bridge; fix getProcList() --- src/cadence.py | 170 ++++++++++++++++++++++++++++++++++-------- src/shared_cadence.py | 4 +- src/ui/cadence.ui | 12 +-- 3 files changed, 147 insertions(+), 39 deletions(-) diff --git a/src/cadence.py b/src/cadence.py index 0a83e6b..c74b298 100755 --- a/src/cadence.py +++ b/src/cadence.py @@ -47,6 +47,16 @@ if haveWine: # --------------------------------------------------------------------- +# jackdbus indexes +iGraphVersion = 0 +iJackClientId = 1 +iJackClientName = 2 +iJackPortId = 3 +iJackPortName = 4 +iJackPortNewName = 5 +iJackPortFlags = 5 +iJackPortType = 6 + DESKTOP_X_IMAGE = [ "eog.desktop", "kde4/digikam.desktop", @@ -208,18 +218,15 @@ def smartHex(value, length): return hexStr +global PA_clientId +PA_clientId = -1 + def PA_is_started(): return bool("pulseaudio" in getProcList()) -#def PA_is_bridged(): - #if (jack.client): - #port_list = jacklib.get_ports(jack.client, "", "", 0) - #if ("PulseAudio JACK Sink:front-left" in port_list): - #return True - #else: - #return False - #else: - #return False +def PA_is_bridged(): + global PA_clientId + return bool(PA_clientId != -1) # --------------------------------------------------------------------- @@ -366,7 +373,7 @@ class ForceRestartThread(QThread): # Start it DBus.jack.StartServer() - self.emit(SIGNAL("progressChanged(int)"), 95) + self.emit(SIGNAL("progressChanged(int)"), 93) # If we made it this far, then JACK is started self.m_wasStarted = True @@ -377,14 +384,15 @@ class ForceRestartThread(QThread): DBus.a2j.set_hw_export(a2jExportHW) DBus.a2j.start() - self.emit(SIGNAL("progressChanged(int)"), 100) + self.emit(SIGNAL("progressChanged(int)"), 96) - # TODO - #if GlobalSettings.value("Pulse2JACK/AutoStart", True, type=bool) and not PA_is_bridged(): - #if GlobalSettings.value("Pulse2JACK/PlaybackModeOnly", False, type=bool): - #os.system("cadence-pulse2jack -p") - #else: - #os.system("cadence-pulse2jack") + if GlobalSettings.value("Pulse2JACK/AutoStart", True, type=bool) and not PA_is_bridged(): + if GlobalSettings.value("Pulse2JACK/PlaybackModeOnly", False, type=bool): + os.system("cadence-pulse2jack -p") + else: + os.system("cadence-pulse2jack") + + self.emit(SIGNAL("progressChanged(int)"), 100) # Force Restart Dialog class ForceWaitDialog(QDialog, ui_cadence_rwait.Ui_Dialog): @@ -498,7 +506,6 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): # TODO - uninplemented self.toolBox_alsaaudio.setEnabled(False) - self.toolBox_pulseaudio.setEnabled(False) self.settings = QSettings("Cadence", "Cadence") self.loadSettings(True) @@ -747,9 +754,9 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.systray.addMenuAction("a2j", "a2j_start", self.tr("Start")) self.systray.addMenuAction("a2j", "a2j_stop", self.tr("Stop")) self.systray.addMenuAction("a2j", "a2j_export_hw", self.tr("Export Hardware Ports...")) - #self.systray.addMenu("pulse", self.tr("Pulse2JACK Bridge")) - #self.systray.addMenuAction("pulse", "pulse_start", self.tr("Start")) - #self.systray.addMenuAction("pulse", "pulse_stop", self.tr("Stop")) + self.systray.addMenu("pulse", self.tr("PulseAudio Bridge")) + self.systray.addMenuAction("pulse", "pulse_start", self.tr("Start")) + self.systray.addMenuAction("pulse", "pulse_stop", self.tr("Stop")) self.systray.addMenu("tools", self.tr("Tools")) self.systray.addMenuAction("tools", "app_catarina", "Catarina") self.systray.addMenuAction("tools", "app_catia", "Catia") @@ -767,8 +774,8 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.systray.setActionIcon("jack_configure", "configure") self.systray.setActionIcon("a2j_start", "media-playback-start") self.systray.setActionIcon("a2j_stop", "media-playback-stop") - #self.systray.setActionIcon("pulse_start", "media-playback-start") - #self.systray.setActionIcon("pulse_stop", "media-playback-stop") + self.systray.setActionIcon("pulse_start", "media-playback-start") + self.systray.setActionIcon("pulse_stop", "media-playback-stop") self.systray.connect("jack_start", self.slot_JackServerStart) self.systray.connect("jack_stop", self.slot_JackServerStop) @@ -776,8 +783,8 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.systray.connect("a2j_start", self.slot_A2JBridgeStart) self.systray.connect("a2j_stop", self.slot_A2JBridgeStop) self.systray.connect("a2j_export_hw", self.slot_A2JBridgeExportHW) - #self.systray.connect("pulse_start", self.PABridgeStart) - #self.systray.connect("pulse_stop", self.PABridgeStop) + self.systray.connect("pulse_start", self.slot_PulseAudioBridgeStart) + self.systray.connect("pulse_stop", self.slot_PulseAudioBridgeStop) self.systray.connect("app_catarina", lambda tool="catarina": self.func_start_tool(tool)) self.systray.connect("app_catia", lambda tool="catia": self.func_start_tool(tool)) self.systray.connect("app_claudia", lambda tool="claudia": self.func_start_tool(tool)) @@ -804,6 +811,10 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.connect(self.b_a2j_export_hw, SIGNAL("clicked()"), SLOT("slot_A2JBridgeExportHW()")) self.connect(self.tb_a2j_options, SIGNAL("clicked()"), SLOT("slot_A2JBridgeOptions()")) + self.connect(self.b_pulse_start, SIGNAL("clicked()"), SLOT("slot_PulseAudioBridgeStart()")) + self.connect(self.b_pulse_stop, SIGNAL("clicked()"), SLOT("slot_PulseAudioBridgeStop()")) + self.connect(self.tb_pulse_options, SIGNAL("clicked()"), SLOT("slot_PulseAudioBridgeOptions()")) + self.connect(self.pic_catia, SIGNAL("clicked()"), lambda tool="catia": self.func_start_tool(tool)) self.connect(self.pic_claudia, SIGNAL("clicked()"), lambda tool="claudia": self.func_start_tool(tool)) self.connect(self.pic_carla, SIGNAL("clicked()"), lambda tool="carla": self.func_start_tool(tool)) @@ -846,6 +857,18 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.connect(self.cb_wineasio_fixed_bsize, SIGNAL("clicked()"), SLOT("slot_tweaksSettingsChanged_wineasio()")) self.connect(self.cb_wineasio_bsizes, SIGNAL("currentIndexChanged(int)"), SLOT("slot_tweaksSettingsChanged_wineasio()")) + # org.jackaudio.JackControl + self.connect(self, SIGNAL("DBusJackServerStartedCallback()"), SLOT("slot_DBusJackServerStartedCallback()")) + self.connect(self, SIGNAL("DBusJackServerStoppedCallback()"), SLOT("slot_DBusJackServerStoppedCallback()")) + + # org.jackaudio.JackPatchbay + self.connect(self, SIGNAL("DBusJackClientAppearedCallback(int, QString)"), SLOT("slot_DBusJackClientAppearedCallback(int, QString)")) + self.connect(self, SIGNAL("DBusJackClientDisappearedCallback(int)"), SLOT("slot_DBusJackClientDisappearedCallback(int)")) + + # org.gna.home.a2jmidid.control + self.connect(self, SIGNAL("DBusA2JBridgeStartedCallback()"), SLOT("slot_DBusA2JBridgeStartedCallback()")) + self.connect(self, SIGNAL("DBusA2JBridgeStoppedCallback()"), SLOT("slot_DBusA2JBridgeStoppedCallback()")) + # ------------------------------------------------------------- self.m_last_dsp_load = None @@ -864,10 +887,12 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): def DBusReconnect(self): if haveDBus: try: - DBus.jack = DBus.bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller") + DBus.jack = DBus.bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller") + DBus.patchbay = dbus.Interface(DBus.jack, "org.jackaudio.JackPatchbay") jacksettings.initBus(DBus.bus) except: - DBus.jack = None + DBus.jack = None + DBus.patchbay = None try: DBus.a2j = dbus.Interface(DBus.bus.get_object("org.gna.home.a2jmidid", "/"), "org.gna.home.a2jmidid.control") @@ -876,7 +901,17 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): if DBus.jack: if DBus.jack.IsStarted(): + # Check for pulseaudio in jack graph + version, groups, conns = DBus.patchbay.GetGraph(0) + + for group_id, group_name, ports in groups: + if group_name == "PulseAudio JACK Sink": + global PA_clientId + PA_clientId = group_id + break + self.jackStarted() + else: self.jackStopped() self.label_jack_realtime.setText("Yes" if jacksettings.isRealtime() else "No") @@ -923,15 +958,24 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): elif kwds['interface'] == "org.jackaudio.JackControl": if DEBUG: print("org.jackaudio.JackControl", kwds['member']) if kwds['member'] == "ServerStarted": - self.jackStarted() + self.emit(SIGNAL("DBusJackServerStartedCallback()")) elif kwds['member'] == "ServerStopped": - self.jackStopped() + self.emit(SIGNAL("DBusJackServerStoppedCallback()")) + + elif kwds['interface'] == "org.jackaudio.JackPatchbay": + if DBus.patchbay and kwds['path'] == DBus.patchbay.object_path: + if DEBUG: print("org.jackaudio.JackPatchbay,", kwds['member']) + if kwds['member'] == "ClientAppeared": + self.emit(SIGNAL("DBusJackClientAppearedCallback(int, QString)"), args[iJackClientId], args[iJackClientName]) + elif kwds['member'] == "ClientDisappeared": + self.emit(SIGNAL("DBusJackClientDisappearedCallback(int)"), args[iJackClientId]) + elif kwds['interface'] == "org.gna.home.a2jmidid.control": if DEBUG: print("org.gna.home.a2jmidid.control", kwds['member']) if kwds['member'] == "bridge_started": - self.a2jStarted() + self.emit(SIGNAL("DBusA2JBridgeStartedCallback()")) elif kwds['member'] == "bridge_stopped": - self.a2jStopped() + self.emit(SIGNAL("DBusA2JBridgeStoppedCallback()")) def jackStarted(self): self.m_last_dsp_load = DBus.jack.GetLoad() @@ -965,6 +1009,8 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.b_a2j_start.setEnabled(True) self.systray.setActionEnabled("a2j_start", True) + self.checkPulseAudio() + def jackStopped(self): if self.m_timer500: self.killTimer(self.m_timer500) @@ -992,6 +1038,10 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.b_a2j_start.setEnabled(False) self.systray.setActionEnabled("a2j_start", False) + global PA_clientId + PA_clientId = -1 + self.checkPulseAudio() + def a2jStarted(self): self.label_bridge_a2j.setText(self.tr("ALSA MIDI Bridge is running")) self.b_a2j_start.setEnabled(False) @@ -1010,6 +1060,22 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.systray.setActionEnabled("a2j_stop", False) self.systray.setActionEnabled("a2j_export_hw", True) + def checkPulseAudio(self): + if PA_is_started(): + global PA_clientId + if PA_clientId != -1: + self.label_bridge_pulse.setText(self.tr("PulseAudio is started and bridged to JACK")) + self.b_pulse_start.setEnabled(False) + self.b_pulse_stop.setEnabled(True) + else: + self.label_bridge_pulse.setText(self.tr("PulseAudio is started but not bridged")) + self.b_pulse_start.setEnabled(DBus.jack and DBus.jack.IsStarted()) + self.b_pulse_stop.setEnabled(False) + else: + self.b_pulse_start.setEnabled(DBus.jack and DBus.jack.IsStarted()) + self.b_pulse_stop.setEnabled(False) + self.label_bridge_pulse.setText(self.tr("PulseAudio is not started")) + def setAppDetails(self, desktop): appContents = getDesktopFileContents(desktop) name = getXdgProperty(appContents, "Name") @@ -1050,6 +1116,36 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): self.settings_changed_types.append(stype) self.frame_tweaks_settings.setVisible(True) + @pyqtSlot() + def slot_DBusJackServerStartedCallback(self): + self.jackStarted() + + @pyqtSlot() + def slot_DBusJackServerStoppedCallback(self): + self.jackStopped() + + @pyqtSlot(int, str) + def slot_DBusJackClientAppearedCallback(self, group_id, group_name): + if group_name == "PulseAudio JACK Sink": + global PA_clientId + PA_clientId = group_id + self.checkPulseAudio() + + @pyqtSlot(int) + def slot_DBusJackClientDisappearedCallback(self, group_id): + global PA_clientId + if group_id == PA_clientId: + PA_clientId = -1 + self.checkPulseAudio() + + @pyqtSlot() + def slot_DBusA2JBridgeStartedCallback(self): + self.a2jStarted() + + @pyqtSlot() + def slot_DBusA2JBridgeStoppedCallback(self): + self.a2jStopped() + @pyqtSlot() def slot_JackServerStart(self): try: @@ -1116,6 +1212,18 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): def slot_A2JBridgeOptions(self): ToolBarA2JDialog(self).exec_() + @pyqtSlot() + def slot_PulseAudioBridgeStart(self): + os.system("cadence-pulse2jack") + + @pyqtSlot() + def slot_PulseAudioBridgeStop(self): + os.system("pulseaudio -k") + + @pyqtSlot() + def slot_PulseAudioBridgeOptions(self): + pass #ToolBarA2JDialog(self).exec_() + @pyqtSlot() def slot_handleCrash_jack(self): self.DBusReconnect() diff --git a/src/shared_cadence.py b/src/shared_cadence.py index 9694fb0..fe6eae9 100644 --- a/src/shared_cadence.py +++ b/src/shared_cadence.py @@ -50,8 +50,8 @@ def getProcList(): for i in range(len(processDump)): if (i == 0): continue - dumpTest = str(processDump[i]).rsplit(":", 1)[-1].split(" ") - if len(dumpTest) > 1: + dumpTest = str(processDump[i], encoding="utf-8").rsplit(":", 1)[-1].split(" ") + if len(dumpTest) > 1 and dumpTest[1]: retProcs.append(dumpTest[1]) return retProcs diff --git a/src/ui/cadence.ui b/src/ui/cadence.ui index 35e70df..0fed4e4 100644 --- a/src/ui/cadence.ui +++ b/src/ui/cadence.ui @@ -483,7 +483,7 @@ 0 0 351 - 114 + 117 @@ -628,7 +628,7 @@ 0 0 351 - 114 + 115 @@ -766,7 +766,7 @@ 0 0 351 - 114 + 115 @@ -814,14 +814,14 @@ - + Start - + Stop @@ -868,7 +868,7 @@ - + ...