5
votes

I recently got into XMonad and unforunately I don't know much Haskell at all. I'm trying to configure my xmonad.hs file such that I can control the volume. However, right now, even though my xmonad.hs file compiles without errors, I cannot control the volume.

I got the volume control code from this link:http://dmwit.com/volume/

Here is my configuration file:

import XMonad
import XMonad.Util.Run(spawnPipe)
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.ManageDocks
import XMonad.Actions.Volume
import XMonad.Util.Dzen
import Data.Map (fromList)
import Data.Monoid (mappend)
import System.IO

alert = dzenConfig centered . show . round
centered =
        onCurr (center 150 66)
    >=> font "-*-helvetica-*-r-*-*-64-*-*-*-*-*-*-*"
    >=> addArgs ["-fg", "#80c0ff"]
    >=> addArgs ["-bg", "#000040"]

main = do 
        xmproc <- spawnPipe "xmobar /home/david/.xmobarrc"
        xmonad $ defaultConfig
                {
                        manageHook = manageDocks <+> manageHook defaultConfig
                        , layoutHook = avoidStruts  $  layoutHook defaultConfig
                        , logHook = dynamicLogWithPP xmobarPP
                                { ppOutput = hPutStrLn xmproc
                                , ppTitle = xmobarColor "green" "" . shorten 50
                                }
                        , modMask = mod4Mask 
                        , keys =
                            keys defaultConfig `mappend`
                            \c -> fromList [
                                ((0, xK_F6), lowerVolume 4 >>= alert),
                                ((0, xK_F7), raiseVolume 4 >>= alert)
                            ]
                }

I have changed my code the following, however the volume has not changed:

main = do 
    xmproc <- spawnPipe "xmobar /home/luren/.xmobarrc"
    xmonad $ defaultConfig
            {
                    manageHook = manageDocks <+> manageHook defaultConfig
                    , layoutHook = avoidStruts  $  layoutHook defaultConfig
                    , logHook = dynamicLogWithPP xmobarPP
                            { ppOutput = hPutStrLn xmproc
                            , ppTitle = xmobarColor "green" "" . shorten 50
                            }
                    , modMask = mod4Mask 
                    , keys =
                        keys defaultConfig `mappend`
                        \c -> fromList [
                            ((0, 0x1008FF11), spawn "amixer -D pulse sset Master 4-"),
                            ((0, 0x1008FF13), spawn "amixer -D pulse sset Master 4+")
                        ]
            }

(END)

Those keys are the codes for the volume media keys on my laptop.

I snooped around and saw some other configurations. I decided to do a little test and make the audio lower volume key print Hi ppl! when pressed. Unfortunately, this does not work.

main = do 
    xmproc <- spawnPipe "xmobar /home/luren/.xmobarrc"
    xmonad $ defaultConfig
            {
                    manageHook = manageDocks <+> manageHook defaultConfig
                    , layoutHook = avoidStruts  $  layoutHook defaultConfig
                    , logHook = dynamicLogWithPP xmobarPP
                            { ppOutput = hPutStrLn xmproc
                            , ppTitle = xmobarColor "green" "" . shorten 50
                            }
                    , modMask = mod4Mask
            }
            `additionalKeys`
            [
                    ((0, xF86XK_AudioLowerVolume), spawn "echo 'Hi ppl!'"),
                    ((0, xF86XK_AudioRaiseVolume), spawn "amixer -D pulse sset Master 15%+")
            ]
2
Yikes, it's been ages since I've written that page! I wouldn't be surprised if the output of amixer has changed in an incompatible way since then. (I've moved from ALSA to pulse.) But, to check: if you bind some key to getVolume >>= alert, does it show the correct answer? Does changing volumes on the command line with amixer change your volume? What channel in alsamixer (if any) actually controls your volume correctly?Daniel Wagner
Oh wow! You actually wrote that page! I binded a key as you said, and unfortunately nothing shows up. I apologize if for me being a newbie at this, but I don't understand what alsa or pulse is. I know that if I use the following command in the terminal, amixer -D pulse sset Master 15%, I can set the volume. As for channel in alsamixer, I'm not sure what that means. I ran alsamixer which led me to three s/pdif. I think that stands for Sony/Philips digital interface.mrQWERTY
Ah, unfortunately, that module doesn't support controlling a specific device (and doesn't have a good way to munge the amixer command line by hand). You have a couple choices: copy the code from the module and fix it (the work of 10 seconds, but not usable by others); patch xmonad-extras to add this functionality (a bit more work, but I will happily include your patch, and others will be able to benefit); use pacmd instead of touching amixer at all (much more work if you want an on-screen volume display after you change it). Which do you want to hear more about?Daniel Wagner
Hmm, sorry for the delay. Its possible to directly call amixer from the script right? Unfortunately, I've just started learning Haskell in my class coincidentally. So fixing and then patching something is probably out of my league at the moment.mrQWERTY
Changing the module to include -D pulse in the amixer command line should be very, very easy -- just copy the module and look for all instances of amixer and change them. You can call amixer directly if you like; but then you might as well call pacmd, since presumably amixer -D pulse means you are using pulseaudio. I'm happy to talk about either approach.Daniel Wagner

2 Answers

4
votes

Here is a script that I wrote to control pulse audio volume. Dump it in a file named pulse_control.pl and make sure it is executable and in your PATH. It uses pacmd and pactl which you may have to install with your distro's package manager.

#!/usr/bin/perl

use List::Util qw[min max];
use Getopt::Long;

my $_IS_MUTED;
my $_TOGGLE_MUTE;
my $_VOL;
my $_INC_VOL;
my $_DEC_VOL;

GetOptions(
    "is-muted"    => \$_IS_MUTED
,   "toggle-mute" => \$_TOGGLE_MUTE
,   "volume"      => \$_VOL
,   "inc-vol"     => \$_INC_VOL
,   "dec-vol"     => \$_DEC_VOL
);

my $sink = `pactl info | sed -rn 's/^Default Sink: (.+)\$/\\1/gp'`;
chomp $sink;

sub ismuted {
    my $ismuted = `pacmd dump | grep "$sink" | grep sink-mute | grep yes`;
    chomp $ismuted;
    if ($ismuted ne "") {
        return 1;
    } else {
        0;
    }
}

sub curvol {
    my $volline = `pacmd dump | grep "$sink" | grep sink-vol`;
    if ($volline =~ m/(0x[0-9a-f]+)/ ) {
        return hex $1;
    } else {
        return 0;
    }
}

if ($_IS_MUTED) {
    print ismuted(), "\n";
}
elsif($_TOGGLE_MUTE) {
    if (ismuted()) {
        `pactl set-sink-mute $sink 0`;
    } else {
        `pactl set-sink-mute $sink 1`;
    }
}
elsif($_VOL) {
    print int(curvol() * 100.0 / hex("0x10000")), "%\n";
}
elsif($_INC_VOL) {
    my $newvol = min(hex("0x10000"), curvol() + int((hex("0x10000") * .04)));
    `pactl set-sink-volume $sink $newvol`;
}
elsif($_DEC_VOL) {
    my $newvol = max(0, curvol() - int((hex("0x10000") * .04)));
    `pactl set-sink-volume $sink $newvol`;
}

Then in my xmonad config, I have volume control bound to ctrl+alt+(page up/page down/end).

`additionalKeys`
[
    ((controlMask .|. mod1Mask, xK_Page_Up), spawn "pulse_control.pl -inc")
,   ((controlMask .|. mod1Mask, xK_Page_Down), spawn "pulse_control.pl -dec")
,   ((controlMask .|. mod1Mask, xK_End), spawn "pulse_control.pl -toggle")
]
0
votes

This can also be achieved using the following spawn:

      [ ((0, 0x1008FF11), spawn "amixer -q sset Master 2%-"),
        ((0, 0x1008FF13), spawn "amixer -q sset Master 2%+"),
        ((0, 0x1008FF12), spawn "amixer set Master toggle")]

These are multimedia keys but you could also use x86Vol binds instead of hex's.