Skip to content

Commit 4884b53

Browse files
authored
Removed the need for JACK
- By using ALSA a PulseAudio device can now be selected
1 parent 26fdc89 commit 4884b53

File tree

2 files changed

+78
-96
lines changed

2 files changed

+78
-96
lines changed

ReVidia.py

+22-12
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,38 @@
99

1010
# Displays device ID options
1111
def deviceNames(q, output=True):
12-
import re
12+
import subprocess
13+
14+
pulseList, inputList, monitorList, idList, samples = [], [], [], [], []
15+
16+
if output: # Get PulseAudio monitors
17+
result = subprocess.getoutput('pactl list sources | grep "Name:" | grep "monitor"')
18+
if result:
19+
resultSplit = result.split('\n')
20+
for line in resultSplit:
21+
monitorList.append(line.split('\tName: ')[1])
22+
23+
for monitor in monitorList:
24+
result = subprocess.getoutput(
25+
'pactl list sources | sed -n /' + monitor + '/,/"Source #"/p | grep -e "alsa.card_name ="')
26+
if result:
27+
pulseList.append('Output: ' + result.split('\t\talsa.card_name = "')[1].split('"')[0] + ' - PulseAudio')
28+
else:
29+
pulseList.append('Output: ' + monitor.split('.monitor')[0] + ' - PulseAudio')
30+
samples.append(0)
31+
1332
p = pyaudio.PyAudio()
14-
outList, inputList, hwIDList, idList, samplesOut, sampleIn = [], [], [], [], [], []
1533
numDevices = p.get_device_count()
16-
1734
for ID in range(numDevices):
1835
API = p.get_host_api_info_by_index(p.get_device_info_by_index(ID).get('hostApi')).get('name')
1936
name = p.get_device_info_by_index(ID).get('name')
2037

21-
if output:
22-
if (p.get_device_info_by_index(ID).get('maxOutputChannels')) > 0:
23-
if re.search(r'(hw:\d+,\d)+', name):
24-
outList.append('Output: ' + name + ' - ' + str(API))
25-
hwIDList.append(re.findall(r'(hw:\d+,\d)+', name)[0])
26-
samplesOut.append(0)
27-
2838
if (p.get_device_info_by_index(ID).get('maxInputChannels')) > 0:
2939
inputList.append('Input: ' + name + ' - ' + str(API))
3040
idList.append(ID)
31-
sampleIn.append(p.get_device_info_by_index(ID).get('defaultSampleRate'))
41+
samples.append(p.get_device_info_by_index(ID).get('defaultSampleRate'))
3242

33-
deviceList = [outList + inputList, hwIDList + idList, samplesOut + sampleIn]
43+
deviceList = [pulseList + inputList, monitorList + idList, samples]
3444
p.terminate()
3545
q.put(deviceList)
3646

ReVidiaGUI.py

+56-84
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,6 @@ class ReVidiaMain(QMainWindow):
1717
def __init__(self):
1818
super(ReVidiaMain, self).__init__()
1919

20-
try: # Get user default pulseaduio device
21-
result = subprocess.getoutput('pactl info | grep "Default Sink:"')
22-
self.userSink = result.split('Default Sink: ')[1]
23-
result = subprocess.getoutput('pactl info | grep "Default Source:"')
24-
self.userSource = result.split('Default Source: ')[1]
25-
except:
26-
print('Can\'t find Pulseaudio: Outputs disabled')
27-
self.userSink = 0
28-
self.userSource = 0
29-
3020
# Sets up window to be in the middle and to be half screen height
3121
screen = QApplication.desktop().screenNumber(
3222
QApplication.desktop().cursor().pos())
@@ -718,97 +708,77 @@ def setProfile(self, request):
718708
if ok and profile and profileList != ['No Profiles Saved']:
719709
os.remove('profiles/' + profile + '.pkl')
720710

721-
def getDevice(self, firstRun, output=False):
722-
# Find default devices for PyAudio
723-
defaultHWOut = ''
724-
defaultHWIn = ''
725-
if self.userSink:
726-
result = subprocess.getoutput(
727-
'pactl list sinks | sed -n /' + self.userSink + '/,/"Sink #"/p | grep -e "alsa.card =" -e "alsa.device"')
728-
if result:
729-
card = result.split('alsa.card = "')[1].split('"')[0]
730-
device = result.split('alsa.device = "')[1].split('"')[0]
731-
defaultHWOut = 'hw:' + card + ',' + device
732-
733-
if self.userSource:
734-
result = subprocess.getoutput(
735-
'pactl list sources | sed -n /' + self.userSource + '/,/"Source #"/p | grep -e "alsa.card =" -e "alsa.device"')
736-
if result:
737-
card = result.split('alsa.card = "')[1].split('"')[0]
738-
device = result.split('alsa.device = "')[1].split('"')[0]
739-
defaultHWIn = 'hw:' + card + ',' + device
740-
741-
if defaultHWOut or defaultHWIn:
742-
# Free up default audio device so it can be detected
743-
subprocess.run('pactl suspend-sink "' + self.userSink + '" 1', shell=True)
711+
def getDevice(self, firstRun, pulseAudio=False):
712+
try: # Get user default pulseaduio device
713+
result = subprocess.getoutput('pactl info | grep "Default Sink:"')
714+
self.userSink = result.split('Default Sink: ')[1] + '.monitor'
715+
result = subprocess.getoutput('pactl info | grep "Default Source:"')
716+
self.userSource = result.split('Default Source: ')[1]
717+
except:
718+
print('Can\'t find Pulseaudio: Outputs disabled')
719+
self.userSink = 0
720+
self.userSource = 0
744721

745722
# Run device getter on separate Process because the other PA won't start if not done
746723
devQ = mp.Queue()
747724
D1 = mp.Process(target=ReVidia.deviceNames, args=(devQ, self.userSink))
748725
D1.start(), D1.join()
749726
deviceList = devQ.get()
750-
if defaultHWOut or defaultHWIn:
751-
subprocess.run('pactl suspend-sink "' + self.userSink + '" 0', shell=True)
752727

753728
defaultList = []
754-
if defaultHWOut:
755-
defaultList.append('Default ALSA Output Device')
756-
if defaultHWIn:
757-
defaultList.append('Default ALSA Input Device')
729+
if self.userSink:
730+
defaultList.append('Default PulseAudio Output Device')
731+
if self.userSource:
732+
defaultList.append('Default PulseAudio Input Device')
758733

759734
itemList = defaultList + deviceList[0]
760-
if not output:
735+
if not pulseAudio:
761736
device, ok = QInputDialog.getItem(self, "ReVidia", "Select Audio Device:", itemList, 0, False)
762-
else: # Auto select JACK
763-
device = 'Input: PulseAudio JACK Sink - JACK Audio Connection Kit'
737+
else: # Auto select PulseAudio
738+
device = 'Input: revidia_capture - ALSA'
764739
ok = 1
765740

766741
if ok and device:
767-
if defaultHWOut in device and 'Output' in device:
768-
device = 'Default ALSA Output Device'
769742
# Getting ID
770-
if device == 'Default ALSA Output Device':
771-
ID = defaultHWOut
772-
elif device == 'Default ALSA Input Device':
773-
ID = defaultHWIn
774-
for device in deviceList[0]:
775-
if defaultHWOut in device and 'Input' in device:
776-
ID = deviceList[1][deviceList[0].index(device)]
743+
if device == 'Default PulseAudio Output Device':
744+
ID = self.userSink
745+
elif device == 'Default PulseAudio Input Device':
746+
ID = self.userSource
777747
else:
778748
ID = deviceList[1][deviceList[0].index(device)]
779749

780-
if isinstance(ID, int): # If PortAudio Index Num, continue
750+
if 'Input:' in device: # If PortAudio Index Num, continue
781751
self.ID = ID
782752
self.sampleRate = deviceList[2][deviceList[0].index(device)]
783-
else: # If hw:x,y run JACK
784-
if not firstRun: # Kill JACK if already running
785-
if hasattr(self, 'jack'):
786-
subprocess.run('pactl set-default-sink "' + self.userSink + '"',
787-
shell=True) # Restore user's default
788-
self.jack.kill()
789-
del self.jack
790-
791-
# Free up default audio device so it can be used
792-
subprocess.run('pactl suspend-sink "' + self.userSink + '" 1', shell=True)
793-
794-
# Start Jack and change Pulseaudio device to Jack_Sink to collect data
795-
self.jack = subprocess.Popen('jackd -T -d alsa -d' + ID, shell=True)
796-
subprocess.run('pactl load-module module-jack-sink sink_name="Jack_Sink" ', shell=True)
797-
subprocess.run('pactl suspend-sink "' + self.userSink + '" 0', shell=True)
798-
799-
if device == 'Default ALSA Output Device':
800-
subprocess.run('pactl set-default-sink "Jack_Sink"', shell=True)
801-
802-
# Moving over streams to Default Sink
803-
result = subprocess.getoutput('pactl list sink-inputs | grep "Sink Input #"')
804-
if result:
805-
streams = result.split('\n')
806-
result = subprocess.getoutput('pactl info | grep "Default Sink:"')
807-
defaultSink = result.split('Default Sink: ')[1]
808-
for stream in streams:
809-
index = stream.split('Sink Input #')[1]
810-
subprocess.run('pactl move-sink-input ' + index + ' "' + defaultSink + '"', shell=True)
811-
753+
else: # If PulseAudio input turn into ALSA input
754+
import os
755+
alsaFolder = os.getenv("HOME") + '/.asoundrc'
756+
757+
# Check/Clean lines for an old "pcm.revidia_capture" device
758+
self.cleanLines = []
759+
skip = 0
760+
with open(alsaFolder, 'r') as alsaConf:
761+
allLines = alsaConf.readlines()
762+
for line in allLines:
763+
if not skip:
764+
if not 'pcm.revidia_capture' in line:
765+
self.cleanLines.append(line)
766+
else:
767+
skip = 1
768+
else:
769+
if '}' in line:
770+
skip = 0
771+
if allLines != self.cleanLines:
772+
with open(alsaFolder, 'w') as alsaConf:
773+
alsaConf.writelines(self.cleanLines)
774+
775+
# Create ALSA device to connect to PulseAudio
776+
with open(alsaFolder, 'a') as alsaConf:
777+
alsaConf.write('pcm.revidia_capture {'
778+
'\n type pulse'
779+
'\n device ' + ID +
780+
'\n}')
781+
# Rerun to select the device just created
812782
if firstRun:
813783
self.getDevice(True, True)
814784
else:
@@ -1154,9 +1124,11 @@ def closeEvent(self, event):
11541124
self.mainQ.put(1) # End main thread
11551125
self.P1.terminate() # Kill Processes
11561126
self.T1.terminate()
1157-
if hasattr(self, 'jack'):
1158-
subprocess.run('pactl set-default-sink "' + self.userSink + '"', shell=True) # Restore user's default
1159-
self.jack.kill()
1127+
if hasattr(self, 'cleanLines'): # Clean up ~/.asoundrc
1128+
import os
1129+
alsaFolder = os.getenv("HOME") + '/.asoundrc'
1130+
with open(alsaFolder, 'w') as alsaConf:
1131+
alsaConf.writelines(self.cleanLines)
11601132
except RuntimeError:
11611133
print('Some processes won\'t close properly, closing anyway.')
11621134

0 commit comments

Comments
 (0)