def _modify_config(config_contents, key, value): """Return a string representing the modified contents of the config file. Parameters ---------- config_contents : string Contents of the config file in ~/.config/alsacontrol/config. It is not edited in place and the config file is not overwritten. key : string Settings key that should be modified value : string, int Value to write """ logger.info('Setting "%s" to "%s"', key, value) split = config_contents.split('\n') if split[-1] == '': split = split[:-1] found = False setting = f'{key}={value}' for i, line in enumerate(split): strip = line.strip() if strip.startswith('#'): continue if strip.startswith(f'{key}='): # replace the setting logger.debug('Overwriting "%s=%s" in config', key, value) split[i] = setting found = True break if not found: logger.debug('Adding "%s=%s" to config', key, value) split.append(setting) return '\n'.join(split)
def load_config(self): """Read the config file.""" logger.debug('Loading configuration') self._config = {} # load config self.mtime = os.path.getmtime(self._path) with open(self._path, 'r') as config_file: for line in config_file: line = line.strip() if not line.startswith('#'): split = line.split('=', 1) if len(split) == 2: key = split[0] value = split[1] else: key = split[0] value = None if value.isdigit(): value = int(value) else: try: value = float(value) except ValueError: pass if value == 'True': value = True if value == 'False': value = False self._config[key] = value
def set(self, key, value): """Write a setting into memory and ~/.config/alsacontrol/config.""" if key not in _defaults: logger.error('Unknown setting %s', key) return None self.check_mtime() if key in self._config and self._config[key] == value: logger.debug('Setting "%s" is already "%s"', key, value) return False self._config[key] = value with open(self._path, 'r+') as config_file: config_contents = config_file.read() config_contents = _modify_config(config_contents, key, value) # overwrite completely with open(self._path, 'w') as config_file: if not config_contents.endswith('\n'): config_contents += '\n' config_file.write(config_contents) self.mtime = os.path.getmtime(self._path) return True
def select_input_pcm(card): """Write the pcm to the configuration. Parameters ---------- card : string "Generic", "jack", ... """ # figure out if this is an actual hardware device or not if card is None: logger.debug('Unselecting the input') get_config().set('pcm_input', 'null') return cards = alsaaudio.cards() if card == 'jack': pcm_name = 'jack' elif card in cards: plugin = get_config().get('input_plugin') pcm_name = f'{plugin}:CARD={card}' else: # card unknown, the device is possibly not connected logger.warning( 'Going to write possibly invalid pcm %s to the settings', card) # might me something similar to jack, a software pcm pcm_name = card get_config().set('pcm_input', pcm_name)
def get_current_card(source): """Get a tuple describing the current card selection based on config. Parameters ---------- source : string one of 'pcm_input' or 'pcm_output' Returns ------- A tuple of (d, card) with d being the index in the list of options from get_cards. """ pcm_name = get_config().get(source) if pcm_name == 'null': logger.debug('No input selected') return None, None cards = get_cards() if len(cards) == 0: logger.error('Could not find any card') return None, None card = get_card(pcm_name) if card not in cards: logger.warning('Found unknown %s "%s" in config', source, pcm_name) return None, card index = cards.index(card) return index, card
def output_exists(func, testcard=True, testmixer=True): """Check if the configured output card and mixer is available.""" # might be a pcm name with plugin and device card = get_card(get_config().get('pcm_output')) if testcard: if card is None: logger.debug('%s, No output selected', func) return None if not card in get_cards(): logger.error('%s, Could not find the output card "%s"', func, card) return False if testmixer and get_config().get('output_use_softvol'): if 'alsacontrol-output-volume' not in alsaaudio.mixers(): logger.error('%s, Could not find the output softvol mixer', func) play_silence() return False return True
def __init__(self, path=None): """Initialize the interface to the config file. Parameters ---------- path : string or None If none, will default to '~/.config/alsacontrol/config' """ if path is None: path = os.path.expanduser('~/.config/alsacontrol/config') logger.debug('Using config file at %s', path) self._path = path self._config = {} self.mtime = 0 self.create_config_file() self.load_config()
def record_to_nowhere(): """Similar problem as in play_silence with the input mixer. Otherwise 'Unable to find mixer control alsacontrol-input-mute' will be thrown at the start. """ logger.debug('Trying to capture sound to make the input mixers visible') try: pcm = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE, device='default') pcm.read() except alsaaudio.ALSAAudioError as error: error = str(error) logger.error(error) if 'resource busy' in error: logger.error( 'Your specified input is currently busy, are jack or pulse' 'using it?') logger.error('Could not initialize input mixer. ' 'Try setting a different device.')
def input_exists(func, testcard=True, testmixer=True): """Check if the configured input card and mixer is available. Returns None if no card is configured, because the existance of 'no' card cannot be determined. """ # might be a pcm name with plugin and device card = get_card(get_config().get('pcm_input')) if testcard: if card is None: logger.debug('%s, No input selected', func) return None if not card in alsaaudio.cards(): logger.error('%s, Could not find the input card "%s"', func, card) return False if testmixer and get_config().get('input_use_softvol'): if 'alsacontrol-input-volume' not in alsaaudio.mixers(): logger.error('%s, Could not find the input softvol mixer', func) record_to_nowhere() return False return True
def stop_speaker_test(self): """Stop the speaker test if it is running.""" if self.speaker_test_process is None: return return_code = self.speaker_test_process.poll() if return_code is None: pid = self.speaker_test_process.pid logger.info('Stopping speaker test') try: os.killpg(os.getpgid(pid), signal.SIGTERM) except ProcessLookupError: logger.debug( 'Tried to stop speaker-test process that has already ' 'been stopped' ) # It has already been stopped if debug_log_on(): stdout = self._read_from_std(self.speaker_test_process.stdout) for line in stdout: logger.debug('speaker-test stdout: %s', line) self.speaker_test_process = None
def play_silence(): """In order to make alsa see the mixers, play some silent audio. Otherwise 'Unable to find mixer control alsacontrol-output-mute' will be thrown at the start. """ logger.debug('Trying to play sound to make the output mixers visible') try: pcm = alsaaudio.PCM(type=alsaaudio.PCM_PLAYBACK, channels=1, periodsize=32, device='default') data = b'\x00' * 32 pcm.write(data) except alsaaudio.ALSAAudioError as error: error = str(error) logger.error(error) if 'resource busy' in error: logger.error( 'Your specified output is currently busy, is jack using it?') logger.error('Could not initialize output mixer, ' 'try setting a different device.')