2008-11-01

Pulseaudio: controlling your volume needs

To get the most volume out of ones speakers (literally) one must first make sure that the underlying sound systems have their volumes at 100%. In my case pulseaudio runs on top of alsa and I had to run

alsamixer -c0

and maximize the needed outputs.

Moving on to PulseAudio I put these lines in my system.pa:

### Automatically restore the volume of streams and devices
load-module module-stream-restore
load-module module-device-restore

# Set some default volumes
load-module module-match table=/etc/pulse/match.table

I think it should be pretty self-explanatory.

Lastly, about the last line, I did not use module-match in my initial configuration, but having the ability to make a stream sound higher than the others made me add it. The basic problem here is that all streams get 100% volume at creation and to have a stream louder than the others forces one to lower all the other streams.

The solution is to use the module-match module, it reads its settings from a simple table formatted like this:

regexp volume
regexp volume
...

(Notice the space separator, this makes the regexps a bit harder to write)

regexp is a (I find out after digging in the source) Posix Extended Regular Expression
volume is the volume, the range is 0-65535 (= 0%-100%)

As I wanted all streams to start at 50% I made this table.

32767

(There's a space character before 32767)

Matching every stream and setting them to 50% (65536/2 - 1) the problem was solved.

I thought.

It turns out that my nicely remapped outputs (see my previous post) have their own remapped streams which of course also were turned to 50%.

As Posix Extended Regular Expressions does not, as far as I know, have any look-behind/ahead or don't-match-this-string functionality I figured that putting the rules in the right order would make my setup work. The documentation is very sparse so I did what one should not do: depend on the internals. The relevant loop is

for (r = u->rules; r; r = r->next) {
if (!regexec(&r->regex, n, 0, NULL, 0)) {
pa_cvolume cv;
pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
pa_sink_input_set_volume(si, &cv);
}
}

and as one can see the last rule wins. (I would have thought it would have been faster just terminating at the first hit, but what do I know ;))

This gives the final table:

32767
^Remapped.Stream$ 65535

(I used a dot between the words as using a space is not possible. It would probably have been more effective to use the hex code, or maybe [[:space:]])

Pulseaudio and speakers+headphones remapping

Just thought I should share my pulseaudio configuration. It allows me to move audio streams between my speakers and my headphones. The drawback is that I had to connect my headphones to the rear-speaker output, effectively stopping any surround usage. The perfect setup would have been if I could instead have used the audio output on the front of my computer (I know this should work as the Realtek utilitiy in Windows is able to do it).

For sanity I just share the relevant parts of my system.pa

#### I do this by hand for the remaps
# ### Automatically load driver modules depending on the hardware available
# .ifexists module-hal-detect.so
# load-module module-hal-detect
# .else
# ### Alternatively use the static hardware detection module (for systems that
# ### lack HAL support)
# load-module module-detect
#.endif

### Automatically suspend sinks/sources that become idle for too long
#load-module module-suspend-on-idle
#bug: http://pulseaudio.org/ticket/326


# Set the speakers as the default device
set-default-sink speakers

# Map device 0 to sound_card (this is my sound card) and use 4 channels
load-module module-alsa-sink sink_name=sound_card device_id=0 channels=4 channel_map=front-left,front-right,rear-left,rear-right

# Map the front channels to the speakers sink
load-module module-remap-sink sink_name=speakers master=sound_card channels=2 master_channel_map=front-left,front-right channel_map=front-left,front-right

# Map the rear channels to the headphones sink
load-module module-remap-sink sink_name=headphones master=sound_card channels=2 master_channel_map=rear-left,rear-right channel_map=front-left,front-right


And here's what I changed in daemon.conf:

;make channel-splitting work (may destroy mono to stereo on mono files)
disable-remixing = yes

default-sample-channels = 4



Unfortunately the sink names are not shown in PulseAudio Volume Control but some trial and error (choosing 1 out of to 2 :P) will tell you which sink is the speakers and which is the headphones.

2008-08-07

Dbus, knotify and pidgin

Threw together an ugly python script sendind pidgin notifications to knotify:

#!/usr/bin/env python

import re

html_tags = re.compile('<.*?>')

def my_func(account, sender, message, conversation, flags):
kn.event("receivedImMsg", "pidgin", [], '<b>' + sender + '</b>: ' + html_tags.sub('', message), [0,0,0,0], [], 0, dbus_interface="org.kde.KNotify")

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

kn = bus.get_object("org.kde.knotify", "/Notify")

bus.add_signal_receiver(my_func,
dbus_interface="im.pidgin.purple.PurpleInterface",
signal_name="ReceivedImMsg")

loop = gobject.MainLoop()
loop.run()

Combine with this file: $HOME/.kde/share/config/pidgin.notifyrc


[Event/receivedImMsg]
Action=Sound|Popup|Taskbar
Execute=
KTTS=
Logfile=
Sound=KDE-Im-Message-In.ogg

... and your're good to go ;)

Pidgin reference

2008-08-06

The story of dbus, knotify4 and erc

I'm using erc in Emacs as my irc client and by default there is no notification outside of Emacs. Trying to get something working in kde4 I first tried calling kdialog, it worked, but it wasn't a fair sight.

After deciding that the kdialog popup is too ugly I started looking for alternatives. The alternative I found where right in front of my eyes: The Notify plasmoid. The problem was that there's no easy way of calling knotify (like kdialog --passivepopup) but some googling turned up http://mvidner.blogspot.com/2008/06/knotify-client.html which describes how to patch knotify4 (the dbus signature is apparently erronous) and how to call it from python.

Sure enough, after patching knotify, the nicely skinned notices popped up, and the text were even customizable using html.

Starting from emacs 23 dbus support is built-in, and luckily I am running a cvs version. I started trying to make some dbus calls but I always got an error stating that the "event" method of knotify did not exist. After many hours and double-checking the parameters I tried it with perl, worked instantly. Now I was starting to lose hope, thinking that maybe Emacs dbus implementation didn't really work.

Just as I were beginning to give up, after staring myself blind on the function signatures, I tried the following, thinking it was really a long shot:

(dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify" "event" "warning" "kde" '(:array (:variant nil)) "message" '(:array :byte 0 :byte 0 :byte 0 :byte 0) '(:array) :int64 0)

And there it was (albeit a bit ugly)! My notification popup, called from Emacs!

But I wasn't finished yet, it still said "from kde" and it was apparently a "warning". Trying to change does values didn't bring up anything.. curios. Looking around my ~/.kde/share/config I found kopete.notifyrc. It was the key! Creating my own called emacs.notifyrc and entering


[Event/erc_nick]
Action=Sound|Popup|Taskbar
Execute=
KTTS=
Logfile=
Sound=KDE-Im-Message-In.ogg


I could call "erc_nick" and "emacs" and now I had my "own" notification. It even got the Emacs icon!

Finally I added this code to my personal erc file and I hade working erc notification:




(defun thomasa88-erc-knotify4 (type message) 
(dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify"
"event"
type "emacs" '(:array (:variant nil)) message '(:array :byte 0 :byte 0 :byte 0 :byte 0) '(:array) :int64 0)

)

;;test (dbus-call-method :session "org.kde.knotify" "/Notify" "org.kde.KNotify" "event" "erc_nick" "emacs" '(:array (:variant nil)) "message" '(:array :byte 0 :byte 0 :byte 0 :byte 0) '(:array) :int64 0)

(defun thomasa88-erc-notify-match (type from rawmsg)
(let ((erc-resp (get-text-property 0 'erc-parsed rawmsg)))
(when (and (or (eq type 'current-nick) (eq type 'keyword))
(equal (erc-response.command erc-resp) "PRIVMSG"))
(thomasa88-erc-notify erc-resp nil))))

(defun thomasa88-erc-notify-PRIVMSG (proc erc-resp)
(thomasa88-erc-notify erc-resp t))

(defun thomasa88-erc-notify (erc-resp check-privmsg)
(let ((target (first (erc-response.command-args erc-resp)))
(from-nick (first (erc-parse-user (erc-response.sender erc-resp))))
(message (erc-response.contents erc-resp)))
(when (or (not check-privmsg)
(and (erc-current-nick-p target)
(not (erc-is-message-ctcp-and-not-action-p message))))
(thomasa88-erc-knotify4 "erc_nick"
(concat target " &lt;" from-nick "&gt; " message)))))

(add-hook 'erc-text-matched-hook 'thomasa88-erc-notify-match)
(add-hook 'erc-server-PRIVMSG-functions 'thomasa88-erc-notify-PRIVMSG t)



edit: Current svn knotify has a correct signature