#!/usr/bin/python
# (C) 2005 Canonical Ltd.
# Author: Martin Pitt <martin.pitt@ubuntu.com>
# License: GNU General Public License, version 2 or any later version
#
# Modified by: Thomas Hood, Daniel T Chen
#
# Get and set configuration parameters in ~/.asoundrc.asoundconf.
import sys, re, os.path
our_conf_file = os.path.expanduser('~/.asoundrc.asoundconf')
asoundrc_file = os.path.expanduser('~/.asoundrc')
predefs_file = '/usr/share/alsa/alsa.conf'
setting_re_template = '!?\s*%s\s*(?:=|\s)\s*([^;,]+)[;,]?$'
our_conf_header = '''# ALSA library configuration file managed by asoundconf(1).
#
# MANUAL CHANGES TO THIS FILE WILL BE OVERWRITTEN!
#
# Manual changes to the ALSA library configuration should be implemented
# by editing the ~/.asoundrc file, not by editing this file.
'''
asoundrc_header = '''# ALSA library configuration file
'''
inclusion_comment = '''# Include settings that are under the control of asoundconf(1).
# (To disable these settings, comment out this line.)'''
usage = '''Usage:
asoundconf is-active
asoundconf get|delete PARAMETER
asoundconf set PARAMETER VALUE
asoundconf list
Convenience macro functions:
asoundconf set-default-card PARAMETER
asoundconf reset-default-card
asoundconf set-pulseaudio
asoundconf unset-pulseaudio
asoundconf set-oss PARAMETER
asoundconf unset-oss
'''
needs_default_card = '''You have omitted a necessary parameter. Please see the output from `asoundconf list`, and use one of those sound card(s) as the parameter.
'''
needs_oss_dev = '''You have omitted a necessary parameter. Please specify an OSS device (e.g., /dev/dsp).
'''
superuser_warn = '''Please note that you are attempting to run asoundconf as a privileged superuser, which may have unintended consequences.
'''
def get_default_predefs():
try:
if not os.path.exists(predefs_file):
return
predefs_file_entire = open(predefs_file).readlines()
r = re.compile('^defaults')
## Between these hashes, add additional unique regexps that
## must exist at the end of the user's custom asoundrc.
s = re.compile('^defaults.namehint')
##
predefs_list = []
must_append_predefs_list = []
for i in predefs_file_entire:
if r.match(i) and not s.match(i):
predefs_list.append(str(i).strip())
elif s.match(i):
must_append_predefs_list.append(str(i).strip())
for i in must_append_predefs_list:
predefs_list.append(str(i).strip())
return predefs_list
except IOError:
pass
def ensure_our_conf_exists():
'''If it does not exist then generate a default configuration
file with no settings.
Return: True on success, False if the file could not be created.
'''
if os.path.exists(our_conf_file):
return True
try:
open(our_conf_file, 'w').write(our_conf_header)
return True
except IOError:
print >> sys.stderr, 'Error: could not create', our_conf_file
return False
def ensure_asound_rc_exists():
'''Generate a default user configuration file with only the
inclusion line.
Return: True on success, False if the file could not be created.
'''
if os.path.exists(asoundrc_file):
return True
try:
open(asoundrc_file, 'w').write('%s\n%s\n<%s>\n\n' % (asoundrc_header, inclusion_comment, our_conf_file))
return True
except IOError:
print >> sys.stderr, 'Error: could not create', asoundrc_file
return False
def sds_transition():
'''Replace the magic comments added to the user configuration file
by the obsolete set-default-soundcard program with the standard
inclusion statement for our configuration file.
'''
if not os.path.exists(asoundrc_file):
return
lines = open(asoundrc_file).readlines()
start_marker_re = re.compile('### BEGIN set-default-soundcard')
end_marker_re = re.compile('### END set-default-soundcard')
userconf_lines = []
our_conf_lines = []
# read up to start comment
lineno = 0
found = 0
for l in lines:
lineno = lineno+1
if start_marker_re.match(l):
found = 1
break
userconf_lines.append(l)
if found:
# replace magic comment section with include
userconf_lines.append('%s\n<%s>\n\n' % (inclusion_comment, our_conf_file))
else:
# no magic comment
return
# read magic comment section until end marker and add it to asoundconf
found = 0
for l in lines[lineno:]:
lineno = lineno+1
if end_marker_re.match(l):
found = 1
break
if not l.startswith('#'):
our_conf_lines.append(l)
if not found:
# no complete magic comment
return
# add the rest to user conf
userconf_lines = userconf_lines + lines[lineno:]
# write our configuration file
if not ensure_our_conf_exists():
return
try:
open(our_conf_file, 'a').writelines(our_conf_lines)
except IOError:
return # panic out
# write user configuration file
try:
open(asoundrc_file, 'w').writelines(userconf_lines)
except IOError:
pass
def is_active():
'''Check that the user configuration file is either absent, or,
if present, that it includifies the asoundconf configuration file;
in those cases asoundconf can be used to change the user's ALSA
library configuration.
Also transition from the legacy set-default-soundcard program.
Return True if the above condition is met, False if not.
'''
if not os.path.exists(asoundrc_file):
return True
sds_transition()
# check whether or not the file has the inclusion line
include_re = re.compile('\s*<\s*%s\s*>' % our_conf_file)
for l in open(asoundrc_file):
if include_re.match(l):
return True
return False
def get(prmtr):
'''Print the value of the given parameter on stdout
Also transition from the legacy set-default-soundcard program.
Return True on success, and False if the value is not set.
'''
sds_transition()
if not os.path.exists(our_conf_file):
return False
setting_re = re.compile(setting_re_template % prmtr)
try:
for line in open(our_conf_file):
m = setting_re.match(line)
if m:
print m.group(1).strip()
return True
return False
except IOError:
return False
def list():
'''Get card names from /proc/asound/cards'''
cardspath = '/proc/asound/cards'
if not os.path.exists(cardspath):
return False
procfile = open(cardspath, 'rb')
cardline = re.compile('^\s*\d+\s*\[')
card_lines = []
lines = procfile.readlines()
for l in lines:
if cardline.match(l):
card_lines.append(re.sub(r'^\s*\d+\s*\[(\w+)\s*\].+','\\1',l))
print "Names of available sound cards:"
for cardname in card_lines:
print cardname.strip()
return True
def delete(prmtr):
'''Delete the given parameter.
Also transition from the legacy set-default-soundcard program.
Return True on success, and False on an error.
'''
sds_transition()
if not os.path.exists(our_conf_file):
return False
setting_re = re.compile(setting_re_template % prmtr)
lines = []
try:
lines = open(our_conf_file).readlines()
except IOError:
return False
found = 0
for i in xrange(len(lines)):
if setting_re.match(lines[i]):
del lines[i]
found = 1
break
if found:
# write back file
try:
f = open(our_conf_file, 'w')
except IOError:
return False
f.writelines(lines)
return True
def set(prmtr, value):
'''Set the given parameter to the given value
Also transition from the legacy set-default-soundcard program.
Return True on success, and False on an error.
'''
sds_transition()
setting_re = re.compile(setting_re_template % prmtr)
lines = []
ensure_asound_rc_exists()
# N.B. We continue even if asoundrc could not be created
# and we do NOT ensure that our configuration is "active"
if not ensure_our_conf_exists():
return False
try:
lines = open(our_conf_file).readlines()
except IOError:
return False
newsetting = '%s %s\n' % (prmtr, value)
# if setting is already present, change it
found = 0
for i in xrange(len(lines)):
if setting_re.match(lines[i]):
lines[i] = newsetting
found = 1
break
if not found:
lines.append(newsetting)
# write back file
try:
f = open(our_conf_file, 'w')
except IOError:
return False
f.writelines(lines)
return True
def set_default_card(card):
clist = get_default_predefs()
sep = re.compile('\s+')
r = re.compile('^defaults.pcm.card')
s = re.compile('^defaults.ctl.card')
## !defaults.pcm.card and defaults.ctl.card should lead
## the user's custom asoundrc.
if set('!defaults.pcm.card', card) and \
set('defaults.ctl.card', card):
for i in clist:
(j, k) = sep.split(i)
if not r.match(j) and not s.match(j):
if not set(j, k):
return False
return True
else:
return False
def reset_default_card():
clist = get_default_predefs()
sep = re.compile('\s+')
for i in clist:
(j, k) = sep.split(i)
if not delete(j):
return False
return True
def delete_pcm_default():
return delete('pcm.!default')
def delete_ctl_default():
return delete('ctl.!default')
def set_pulseaudio():
return set('pcm.!default', '{ type pulse }') and \
set('ctl.!default', '{ type pulse }')
def unset_pulseaudio():
return delete_pcm_default() and \
delete_ctl_default()
def set_oss(device):
endbrace = ' }'
return set('pcm.!default { type oss device', device + endbrace)
def unset_oss():
return delete_pcm_default()
def exit_code(result):
'''Exit program with code 0 if result is True, otherwise exit with code
1.
'''
if result:
sys.exit(0)
else:
sys.exit(1)
##
## main
##
if os.geteuid() == 0:
print superuser_warn
if len(sys.argv) < 2 or sys.argv[1] == '--help' or sys.argv[1] == '-h':
print usage
sys.exit(0)
if sys.argv[1] == 'is-active':
exit_code(is_active())
if sys.argv[1] == 'get':
if len(sys.argv) != 3:
print usage
sys.exit(1)
exit_code(get(sys.argv[2]))
if sys.argv[1] == 'delete':
if len(sys.argv) != 3:
print usage
sys.exit(1)
exit_code(delete(sys.argv[2]))
if sys.argv[1] == 'set':
if len(sys.argv) != 4:
print usage
sys.exit(1)
exit_code(set(sys.argv[2], sys.argv[3]))
if sys.argv[1] == 'list':
exit_code(list())
if sys.argv[1] == 'set-default-card':
if len(sys.argv) != 3:
print needs_default_card
sys.exit(1)
exit_code(set_default_card(sys.argv[2]))
if sys.argv[1] == 'reset-default-card':
exit_code(reset_default_card())
if sys.argv[1] == 'set-pulseaudio':
exit_code(set_pulseaudio())
if sys.argv[1] == 'unset-pulseaudio':
exit_code(unset_pulseaudio())
if sys.argv[1] == 'set-oss':
if len(sys.argv) != 3:
print needs_oss_dev
sys.exit(1)
exit_code(set_oss(sys.argv[2]))
if sys.argv[1] == 'unset-oss':
exit_code(unset_oss())
print usage
sys.exit(1)