示例#1
0
class pulseaudiostuff():
    def __init__(self):
        logging.debug("Initing %s", self.__class__.__name__)
        self.pulse = Pulse()
        self.barecmd = connect_to_cli()

        default_sink_name = self.pulse.server_info().default_sink_name
        self.default_sink_info = self.pulse.get_sink_by_name(default_sink_name)

        out_sink_name = "game-out"
        # music + mic
        self.out_sink_module_id = self.pulse.module_load(
            "module-null-sink", 'sink_name=' + out_sink_name)
        self.out_sink_info = self.pulse.get_sink_by_name(out_sink_name)

        MP_sink_name = "Media-player"
        # that is our main sink. send your media here
        # everything that comes in is being copied to game sink and default sink
        self.MP_sink_module_id = self.pulse.module_load(
            "module-combine-sink", 'sink_name=' + MP_sink_name + ' slaves=' +
            str(self.out_sink_info.index) + ',' +
            str(self.default_sink_info.index))
        self.MP_sink_info = self.pulse.get_sink_by_name(MP_sink_name)

        # Get stream media -> speakers
        # TODO: this is also gay but it is somehow possible to retreve all inputs for sink. (sink_input_list(sinkIndex))
        for stream in self.pulse.sink_input_list():
            if stream.owner_module == self.MP_sink_module_id:
                if stream.sink == self.default_sink_info.index:
                    self.sound2speakers = stream
                elif stream.sink == self.out_sink_info.index:
                    self.sound2game = stream

        # send mic stream to game sink. (btw rip 20 ms)
        self.loopback_module_id = self.pulse.module_load(
            "module-loopback", 'sink=' + str(self.out_sink_info.index) +
            ' latency_msec=20 source_dont_move=true sink_dont_move=true')

        # Get stream mic -> game
        # TODO: this is also gay but it is somehow possible to retreve all inputs for sink. (sink_input_list(sinkIndex))
        for stream in self.pulse.sink_input_list():
            if stream.sink == self.out_sink_info.index and stream.owner_module == self.loopback_module_id:
                self.mic2game = stream

        # TODO: combine sink sets volume to earrape because reasons?
        hell = self.sound2speakers.volume
        hell.value_flat = 0.5
        self.pulse.volume_set(self.sound2speakers, hell)
        hell.value_flat = 1.0
        self.pulse.volume_set(self.sound2game, hell)

        # TODO: change names of sinks.
        # https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/615
        # self.barecmd.write(
        #     'update-sink-proplist ' + str(self.out_sink_info.index) + ' device.description="Game media player out"')
        # self.barecmd.write(
        #     'update-sink-proplist ' + str(self.MP_sink_info.index) + ' device.description="Game media player sink"')

        self.pulse.sink_default_set(self.default_sink_info)

        logging.debug("%s class loaded", self.__class__.__name__)
        logging.info("out sink module id: %d", self.out_sink_module_id)
        logging.info("MP sink module id: %d", self.MP_sink_module_id)
        logging.info("loopback module id: %d", self.loopback_module_id)

    def __del__(self):
        self.pulse.module_unload(self.loopback_module_id)
        self.pulse.module_unload(self.out_sink_module_id)
        self.pulse.module_unload(self.MP_sink_module_id)
        logging.debug("%s class unloaded", self.__class__.__name__)

    def printstuff(self):
        print('csgo launch options: "pacmd set-default-source ' +
              self.out_sink_info.name + '.monitor"')
示例#2
0
import sys
import time
from pulsectl import Pulse

pulse = Pulse()
arg = "".join(sys.argv[1:]).strip()
if arg == "":
    exit()

sink = int(arg)
assert isinstance(sink, int)
sink_obj = pulse.get_sink_by_name(arg)

for sink_in in pulse.sink_input_list():
    pulse.sink_input_move(sink_in.index, sink)

time.sleep(1)
pulse.sink_default_set(sink_obj)
def main(args):
    pulse = Pulse("pulse-audio-cycle")

    current_sink = pulse.get_sink_by_name(
        pulse.server_info().default_sink_name)

    current_profile = pulse.card_info(current_sink.card).profile_active

    # card -> holds all profiles and sets the active one
    # sink uses a card+profile combination and is names accordingly

    matching_cards_with_profiles = []
    card_pattern = re.compile(args.card)
    logging.debug(f"card_pattern: {card_pattern}")
    # Get a list of all matching cards
    for card in pulse.card_list():
        # Prepare to also match against sink description
        sink_for_current_card = None
        if args.use_sink_description:
            sink_for_current_card = sink_for_card(card, pulse)

        card_pattern_matched = False
        if re.search(card_pattern, card.name):
            card_pattern_matched = True
            logging.info(f"Card matched: {card.name}")
            if sink_for_current_card:
                logging.info(
                    f"-> Sink Description: {sink_for_current_card.description}"
                )
        elif args.use_sink_description:
            if sink_for_current_card:
                if re.search(card_pattern, sink_for_current_card.description):
                    card_pattern_matched = True
                    logging.info(f"Card matched: {card.name}")
                    logging.info(
                        f"-> Sink Description: {sink_for_current_card.description}"
                    )
                    logging.info("-> matched via Sink Description")
        # Ignore cards that are not wanted by the user given pattern
        if not card_pattern_matched:
            continue

        matched_profiles = []
        # Check if we need filter for certain profiles or leave as is
        for profile in card.profile_list:
            # skip unavailable profiles (unless wanted)
            if not profile.available and not args.with_unavailable:
                continue

            # Check every given profile
            for cp_card_pattern, cp_profile_pattern in args.profile:
                cp_card_pattern_matched = False
                if re.search(cp_card_pattern, card.name):
                    cp_card_pattern_matched = True
                elif args.use_sink_description:
                    if sink_for_current_card:
                        if re.search(cp_card_pattern,
                                     sink_for_current_card.description):
                            cp_card_pattern_matched = True

                # This cp_profile_pattern does not apply to this card
                if not cp_card_pattern_matched:
                    continue

                if re.search(cp_profile_pattern, profile.name):
                    logging.info(f"  Profile matched: {profile.name} ")
                    matched_profiles.append(profile)

        if not matched_profiles:
            logging.info("  No Profile matched – Keeping profile.")

        # put infos into list
        matching_cards_with_profiles.append((card, matched_profiles))

        # separator betweem cards
        logging.info("")

    new_card, new_profile = new_card_and_profile(matching_cards_with_profiles,
                                                 pulse)

    # change profile if necessary
    if new_profile:
        if args.verbose:
            logging.info(f"New Profile: {new_profile.description}")
        if not args.dry:
            pulse.card_profile_set(new_card, new_profile)
    else:
        if args.verbose:
            logging.info("NO new Profile.")

    # change sink (has to be done always because card profile also changes sink)
    new_sink = sink_for_card(new_card, pulse)
    if args.verbose:
        logging.info(f"New Card: {new_card.name}")
        if args.use_sink_description:
            logging.info(f"-> New Sink: {new_sink.description} ")

    if not args.dry:
        pulse.sink_default_set(new_sink)

    # move all input sinks (apps/X clients) to new output sink
    for input_sink in pulse.sink_input_list():
        if args.verbose:
            logging.info(
                f"  -> Switching {input_sink.proplist['application.name']}")
        if not args.dry:
            pulse.sink_input_move(input_sink.index, new_sink.index)

    # Show notification
    if args.notify:
        details = f"New Sink: {new_sink.description}"
        if new_profile:
            details += f"\nNew Profile: {new_profile.description}"

        notify("Sink Changed", details)