pipewire_python.controller API documentation

Edit on GitHub

# pipewire_python.controller

## Documentation

In the next pages you'll see documentation of each python component `controller.py`.

View Source

      1"""
      2## Documentation
      3
      4In the next pages you'll see documentation of each python component
      5`controller.py`.
      6"""
      7
      8import warnings
      9
     10# Loading internal functions
     11from ._utils import (
     12    _drop_keys_with_none_values,
     13    _execute_shell_command,
     14    _filter_by_type,
     15    _generate_command_by_dict,
     16    _generate_dict_interfaces,
     17    _generate_dict_list_targets,
     18    _get_dict_from_stdout,
     19)
     20
     21# Loading constants Constants.py
     22from ._constants import MESSAGES_ERROR, RECOMMENDED_FORMATS, RECOMMENDED_RATES
     23
     24# [DEPRECATED] [FLAKE8] TO_AVOID_F401 PEP8
     25# [DEPRECATED] https://stackoverflow.com/a/31079085/10491422
     26# NOW USED IN DOCUMENTATION
     27# __all__ = [
     28#     # Classes and fucntions to doc
     29#     'Controller',
     30#     # [DEPRECATED] Unused files pylint
     31#     # "_print_std",
     32#     # "_get_dict_from_stdout",
     33#     # "_update_dict_by_dict",
     34#     # "_drop_keys_with_none_values",
     35#     # "_generate_command_by_dict",
     36#     # "_execute_shell_command",
     37# ]
     38
     39
     40class Controller:
     41    """
     42    Class that controls pipewire command line interface
     43    with shell commands, handling outputs, loading default
     44    configs and more.
     45    """
     46
     47    _pipewire_cli = {  # Help
     48        "--help": "--help",  # -h
     49        "--version": "--version",
     50        "--remote": None,  # -r
     51    }
     52
     53    _pipewire_modes = {  # Modes
     54        "--playback": None,  # -p
     55        "--record": None,  # -r
     56        "--midi": None,  # -m
     57    }
     58
     59    _pipewire_list_targets = {  # "--list-targets": None,
     60        "list_playback": None,
     61        "list_record": None,
     62    }
     63
     64    _pipewire_configs = {  # Configs
     65        "--media-type": None,  # *default=Audio
     66        "--media-category": None,  # *default=Playback
     67        "--media-role": None,  # *default=Music
     68        "--target": None,  # *default=auto
     69        "--latency": None,  # *default=100ms (SOURCE FILE if not specified)
     70        "--rate": None,  # *default=48000
     71        "--channels": None,  # [1,2] *default=2
     72        "--channel-map": None,  # ["stereo", "surround-51", "FL,FR"...] *default="FL,FR"
     73        "--format": None,  # [u8|s8|s16|s32|f32|f64] *default=s16
     74        "--volume": None,  # [0.0,1.0] *default=1.000
     75        "--quality": None,  # -q # [0,15] *default=4
     76        "--verbose": None,  # -v
     77    }
     78
     79    _kill_pipewire = {
     80        "all": ["kill", "$(pidof pw-cat)"],
     81        "playback": ["kill", "$(pidof pw-play)"],
     82        "record": ["kill", "$(pidof pw-record)"],
     83    }
     84
     85    def __init__(
     86        self,
     87        # Debug
     88        verbose: bool = False,
     89    ):
     90        """This constructor load default configs from OS executing
     91        the following pipewire command
     92
     93        ```bash
     94        #!/bin/bash
     95        # Get defaults from output of:
     96        pw-cat -h
     97        ```
     98        """
     99        # LOAD ALL DEFAULT PARAMETERS
    100
    101        mycommand = ["pw-cat", "-h"]
    102
    103        # get default parameters with help
    104        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)  # stderr
    105        # convert stdout to dictionary
    106        dict_default_values = _get_dict_from_stdout(stdout=str(stdout.decode()), verbose=verbose)
    107
    108        if verbose:
    109            print(self._pipewire_configs)
    110
    111        # Save default system configs to our json
    112        self._pipewire_configs.update(
    113            ([(key, dict_default_values[key]) for key in dict_default_values])
    114        )
    115
    116        if verbose:
    117            print(self._pipewire_configs)
    118
    119        # Delete keys with None values
    120        self._pipewire_configs = _drop_keys_with_none_values(self._pipewire_configs)
    121
    122        if verbose:
    123            print(self._pipewire_configs)
    124
    125        # Load values of list targets
    126        self.load_list_targets(mode="playback", verbose=verbose)
    127        self.load_list_targets(mode="record", verbose=verbose)
    128
    129    def _help_cli(
    130        self,
    131        # Debug
    132        verbose: bool = True,
    133    ):
    134        """Get pipewire command line help"""
    135
    136        mycommand = ["pipewire", self._pipewire_cli["--help"]]
    137
    138        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)  # stderr
    139
    140        return stdout
    141
    142    def get_version(
    143        self,
    144        # Debug
    145        verbose: bool = False,
    146    ):
    147        """Get version of pipewire installed on OS by executing the following
    148        code:
    149
    150        ```bash
    151        #!/bin/bash
    152        pw-cli --version
    153        ```
    154
    155        Args:
    156            verbose (bool) : True enable debug logs. *default=False
    157
    158        Returns:
    159            - versions (list) : Versions of pipewire compiled
    160        """
    161
    162        mycommand = ["pw-cli", "--version"]
    163
    164        if verbose:
    165            print(f"[mycommand]{mycommand}")
    166
    167        stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    168        versions = stdout.decode().split("\n")[1:]
    169
    170        self._pipewire_cli["--version"] = versions
    171
    172        return versions
    173
    174    def verbose(
    175        self,
    176        status: bool = True,
    177    ):
    178        """Get full log of pipewire stream status with the command `pw-cat`
    179
    180        An example of pw-cli usage is the code below:
    181
    182        ```bash
    183        #!/bin/bash
    184        # For example
    185        pw-cat --playback beers.wav --verbose
    186        ```
    187
    188        that will generate an output like this:
    189
    190        ```bash
    191        opened file "beers.wav" format 00010002 channels:2 rate:44100
    192        using default channel map: FL,FR
    193        rate=44100 channels=2 fmt=s16 samplesize=2 stride=4 latency=4410 (0.100s)
    194        connecting playback stream; target_id=4294967295
    195        stream state changed unconnected -> connecting
    196        stream param change: id=2
    197        stream properties:
    198            media.type = "Audio"
    199            ...
    200        now=0 rate=0/0 ticks=0 delay=0 queued=0
    201        remote 0 is named "pipewire-0"
    202        core done
    203        stream state changed connecting -> paused
    204        stream param change: id=2
    205        ...
    206        stream param change: id=15
    207        stream param change: id=15
    208        now=13465394419270 rate=1/48000 ticks=35840 delay=512 queued=0
    209        now=13466525228363 rate=1/48000 ticks=90112 delay=512 queued=0
    210        ...
    211        stream drained
    212        stream state changed streaming -> paused
    213        stream param change: id=4
    214        stream state changed paused -> unconnected
    215        stream param change: id=4
    216        ```
    217        """
    218
    219        if status:
    220            self._pipewire_configs["--verbose"] = "    "
    221        else:
    222            pass
    223
    224    def get_config(self):
    225        """Return config dictionary with default or setup variables, remember that
    226        this object changes only on python-side. Is not updated on real time,
    227        For real-time, please create and destroy the class.
    228
    229        Args:
    230            Nothing
    231
    232        Returns:
    233            - _pipewire_configs (`dict`) : dictionary with config values
    234
    235        """
    236
    237        return self._pipewire_configs
    238
    239    def set_config(
    240        self,
    241        # configs
    242        media_type=None,
    243        media_category=None,
    244        media_role=None,
    245        target=None,
    246        latency=None,
    247        rate=None,
    248        channels=None,
    249        channels_map=None,
    250        _format=None,
    251        volume=None,
    252        quality=None,
    253        # Debug
    254        verbose=False,
    255    ):
    256        """Method that get args as variables and set them
    257        to the `json` parameter of the class `_pipewire_configs`,
    258        then you can use in other method, such as `playback(...)` or
    259        `record(...)`. This method verifies values to avoid wrong
    260        settings.
    261
    262        Args:
    263            media_type : Set media type
    264            media_category : Set media category
    265            media_role : Set media role
    266            target : Set node target
    267            latency : Set node latency *example=100ms
    268            rate : Set sample rate [8000,11025,16000,22050,44100,48000,88200,96000,176400,192000,352800,384000]
    269            channels : Numbers of channels [1,2]
    270            channels_map : ["stereo", "surround-51", "FL,FR", ...]
    271            _format : ["u8", "s8", "s16", "s32", "f32", "f64"]
    272            volume : Stream volume [0.000, 1.000]
    273            quality : Resampler quality [0, 15]
    274            verbose (`bool`): True enable debug logs. *default=False
    275
    276        Returns:
    277            - Nothing
    278
    279        More:
    280            Check all links listed at the beginning of this page
    281        """  # 1 - media_type
    282        if media_type:
    283            self._pipewire_configs["--media-type"] = str(media_type)
    284        elif media_type is None:
    285            pass
    286        else:
    287            raise ValueError(
    288                f"{MESSAGES_ERROR['ValueError']}[media_type='{media_type}'] EMPTY VALUE"
    289            )
    290        # 2 - media_category
    291        if media_category:
    292            self._pipewire_configs["--media-category"] = str(media_category)
    293        elif media_category is None:
    294            pass
    295        else:
    296            raise ValueError(
    297                f"{MESSAGES_ERROR['ValueError']}[media_category='{media_category}'] EMPTY VALUE"
    298            )
    299        # 3 - media_role
    300        if media_role:
    301            self._pipewire_configs["--media-role"] = str(media_role)
    302        elif media_role is None:
    303            pass
    304        else:
    305            raise ValueError(
    306                f"{MESSAGES_ERROR['ValueError']}[media_role='{media_role}'] EMPTY VALUE"
    307            )
    308        # 4 - target
    309        if target:
    310            self._pipewire_configs["--target"] = str(target)
    311        elif target is None:
    312            pass
    313        else:
    314            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[target='{target}'] EMPTY VALUE")
    315        # 5 - latency
    316        if latency:
    317            if any(chr.isdigit() for chr in latency):  # Contain numbers
    318                self._pipewire_configs["--latency"] = str(latency)
    319            else:
    320                raise ValueError(
    321                    f"{MESSAGES_ERROR['ValueError']}[latency='{latency}'] NO NUMBER IN VARIABLE"
    322                )
    323        elif latency is None:
    324            pass
    325        else:
    326            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[latency='{latency}'] EMPTY VALUE")
    327        # 6 - rate
    328        if rate:
    329            if rate in RECOMMENDED_RATES:
    330                self._pipewire_configs["--rate"] = str(rate)
    331            else:
    332                raise ValueError(
    333                    f"{MESSAGES_ERROR['ValueError']}[rate='{rate}']\
    334                         VALUE NOT IN RECOMMENDED LIST \n{RECOMMENDED_RATES}"
    335                )
    336        elif rate is None:
    337            pass
    338        else:
    339            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[rate='{rate}'] EMPTY VALUE")
    340        # 7 - channels
    341        if channels:
    342            if channels in [1, 2]:  # values
    343                self._pipewire_configs["--channels"] = str(channels)
    344            else:
    345                raise ValueError(
    346                    f"{MESSAGES_ERROR['ValueError']}[channels='{channels}']\
    347                         WRONG VALUE\n ONLY 1 or 2."
    348                )
    349        elif channels is None:
    350            pass
    351        else:
    352            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[channels='{channels}'] EMPTY VALUE")
    353        # 8 - channels-map
    354        if channels_map:
    355            self._pipewire_configs["--channels-map"] = str(channels_map)
    356        elif channels_map is None:
    357            pass
    358        else:
    359            raise ValueError(
    360                f"{MESSAGES_ERROR['ValueError']}[channels_map='{channels_map}'] EMPTY VALUE"
    361            )
    362        # 9 - format
    363        if _format:
    364            if _format in RECOMMENDED_FORMATS:
    365                self._pipewire_configs["--format"] = str(_format)
    366            else:
    367                raise ValueError(
    368                    f"{MESSAGES_ERROR['ValueError']}[_format='{_format}']\
    369                         VALUE NOT IN RECOMMENDED LIST \n{RECOMMENDED_FORMATS}"
    370                )
    371        elif _format is None:
    372            pass
    373        else:
    374            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[_format='{_format}'] EMPTY VALUE")
    375        # 10 - volume
    376        if volume:
    377            if 0.0 <= volume <= 1.0:
    378                self._pipewire_configs["--volume"] = str(volume)
    379            else:
    380                raise ValueError(
    381                    f"{MESSAGES_ERROR['ValueError']}[volume='{volume}']\
    382                         OUT OF RANGE \n [0.000, 1.000]"
    383                )
    384        elif volume is None:
    385            pass
    386        else:
    387            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[volume='{volume}'] EMPTY VALUE")
    388        # 11 - quality
    389        if quality:
    390            if 0 <= quality <= 15:
    391                self._pipewire_configs["--quality"] = str(quality)
    392            else:
    393                raise ValueError(
    394                    f"{MESSAGES_ERROR['ValueError']}[quality='{quality}'] OUT OF RANGE \n [0, 15]"
    395                )
    396        elif quality is None:
    397            pass
    398        else:
    399            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[volume='{volume}'] EMPTY VALUE")
    400
    401        # 12 - verbose cli
    402        if verbose:  # True
    403            self._pipewire_configs["--verbose"] = "    "
    404        else:
    405            pass
    406
    407        if verbose:
    408            print(self._pipewire_configs)
    409
    410    def load_list_targets(
    411        self,
    412        mode,  # playback or record
    413        # Debug,
    414        verbose: bool = False,
    415    ):
    416        """Returns a list of targets to playback or record. Then you can use
    417        the output to select a device to playback or record.
    418        """
    419
    420        if mode == "playback":
    421            mycommand = ["pw-cat", "--playback", "--list-targets"]
    422            stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    423            self._pipewire_list_targets["list_playback"] = _generate_dict_list_targets(
    424                longstring=stdout.decode(), verbose=verbose
    425            )
    426        elif mode == "record":
    427            mycommand = ["pw-cat", "--record", "--list-targets"]
    428            stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    429            self._pipewire_list_targets["list_record"] = _generate_dict_list_targets(
    430                longstring=stdout.decode(), verbose=verbose
    431            )
    432        else:
    433            raise AttributeError(MESSAGES_ERROR["ValueError"])
    434
    435        if verbose:
    436            print(f"[mycommand]{mycommand}")
    437
    438    def get_list_targets(
    439        self,
    440        # Debug,
    441        verbose: bool = False,
    442    ):
    443        """Returns a list of targets to playback or record. Then you can use
    444        the output to select a device to playback or record.
    445
    446        Returns:
    447            - `_pipewire_list_targets`
    448
    449        Examples:
    450        ```python
    451        >>> Controller().get_list_targets()
    452        {
    453        "list_playback": {
    454            "86": {
    455            "description": "Starship/Matisse HD Audio Controller Pro",
    456            "prior": "936"
    457            },
    458            "_list_nodes": [
    459            "86"
    460            ],
    461            "_node_default": [
    462            "86"
    463            ],
    464            "_alsa_node": [
    465            "alsa_output.pci-0000_0a_00.4.pro-output-0"
    466            ]
    467        },
    468        "list_record": {
    469            "86": {
    470            "description": "Starship/Matisse HD Audio Controller Pro",
    471            "prior": "936"
    472            },
    473            "_list_nodes": [
    474            "86"
    475            ],
    476            "_node_default": [
    477            "86"
    478            ],
    479            "_alsa_node": [
    480            "alsa_output.pci-0000_0a_00.4.pro-output-0"
    481            ]
    482        }
    483        }
    484        ```
    485        """
    486        if verbose:
    487            print(self._pipewire_list_targets)
    488        return self._pipewire_list_targets
    489
    490    def get_list_interfaces(
    491        self,
    492        filtered_by_type: str = True,
    493        type_interfaces: str = "Client",
    494        # Debug
    495        verbose: bool = False,
    496    ):
    497        """Returns a list of applications currently using pipewire on Client.
    498        An example of pw-cli usage is the code below:
    499
    500        ```bash
    501        #!/bin/bash
    502        pw-cli ls Client
    503        ```
    504        Args:
    505            filtered_by_type : If False, returns all. If not, returns a fitered dict
    506            type_interfaces : Set type of Interface ["Client","Link","Node","Factory","Module","Metadata","Endpoint","Session","Endpoint Stream","EndpointLink","Port"]
    507
    508        Returns:
    509            - dict_interfaces_filtered: dictionary with list of interfaces matching conditions
    510
    511        Examples:
    512        ```python
    513        >>> Controller().get_list_interfaces()
    514
    515        ```
    516        """
    517        mycommand = ["pw-cli", "info", "all"]
    518
    519        # if verbose:
    520        #     print(f"[mycommand]{mycommand}")
    521
    522        stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    523        dict_interfaces = _generate_dict_interfaces(longstring=stdout.decode(), verbose=verbose)
    524
    525        if filtered_by_type:
    526            dict_interfaces_filtered = _filter_by_type(
    527                dict_interfaces=dict_interfaces, type_interfaces=type_interfaces
    528            )
    529        else:
    530            dict_interfaces_filtered = dict_interfaces
    531
    532        return dict_interfaces_filtered
    533
    534    def playback(
    535        self,
    536        audio_filename: str = "myplayback.wav",
    537        # Debug
    538        verbose: bool = False,
    539    ):
    540        """Execute pipewire command to play an audio file with the following
    541        command:
    542
    543        ```bash
    544        #!/bin/bash
    545        pw-cat --playback {audio_filename} + {configs}
    546        # configs are a concatenated params
    547        ```
    548
    549        Args:
    550            audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
    551            verbose (`bool`): True enable debug logs. *default=False
    552
    553        Returns:
    554            - stdout (`str`): Shell response to the command in stdout format
    555            - stderr (`str`): Shell response response to the command in stderr format
    556        """
    557        warnings.warn("The name of the function may change on future releases", DeprecationWarning)
    558
    559        mycommand = ["pw-cat", "--playback", audio_filename] + _generate_command_by_dict(
    560            mydict=self._pipewire_configs, verbose=verbose
    561        )
    562
    563        if verbose:
    564            print(f"[mycommand]{mycommand}")
    565
    566        stdout, stderr = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    567        return stdout, stderr
    568
    569    def record(
    570        self,
    571        audio_filename: str = "myplayback.wav",
    572        timeout_seconds=5,
    573        # Debug
    574        verbose: bool = False,
    575    ):
    576        """Execute pipewire command to record an audio file, with a timeout of 5
    577        seconds with the following code and exiting the shell when tiomeout is over.
    578
    579        ```bash
    580        #!/bin/bash
    581        pw-cat --record {audio_filename}
    582        # timeout is managed by python3 (when signal CTRL+C is sended)
    583        ```
    584
    585        Args:
    586            audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
    587            verbose (`bool`): True enable debug logs. *default=False
    588
    589        Returns:
    590            - stdout (`str`): Shell response to the command in stdout format
    591            - stderr (`str`): Shell response response to the command in stderr format
    592        """
    593        warnings.warn("The name of the function may change on future releases", DeprecationWarning)
    594
    595        mycommand = ["pw-cat", "--record", audio_filename] + _generate_command_by_dict(
    596            mydict=self._pipewire_configs, verbose=verbose
    597        )
    598
    599        if verbose:
    600            print(f"[mycommand]{mycommand}")
    601
    602        stdout, stderr = _execute_shell_command(
    603            command=mycommand, timeout=timeout_seconds, verbose=verbose
    604        )
    605        return stdout, stderr
    606
    607    def clear_devices(
    608        self,
    609        mode: str = "all",  # ['all','playback','record']
    610        # Debug
    611        verbose: bool = False,
    612    ):
    613        """Function to stop process running under pipewire executed by
    614        python controller and with default process name of `pw-cat`, `pw-play` or `pw-record`.
    615
    616        Args:
    617            mode (`str`) : string to kill process under `pw-cat`, `pw-play` or `pw-record`.
    618
    619        Returns:
    620            - stdoutdict (`dict`) : a dictionary with keys of `mode`.
    621
    622        Example with pipewire:
    623            pw-cat process
    624        """
    625
    626        mycommand = self._kill_pipewire[mode]
    627
    628        if verbose:
    629            print(f"[mycommands]{mycommand}")
    630
    631        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)
    632
    633        return {mode: stdout}

class Controller: View Source

     41class Controller:
     42    """
     43    Class that controls pipewire command line interface
     44    with shell commands, handling outputs, loading default
     45    configs and more.
     46    """
     47
     48    _pipewire_cli = {  # Help
     49        "--help": "--help",  # -h
     50        "--version": "--version",
     51        "--remote": None,  # -r
     52    }
     53
     54    _pipewire_modes = {  # Modes
     55        "--playback": None,  # -p
     56        "--record": None,  # -r
     57        "--midi": None,  # -m
     58    }
     59
     60    _pipewire_list_targets = {  # "--list-targets": None,
     61        "list_playback": None,
     62        "list_record": None,
     63    }
     64
     65    _pipewire_configs = {  # Configs
     66        "--media-type": None,  # *default=Audio
     67        "--media-category": None,  # *default=Playback
     68        "--media-role": None,  # *default=Music
     69        "--target": None,  # *default=auto
     70        "--latency": None,  # *default=100ms (SOURCE FILE if not specified)
     71        "--rate": None,  # *default=48000
     72        "--channels": None,  # [1,2] *default=2
     73        "--channel-map": None,  # ["stereo", "surround-51", "FL,FR"...] *default="FL,FR"
     74        "--format": None,  # [u8|s8|s16|s32|f32|f64] *default=s16
     75        "--volume": None,  # [0.0,1.0] *default=1.000
     76        "--quality": None,  # -q # [0,15] *default=4
     77        "--verbose": None,  # -v
     78    }
     79
     80    _kill_pipewire = {
     81        "all": ["kill", "$(pidof pw-cat)"],
     82        "playback": ["kill", "$(pidof pw-play)"],
     83        "record": ["kill", "$(pidof pw-record)"],
     84    }
     85
     86    def __init__(
     87        self,
     88        # Debug
     89        verbose: bool = False,
     90    ):
     91        """This constructor load default configs from OS executing
     92        the following pipewire command
     93
     94        ```bash
     95        #!/bin/bash
     96        # Get defaults from output of:
     97        pw-cat -h
     98        ```
     99        """
    100        # LOAD ALL DEFAULT PARAMETERS
    101
    102        mycommand = ["pw-cat", "-h"]
    103
    104        # get default parameters with help
    105        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)  # stderr
    106        # convert stdout to dictionary
    107        dict_default_values = _get_dict_from_stdout(stdout=str(stdout.decode()), verbose=verbose)
    108
    109        if verbose:
    110            print(self._pipewire_configs)
    111
    112        # Save default system configs to our json
    113        self._pipewire_configs.update(
    114            ([(key, dict_default_values[key]) for key in dict_default_values])
    115        )
    116
    117        if verbose:
    118            print(self._pipewire_configs)
    119
    120        # Delete keys with None values
    121        self._pipewire_configs = _drop_keys_with_none_values(self._pipewire_configs)
    122
    123        if verbose:
    124            print(self._pipewire_configs)
    125
    126        # Load values of list targets
    127        self.load_list_targets(mode="playback", verbose=verbose)
    128        self.load_list_targets(mode="record", verbose=verbose)
    129
    130    def _help_cli(
    131        self,
    132        # Debug
    133        verbose: bool = True,
    134    ):
    135        """Get pipewire command line help"""
    136
    137        mycommand = ["pipewire", self._pipewire_cli["--help"]]
    138
    139        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)  # stderr
    140
    141        return stdout
    142
    143    def get_version(
    144        self,
    145        # Debug
    146        verbose: bool = False,
    147    ):
    148        """Get version of pipewire installed on OS by executing the following
    149        code:
    150
    151        ```bash
    152        #!/bin/bash
    153        pw-cli --version
    154        ```
    155
    156        Args:
    157            verbose (bool) : True enable debug logs. *default=False
    158
    159        Returns:
    160            - versions (list) : Versions of pipewire compiled
    161        """
    162
    163        mycommand = ["pw-cli", "--version"]
    164
    165        if verbose:
    166            print(f"[mycommand]{mycommand}")
    167
    168        stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    169        versions = stdout.decode().split("\n")[1:]
    170
    171        self._pipewire_cli["--version"] = versions
    172
    173        return versions
    174
    175    def verbose(
    176        self,
    177        status: bool = True,
    178    ):
    179        """Get full log of pipewire stream status with the command `pw-cat`
    180
    181        An example of pw-cli usage is the code below:
    182
    183        ```bash
    184        #!/bin/bash
    185        # For example
    186        pw-cat --playback beers.wav --verbose
    187        ```
    188
    189        that will generate an output like this:
    190
    191        ```bash
    192        opened file "beers.wav" format 00010002 channels:2 rate:44100
    193        using default channel map: FL,FR
    194        rate=44100 channels=2 fmt=s16 samplesize=2 stride=4 latency=4410 (0.100s)
    195        connecting playback stream; target_id=4294967295
    196        stream state changed unconnected -> connecting
    197        stream param change: id=2
    198        stream properties:
    199            media.type = "Audio"
    200            ...
    201        now=0 rate=0/0 ticks=0 delay=0 queued=0
    202        remote 0 is named "pipewire-0"
    203        core done
    204        stream state changed connecting -> paused
    205        stream param change: id=2
    206        ...
    207        stream param change: id=15
    208        stream param change: id=15
    209        now=13465394419270 rate=1/48000 ticks=35840 delay=512 queued=0
    210        now=13466525228363 rate=1/48000 ticks=90112 delay=512 queued=0
    211        ...
    212        stream drained
    213        stream state changed streaming -> paused
    214        stream param change: id=4
    215        stream state changed paused -> unconnected
    216        stream param change: id=4
    217        ```
    218        """
    219
    220        if status:
    221            self._pipewire_configs["--verbose"] = "    "
    222        else:
    223            pass
    224
    225    def get_config(self):
    226        """Return config dictionary with default or setup variables, remember that
    227        this object changes only on python-side. Is not updated on real time,
    228        For real-time, please create and destroy the class.
    229
    230        Args:
    231            Nothing
    232
    233        Returns:
    234            - _pipewire_configs (`dict`) : dictionary with config values
    235
    236        """
    237
    238        return self._pipewire_configs
    239
    240    def set_config(
    241        self,
    242        # configs
    243        media_type=None,
    244        media_category=None,
    245        media_role=None,
    246        target=None,
    247        latency=None,
    248        rate=None,
    249        channels=None,
    250        channels_map=None,
    251        _format=None,
    252        volume=None,
    253        quality=None,
    254        # Debug
    255        verbose=False,
    256    ):
    257        """Method that get args as variables and set them
    258        to the `json` parameter of the class `_pipewire_configs`,
    259        then you can use in other method, such as `playback(...)` or
    260        `record(...)`. This method verifies values to avoid wrong
    261        settings.
    262
    263        Args:
    264            media_type : Set media type
    265            media_category : Set media category
    266            media_role : Set media role
    267            target : Set node target
    268            latency : Set node latency *example=100ms
    269            rate : Set sample rate [8000,11025,16000,22050,44100,48000,88200,96000,176400,192000,352800,384000]
    270            channels : Numbers of channels [1,2]
    271            channels_map : ["stereo", "surround-51", "FL,FR", ...]
    272            _format : ["u8", "s8", "s16", "s32", "f32", "f64"]
    273            volume : Stream volume [0.000, 1.000]
    274            quality : Resampler quality [0, 15]
    275            verbose (`bool`): True enable debug logs. *default=False
    276
    277        Returns:
    278            - Nothing
    279
    280        More:
    281            Check all links listed at the beginning of this page
    282        """  # 1 - media_type
    283        if media_type:
    284            self._pipewire_configs["--media-type"] = str(media_type)
    285        elif media_type is None:
    286            pass
    287        else:
    288            raise ValueError(
    289                f"{MESSAGES_ERROR['ValueError']}[media_type='{media_type}'] EMPTY VALUE"
    290            )
    291        # 2 - media_category
    292        if media_category:
    293            self._pipewire_configs["--media-category"] = str(media_category)
    294        elif media_category is None:
    295            pass
    296        else:
    297            raise ValueError(
    298                f"{MESSAGES_ERROR['ValueError']}[media_category='{media_category}'] EMPTY VALUE"
    299            )
    300        # 3 - media_role
    301        if media_role:
    302            self._pipewire_configs["--media-role"] = str(media_role)
    303        elif media_role is None:
    304            pass
    305        else:
    306            raise ValueError(
    307                f"{MESSAGES_ERROR['ValueError']}[media_role='{media_role}'] EMPTY VALUE"
    308            )
    309        # 4 - target
    310        if target:
    311            self._pipewire_configs["--target"] = str(target)
    312        elif target is None:
    313            pass
    314        else:
    315            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[target='{target}'] EMPTY VALUE")
    316        # 5 - latency
    317        if latency:
    318            if any(chr.isdigit() for chr in latency):  # Contain numbers
    319                self._pipewire_configs["--latency"] = str(latency)
    320            else:
    321                raise ValueError(
    322                    f"{MESSAGES_ERROR['ValueError']}[latency='{latency}'] NO NUMBER IN VARIABLE"
    323                )
    324        elif latency is None:
    325            pass
    326        else:
    327            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[latency='{latency}'] EMPTY VALUE")
    328        # 6 - rate
    329        if rate:
    330            if rate in RECOMMENDED_RATES:
    331                self._pipewire_configs["--rate"] = str(rate)
    332            else:
    333                raise ValueError(
    334                    f"{MESSAGES_ERROR['ValueError']}[rate='{rate}']\
    335                         VALUE NOT IN RECOMMENDED LIST \n{RECOMMENDED_RATES}"
    336                )
    337        elif rate is None:
    338            pass
    339        else:
    340            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[rate='{rate}'] EMPTY VALUE")
    341        # 7 - channels
    342        if channels:
    343            if channels in [1, 2]:  # values
    344                self._pipewire_configs["--channels"] = str(channels)
    345            else:
    346                raise ValueError(
    347                    f"{MESSAGES_ERROR['ValueError']}[channels='{channels}']\
    348                         WRONG VALUE\n ONLY 1 or 2."
    349                )
    350        elif channels is None:
    351            pass
    352        else:
    353            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[channels='{channels}'] EMPTY VALUE")
    354        # 8 - channels-map
    355        if channels_map:
    356            self._pipewire_configs["--channels-map"] = str(channels_map)
    357        elif channels_map is None:
    358            pass
    359        else:
    360            raise ValueError(
    361                f"{MESSAGES_ERROR['ValueError']}[channels_map='{channels_map}'] EMPTY VALUE"
    362            )
    363        # 9 - format
    364        if _format:
    365            if _format in RECOMMENDED_FORMATS:
    366                self._pipewire_configs["--format"] = str(_format)
    367            else:
    368                raise ValueError(
    369                    f"{MESSAGES_ERROR['ValueError']}[_format='{_format}']\
    370                         VALUE NOT IN RECOMMENDED LIST \n{RECOMMENDED_FORMATS}"
    371                )
    372        elif _format is None:
    373            pass
    374        else:
    375            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[_format='{_format}'] EMPTY VALUE")
    376        # 10 - volume
    377        if volume:
    378            if 0.0 <= volume <= 1.0:
    379                self._pipewire_configs["--volume"] = str(volume)
    380            else:
    381                raise ValueError(
    382                    f"{MESSAGES_ERROR['ValueError']}[volume='{volume}']\
    383                         OUT OF RANGE \n [0.000, 1.000]"
    384                )
    385        elif volume is None:
    386            pass
    387        else:
    388            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[volume='{volume}'] EMPTY VALUE")
    389        # 11 - quality
    390        if quality:
    391            if 0 <= quality <= 15:
    392                self._pipewire_configs["--quality"] = str(quality)
    393            else:
    394                raise ValueError(
    395                    f"{MESSAGES_ERROR['ValueError']}[quality='{quality}'] OUT OF RANGE \n [0, 15]"
    396                )
    397        elif quality is None:
    398            pass
    399        else:
    400            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[volume='{volume}'] EMPTY VALUE")
    401
    402        # 12 - verbose cli
    403        if verbose:  # True
    404            self._pipewire_configs["--verbose"] = "    "
    405        else:
    406            pass
    407
    408        if verbose:
    409            print(self._pipewire_configs)
    410
    411    def load_list_targets(
    412        self,
    413        mode,  # playback or record
    414        # Debug,
    415        verbose: bool = False,
    416    ):
    417        """Returns a list of targets to playback or record. Then you can use
    418        the output to select a device to playback or record.
    419        """
    420
    421        if mode == "playback":
    422            mycommand = ["pw-cat", "--playback", "--list-targets"]
    423            stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    424            self._pipewire_list_targets["list_playback"] = _generate_dict_list_targets(
    425                longstring=stdout.decode(), verbose=verbose
    426            )
    427        elif mode == "record":
    428            mycommand = ["pw-cat", "--record", "--list-targets"]
    429            stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    430            self._pipewire_list_targets["list_record"] = _generate_dict_list_targets(
    431                longstring=stdout.decode(), verbose=verbose
    432            )
    433        else:
    434            raise AttributeError(MESSAGES_ERROR["ValueError"])
    435
    436        if verbose:
    437            print(f"[mycommand]{mycommand}")
    438
    439    def get_list_targets(
    440        self,
    441        # Debug,
    442        verbose: bool = False,
    443    ):
    444        """Returns a list of targets to playback or record. Then you can use
    445        the output to select a device to playback or record.
    446
    447        Returns:
    448            - `_pipewire_list_targets`
    449
    450        Examples:
    451        ```python
    452        >>> Controller().get_list_targets()
    453        {
    454        "list_playback": {
    455            "86": {
    456            "description": "Starship/Matisse HD Audio Controller Pro",
    457            "prior": "936"
    458            },
    459            "_list_nodes": [
    460            "86"
    461            ],
    462            "_node_default": [
    463            "86"
    464            ],
    465            "_alsa_node": [
    466            "alsa_output.pci-0000_0a_00.4.pro-output-0"
    467            ]
    468        },
    469        "list_record": {
    470            "86": {
    471            "description": "Starship/Matisse HD Audio Controller Pro",
    472            "prior": "936"
    473            },
    474            "_list_nodes": [
    475            "86"
    476            ],
    477            "_node_default": [
    478            "86"
    479            ],
    480            "_alsa_node": [
    481            "alsa_output.pci-0000_0a_00.4.pro-output-0"
    482            ]
    483        }
    484        }
    485        ```
    486        """
    487        if verbose:
    488            print(self._pipewire_list_targets)
    489        return self._pipewire_list_targets
    490
    491    def get_list_interfaces(
    492        self,
    493        filtered_by_type: str = True,
    494        type_interfaces: str = "Client",
    495        # Debug
    496        verbose: bool = False,
    497    ):
    498        """Returns a list of applications currently using pipewire on Client.
    499        An example of pw-cli usage is the code below:
    500
    501        ```bash
    502        #!/bin/bash
    503        pw-cli ls Client
    504        ```
    505        Args:
    506            filtered_by_type : If False, returns all. If not, returns a fitered dict
    507            type_interfaces : Set type of Interface ["Client","Link","Node","Factory","Module","Metadata","Endpoint","Session","Endpoint Stream","EndpointLink","Port"]
    508
    509        Returns:
    510            - dict_interfaces_filtered: dictionary with list of interfaces matching conditions
    511
    512        Examples:
    513        ```python
    514        >>> Controller().get_list_interfaces()
    515
    516        ```
    517        """
    518        mycommand = ["pw-cli", "info", "all"]
    519
    520        # if verbose:
    521        #     print(f"[mycommand]{mycommand}")
    522
    523        stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    524        dict_interfaces = _generate_dict_interfaces(longstring=stdout.decode(), verbose=verbose)
    525
    526        if filtered_by_type:
    527            dict_interfaces_filtered = _filter_by_type(
    528                dict_interfaces=dict_interfaces, type_interfaces=type_interfaces
    529            )
    530        else:
    531            dict_interfaces_filtered = dict_interfaces
    532
    533        return dict_interfaces_filtered
    534
    535    def playback(
    536        self,
    537        audio_filename: str = "myplayback.wav",
    538        # Debug
    539        verbose: bool = False,
    540    ):
    541        """Execute pipewire command to play an audio file with the following
    542        command:
    543
    544        ```bash
    545        #!/bin/bash
    546        pw-cat --playback {audio_filename} + {configs}
    547        # configs are a concatenated params
    548        ```
    549
    550        Args:
    551            audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
    552            verbose (`bool`): True enable debug logs. *default=False
    553
    554        Returns:
    555            - stdout (`str`): Shell response to the command in stdout format
    556            - stderr (`str`): Shell response response to the command in stderr format
    557        """
    558        warnings.warn("The name of the function may change on future releases", DeprecationWarning)
    559
    560        mycommand = ["pw-cat", "--playback", audio_filename] + _generate_command_by_dict(
    561            mydict=self._pipewire_configs, verbose=verbose
    562        )
    563
    564        if verbose:
    565            print(f"[mycommand]{mycommand}")
    566
    567        stdout, stderr = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    568        return stdout, stderr
    569
    570    def record(
    571        self,
    572        audio_filename: str = "myplayback.wav",
    573        timeout_seconds=5,
    574        # Debug
    575        verbose: bool = False,
    576    ):
    577        """Execute pipewire command to record an audio file, with a timeout of 5
    578        seconds with the following code and exiting the shell when tiomeout is over.
    579
    580        ```bash
    581        #!/bin/bash
    582        pw-cat --record {audio_filename}
    583        # timeout is managed by python3 (when signal CTRL+C is sended)
    584        ```
    585
    586        Args:
    587            audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
    588            verbose (`bool`): True enable debug logs. *default=False
    589
    590        Returns:
    591            - stdout (`str`): Shell response to the command in stdout format
    592            - stderr (`str`): Shell response response to the command in stderr format
    593        """
    594        warnings.warn("The name of the function may change on future releases", DeprecationWarning)
    595
    596        mycommand = ["pw-cat", "--record", audio_filename] + _generate_command_by_dict(
    597            mydict=self._pipewire_configs, verbose=verbose
    598        )
    599
    600        if verbose:
    601            print(f"[mycommand]{mycommand}")
    602
    603        stdout, stderr = _execute_shell_command(
    604            command=mycommand, timeout=timeout_seconds, verbose=verbose
    605        )
    606        return stdout, stderr
    607
    608    def clear_devices(
    609        self,
    610        mode: str = "all",  # ['all','playback','record']
    611        # Debug
    612        verbose: bool = False,
    613    ):
    614        """Function to stop process running under pipewire executed by
    615        python controller and with default process name of `pw-cat`, `pw-play` or `pw-record`.
    616
    617        Args:
    618            mode (`str`) : string to kill process under `pw-cat`, `pw-play` or `pw-record`.
    619
    620        Returns:
    621            - stdoutdict (`dict`) : a dictionary with keys of `mode`.
    622
    623        Example with pipewire:
    624            pw-cat process
    625        """
    626
    627        mycommand = self._kill_pipewire[mode]
    628
    629        if verbose:
    630            print(f"[mycommands]{mycommand}")
    631
    632        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)
    633
    634        return {mode: stdout}

Class that controls pipewire command line interface with shell commands, handling outputs, loading default configs and more.

Controller(verbose: bool = False) View Source

     86    def __init__(
     87        self,
     88        # Debug
     89        verbose: bool = False,
     90    ):
     91        """This constructor load default configs from OS executing
     92        the following pipewire command
     93
     94        ```bash
     95        #!/bin/bash
     96        # Get defaults from output of:
     97        pw-cat -h
     98        ```
     99        """
    100        # LOAD ALL DEFAULT PARAMETERS
    101
    102        mycommand = ["pw-cat", "-h"]
    103
    104        # get default parameters with help
    105        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)  # stderr
    106        # convert stdout to dictionary
    107        dict_default_values = _get_dict_from_stdout(stdout=str(stdout.decode()), verbose=verbose)
    108
    109        if verbose:
    110            print(self._pipewire_configs)
    111
    112        # Save default system configs to our json
    113        self._pipewire_configs.update(
    114            ([(key, dict_default_values[key]) for key in dict_default_values])
    115        )
    116
    117        if verbose:
    118            print(self._pipewire_configs)
    119
    120        # Delete keys with None values
    121        self._pipewire_configs = _drop_keys_with_none_values(self._pipewire_configs)
    122
    123        if verbose:
    124            print(self._pipewire_configs)
    125
    126        # Load values of list targets
    127        self.load_list_targets(mode="playback", verbose=verbose)
    128        self.load_list_targets(mode="record", verbose=verbose)

This constructor load default configs from OS executing the following pipewire command

    #!/bin/bash
    # Get defaults from output of:
    pw-cat -h

def get_version(self, verbose: bool = False): View Source

    143    def get_version(
    144        self,
    145        # Debug
    146        verbose: bool = False,
    147    ):
    148        """Get version of pipewire installed on OS by executing the following
    149        code:
    150
    151        ```bash
    152        #!/bin/bash
    153        pw-cli --version
    154        ```
    155
    156        Args:
    157            verbose (bool) : True enable debug logs. *default=False
    158
    159        Returns:
    160            - versions (list) : Versions of pipewire compiled
    161        """
    162
    163        mycommand = ["pw-cli", "--version"]
    164
    165        if verbose:
    166            print(f"[mycommand]{mycommand}")
    167
    168        stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    169        versions = stdout.decode().split("\n")[1:]
    170
    171        self._pipewire_cli["--version"] = versions
    172
    173        return versions

Get version of pipewire installed on OS by executing the following code:

    #!/bin/bash
    pw-cli --version

###### Arguments:

- verbose (bool) : True enable debug logs. *default=False

###### Returns:

> - versions (list) : Versions of pipewire compiled

def verbose(self, status: bool = True): View Source

    175    def verbose(
    176        self,
    177        status: bool = True,
    178    ):
    179        """Get full log of pipewire stream status with the command `pw-cat`
    180
    181        An example of pw-cli usage is the code below:
    182
    183        ```bash
    184        #!/bin/bash
    185        # For example
    186        pw-cat --playback beers.wav --verbose
    187        ```
    188
    189        that will generate an output like this:
    190
    191        ```bash
    192        opened file "beers.wav" format 00010002 channels:2 rate:44100
    193        using default channel map: FL,FR
    194        rate=44100 channels=2 fmt=s16 samplesize=2 stride=4 latency=4410 (0.100s)
    195        connecting playback stream; target_id=4294967295
    196        stream state changed unconnected -> connecting
    197        stream param change: id=2
    198        stream properties:
    199            media.type = "Audio"
    200            ...
    201        now=0 rate=0/0 ticks=0 delay=0 queued=0
    202        remote 0 is named "pipewire-0"
    203        core done
    204        stream state changed connecting -> paused
    205        stream param change: id=2
    206        ...
    207        stream param change: id=15
    208        stream param change: id=15
    209        now=13465394419270 rate=1/48000 ticks=35840 delay=512 queued=0
    210        now=13466525228363 rate=1/48000 ticks=90112 delay=512 queued=0
    211        ...
    212        stream drained
    213        stream state changed streaming -> paused
    214        stream param change: id=4
    215        stream state changed paused -> unconnected
    216        stream param change: id=4
    217        ```
    218        """
    219
    220        if status:
    221            self._pipewire_configs["--verbose"] = "    "
    222        else:
    223            pass

Get full log of pipewire stream status with the command `pw-cat`

An example of pw-cli usage is the code below:

    #!/bin/bash
    # For example
    pw-cat --playback beers.wav --verbose

that will generate an output like this:

    opened file "beers.wav" format 00010002 channels:2 rate:44100
    using default channel map: FL,FR
    rate=44100 channels=2 fmt=s16 samplesize=2 stride=4 latency=4410 (0.100s)
    connecting playback stream; target_id=4294967295
    stream state changed unconnected -> connecting
    stream param change: id=2
    stream properties:
        media.type = "Audio"
        ...
    now=0 rate=0/0 ticks=0 delay=0 queued=0
    remote 0 is named "pipewire-0"
    core done
    stream state changed connecting -> paused
    stream param change: id=2
    ...
    stream param change: id=15
    stream param change: id=15
    now=13465394419270 rate=1/48000 ticks=35840 delay=512 queued=0
    now=13466525228363 rate=1/48000 ticks=90112 delay=512 queued=0
    ...
    stream drained
    stream state changed streaming -> paused
    stream param change: id=4
    stream state changed paused -> unconnected
    stream param change: id=4

def get_config(self): View Source

    225    def get_config(self):
    226        """Return config dictionary with default or setup variables, remember that
    227        this object changes only on python-side. Is not updated on real time,
    228        For real-time, please create and destroy the class.
    229
    230        Args:
    231            Nothing
    232
    233        Returns:
    234            - _pipewire_configs (`dict`) : dictionary with config values
    235
    236        """
    237
    238        return self._pipewire_configs

Return config dictionary with default or setup variables, remember that this object changes only on python-side. Is not updated on real time, For real-time, please create and destroy the class.

###### Arguments:

- Nothing

###### Returns:

> - _pipewire_configs (`dict`) : dictionary with config values

def set_config( self, media_type=None, media_category=None, media_role=None, target=None, latency=None, rate=None, channels=None, channels_map=None, _format=None, volume=None, quality=None, verbose=False): View Source

    240    def set_config(
    241        self,
    242        # configs
    243        media_type=None,
    244        media_category=None,
    245        media_role=None,
    246        target=None,
    247        latency=None,
    248        rate=None,
    249        channels=None,
    250        channels_map=None,
    251        _format=None,
    252        volume=None,
    253        quality=None,
    254        # Debug
    255        verbose=False,
    256    ):
    257        """Method that get args as variables and set them
    258        to the `json` parameter of the class `_pipewire_configs`,
    259        then you can use in other method, such as `playback(...)` or
    260        `record(...)`. This method verifies values to avoid wrong
    261        settings.
    262
    263        Args:
    264            media_type : Set media type
    265            media_category : Set media category
    266            media_role : Set media role
    267            target : Set node target
    268            latency : Set node latency *example=100ms
    269            rate : Set sample rate [8000,11025,16000,22050,44100,48000,88200,96000,176400,192000,352800,384000]
    270            channels : Numbers of channels [1,2]
    271            channels_map : ["stereo", "surround-51", "FL,FR", ...]
    272            _format : ["u8", "s8", "s16", "s32", "f32", "f64"]
    273            volume : Stream volume [0.000, 1.000]
    274            quality : Resampler quality [0, 15]
    275            verbose (`bool`): True enable debug logs. *default=False
    276
    277        Returns:
    278            - Nothing
    279
    280        More:
    281            Check all links listed at the beginning of this page
    282        """  # 1 - media_type
    283        if media_type:
    284            self._pipewire_configs["--media-type"] = str(media_type)
    285        elif media_type is None:
    286            pass
    287        else:
    288            raise ValueError(
    289                f"{MESSAGES_ERROR['ValueError']}[media_type='{media_type}'] EMPTY VALUE"
    290            )
    291        # 2 - media_category
    292        if media_category:
    293            self._pipewire_configs["--media-category"] = str(media_category)
    294        elif media_category is None:
    295            pass
    296        else:
    297            raise ValueError(
    298                f"{MESSAGES_ERROR['ValueError']}[media_category='{media_category}'] EMPTY VALUE"
    299            )
    300        # 3 - media_role
    301        if media_role:
    302            self._pipewire_configs["--media-role"] = str(media_role)
    303        elif media_role is None:
    304            pass
    305        else:
    306            raise ValueError(
    307                f"{MESSAGES_ERROR['ValueError']}[media_role='{media_role}'] EMPTY VALUE"
    308            )
    309        # 4 - target
    310        if target:
    311            self._pipewire_configs["--target"] = str(target)
    312        elif target is None:
    313            pass
    314        else:
    315            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[target='{target}'] EMPTY VALUE")
    316        # 5 - latency
    317        if latency:
    318            if any(chr.isdigit() for chr in latency):  # Contain numbers
    319                self._pipewire_configs["--latency"] = str(latency)
    320            else:
    321                raise ValueError(
    322                    f"{MESSAGES_ERROR['ValueError']}[latency='{latency}'] NO NUMBER IN VARIABLE"
    323                )
    324        elif latency is None:
    325            pass
    326        else:
    327            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[latency='{latency}'] EMPTY VALUE")
    328        # 6 - rate
    329        if rate:
    330            if rate in RECOMMENDED_RATES:
    331                self._pipewire_configs["--rate"] = str(rate)
    332            else:
    333                raise ValueError(
    334                    f"{MESSAGES_ERROR['ValueError']}[rate='{rate}']\
    335                         VALUE NOT IN RECOMMENDED LIST \n{RECOMMENDED_RATES}"
    336                )
    337        elif rate is None:
    338            pass
    339        else:
    340            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[rate='{rate}'] EMPTY VALUE")
    341        # 7 - channels
    342        if channels:
    343            if channels in [1, 2]:  # values
    344                self._pipewire_configs["--channels"] = str(channels)
    345            else:
    346                raise ValueError(
    347                    f"{MESSAGES_ERROR['ValueError']}[channels='{channels}']\
    348                         WRONG VALUE\n ONLY 1 or 2."
    349                )
    350        elif channels is None:
    351            pass
    352        else:
    353            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[channels='{channels}'] EMPTY VALUE")
    354        # 8 - channels-map
    355        if channels_map:
    356            self._pipewire_configs["--channels-map"] = str(channels_map)
    357        elif channels_map is None:
    358            pass
    359        else:
    360            raise ValueError(
    361                f"{MESSAGES_ERROR['ValueError']}[channels_map='{channels_map}'] EMPTY VALUE"
    362            )
    363        # 9 - format
    364        if _format:
    365            if _format in RECOMMENDED_FORMATS:
    366                self._pipewire_configs["--format"] = str(_format)
    367            else:
    368                raise ValueError(
    369                    f"{MESSAGES_ERROR['ValueError']}[_format='{_format}']\
    370                         VALUE NOT IN RECOMMENDED LIST \n{RECOMMENDED_FORMATS}"
    371                )
    372        elif _format is None:
    373            pass
    374        else:
    375            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[_format='{_format}'] EMPTY VALUE")
    376        # 10 - volume
    377        if volume:
    378            if 0.0 <= volume <= 1.0:
    379                self._pipewire_configs["--volume"] = str(volume)
    380            else:
    381                raise ValueError(
    382                    f"{MESSAGES_ERROR['ValueError']}[volume='{volume}']\
    383                         OUT OF RANGE \n [0.000, 1.000]"
    384                )
    385        elif volume is None:
    386            pass
    387        else:
    388            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[volume='{volume}'] EMPTY VALUE")
    389        # 11 - quality
    390        if quality:
    391            if 0 <= quality <= 15:
    392                self._pipewire_configs["--quality"] = str(quality)
    393            else:
    394                raise ValueError(
    395                    f"{MESSAGES_ERROR['ValueError']}[quality='{quality}'] OUT OF RANGE \n [0, 15]"
    396                )
    397        elif quality is None:
    398            pass
    399        else:
    400            raise ValueError(f"{MESSAGES_ERROR['ValueError']}[volume='{volume}'] EMPTY VALUE")
    401
    402        # 12 - verbose cli
    403        if verbose:  # True
    404            self._pipewire_configs["--verbose"] = "    "
    405        else:
    406            pass
    407
    408        if verbose:
    409            print(self._pipewire_configs)

Method that get args as variables and set them to the `json` parameter of the class `_pipewire_configs`, then you can use in other method, such as `playback(...)` or `record(...)`. This method verifies values to avoid wrong settings.

###### Arguments:

- media_type : Set media type
- media_category : Set media category
- media_role : Set media role
- target : Set node target
- latency : Set node latency *example=100ms
- rate : Set sample rate [8000,11025,16000,22050,44100,48000,88200,96000,176400,192000,352800,384000]
- channels : Numbers of channels [1,2]
- channels_map : ["stereo", "surround-51", "FL,FR", ...]
- _format : ["u8", "s8", "s16", "s32", "f32", "f64"]
- volume : Stream volume [0.000, 1.000]
- quality : Resampler quality [0, 15]
- verbose (`bool`): True enable debug logs. *default=False

###### Returns:

> - Nothing

###### More:

> Check all links listed at the beginning of this page

def load_list_targets(self, mode, verbose: bool = False): View Source

    411    def load_list_targets(
    412        self,
    413        mode,  # playback or record
    414        # Debug,
    415        verbose: bool = False,
    416    ):
    417        """Returns a list of targets to playback or record. Then you can use
    418        the output to select a device to playback or record.
    419        """
    420
    421        if mode == "playback":
    422            mycommand = ["pw-cat", "--playback", "--list-targets"]
    423            stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    424            self._pipewire_list_targets["list_playback"] = _generate_dict_list_targets(
    425                longstring=stdout.decode(), verbose=verbose
    426            )
    427        elif mode == "record":
    428            mycommand = ["pw-cat", "--record", "--list-targets"]
    429            stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    430            self._pipewire_list_targets["list_record"] = _generate_dict_list_targets(
    431                longstring=stdout.decode(), verbose=verbose
    432            )
    433        else:
    434            raise AttributeError(MESSAGES_ERROR["ValueError"])
    435
    436        if verbose:
    437            print(f"[mycommand]{mycommand}")

Returns a list of targets to playback or record. Then you can use the output to select a device to playback or record.

def get_list_targets(self, verbose: bool = False): View Source

    439    def get_list_targets(
    440        self,
    441        # Debug,
    442        verbose: bool = False,
    443    ):
    444        """Returns a list of targets to playback or record. Then you can use
    445        the output to select a device to playback or record.
    446
    447        Returns:
    448            - `_pipewire_list_targets`
    449
    450        Examples:
    451        ```python
    452        >>> Controller().get_list_targets()
    453        {
    454        "list_playback": {
    455            "86": {
    456            "description": "Starship/Matisse HD Audio Controller Pro",
    457            "prior": "936"
    458            },
    459            "_list_nodes": [
    460            "86"
    461            ],
    462            "_node_default": [
    463            "86"
    464            ],
    465            "_alsa_node": [
    466            "alsa_output.pci-0000_0a_00.4.pro-output-0"
    467            ]
    468        },
    469        "list_record": {
    470            "86": {
    471            "description": "Starship/Matisse HD Audio Controller Pro",
    472            "prior": "936"
    473            },
    474            "_list_nodes": [
    475            "86"
    476            ],
    477            "_node_default": [
    478            "86"
    479            ],
    480            "_alsa_node": [
    481            "alsa_output.pci-0000_0a_00.4.pro-output-0"
    482            ]
    483        }
    484        }
    485        ```
    486        """
    487        if verbose:
    488            print(self._pipewire_list_targets)
    489        return self._pipewire_list_targets

Returns a list of targets to playback or record. Then you can use the output to select a device to playback or record.

###### Returns:

> - `_pipewire_list_targets`

Examples:

    >>> Controller().get_list_targets()
    {
    "list_playback": {
        "86": {
        "description": "Starship/Matisse HD Audio Controller Pro",
        "prior": "936"
        },
        "_list_nodes": [
        "86"
        ],
        "_node_default": [
        "86"
        ],
        "_alsa_node": [
        "alsa_output.pci-0000_0a_00.4.pro-output-0"
        ]
    },
    "list_record": {
        "86": {
        "description": "Starship/Matisse HD Audio Controller Pro",
        "prior": "936"
        },
        "_list_nodes": [
        "86"
        ],
        "_node_default": [
        "86"
        ],
        "_alsa_node": [
        "alsa_output.pci-0000_0a_00.4.pro-output-0"
        ]
    }
    }

def get_list_interfaces( self, filtered_by_type: str = True, type_interfaces: str = 'Client', verbose: bool = False): View Source

    491    def get_list_interfaces(
    492        self,
    493        filtered_by_type: str = True,
    494        type_interfaces: str = "Client",
    495        # Debug
    496        verbose: bool = False,
    497    ):
    498        """Returns a list of applications currently using pipewire on Client.
    499        An example of pw-cli usage is the code below:
    500
    501        ```bash
    502        #!/bin/bash
    503        pw-cli ls Client
    504        ```
    505        Args:
    506            filtered_by_type : If False, returns all. If not, returns a fitered dict
    507            type_interfaces : Set type of Interface ["Client","Link","Node","Factory","Module","Metadata","Endpoint","Session","Endpoint Stream","EndpointLink","Port"]
    508
    509        Returns:
    510            - dict_interfaces_filtered: dictionary with list of interfaces matching conditions
    511
    512        Examples:
    513        ```python
    514        >>> Controller().get_list_interfaces()
    515
    516        ```
    517        """
    518        mycommand = ["pw-cli", "info", "all"]
    519
    520        # if verbose:
    521        #     print(f"[mycommand]{mycommand}")
    522
    523        stdout, _ = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    524        dict_interfaces = _generate_dict_interfaces(longstring=stdout.decode(), verbose=verbose)
    525
    526        if filtered_by_type:
    527            dict_interfaces_filtered = _filter_by_type(
    528                dict_interfaces=dict_interfaces, type_interfaces=type_interfaces
    529            )
    530        else:
    531            dict_interfaces_filtered = dict_interfaces
    532
    533        return dict_interfaces_filtered

Returns a list of applications currently using pipewire on Client. An example of pw-cli usage is the code below:

    #!/bin/bash
    pw-cli ls Client

###### Arguments:

- filtered_by_type : If False, returns all. If not, returns a fitered dict
- type_interfaces : Set type of Interface ["Client","Link","Node","Factory","Module","Metadata","Endpoint","Session","Endpoint Stream","EndpointLink","Port"]

###### Returns:

> - dict_interfaces_filtered: dictionary with list of interfaces matching conditions

Examples:

    >>> Controller().get_list_interfaces()

def playback(self, audio_filename: str = 'myplayback.wav', verbose: bool = False): View Source

    535    def playback(
    536        self,
    537        audio_filename: str = "myplayback.wav",
    538        # Debug
    539        verbose: bool = False,
    540    ):
    541        """Execute pipewire command to play an audio file with the following
    542        command:
    543
    544        ```bash
    545        #!/bin/bash
    546        pw-cat --playback {audio_filename} + {configs}
    547        # configs are a concatenated params
    548        ```
    549
    550        Args:
    551            audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
    552            verbose (`bool`): True enable debug logs. *default=False
    553
    554        Returns:
    555            - stdout (`str`): Shell response to the command in stdout format
    556            - stderr (`str`): Shell response response to the command in stderr format
    557        """
    558        warnings.warn("The name of the function may change on future releases", DeprecationWarning)
    559
    560        mycommand = ["pw-cat", "--playback", audio_filename] + _generate_command_by_dict(
    561            mydict=self._pipewire_configs, verbose=verbose
    562        )
    563
    564        if verbose:
    565            print(f"[mycommand]{mycommand}")
    566
    567        stdout, stderr = _execute_shell_command(command=mycommand, timeout=-1, verbose=verbose)
    568        return stdout, stderr

Execute pipewire command to play an audio file with the following command:

    #!/bin/bash
    pw-cat --playback {audio_filename} + {configs}
    # configs are a concatenated params

###### Arguments:

- audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
- verbose (`bool`): True enable debug logs. *default=False

###### Returns:

> - stdout (`str`): Shell response to the command in stdout format
> - stderr (`str`): Shell response response to the command in stderr format

def record( self, audio_filename: str = 'myplayback.wav', timeout_seconds=5, verbose: bool = False): View Source

    570    def record(
    571        self,
    572        audio_filename: str = "myplayback.wav",
    573        timeout_seconds=5,
    574        # Debug
    575        verbose: bool = False,
    576    ):
    577        """Execute pipewire command to record an audio file, with a timeout of 5
    578        seconds with the following code and exiting the shell when tiomeout is over.
    579
    580        ```bash
    581        #!/bin/bash
    582        pw-cat --record {audio_filename}
    583        # timeout is managed by python3 (when signal CTRL+C is sended)
    584        ```
    585
    586        Args:
    587            audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
    588            verbose (`bool`): True enable debug logs. *default=False
    589
    590        Returns:
    591            - stdout (`str`): Shell response to the command in stdout format
    592            - stderr (`str`): Shell response response to the command in stderr format
    593        """
    594        warnings.warn("The name of the function may change on future releases", DeprecationWarning)
    595
    596        mycommand = ["pw-cat", "--record", audio_filename] + _generate_command_by_dict(
    597            mydict=self._pipewire_configs, verbose=verbose
    598        )
    599
    600        if verbose:
    601            print(f"[mycommand]{mycommand}")
    602
    603        stdout, stderr = _execute_shell_command(
    604            command=mycommand, timeout=timeout_seconds, verbose=verbose
    605        )
    606        return stdout, stderr

Execute pipewire command to record an audio file, with a timeout of 5 seconds with the following code and exiting the shell when tiomeout is over.

    #!/bin/bash
    pw-cat --record {audio_filename}
    # timeout is managed by python3 (when signal CTRL+C is sended)

###### Arguments:

- audio_filename (`str`): Path of the file to be played. *default='myplayback.wav'
- verbose (`bool`): True enable debug logs. *default=False

###### Returns:

> - stdout (`str`): Shell response to the command in stdout format
> - stderr (`str`): Shell response response to the command in stderr format

def clear_devices(self, mode: str = 'all', verbose: bool = False): View Source

    608    def clear_devices(
    609        self,
    610        mode: str = "all",  # ['all','playback','record']
    611        # Debug
    612        verbose: bool = False,
    613    ):
    614        """Function to stop process running under pipewire executed by
    615        python controller and with default process name of `pw-cat`, `pw-play` or `pw-record`.
    616
    617        Args:
    618            mode (`str`) : string to kill process under `pw-cat`, `pw-play` or `pw-record`.
    619
    620        Returns:
    621            - stdoutdict (`dict`) : a dictionary with keys of `mode`.
    622
    623        Example with pipewire:
    624            pw-cat process
    625        """
    626
    627        mycommand = self._kill_pipewire[mode]
    628
    629        if verbose:
    630            print(f"[mycommands]{mycommand}")
    631
    632        stdout, _ = _execute_shell_command(command=mycommand, verbose=verbose)
    633
    634        return {mode: stdout}

Function to stop process running under pipewire executed by python controller and with default process name of `pw-cat`, `pw-play` or `pw-record`.

###### Arguments:

- mode (`str`) : string to kill process under `pw-cat`, `pw-play` or `pw-record`.

###### Returns:

> - stdoutdict (`dict`) : a dictionary with keys of `mode`.

###### Example with pipewire:

> pw-cat process