Skip to content

Commit

Permalink
Merge branch 'cdelorme-master'
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidRisch committed Feb 14, 2021
2 parents 5970a39 + 6f86116 commit b48cf3e
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 42 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -6,3 +6,5 @@
/images/*.xcf

/.idea/

*.pyc
3 changes: 2 additions & 1 deletion config/config_template.yaml
Expand Up @@ -40,7 +40,8 @@ audio:
excluded_clients_regexes: # List of regexes. Used to ignore some audio clients. Intended for application not needed in vr.
- 'firefox'
set_card_port: true # Boolean. Enable automatic changing the correct port.
card_port_product_name_regex: '(Index HMD)|(VIVE)' # Regex. Used to find the card and port on that card which the vr headset is connected to.
card_port_vr_product_name_regex: '(Index HMD)|(VIVE)' # Regex. Used to find the card and port on that card which the vr headset is connected to.
card_port_normal_product_name_regex: '' # Regex. Used to find the card and port on that card to restore audio to the normal device.
# Use `pactl list cards | grep 'device.product.name'` while SteamVR is running to find the name of your vr headset.
card_rescan_pause_time: 10 # Float. Number of seconds to wait between suspending and resuming a sink to rescan the ports of its card.

Expand Down
77 changes: 41 additions & 36 deletions scripts/audio_switcher.py
Expand Up @@ -7,7 +7,7 @@
import pactl_interface


# tested with pactl version 13.99.1
# tested with pactl version 13.99.1, 14.2

class AudioSwitcher:
class Failure:
Expand Down Expand Up @@ -59,8 +59,6 @@ def __init__(self, config):
self.vr_sink = self.find_matching_sink(sinks, vr_sink_regex, "vr")
log.d('vr sink: {}'.format(self.vr_sink.name))

self.port = None

@staticmethod
def find_matching_sink(sinks, regex, name):
vr_matches = [
Expand All @@ -75,62 +73,57 @@ def find_matching_sink(sinks, regex, name):
raise RuntimeError(
'Multiple matches for the {} audio sink found. Tried to find a match for: {}'.format(name, regex))

def switch_to_sink(self, sink, device_type):
if self.config.audio_set_card_port():
port = self.get_port(device_type)

if port is not None:
port.card.set_profile(self.config, port.profiles[0])
else:
sink.set_suspend_state(self.config, True)
time.sleep(self.config.audio_card_rescan_pause_time())
# Causes a rescan of connected ports, only works if time passes between suspend and resume
sink.set_suspend_state(self.config, False)

self.set_sink_for_all_sink_inputs(sink)

def switch_to_vr(self):
old_vr_sink = self.vr_sink
sinks = pactl_interface.Sink.get_all_sinks(self)
self.vr_sink = self.find_matching_sink(sinks, self.config.audio_vr_sink_regex(), "vr")
if self.vr_sink.name != old_vr_sink.name:
log.d('New vr sink: {}'.format(self.vr_sink.name))

if self.config.audio_set_card_port():
last_port = self.port
self.port = self.get_port()
if last_port != self.port:
log.d('New port: {}'.format(self.port))

if self.port is not None:
self.port.card.set_profile(self.config, self.port.profiles[0])
else:
self.vr_sink.set_suspend_state(self.config, True)
time.sleep(self.config.audio_card_rescan_pause_time())
# Causes a rescan of connected ports, only works if time passes between suspend and resume
self.vr_sink.set_suspend_state(self.config, False)

self.set_sink(self.vr_sink)
self.switch_to_sink(self.vr_sink, "vr")

def switch_to_normal(self):
self.set_sink(self.normal_sink)

def set_sink(self, sink):
# self.set_default_sink(sink)
self.set_sink_for_all_sink_inputs(sink)
self.switch_to_sink(self.normal_sink, "normal")

def log_state(self):
log.d('last_pactl_sinks:\n{}'.format(self.last_pactl_sinks))
log.d('last_pactl_sink_inputs:\n{}'.format(self.last_pactl_sink_inputs))
log.d('last_pactl_clients:\n{}'.format(self.last_pactl_clients))

def set_default_sink(self, sink):
def set_sink_for_all_sink_inputs(self, sink):
if self.config.dry_run():
log.w('Skipping because of dry run')
return

arguments = ['pactl', 'set-default-sink', sink.name]
return_code, stdout, stderr = pactl_interface.utlis.run(arguments)
# verify sink name exists before proceeding
sinks = pactl_interface.Sink.get_all_sinks(self)
found = False
for s in sinks:
if s.name == sink.name:
found = True

if return_code != 0:
log.e('\'{}\' () failed, stderr:\n{}'.format(" ".join(arguments), stderr))
self.log_state()
if not found:
log.w('Skipping move-sink-input since the sink name does not exist')
return

def set_sink_for_all_sink_inputs(self, sink):
sink_inputs = pactl_interface.SinkInput.get_all_sink_inputs(self)
pactl_interface.Client.get_client_names(sink_inputs)
sink_inputs = self.filter_by_client_name(sink_inputs)

if self.config.dry_run():
log.w('Skipping because of dry run')
return

for sink_input in sink_inputs:
failure = \
([failure for failure in self.failed_sink_inputs if failure.sink_input_id == sink_input.id] + [None])[0]
Expand Down Expand Up @@ -170,9 +163,21 @@ def get_default_sink_name():
'Workaround: Fill out the "normal_sink_regex" field in the config file.\n\n'
'Output of `pactl info`:\n{}'.format(stdout))

def get_port(self):
def get_port(self, device_type):
if device_type == "vr":
card_port_product_name_regex = self.config.audio_card_port_vr_product_name_regex()
elif device_type == "normal":
card_port_product_name_regex = self.config.audio_card_port_normal_product_name_regex()
else:
raise NotImplementedError()

if card_port_product_name_regex is None:
log.d(
"Skipping port selection for {device_type} device because card_port_{device_type}_product_name_regex is not set.".format(
device_type=device_type))
return None

cards = pactl_interface.Card.get_all_cards()
card_port_product_name_regex = self.config.audio_card_port_product_name_regex()
for card in cards:
for port in card.ports:
if port.product_name is not None:
Expand Down
15 changes: 14 additions & 1 deletion scripts/config.py
Expand Up @@ -5,6 +5,8 @@

import yaml

import log


class Config:
def __init__(self, config_path=None, dry_run_overwrite=False):
Expand Down Expand Up @@ -152,12 +154,23 @@ def audio_set_card_port(self):

return True

def audio_card_port_product_name_regex(self):
def audio_card_port_vr_product_name_regex(self):
if 'audio' in self.data and 'card_port_vr_product_name_regex' in self.data['audio']:
return self.data['audio']['card_port_vr_product_name_regex']

if 'audio' in self.data and 'card_port_product_name_regex' in self.data['audio']:
log.w(
"Using deprecated config value audio:card_port_product_name_regex, use audio:card_port_vr_product_name_regex instead!")
return self.data['audio']['card_port_product_name_regex']

return '(Index HMD)|(VIVE)'

def audio_card_port_normal_product_name_regex(self):
if 'audio' in self.data and 'card_port_normal_product_name_regex' in self.data['audio']:
return self.data['audio']['card_port_normal_product_name_regex']

return None

def audio_card_rescan_pause_time(self):
if 'audio' in self.data and 'card_rescan_pause_time' in self.data['audio']:
return float(self.data['audio']['card_rescan_pause_time'])
Expand Down
11 changes: 7 additions & 4 deletions scripts/config_helper.py
Expand Up @@ -8,7 +8,7 @@ def __init__(self, config):

def print_help(self):
help_text = '''
# Because regular expressions can often be unintuitive the following output
# Because regular expressions can often be unintuitive the following output
# provides help for configuring steamvr_utils to your setup/preferences.
# Recommended procedure:
Expand Down Expand Up @@ -55,15 +55,18 @@ def print_help(self):
card_port_product_names.append(port.product_name)

help_text += '''
# ==== Help for audio:card_port_product_name_regex ====
# current value for audio:card_port_product_name_regex:
# ==== Help for audio:card_vr_port_product_name_regex and audio:card_normal_port_product_name_regex ====
# current value for audio:card_vr_port_product_name_regex:
{}
# current value for audio:card_normal_port_product_name_regex:
{}
# Exactly one line should match your regex.
# Card port product names: (SteamVR needs to run for the HMD to show up here)
{}
'''.format(
self.config.audio_card_port_product_name_regex(),
self.config.audio_card_port_vr_product_name_regex(),
self.config.audio_card_port_normal_product_name_regex(),
'\n'.join(card_port_product_names)
)

Expand Down

0 comments on commit b48cf3e

Please sign in to comment.