#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; my $selection = $ARGV[0]; my $app = {}; if (defined $selection) { select_sink($app, $selection); } else { show_sinks($app); } # ----------- # Subroutines # ----------- sub show_sinks { my ($app) = @_; my @sinks = get_sinks($app); print join("\n", @sinks); } # ----------- sub select_sink { my ($app, $sink) = @_; my $wp_h; my ($id,$name) = $sink =~ /(\d+):(.*)/; my $seld_sink = "NONE"; my $seld_id = "NONE"; open($wp_h, "pw-cli info $id |") or die "Could not run pw-cli command"; while (my $ln = <$wp_h>) { if (my ($node_name) = $ln =~ /node.name\s*=\s*"(.*)"/) { warn "Selected [$id] ($name) - $node_name\n"; $seld_sink = $node_name; $seld_id = $id; last; } } enable_sink($app, $seld_sink, $seld_id); } # ----------- sub enable_sink { my ($app, $sink, $id) = @_; `wpctl set-default $id`; unmute_master($app); } # ----------- sub unmute_master { my ($app) = @_; `amixer -c 0 set Master unmute`; } # ----------- sub parse_wpctl { my $wp_h; open($wp_h, "wpctl status |") or die "Could not run wpctl command"; my @lines = <$wp_h>; my $section = "None"; my $sub_section = "None"; my $wp_data = {}; foreach my $ln (@lines) { # Section if (my ($mat) = $ln =~ /(^(PipeWire)|^(\w+)$)/) { $section = $mat; $wp_data->{$section} = {} unless (exists $wp_data->{$section}); } # Sub-section if (my ($mat) = $ln =~ /\s+(\w+):$/) { $sub_section = $mat; $wp_data->{$section}->{$sub_section} = [] unless (exists $wp_data->{$section}->{$sub_section}); } # Entries if (($section ne "None") && ($sub_section ne "None")) { if (my ($sel, $id, $name) = $ln =~ /\s+(\*)?\s+(\d+)\.\s+(.*?)(\[|$)/) { my $seld = (defined $sel) ? 1 : 0; $name =~ s/^\s*//; $name =~ s/\s*$//; push(@{$wp_data->{$section}->{$sub_section}}, { 'sel' => $seld, 'id' => $id, 'name' => $name }); } } } $app->{'wp_data'} = $wp_data; } # ----------- sub get_sinks { my ($app) = @_; parse_wpctl($app); # warn Data::Dumper->Dump([$app->{'wp_data'}]); return map { "$_->{'id'}:$_->{'name'}" } @{$app->{'wp_data'}->{'Audio'}->{'Sinks'}} }