#!/usr/bin/perl
# powermixer 1.0
# (c) 2002 Alex Efros <powerman@powerman.name>

# %mixer = Mixer();	    # GET
# %mixer = Mixer(%mixer);   # SET
sub Mixer {
    my %MIXER = @_;
    # from /usr/include/linux/soundcard.h :
    my $SOUND_MIXER_READ_DEVMASK    = 0x80044dfe;
    my $SOUND_MIXER_READ_STEREODEVS = 0x80044dfb;
    my $SOUND_MIXER_NRDEVICES	    = 25;
    my $MIXER_READ		    = 0x80044d00;
    my $MIXER_WRITE		    = 0xc0044d00;
    my @SOUND_DEVICE_NAMES	    = (qw(vol bass treble synth pcm speaker
	line mic cd mix pcm2 rec igain ogain line1 line2 line3
	dig1 dig2 dig3 phin phout video radio monitor));
    my @SOUND_DEVICE_LABELS	    = (qw(Vol Bass Trebl Synth Pcm Spkr
	Line Mic CD Mix Pcm2 Rec IGain OGain Line1 Line2 Line3
	Digital1 Digital2 Digital3 PhoneIn PhoneOut Video Radio Monitor));
    # open mixer
    open my $MIXER, '+< /dev/mixer' or die "open: $!";
    ioctl $MIXER, $SOUND_MIXER_READ_DEVMASK, $devmask or die "SOUND_MIXER_READ_DEVMASK";
    ioctl $MIXER, $SOUND_MIXER_READ_STEREODEVS, $stereod or die "SOUND_MIXER_READ_STEREODEVS";
    my $devmask = unpack "V", $devmask;
    my $stereod = unpack "V", $stereod;
    die "No device found.\n" unless $devmask;
    # set mixer
    for (values %MIXER) {
	next unless exists $_->{id} and $_->{id} >= 0 and
	    $_->{id} < $SOUND_MIXER_NRDEVICES;
	next unless $_->{exist} and ($devmask & (1 << $_->{id}));
	$_ = ($_ < 0) ? 0 : ($_ > 100) ? 100 : $_ for $_->{left}, $_->{right};
	my $val = pack "V", ($stereod & (1 << $_->{id})) ? 
	    ($_->{left} | ($_->{right} << 8)) : $_->{left};
	ioctl $MIXER, $MIXER_WRITE | $_->{id}, $val or die "MIXER_WRITE";
    }
    # get mixer
    %MIXER = map { $SOUND_DEVICE_LABELS[$_] => {
	    id	=> $_,
	    label   => $SOUND_DEVICE_LABELS[$_],
	    name    => $SOUND_DEVICE_NAMES[$_],
	    exist   => 0,
	    stereo  => 0,
	    left    => 0,
	    right   => 0,
	} } 0 .. ($SOUND_MIXER_NRDEVICES-1);
    for (values %MIXER) {
	next unless $_->{exist} = ($devmask & (1 << $_->{id})) ? 1 : 0;
	ioctl $MIXER, $MIXER_READ | $_->{id}, my $val or die "MIXER_READ";
	$val = unpack "V", $val;
	$_->{left}  =  $val & 0x007f;
	$_->{right} = ($val & 0x7f00) >> 8
	    if $_->{stereo} = ($stereod & (1 << $_->{id})) ? 1 : 0;
    }
    close $MIXER or die "close: $!";
    return %MIXER;
}

my %m = Mixer();
# restore mixer from /etc/aumixrc
for (`/bin/cat /etc/aumixrc`) {
    my ($name, $left, $right) = split /:/;
    ($_->{left}, $_->{right}) = ($left, $right)
	for grep {$_->{name} eq $name} values %m;
}
Mixer(%m);

