Saturday, November 3, 2012

Multimedia keys and osd volume bars on Linux with Alsa and bare-metal X-Window

Update: on recent distros, some dunce obviously decided we didn't need /dev/dsp etc any more but failed to realize that would break several packages, including but not limited to aumix, so on a recent Ubuntu the volume bars WILL NOT WORK. Sad to say, the mute command is also affected. Temporary fix is described in this post. Stand by for a more permanent fix, but I won't apologize for the inconvenience. If you want an apology, find said dunce and make him say sorry.

Up until recently, the multimedia keys on my big, (once) shiny, expensive Logitech G15 keyboard were mostly laying waste - I simply never saw any need to get them working. Usually I do those things only when something breaks, but alas, I had a whim. So here's the story of what I did.

If you read my previous posts, you know already that I like to get down to the bare metal with my designs. That holds true not only in my tinkering but also in my coding and in my sysadmining. The reason's quite simple: The deeper down I clamp my works onto the things I want to affect, the less other stuff I depend on, and the less stuff I depend on the less sources of failure I have to worry about. Of course there's more: the less layers I have in between my designs and the stuff I actually try to control/affect/do, the less things I have to pay attention to to get stuff "just right", and the less places I have to worry about to have their own ideas about what I might want to do. That's probably why my minimalistic, bare-metal X-Window setup works so well for me. Call me fickle, but that's how I like it, and it works well for me.

What I didn't want to do is bring one of the Great Big Frameworks into things - you know my views on those. But, as I wrote elsewhere, the tools used in the good old times are still around, they just lay dormant under thick layers of framework-glaciers, waiting their time to reemerge and take the world back from them. OK, enough quoting Pratchett and poetry, back to the subject at hand, which is, all you have to do is look for them and unearth them.

First order of business: get the Multimedia keys to work in principle. 

Some googling scared up this article on xbindkeys (which isn't that old after all): xbindkeys is a very nice tool that can bind totally arbitrary key combinations to equally arbitrary commands. In other words: it will do what you tell it to and leave the thinking to you, just the way I like it. Also it comes with an equally nice graphical configuration tool named xbindkeys-config, which allows you to configure key combos...

Hold it, grasshopper, not so fast, ... Oh well. He'll be back.

Where was I? Oh yes: lets you define function keys without the hassle of hand-editing the config file. Now I don't have a problem with that, but I know a lot of you guys like your point-and-click interface. By the way, did I mention that you can also bind mouse button clicks and key-click-combos? Very versatile that tool. That thing is SO going into my work notebook where I use nautilus, because on a notebook evilwm is a bitch to use. Nautilus is a really nice WM once you get to know it, and it can do a lot more than you usually see of it in the Gnome environment. Note to self: write an article about using nautilus bare-metal.

Back so soon, grasshopper? No, xbindkeys-config isn't broken, YOU caused that segfault. Now will you read the rest of the article? VERY good of you, grasshopper.

OK, let's go hands-on, before somebody else gets his fingers where (s)he shouldn't, shall we?
First order of business with xbindkeys is to init the configuration: Run

xbindkeys --defaults >>~/.xbindkeysrc

to create the .xbindkeysrc file, without which xbindkeys-config will indeed segfault. No, that's not a bad bug, because it TELLS you you have to init .xbindkeysrc before conking out. Never read your console output, did you grasshopper? Serves you right.

Then, and ONLY then does it make sense to run xbindkeys-config. Mind you, from there on out it is pretty much smooth sailing downhill all the way. Does TOO make sense, there is such a thing as land-sailing, where you CAN sail downhill. Here's a nice screenshot for you:

The first three items are examples I left in since they don't bother me. You should have them too, so have a look at them and enjoy. The other three are what I have so far.

Here's the work flow: Click "new" below the list, click "Get Key", type in the desired key combination (special keys like media keys, etc work fine) and specify the action, i.e. the command you would like to have run upon that key press.
One noteworthy limitation there: you can run one command and one command only. bash-style command lists do NOT work. Note how I have scripts in there? More about that later, but that's the reason.
Last but not least, either click "Save Apply & Exit" or just click apply and try it out. It may or may not work, if it doesn't re-start xbindkeys and it will, apparently there is some glitch in the re-loading of .xbindkeysrc or the triggering thereof.

Second order of business: Do something with the keys configured. 

Mute was easy as you can see in the screenshot above. Alsa brings a nice little tool named - nomen est omen - mute that will either mute or unmute sound with every call of it. That being taken care of, I had to find a way to actually change the settings of the volume without resorting to graphical tools and stuff. I never did that before, never had to. Again, google is your friend. I found this here page: which started me off into the right direction, which is amixer. Simple tool from the alsa suite that lets you get and set stuff in the alsa framework from the command line. There are more, more about another one below. But one thing at a time, OK?

amixer is an amazing tool full of possibilities that can do stuff you probably never dreamt of if you never used anything but those simple-minded graphical mixers. I'll probably write a whole article on alsa and amixer and it's friends some day, but for the scope of this article, it is completely sufficient to go into what I did indeed use here, if you're Linux savvy you can probably hack your way from there.

So here's the command I had in the command field initially (now coming to you from the script):

amixer sset Master,0 3%+

Let me break that down for you a bit.

sset means "set simple control" which implies correctly that there are non-simple controls too, which would be set with "set". Equally, simple controls are gotten with "sget" whereas non-simple controls are gotten with "get" and so on. As usual, have a peek at the man page.

Master,0 is the control I'm planning to affect, the master volume.

3%+ means "up by three percent". The script uses the exact same command except it's 3%- there - you're smart, you get the pattern. amixer is smart too, it can do absolute and relative percent values, relative dB values and absolute and relative values relating to the limits of the control(which you can find out with a "sget" call):

user@machine:~$ amixer sget Master,0
Simple mixer control 'Master',0
   Capabilities: pvolume pvolume-joined pswitch pswitch-joined penum
   Playback channels: Mono
   Limits: Playback 0 - 64
   Mono: Playback 37 [58%] [-27.00dB] [on]

Last but not least: make it comfy, make it pretty

Now with that, I could control my volume just fine. But you know me: I wanted more. I always want more. I never settle for "good enough". I always... Oh, let's just quote George Bernhard Shaw:

"Many people strife for the extraordinary and never get past the ordinary. I prefer to strife for perfection and never get past the extraordinary"

There you have it. Never could have said it as well as old George. Then he probably couldn't program his way out of a wet cardboard box. To each his own.

Again, enough poetry, back to the hard facts.  What I wanted was a visual feedback of the volume setting. I've often fiddled with xosd, e.g. since I don't use a panel (evilwm isn't panel-aware) I use osd_clock instead. Bare metal again, right there. Now xosd does those pretty percentage bars. Why not use one of them? Sure thing, let's do it!

Now, as a little excursion, this is where the scripts came in, since, as I wrote above, xbindkeys does not do more than one command per key entry. That serves me fine, I like keeping my stuff in little packets, makes it easier to modify. There's your basic modular concept. That's why I always keep a .scripts folder in my home directory where I throw all the little scripts and snippets I code to be Santa's little helpers and do the background work for me. Let's look at one of those, shall we? Here's the listing of for you:

amixer sset Master,0 3%+
killall -q osd_cat
osd_cat -i20 -o5 -b percentage -P$(aumix -q|grep vol|cut -d' ' -f3) \
-T "Master Left" &
osd_cat -i20 -o35 -b percentage -P$(aumix -q|grep vol|cut -d' ' -f2|cut \
-d',' -f1) -T "Master Right"

Here's a nice little snapshot of that script in action:
OK, let's break it down. The first two lines I'll just skip here. The third line I'll explain, but a little later.

The main work is done by the two osd_cat calls. I won't go into the details of osd_cat here, it's all in the man page, just a few quick hints:
-i and -o set the position
-P takes the percentage of the bar requested with -b.
 Note also that the first of the osd_cat calls is sent to background, else they would show one after the other.

The interesting stuff happens inside the parentheses. Now why use aumix here instead of amixer? Simple: aumix, while being a much simpler tool than amixer, gives me separate values for left and right channel with a simple call, whereas I would have to go deep into the channels with amixer.

user@machine:~$ aumix -q
vol 59, 59
line 0, 0
mic 0, 0
cd 90, 90
pcm2 100, 100
igain 100, 100
dig1 100, 100, P

Right tool for the right job, remember? You CAN loosen a bolt with a pair of pliers, but a wrench will work much better.

The rest of the command is just to cut out the two numbers.

That leaves that peculiar killall osd_cat to be explained. Look at this page(German) I found some time later:
Just to tell you right now: what they're doing there won't work for xbindkeys for a simple reason: You can't update a percentage bar created by xosd. Each time that script is called, it creates a new instance of osd_cat - which overlap. So if you press that key five times in a row, you get a garbled percentage bar. Allow me to demonstrate:

If I run this:
osd_cat -i20 -o20 -bpercentage -P60&  osd_cat -i20 -o20 -bpercentage -P50 --colour=Green& \
osd_cat -i20 -o40 -bpercentage -P60&  osd_cat -i20 -o40 -bpercentage -P50

I get this view on screen:
I did it twice, to show you the effect better. Above I used a different color for the later-created bar to make the effect clearer, below is how it would look in the real world if you bound that script from the archlinux wiki to a volume key and pressed that twice.

That's where that killall command comes in. What it does is, it clears all previously existing percentage bars from the screen, allowing those created right after to replace them instead of overlapping them. To tell the truth, that killall command could probably be written a little more specific so it won't kill all osd_cat instances but only those associated with the script. I'll probably re-do that, latest when I find another use for osd_cat which clashes with what I have now.

Guess that about sums it up for today. Now that I've taught you  how to make key combinations execute commands and what key commands to use to control alsa, and as a little bonus how to work those nice percentage bars of xosd's, go out there and knock em dead!