| @@ -42,3 +42,6 @@ src/dist/ | |||||
| # Other | # Other | ||||
| data/templates/energyXT.xt | data/templates/energyXT.xt | ||||
| # IDEs | |||||
| .idea/ | |||||
| @@ -1,44 +1,48 @@ | |||||
| #!/bin/bash | |||||
| #! /usr/bin/env bash | |||||
| # Script to bridge/start pulseaudio into JACK mode | # Script to bridge/start pulseaudio into JACK mode | ||||
| INSTALL_PREFIX="X-PREFIX-X" | INSTALL_PREFIX="X-PREFIX-X" | ||||
| PULSE_CONFIG_DIR=${PULSE_CONFIG_DIR:-"$HOME/.pulse"} | |||||
| JACK_CONNFILE="$PULSE_CONFIG_DIR/jack-connections" | |||||
| PA_CTLFILE="$PULSE_CONFIG_DIR/ctl.pa" | |||||
| # ---------------------------------------------- | # ---------------------------------------------- | ||||
| if [ ! -d ~/.pulse ]; then | |||||
| mkdir -p ~/.pulse | |||||
| if [ ! -d $PULSE_CONFIG_DIR ]; then | |||||
| mkdir -p $PULSE_CONFIG_DIR | |||||
| fi | fi | ||||
| if [ ! -f ~/.pulse/client.conf ]; then | |||||
| echo "autospawn = no" > ~/.pulse/client.conf | |||||
| if [ ! -f $PULSE_CONFIG_DIR/client.conf ]; then | |||||
| echo "autospawn = no" > $PULSE_CONFIG_DIR/client.conf | |||||
| else | else | ||||
| if (! cat ~/.pulse/client.conf | grep "autospawn = no" > /dev/null); then | |||||
| sed -i '/autospawn =/d' ~/.pulse/client.conf | |||||
| echo "autospawn = no" >> ~/.pulse/client.conf | |||||
| if (! cat $PULSE_CONFIG_DIR/client.conf | grep "autospawn = no" > /dev/null); then | |||||
| sed -i '/autospawn =/d' $PULSE_CONFIG_DIR/client.conf | |||||
| echo "autospawn = no" >> $PULSE_CONFIG_DIR/client.conf | |||||
| fi | fi | ||||
| fi | fi | ||||
| if [ ! -f ~/.pulse/daemon.conf ]; then | |||||
| echo "default-sample-format = float32le" > ~/.pulse/daemon.conf | |||||
| echo "realtime-scheduling = yes" >> ~/.pulse/daemon.conf | |||||
| echo "rlimit-rttime = -1" >> ~/.pulse/daemon.conf | |||||
| echo "exit-idle-time = -1" >> ~/.pulse/daemon.conf | |||||
| if [ ! -f $PULSE_CONFIG_DIR/daemon.conf ]; then | |||||
| echo "default-sample-format = float32le" > $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "realtime-scheduling = yes" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "rlimit-rttime = -1" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "exit-idle-time = -1" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| else | else | ||||
| if (! cat ~/.pulse/daemon.conf | grep "default-sample-format = float32le" > /dev/null); then | |||||
| sed -i '/default-sample-format = /d' ~/.pulse/daemon.conf | |||||
| echo "default-sample-format = float32le" >> ~/.pulse/daemon.conf | |||||
| if (! cat $PULSE_CONFIG_DIR/daemon.conf | grep "default-sample-format = float32le" > /dev/null); then | |||||
| sed -i '/default-sample-format = /d' $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "default-sample-format = float32le" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| fi | fi | ||||
| if (! cat ~/.pulse/daemon.conf | grep "realtime-scheduling = yes" > /dev/null); then | |||||
| sed -i '/realtime-scheduling = /d' ~/.pulse/daemon.conf | |||||
| echo "realtime-scheduling = yes" >> ~/.pulse/daemon.conf | |||||
| if (! cat $PULSE_CONFIG_DIR/daemon.conf | grep "realtime-scheduling = yes" > /dev/null); then | |||||
| sed -i '/realtime-scheduling = /d' $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "realtime-scheduling = yes" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| fi | fi | ||||
| if (! cat ~/.pulse/daemon.conf | grep "rlimit-rttime = -1" > /dev/null); then | |||||
| sed -i '/rlimit-rttime =/d' ~/.pulse/daemon.conf | |||||
| echo "rlimit-rttime = -1" >> ~/.pulse/daemon.conf | |||||
| if (! cat $PULSE_CONFIG_DIR/daemon.conf | grep "rlimit-rttime = -1" > /dev/null); then | |||||
| sed -i '/rlimit-rttime =/d' $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "rlimit-rttime = -1" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| fi | fi | ||||
| if (! cat ~/.pulse/daemon.conf | grep "exit-idle-time = -1" > /dev/null); then | |||||
| sed -i '/exit-idle-time =/d' ~/.pulse/daemon.conf | |||||
| echo "exit-idle-time = -1" >> ~/.pulse/daemon.conf | |||||
| if (! cat $PULSE_CONFIG_DIR/daemon.conf | grep "exit-idle-time = -1" > /dev/null); then | |||||
| sed -i '/exit-idle-time =/d' $PULSE_CONFIG_DIR/daemon.conf | |||||
| echo "exit-idle-time = -1" >> $PULSE_CONFIG_DIR/daemon.conf | |||||
| fi | fi | ||||
| fi | fi | ||||
| @@ -56,7 +60,7 @@ echo "usage: $0 [command] | |||||
| --dummy Don't do anything, just create the needed files | --dummy Don't do anything, just create the needed files | ||||
| NOTE: | NOTE: | ||||
| When runned with no arguments, pulse2jack will | |||||
| When ran with no arguments, pulse2jack will | |||||
| activate PulseAudio with both playback and record modes. | activate PulseAudio with both playback and record modes. | ||||
| " | " | ||||
| exit | exit | ||||
| @@ -68,18 +72,56 @@ exit | |||||
| -p|--p|--play) | -p|--p|--play) | ||||
| PLAY_ONLY="yes" | PLAY_ONLY="yes" | ||||
| FILE=$INSTALL_PREFIX/share/cadence/pulse2jack/play.pa | |||||
| ;; | ;; | ||||
| *) | *) | ||||
| FILE=$INSTALL_PREFIX/share/cadence/pulse2jack/play+rec.pa | |||||
| ;; | ;; | ||||
| esac | esac | ||||
| TEMPLATE_PA_FILE=$INSTALL_PREFIX/share/cadence/pulse2jack/template.pa | |||||
| # ---------------------------------------------- | # ---------------------------------------------- | ||||
| IsPulseAudioRunning() | |||||
| { | |||||
| addJackConnectionsToPAFile() { | |||||
| PAFILE=$1 | |||||
| OUTFILE=$2 | |||||
| cp $PAFILE $OUTFILE | |||||
| tac $JACK_CONNFILE | while IFS=\| read name type channels connect; do | |||||
| sed -i "/### Load Jack modules/a load-module module-jack-$type channels=$channels connect=$connect client_name=\"$name\"" $OUTFILE | |||||
| done | |||||
| } | |||||
| loadConnectionsIntoPA() { | |||||
| CONNTYPE=$1 | |||||
| while IFS=\| read name type channels connect; do | |||||
| if [ $CONNTYPE == "$type" ] ; then | |||||
| pactl load-module module-jack-$type channels=$channels connect=$connect client_name="$name" > /dev/null | |||||
| fi | |||||
| done < $JACK_CONNFILE | |||||
| } | |||||
| addDefaultSink() { | |||||
| INFILE=$1 | |||||
| sed -i "/### Make Jack default/a set-default-sink jack_out" $INFILE | |||||
| } | |||||
| addDefaultSource() { | |||||
| INFILE=$1 | |||||
| sed -i "/### Make Jack default/a set-default-source jack_in" $INFILE | |||||
| } | |||||
| if [ ! -f $PULSE_CONFIG_DIR/jack-connections ] ; then | |||||
| # safety in case there's no config generated yet from GUI | |||||
| sed "/### Load Jack modules/a load-module module-jack-sink | |||||
| /### Load Jack modules/a load-module module-jack-source" $TEMPLATE_PA_FILE > $PA_CTLFILE | |||||
| else | |||||
| addJackConnectionsToPAFile $TEMPLATE_PA_FILE $PA_CTLFILE | |||||
| fi | |||||
| addDefaultSource $PA_CTLFILE | |||||
| addDefaultSink $PA_CTLFILE | |||||
| IsPulseAudioRunning() { | |||||
| PROCESS=`ps -u $USER | grep pulseaudio` | PROCESS=`ps -u $USER | grep pulseaudio` | ||||
| if [ "$PROCESS" == "" ]; then | if [ "$PROCESS" == "" ]; then | ||||
| false | false | ||||
| @@ -89,39 +131,29 @@ IsPulseAudioRunning() | |||||
| } | } | ||||
| if (IsPulseAudioRunning); then | if (IsPulseAudioRunning); then | ||||
| { | |||||
| if (`jack_lsp | grep "PulseAudio JACK Sink:" > /dev/null`); then | |||||
| { | |||||
| # get the first sink name from the table | |||||
| FIRST_SINK_NAME=$(grep '|sink|' $JACK_CONNFILE | head -1 | cut -d\| -f1) | |||||
| if ($(jack_lsp 2>/dev/null | grep "$FIRST_SINK_NAME" > /dev/null)); then | |||||
| echo "PulseAudio is already running and bridged to JACK" | echo "PulseAudio is already running and bridged to JACK" | ||||
| } | |||||
| else | else | ||||
| { | |||||
| echo "PulseAudio is already running, bridge it..." | echo "PulseAudio is already running, bridge it..." | ||||
| if [ "$PLAY_ONLY" == "yes" ]; then | if [ "$PLAY_ONLY" == "yes" ]; then | ||||
| { | |||||
| pactl load-module module-jack-sink > /dev/null | |||||
| loadConnectionsIntoPA "sink" | |||||
| pacmd set-default-source jack_in > /dev/null | pacmd set-default-source jack_in > /dev/null | ||||
| } | |||||
| else | else | ||||
| { | |||||
| pactl load-module module-jack-sink > /dev/null | |||||
| pactl load-module module-jack-source > /dev/null | |||||
| loadConnectionsIntoPA "source" | |||||
| loadConnectionsIntoPA "sink" | |||||
| pacmd set-default-sink jack_out > /dev/null | pacmd set-default-sink jack_out > /dev/null | ||||
| pacmd set-default-source jack_in > /dev/null | pacmd set-default-source jack_in > /dev/null | ||||
| } | |||||
| fi | fi | ||||
| echo "Done" | echo "Done" | ||||
| } | |||||
| fi | fi | ||||
| } | |||||
| else | else | ||||
| { | |||||
| if (`pulseaudio --daemonize --high-priority --realtime --exit-idle-time=-1 --file=$FILE -n`); then | |||||
| if ($(pulseaudio --daemonize --high-priority --realtime --exit-idle-time=-1 --file=$PA_CTLFILE -n)); then | |||||
| echo "Initiated PulseAudio successfully!" | echo "Initiated PulseAudio successfully!" | ||||
| else | else | ||||
| echo "Failed to initialize PulseAudio!" | echo "Failed to initialize PulseAudio!" | ||||
| fi | fi | ||||
| } | |||||
| fi | fi | ||||
| @@ -28,7 +28,6 @@ load-module module-stream-restore | |||||
| load-module module-card-restore | load-module module-card-restore | ||||
| ### Load Jack modules | ### Load Jack modules | ||||
| load-module module-jack-sink | |||||
| ### Load unix protocol | ### Load unix protocol | ||||
| load-module module-native-protocol-unix | load-module module-native-protocol-unix | ||||
| @@ -47,4 +46,4 @@ load-module module-rescue-streams | |||||
| load-module module-always-sink | load-module module-always-sink | ||||
| ### Make Jack default | ### Make Jack default | ||||
| set-default-sink jack_out | |||||
| @@ -0,0 +1,271 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Custom QTableWidget that handles pulseaudio source and sinks | |||||
| # Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # This program is free software; you can redistribute it and/or modify | |||||
| # it under the terms of the GNU General Public License as published by | |||||
| # the Free Software Foundation; either version 2 of the License, or | |||||
| # any later version. | |||||
| # | |||||
| # This program is distributed in the hope that it will be useful, | |||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| # GNU General Public License for more details. | |||||
| # | |||||
| # For a full copy of the GNU General Public License see the COPYING file | |||||
| # --------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| from collections import namedtuple | |||||
| from PyQt5.QtCore import Qt, QRegExp, pyqtSignal | |||||
| from PyQt5.QtGui import QRegExpValidator | |||||
| from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QComboBox, QLineEdit, QSpinBox, QPushButton, QCheckBox, QHBoxLayout, QWidget | |||||
| from shared import * | |||||
| from shared_cadence import GlobalSettings | |||||
| # Python3/4 function name normalisation | |||||
| try: | |||||
| range = xrange | |||||
| except NameError: | |||||
| pass | |||||
| PULSE_USER_CONFIG_DIR = os.getenv("PULSE_USER_CONFIG_DIR") | |||||
| if not PULSE_USER_CONFIG_DIR: | |||||
| PULSE_USER_CONFIG_DIR = os.path.join(HOME, ".pulse") | |||||
| if not os.path.exists(PULSE_USER_CONFIG_DIR): | |||||
| os.path.mkdir(PULSE_USER_CONFIG_DIR) | |||||
| # a data class to hold the Sink/Source Data. Use strings in tuple for easy map(_make) | |||||
| # but convert to type in table for editor | |||||
| SSData = namedtuple('SSData', 'name s_type channels connected') | |||||
| # --------------------------------------------------------------------- | |||||
| # Extend QTableWidget to hold Sink/Source data | |||||
| class BridgeSourceSink(QTableWidget): | |||||
| defaultPASourceData = SSData( | |||||
| name="PulseAudio JACK Source", | |||||
| s_type="source", | |||||
| channels="2", | |||||
| connected="True") | |||||
| defaultPASinkData = SSData( | |||||
| name="PulseAudio JACK Sink", | |||||
| s_type="sink", | |||||
| channels="2", | |||||
| connected="True") | |||||
| customChanged = pyqtSignal() | |||||
| def __init__(self, parent): | |||||
| QTableWidget.__init__(self, parent) | |||||
| self.bridgeData = [] | |||||
| if not GlobalSettings.contains("Pulse2JACK/PABridges"): | |||||
| self.initialise_settings() | |||||
| self.load_from_settings() | |||||
| def load_data_into_cells(self): | |||||
| self.setHorizontalHeaderLabels(['Name', 'Type', 'Channels', 'Conn?']) | |||||
| self.setRowCount(0) | |||||
| for data in self.bridgeData: | |||||
| row = self.rowCount() | |||||
| self.insertRow(row) | |||||
| # Name | |||||
| name_col = QLineEdit() | |||||
| name_col.setText(data.name) | |||||
| name_col.textChanged.connect(self.customChanged.emit) | |||||
| rx = QRegExp("[^|]+") | |||||
| validator = QRegExpValidator(rx, self) | |||||
| name_col.setValidator(validator) | |||||
| self.setCellWidget(row, 0, name_col) | |||||
| # Type | |||||
| combo_box = QComboBox() | |||||
| microphone_icon = QIcon.fromTheme('audio-input-microphone') | |||||
| if microphone_icon.isNull(): | |||||
| microphone_icon = QIcon.fromTheme('microphone') | |||||
| loudspeaker_icon = QIcon.fromTheme('audio-volume-high') | |||||
| if loudspeaker_icon.isNull(): | |||||
| loudspeaker_icon = QIcon.fromTheme('player-volume') | |||||
| combo_box.addItem(microphone_icon, "source") | |||||
| combo_box.addItem(loudspeaker_icon, "sink") | |||||
| combo_box.setCurrentIndex(0 if data.s_type == "source" else 1) | |||||
| combo_box.currentTextChanged.connect(self.customChanged.emit) | |||||
| self.setCellWidget(row, 1, combo_box) | |||||
| # Channels | |||||
| chan_col = QSpinBox() | |||||
| chan_col.setValue(int(data.channels)) | |||||
| chan_col.setMinimum(1) | |||||
| chan_col.setAlignment(Qt.AlignCenter) | |||||
| chan_col.valueChanged.connect(self.customChanged.emit) | |||||
| self.setCellWidget(row, 2, chan_col) | |||||
| # Auto connect? | |||||
| auto_cb = QCheckBox() | |||||
| auto_cb.setObjectName("auto_cb") | |||||
| auto_cb.setCheckState(Qt.Checked if data.connected in ['true', 'True', 'TRUE'] else Qt.Unchecked) | |||||
| auto_cb.stateChanged.connect(self.customChanged.emit) | |||||
| widget = QWidget() | |||||
| h_layout = QHBoxLayout(widget) | |||||
| h_layout.addWidget(auto_cb) | |||||
| h_layout.setAlignment(Qt.AlignCenter) | |||||
| h_layout.setContentsMargins(0, 0, 0, 0) | |||||
| widget.setLayout(h_layout) | |||||
| self.setCellWidget(row, 3, widget) | |||||
| self.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) | |||||
| def defaults(self): | |||||
| self.bridgeData = [self.defaultPASourceData, self.defaultPASinkData] | |||||
| self.load_data_into_cells() | |||||
| self.customChanged.emit() | |||||
| def undo(self): | |||||
| self.load_from_settings() | |||||
| self.load_data_into_cells() | |||||
| self.customChanged.emit() | |||||
| def initialise_settings(self): | |||||
| GlobalSettings.setValue( | |||||
| "Pulse2JACK/PABridges", | |||||
| self.encode_bridge_data([self.defaultPASourceData, self.defaultPASinkData])) | |||||
| def load_from_settings(self): | |||||
| bridgeDataText = GlobalSettings.value("Pulse2JACK/PABridges") | |||||
| self.bridgeData = self.decode_bridge_data(bridgeDataText) | |||||
| def hasChanges(self)->bool: | |||||
| bridgeDataText = GlobalSettings.value("Pulse2JACK/PABridges") | |||||
| saved_data = self.decode_bridge_data(bridgeDataText) | |||||
| if self.rowCount() != len(saved_data): | |||||
| return True | |||||
| for row in range(self.rowCount()): | |||||
| orig_data = saved_data[row] | |||||
| name = self.cellWidget(row, 0).text() | |||||
| if name != orig_data[0]: | |||||
| return True | |||||
| type = self.cellWidget(row, 1).currentText() | |||||
| if type != orig_data[1]: | |||||
| return True | |||||
| channels = self.cellWidget(row, 2).value() | |||||
| if channels != int(orig_data[2]): | |||||
| return True | |||||
| auto_cb = self.cellWidget(row, 3).findChild(QCheckBox, "auto_cb") | |||||
| connected = auto_cb.isChecked() | |||||
| if connected != bool(orig_data[3]): | |||||
| return True | |||||
| return False | |||||
| def hasValidValues(self)->bool: | |||||
| used_names = [] | |||||
| row_count = self.rowCount() | |||||
| # Prevent save without any bridge | |||||
| if not row_count: | |||||
| return False | |||||
| for row in range(row_count): | |||||
| line_edit = self.cellWidget(row, 0) | |||||
| name = line_edit.text() | |||||
| if not name or name in used_names: | |||||
| # prevent double name entries | |||||
| return False | |||||
| used_names.append(name) | |||||
| return True | |||||
| def add_row(self): | |||||
| # first, search in table which bridge exists | |||||
| # to add the most pertinent new bridge | |||||
| has_source = False | |||||
| has_sink = False | |||||
| for row in range(self.rowCount()): | |||||
| cell_widget = self.cellWidget(row, 1) | |||||
| group_type = "" | |||||
| if cell_widget: | |||||
| group_type = cell_widget.currentText() | |||||
| if group_type == "source": | |||||
| has_source = True | |||||
| elif group_type == "sink": | |||||
| has_sink = True | |||||
| if has_source and has_sink: | |||||
| break | |||||
| ss_data = SSData(name="", s_type="source", channels="2", connected="False") | |||||
| if not has_sink: | |||||
| ss_data = self.defaultPASinkData | |||||
| elif not has_source: | |||||
| ss_data = self.defaultPASourceData | |||||
| self.bridgeData.append(ss_data) | |||||
| self.load_data_into_cells() | |||||
| self.editItem(self.item(self.rowCount() - 1, 0)) | |||||
| self.customChanged.emit() | |||||
| def remove_row(self): | |||||
| del self.bridgeData[self.currentRow()] | |||||
| self.load_data_into_cells() | |||||
| self.customChanged.emit() | |||||
| def save_bridges(self): | |||||
| self.bridgeData = [] | |||||
| for row in range(0, self.rowCount()): | |||||
| new_name = self.cellWidget(row, 0).property("text") | |||||
| new_type = self.cellWidget(row, 1).currentText() | |||||
| new_channels = self.cellWidget(row, 2).value() | |||||
| auto_cb = self.cellWidget(row, 3).findChild(QCheckBox, "auto_cb") | |||||
| new_conn = auto_cb.checkState() == Qt.Checked | |||||
| self.bridgeData.append( | |||||
| SSData(name=new_name, | |||||
| s_type=new_type, | |||||
| channels=new_channels, | |||||
| connected=str(new_conn))) | |||||
| GlobalSettings.setValue("Pulse2JACK/PABridges", self.encode_bridge_data(self.bridgeData)) | |||||
| conn_file_path = os.path.join(PULSE_USER_CONFIG_DIR, "jack-connections") | |||||
| conn_file = open(conn_file_path, "w") | |||||
| conn_file.write("\n".join(self.encode_bridge_data(self.bridgeData))) | |||||
| # Need an extra line at the end | |||||
| conn_file.write("\n") | |||||
| conn_file.close() | |||||
| self.customChanged.emit() | |||||
| # encode and decode from tuple so it isn't stored in the settings file as a type, and thus the | |||||
| # configuration is backwards compatible with versions that don't understand SSData types. | |||||
| # Uses PIPE symbol as separator | |||||
| def encode_bridge_data(self, data): | |||||
| return list(map(lambda s: s.name + "|" + s.s_type + "|" + str(s.channels) + "|" + str(s.connected), data)) | |||||
| def decode_bridge_data(self, data): | |||||
| return list(map(lambda d: SSData._make(d.split("|")), data)) | |||||
| def resizeEvent(self, event): | |||||
| self.setColumnWidth(0, int(self.width() * 0.49)) | |||||
| self.setColumnWidth(1, int(self.width() * 0.17)) | |||||
| self.setColumnWidth(2, int(self.width() * 0.17)) | |||||
| self.setColumnWidth(3, int(self.width() * 0.17)) | |||||
| @@ -23,10 +23,10 @@ from platform import architecture | |||||
| if True: | if True: | ||||
| from PyQt5.QtCore import QFileSystemWatcher, QThread, QSemaphore | from PyQt5.QtCore import QFileSystemWatcher, QThread, QSemaphore | ||||
| from PyQt5.QtWidgets import QApplication, QDialogButtonBox, QLabel, QMainWindow, QSizePolicy | |||||
| from PyQt5.QtWidgets import QApplication, QDialogButtonBox, QLabel, QMainWindow #, QSizePolicy, QTableWidget, QTableWidgetItem, QHeaderView | |||||
| else: | else: | ||||
| from PyQt4.QtCore import QFileSystemWatcher, QThread, QSemaphore | from PyQt4.QtCore import QFileSystemWatcher, QThread, QSemaphore | ||||
| from PyQt4.QtGui import QApplication, QDialogButtonBox, QLabel, QMainWindow, QSizePolicy | |||||
| from PyQt4.QtGui import QApplication, QDialogButtonBox, QLabel, QMainWindow # , QSizePolicy, QTableWidget, QTableWidgetItem, QHeaderView | |||||
| # ------------------------------------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------------------------------------ | ||||
| # Imports (Custom Stuff) | # Imports (Custom Stuff) | ||||
| @@ -985,6 +985,8 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): | |||||
| if self.cb_app_browser.count() == 0: | if self.cb_app_browser.count() == 0: | ||||
| self.ch_app_browser.setEnabled(False) | self.ch_app_browser.setEnabled(False) | ||||
| self.tableSinkSourceData.load_data_into_cells() | |||||
| mimeappsPath = os.path.join(HOME, ".local", "share", "applications", "mimeapps.list") | mimeappsPath = os.path.join(HOME, ".local", "share", "applications", "mimeapps.list") | ||||
| if os.path.exists(mimeappsPath): | if os.path.exists(mimeappsPath): | ||||
| @@ -1135,6 +1137,14 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): | |||||
| self.b_pulse_stop.clicked.connect(self.slot_PulseAudioBridgeStop) | self.b_pulse_stop.clicked.connect(self.slot_PulseAudioBridgeStop) | ||||
| self.tb_pulse_options.clicked.connect(self.slot_PulseAudioBridgeOptions) | self.tb_pulse_options.clicked.connect(self.slot_PulseAudioBridgeOptions) | ||||
| self.b_bridge_add.clicked.connect(self.slot_PulseAudioBridgeAdd) | |||||
| self.b_bridge_remove.clicked.connect(self.slot_PulseAudioBridgeRemove) | |||||
| self.b_bridge_undo.clicked.connect(self.slot_PulseAudioBridgeUndo) | |||||
| self.b_bridge_save.clicked.connect(self.slot_PulseAudioBridgeSave) | |||||
| self.b_bridge_defaults.clicked.connect(self.slot_PulseAudioBridgeDefaults) | |||||
| self.tableSinkSourceData.customChanged.connect(self.slot_PulseAudioBridgeTableChanged) | |||||
| self.tableSinkSourceData.doubleClicked.connect(self.slot_PulseAudioBridgeTableChanged) | |||||
| self.pic_catia.clicked.connect(self.func_start_catia) | self.pic_catia.clicked.connect(self.func_start_catia) | ||||
| self.pic_claudia.clicked.connect(self.func_start_claudia) | self.pic_claudia.clicked.connect(self.func_start_claudia) | ||||
| self.pic_meter_in.clicked.connect(self.func_start_jackmeter_in) | self.pic_meter_in.clicked.connect(self.func_start_jackmeter_in) | ||||
| @@ -1229,11 +1239,13 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): | |||||
| except: | except: | ||||
| version, groups, conns = (list(), list(), list()) | version, groups, conns = (list(), list(), list()) | ||||
| pa_first_group_name = self.getFirstPulseAudioGroupName() | |||||
| for group_id, group_name, ports in groups: | for group_id, group_name, ports in groups: | ||||
| if group_name == "alsa2jack": | if group_name == "alsa2jack": | ||||
| global jackClientIdALSA | global jackClientIdALSA | ||||
| jackClientIdALSA = group_id | jackClientIdALSA = group_id | ||||
| elif group_name == "PulseAudio JACK Sink": | |||||
| elif group_name in (pa_first_group_name, "PulseAudio JACK Sink"): | |||||
| global jackClientIdPulse | global jackClientIdPulse | ||||
| jackClientIdPulse = group_id | jackClientIdPulse = group_id | ||||
| @@ -1408,6 +1420,14 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): | |||||
| self.systray.setActionEnabled("a2j_stop", False) | self.systray.setActionEnabled("a2j_stop", False) | ||||
| self.label_bridge_a2j.setText(self.tr("ALSA MIDI Bridge is stopped")) | self.label_bridge_a2j.setText(self.tr("ALSA MIDI Bridge is stopped")) | ||||
| def getFirstPulseAudioGroupName(self)->str: | |||||
| # search PulseAudio JACK first group in settings | |||||
| pa_bridges_settings = GlobalSettings.value("Pulse2JACK/PABridges", type=list) | |||||
| if pa_bridges_settings: | |||||
| return pa_bridges_settings[0].partition('|')[0] | |||||
| return "PulseAudio JACK Sink" | |||||
| def checkAlsaAudio(self): | def checkAlsaAudio(self): | ||||
| asoundrcFile = os.path.join(HOME, ".asoundrc") | asoundrcFile = os.path.join(HOME, ".asoundrc") | ||||
| @@ -1617,11 +1637,13 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): | |||||
| @pyqtSlot(int, str) | @pyqtSlot(int, str) | ||||
| def slot_DBusJackClientAppearedCallback(self, group_id, group_name): | def slot_DBusJackClientAppearedCallback(self, group_id, group_name): | ||||
| pa_first_group_name = self.getFirstPulseAudioGroupName() | |||||
| if group_name == "alsa2jack": | if group_name == "alsa2jack": | ||||
| global jackClientIdALSA | global jackClientIdALSA | ||||
| jackClientIdALSA = group_id | jackClientIdALSA = group_id | ||||
| self.checkAlsaAudio() | self.checkAlsaAudio() | ||||
| elif group_name == "PulseAudio JACK Sink": | |||||
| elif group_name in ("PulseAudio JACK Sink", pa_first_group_name): | |||||
| global jackClientIdPulse | global jackClientIdPulse | ||||
| jackClientIdPulse = group_id | jackClientIdPulse = group_id | ||||
| self.checkPulseAudio() | self.checkPulseAudio() | ||||
| @@ -1798,6 +1820,33 @@ class CadenceMainW(QMainWindow, ui_cadence.Ui_CadenceMainW): | |||||
| def slot_PulseAudioBridgeStop(self): | def slot_PulseAudioBridgeStop(self): | ||||
| os.system("pulseaudio -k") | os.system("pulseaudio -k") | ||||
| @pyqtSlot() | |||||
| def slot_PulseAudioBridgeTableChanged(self): | |||||
| has_changes = self.tableSinkSourceData.hasChanges() | |||||
| has_valid_values = self.tableSinkSourceData.hasValidValues() | |||||
| self.b_bridge_save.setEnabled(has_changes and has_valid_values) | |||||
| self.b_bridge_undo.setEnabled(has_changes) | |||||
| @pyqtSlot() | |||||
| def slot_PulseAudioBridgeAdd(self): | |||||
| self.tableSinkSourceData.add_row() | |||||
| @pyqtSlot() | |||||
| def slot_PulseAudioBridgeRemove(self): | |||||
| self.tableSinkSourceData.remove_row() | |||||
| @pyqtSlot() | |||||
| def slot_PulseAudioBridgeUndo(self): | |||||
| self.tableSinkSourceData.undo() | |||||
| @pyqtSlot() | |||||
| def slot_PulseAudioBridgeSave(self): | |||||
| self.tableSinkSourceData.save_bridges() | |||||
| @pyqtSlot() | |||||
| def slot_PulseAudioBridgeDefaults(self): | |||||
| self.tableSinkSourceData.defaults() | |||||
| @pyqtSlot() | @pyqtSlot() | ||||
| def slot_PulseAudioBridgeOptions(self): | def slot_PulseAudioBridgeOptions(self): | ||||
| ToolBarPADialog(self).exec_() | ToolBarPADialog(self).exec_() | ||||
| @@ -168,7 +168,7 @@ class ClaudiaLauncher(QWidget, ui_claudia_launcher.Ui_ClaudiaLauncherW): | |||||
| self.clearInfo_DAW() | self.clearInfo_DAW() | ||||
| self.clearInfo_Host() | self.clearInfo_Host() | ||||
| self.clearInfo_Intrument() | |||||
| self.clearInfo_Instrument() | |||||
| self.clearInfo_Bristol() | self.clearInfo_Bristol() | ||||
| self.clearInfo_Plugin() | self.clearInfo_Plugin() | ||||
| self.clearInfo_Effect() | self.clearInfo_Effect() | ||||
| @@ -547,7 +547,7 @@ class ClaudiaLauncher(QWidget, ui_claudia_launcher.Ui_ClaudiaLauncherW): | |||||
| self.frame_Host.setEnabled(False) | self.frame_Host.setEnabled(False) | ||||
| self.showDoc_Host("", "") | self.showDoc_Host("", "") | ||||
| def clearInfo_Intrument(self): | |||||
| def clearInfo_Instrument(self): | |||||
| self.ico_app_ins.setPixmap(self.getIcon("start-here").pixmap(48, 48)) | self.ico_app_ins.setPixmap(self.getIcon("start-here").pixmap(48, 48)) | ||||
| self.label_name_ins.setText("App Name") | self.label_name_ins.setText("App Name") | ||||
| self.ico_builtin_fx_ins.setPixmap(self.getIconForYesNo(False).pixmap(16, 16)) | self.ico_builtin_fx_ins.setPixmap(self.getIconForYesNo(False).pixmap(16, 16)) | ||||
| @@ -983,7 +983,7 @@ class ClaudiaLauncher(QWidget, ui_claudia_launcher.Ui_ClaudiaLauncherW): | |||||
| Docs0 = Docs[0] if (os.path.exists(Docs[0].replace("file://", ""))) else "" | Docs0 = Docs[0] if (os.path.exists(Docs[0].replace("file://", ""))) else "" | ||||
| self.showDoc_Instrument(Docs0, Docs[1]) | self.showDoc_Instrument(Docs0, Docs[1]) | ||||
| else: | else: | ||||
| self.clearInfo_Intrument() | |||||
| self.clearInfo_Instrument() | |||||
| self.callback_checkGUI(row >= 0) | self.callback_checkGUI(row >= 0) | ||||