Is there a fancy vertical notification OSD that works for both ALSA and pulseaudio?

Is there a fancy way to get the volume notification OSD to work with both pulseaudio and ALSA? Right now the standard desktop ones only work with pulseaudio for me. How about a vertical OSD that I can use as a drop in replacement or call from the command line to report changes in arbitrary percentages graphically, as a bar that moves up and down?

The reason why I need it to work with both ALSA and pulseaudio is that I'm using a WINE application which doesn't play well with pulse, so I kill pulse before starting up the Windows app to use ALSA without the extra abstraction layer. When I realized that the volume keys on my keyboard didn't work without pulse, I wrote some bash scripts that I call with either Compiz or Openbox (configured through CCSM and lxde-rc.xml, respectively) to catch the exit signal from pulseaudio --check and then adjust the volume accordingly:

vol_step_up

#!/bin/bash
pulseaudio --check
if [ $? -eq 0 ] ; then
        pactl set-sink-volume 0 -- +3db
    else
        amixer -c0 set Master playback 3+
fi

vol_step_down

#!/bin/bash
pulseaudio --check
if [ $? -eq 0 ] ; then
        pactl set-sink-volume 0 -- -3db
    else
        amixer -c0 set Master playback 3-
fi

The scripts work great and map to the buttons just fine, but I don't have a good way of seeing the visual feedback anymore--not even with the pulseaudio ones since I' m catching the button events (XF86AudioLowerVolume, etc.). Obviously I could just map the ALSA volume keys to something else, but there's no sense in duplicating shortcut keys.

I did find a python volume control that I can call in the scripts above:
https://github.com/fishman/utils/blob/master/pvol.py

pvol.py -s shows the current volume level on screen for both ALSA and pulseaudio, but it's awfully tiny compared to the gnome OSD I had been using, and it isn't vertical (bar on top, old OSD at bottom):

Size comparison of Standard OSD and pvol.py

So, I made it bigger and flopped it around:

enter image description here

But, even with switching the orientation to a vertical one, the blue default GTK theme isn't quite as slick as VLC (see below).

Much of what I've found in searching for OSD implementations are posts about notify-send commands which lack the whole progress bar concept. Otherwise, it's mostly horizontal bars (and lots of counting placeholders within bash scripts). Really all I need to do is call amix & pactl, so something simple like the gtk progress bar in pvol.py would be great--just not so blue and not right in the middle of the screen.

VLC has a good example of what I have in mind when you scroll the mouse wheel in full-screen mode:

VLC Vertical Volume Bar

It's a lot less obstructive than the usual boxes that sit in the center of the screen:

Horizontal OSD Volume Notification

The whole horizontal slider analogy has never made much sense to me outside of panning audio between the left and right speakers.

Anyway, how is it that the default desktop notifications get called (especially LXDE)? I see a lot of posts about configuring key press events, but not much on what scripts those events trigger. What other options are out there in the vertically fancy department?

Also, is there some package I should uninstall to prevent conflicts from springing up between the events I'm handling through scripting and compiz or openbox commands?

Update: For the sake of figuring out what OSD I'm currently using, I didn't change the way I handle the mute button right away. Killing xfce4-notifyd and then pressing the mute button spawns a new xfce4-notifyd process, so I was guessing the big speaker icon came from something like xfce4-volumed, but I don't actually have that package installed...Ah ha! Killing gnome-settings-daemon stops the big OSD in the center of the screen.


Solution 1:

Alright, at the risk of answering my own question, I came up with a bit of a hacked together pyqt version of pvol from the link in the question above. If nothing else, maybe someone else can improve on my code. Eventually, I plan to either get rid of the parts in the script below which go unused or to take the bash scripts out of the equation and have one pyqt script handle all of the button events. Right now, the OSD times out at a constant rate from the first button press instead of staying on for a fixed amount of time after the last button press.

Just copy, paste and save the files (with the names in bold), put them all in the same directory, set the executable bits, and modify the system calls in the pyqt script according to wherever you save them, or put them all in directory that's in your path. Then map the shell scripts to Compiz commands, Openbox shortcuts, or something similar, and change the pyqt script if you're not using multimedia keyboard volume buttons.

Note: The class name Qvol was a working title, and I didn't bother to change it. Please also note that the mute button goes unhandled--This is just a prototype to express a possible avenue for fulfilling the requested features, and it is not currently associated with any kind of hosted project or standard development model. Any kind of significant development derived from the code below should probably belong on Sourceforge, GitHub or a project website. That said, feel free to edit this answer or to suggest an existing project which allows is similar in function and design.

pqvol

vol_step_down

#!/bin/bash
pulseaudio --check
#if [ $? -ne 0 ] ; then
if [ $? -eq 0 ] ; then
        pactl set-sink-volume 0 -- -3db
    else
        amixer -c0 set Master playback 3-
fi

if [ -z "$1" ] ; then
        pqvol -s
fi

vol_step_up

#!/bin/bash
pulseaudio --check
#if [ $? -ne 0 ] ; then
if [ $? -eq 0 ] ; then
        pactl set-sink-volume 0 -- +3db
    else
        amixer -c0 set Master playback 3+
fi

if [ -z "$1" ] ; then
    pqvol -s
fi

pqvol

#!/usr/bin/env python2

# pvol -- Commandline audio volume utility
#         with an optional GTK progressbar
# Copyright (C) 2009 Adrian C. <anrxc_sysphere_org>
# Modified by 2011 Reza Jelveh
# Ported to pyqt and renamed to pqvol 2013 by Adam R.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.


import os.path
import optparse
import alsaaudio
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QTimer

appname = "Qvol"
#appicon = "/usr/share/icons/ubuntu-mono-light/status/24/audio-volume-high-panel.svg"

DEFAULT_STYLE = """
QProgressBar{
    border: 2px solid grey;
    border-radius: 5px;
    background-color: transparent;
}

QProgressBar::chunk {
    background-color: Gainsboro;
}
"""

class AlsaMixer():
    def __init__(self, pcm=False, mute=False, arg=None):
        self.mixer = alsaaudio.Mixer()
        self.percent = self.mixer.getvolume()[0]
        print self.percent
        self.label = "dB" #% name
        if arg:
            self.percent = min(100, max(0, self.percent + int(arg)))
            self.mixer.setvolume(self.percent)
        if mute:
            mutestate = self.mixer.getmute()[0]
            if mutestate:
                self.label = "Unmuted: "
            else:
                self.label = "Muted: "

            self.mixer.setmute(mutestate^1)
 #     self.label = self.label + "%.0f%%" % self.percent

class Qvol(QtGui.QWidget):

    def __init__(self):
        super(Qvol, self).__init__()
#       self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setWindowFlags(QtCore.Qt.Popup)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        self.setWindowTitle("Qvol")
        self.initUI()

    def initUI(self):     

        self.pbar = QtGui.QProgressBar(self)
        self.pbar.setGeometry(5, 5, 20, 470)
        self.pbar.setOrientation(QtCore.Qt.Vertical)
        self.pbar.setRange(0,100)
        volume = AlsaMixer()
        self.pbar.setValue(volume.percent)
        self.pbar.setTextVisible(False)
        self.setStyleSheet(DEFAULT_STYLE)

        self.setGeometry(1260, 180, 30, 480)
        self.setWindowTitle('QtGui.QProgressBar')
        self.show()


        QTimer.singleShot(2000, finished)

    def keyPressEvent(self, event):
        if event.key()==QtCore.Qt.Key_VolumeMute:
#           QtGui.QWidget.paintEvent()
            finished()
        elif event.key()==QtCore.Qt.Key_VolumeDown:
            launch_process ("vol_step_down silent")
            volume=AlsaMixer()
            self.pbar.setValue(volume.percent)
#           finished()
        elif event.key()==QtCore.Qt.Key_VolumeUp:
            launch_process ("vol_step_up silent")
            volume=AlsaMixer()
            self.pbar.setValue(volume.percent)
#           finished()

#       else:
#           QtGui.QWidget.keyPressEvent(self, event)


processes = set([])

def launch_process(process):
    # Do something asynchronously
    proc = QtCore.QProcess()
    processes.add(proc)
    proc.start(process)
    proc.waitForFinished(-1)

def finished():
    print "The process is done!"
    # Quit the app
    QtCore.QCoreApplication.instance().quit()


def main():

    app = QtGui.QApplication(sys.argv)
    ex = Qvol()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()