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 create_asoundrc(): """Create and populate ~/.config/alsacontrol/asoundrc.""" pcm_input, pcm_output = get_pcms() if pcm_output == 'jack': last_output_step = 'alsacontrol-plug' elif should_use_dmix(pcm_output): last_output_step = 'alsacontrol-dmix' else: last_output_step = pcm_output # either from asym directly to the last step, or over softvol if get_config().get('output_use_softvol'): output_pcm_asym = 'alsacontrol-output-softvol' output_pcm_softvol = last_output_step else: output_pcm_asym = last_output_step output_pcm_softvol = 'null' if pcm_input == 'jack': last_input_step = 'alsacontrol-plug' elif should_use_dsnoop(pcm_input): last_input_step = 'alsacontrol-dsnoop' else: last_input_step = pcm_input # either from asym directly to the last step, or over softvol if get_config().get('input_use_softvol'): input_pcm_asym = 'alsacontrol-input-softvol' input_pcm_softvol = last_input_step else: input_pcm_asym = last_input_step input_pcm_softvol = 'null' # dmix and dsnoop always have to be the last step asoundrc_config = { 'output_pcm_asym': output_pcm_asym, 'output_pcm_softvol': output_pcm_softvol, 'output_pcm': pcm_output, 'output_channels': get_config().get('output_channels'), 'input_pcm_asym': input_pcm_asym, 'input_pcm_softvol': input_pcm_softvol, 'input_pcm': pcm_input, } template_path = os.path.join(get_data_path(), 'asoundrc-template') with open(template_path, 'r') as template_file: template = template_file.read() asoundrc_content = template.format(**asoundrc_config) with open(alsactl_asoundrc, 'w+') as asoundrc_file: logger.info('Writing file %s', alsactl_asoundrc) asoundrc_file.write(asoundrc_content)
def get_pcms(): """Return the configured input and output pcm string.""" pcm_input = get_config().get('pcm_input') pcm_output = get_config().get('pcm_output') if pcm_input == 'null': logger.warning('No input specified') else: logger.info('Using input %s', pcm_input) if pcm_output == 'null': logger.warning('No output specified') else: logger.info('Using output %s', pcm_output) return pcm_input, pcm_output
def test_toggle_input(self): input_row = self.window.input_rows[0] input_card_name = self.window.builder.get_object('input_card_name') # since the config starts with 'null' as default, # no card should be selected self.assertEqual(get_config().get('pcm_input'), 'null') self.assertIn('No card selected', input_card_name.get_label()) input_row.select_callback(input_row.card) self.assertNotEqual(get_config().get('pcm_input'), 'null') self.assertNotIn('No card selected', input_card_name.get_label()) input_row.select_callback(input_row.card) self.assertEqual(get_config().get('pcm_input'), 'null') self.assertIn('No card selected', input_card_name.get_label())
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 test_select_output(self): cards = alsaaudio.cards() config = get_config() selected_index = 0 class FakeDropdown: """Acts like a Gtk.ComboBoxText object.""" def get_active_text(self): return cards[selected_index] self.window.on_output_card_selected(FakeDropdown()) self.assertEqual(config.get('pcm_output'), f'hw:CARD={cards[selected_index]}') with open(alsactl_asoundrc, 'r') as f: asoundrc = f.read() self.assertIn(config.get('pcm_output'), asoundrc) self.assertNotIn(cards[1], asoundrc) selected_index = 1 config.set('output_plugin', 'ab') self.window.on_output_card_selected(FakeDropdown()) self.assertEqual(config.get('pcm_output'), f'ab:CARD={cards[selected_index]}') with open(alsactl_asoundrc, 'r') as f: asoundrc = f.read() self.assertNotIn(cards[0], asoundrc) self.assertIn(config.get('pcm_output'), asoundrc)
def test_select_input(self): cards = alsaaudio.cards() config = get_config() # only show the input in the generated asoundrc to make # assertIn not get false positives. # Since there are some comments and stuff about jack in it, this # test avoids to also try to select jack. config.set('pcm_output', 'null') # first card self.window.on_input_card_selected(cards[0]) self.assertEqual(config.get('pcm_input'), f'hw:CARD={cards[0]}') with open(alsactl_asoundrc, 'r') as f: asoundrc = f.read() self.assertIn(config.get('pcm_input'), asoundrc) self.assertNotIn(cards[1], asoundrc) # selecting it a second time unselects it self.window.on_input_card_selected(cards[0]) self.assertEqual(config.get('pcm_input'), 'null') with open(alsactl_asoundrc, 'r') as f: asoundrc = f.read() self.assertNotIn(cards[0], asoundrc) self.assertNotIn(cards[1], asoundrc) # second card config.set('input_plugin', 'ab') self.window.on_input_card_selected(cards[1]) self.assertEqual(config.get('pcm_input'), f'ab:CARD={cards[1]}') with open(alsactl_asoundrc, 'r') as f: asoundrc = f.read() self.assertNotIn(cards[0], asoundrc) self.assertIn(config.get('pcm_input'), asoundrc)
def should_use_dmix(pcm_output): """Check if, according to config, dmix should and can be used.""" # use dmix only for hardware devices, # if the config is on and if the plugin is hw hardware_device = 'CARD=' in pcm_output output_use_dmix = get_config().get('output_use_dmix') output_plugin_hw = pcm_output.startswith('hw:') return hardware_device and output_use_dmix and output_plugin_hw
def test_null_output(self): config = get_config() config.set('pcm_output', 'null') self.assertFalse(input_exists('test_null_output')) self.assertIsNone(get_current_card('pcm_output')[0]) self.assertIsNone(get_current_card('pcm_output')[1]) self.assertIsNone(get_card(config.get('pcm_output')))
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 select_output_pcm(card): """Write this pcm to the configuration. Parameters ---------- card : string "Generic", "jack", ... """ # figure out if this is an actual hardware device or not cards = alsaaudio.cards() if card is None: card = 'null' if card in cards: plugin = get_config().get('output_plugin') pcm_name = f'{plugin}:CARD={card}' else: pcm_name = card # otherwise probably jack get_config().set('pcm_output', pcm_name)
def mixers(): config = get_config() mixers = [] if config.get('input_use_softvol'): mixers.append('alsacontrol-input-volume') mixers.append('alsacontrol-input-mute') if config.get('output_use_softvol'): mixers.append('alsacontrol-output-volume') mixers.append('alsacontrol-output-mute') return mixers
def tearDown(self): self.window.on_close() self.window.window.destroy() gtk_iteration() self.fakes.restore() if os.path.exists(fake_config_path): os.remove(fake_config_path) config = get_config() config.create_config_file() config.load_config()
def get_error_advice(error): """Get some help for errors.""" if 'resource busy' in error: return ('You can try to run `lsof +D /dev/snd/` to ' 'see which process is blocking it. It might be jack' 'or pulseaudio.') if 'No such card' in error: pcm_output = get_config().get('pcm_output') return (f'The pcm card "{pcm_output}" does not exist. ' + 'Try to select something different.') return None
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 __init__(self, type, device, *args, **kwargs): self.type = type config_key = { alsaaudio.PCM_CAPTURE: 'pcm_input', alsaaudio.PCM_PLAYBACK: 'pcm_output' }[type] if device is None or device == 'default': self.card = get_card(get_config().get(config_key)) if self.card is None: raise ValueError( 'I don\'t think the PCM constructor should ' 'be called when no device is set' ) else: self.card = device
def toggle_speaker_test(self): """Run the speaker-test script or stop it, if it is running. Returns the subprocess or False if it has been stopped. """ if self.speaker_test_process is None: num_channels = get_config().get('output_channels') cmd = f'speaker-test -D default -c {num_channels} -twav' logger.info('Testing speakers, %d channels (%s)', num_channels, cmd) process = subprocess.Popen( cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid ) self.speaker_test_process = process return process self.stop_speaker_test() return False
import os import sys import unittest from alsacontrol.config import get_config from alsacontrol.logger import update_verbosity from fakes import fake_config_path if __name__ == "__main__": update_verbosity(debug=True) if os.path.exists(fake_config_path): os.remove(fake_config_path) # don't overwrite the users settings in unittests get_config(fake_config_path) modules = sys.argv[1:] # discoverer is really convenient, but it can't find a specific test # in all of the available tests like unittest.main() does..., # so provide both options. if len(modules) > 0: # for example `tests/test.py ConfigTest.testFirstLine` testsuite = unittest.defaultTestLoader.loadTestsFromNames( [f'testcases.{module}' for module in modules]) else: # run all tests by default testsuite = unittest.defaultTestLoader.discover('testcases', pattern='*.py') testrunner = unittest.TextTestRunner(verbosity=1).run(testsuite)
def should_use_dsnoop(pcm_input): """Check if, according to config, dsnoop should and can be used.""" hardware_device = 'CARD=' in pcm_input input_use_dmix = get_config().get('input_use_dsnoop') input_plugin_hw = pcm_input.startswith('hw:') return hardware_device and input_use_dmix and input_plugin_hw