Phonon + Volume Changes and feedback loops

Colin Guthrie gmane at
Sat Mar 27 21:43:25 GMT 2010


I'm working on integrating proper per-application volume controls
provided by PulseAudio into Phonon.

So far the work is going well and I've got things working quite nicely.
I can now use KMix to change Amarok's volume and have this change
reflected in the Amarok GUI. It all works rather nicely.

However there is a major problem and that is one of feedback loops.

In both Dragon (which uses the Phonon VolumeSlider widget) and Amarok
(which rolls it's own volume widget) there is a bit of a problem when PA
indicates a volume changes and when a change is triggered from with in
the app itself. The two problems are different but related.

To explain:

If PA indicates a change (e.g. because KMix was used), then this will
come to the application (Dragon, Amarok) via a volumeChanged signal.
This will then be used to update a widget by calling e.g. setValue(). In
both applications the widget's setValue signal is is connected to the
engines setVolume slot.

This means that a change from KMix will:
 1. Set the volume to X.
 2. Phonon application updates GUI to reflect X
 3. Phonon application sets volume to GUI Value (which ~= X).
 4. If GUI Value != X, Goto 2.

Now this is fine and dandy, but if X is not a perfect match, this loop
can go on forever until the upper or lower bounds are hit.

This problem exists in both Amarok's and Phonon's own Volume GUI widgets.

The second problem is one of asynchronisity. In the current Phonon
VolumeSlider, there is protection against a feedback loop in that when
the volume is set from the widget, it set's an "ignoreVolumeChange" flag
to true before calling the output's setVolume method and resets it
afterwards. This flag is checked in the volumeChanged slot and the
change is (obviously) ignored if the flag is set. In order for this to
work correctly the setVolume call must be synchronous. With PulseAudio,
all calls are asynchronous. This means that we request a volume change,
and some time later we get information about that change. The
information about the change comes in in the same way as if an external
application (e.g. kmix) initiated the change. Therefore, this flag is
simply not sufficient to deal with an async setVolume.

The question is how do I deal with both situations? I could make the
setVolume() to PA synchronous by letting the mainloop run while I wait
for the response to come in, but this would only solve the second
problem and could also result in some interesting quirks anyway. I don't
really like this idea.

Rather than a simple ignoreVolumeChange flag, I could implement an
"ignoreVolumeCounter" and incrmeent it when a change is made and
decrement it when a change is observed. only when the counter is 0
should an observed event result in a GUI change. This would solve the
problem of async volume changes although there is a small chance that
the wrong volume changes are ignored. e.g. a GUI is used to make two
volume changes before either one is observed. In between these two
changes an other application also changes the volume externally to the
GUI in question. This volume change *should* be observed, but because
our counter is 2 it will be ignored and the second GUI change will
actually be the one that is noticed. As it technically happened latest
in time, it is perhaps desirable that this is the one that is noticed?
On reflection I think this is OK.

BUT, this only solved half the problem! It solves a feedback loop that
starts at the GUI and comes full circle around an async change. If the
change is started externally and begins with an observed volume change
(as described above in the 4 point sequence), the feedback loop still
exists. In theory I need two counters, one as described above that
ignores feedback from a volume changes initiated by the GUI and a second
that allows a GUI update to suppress the request for a volume change.

I'm not sure I've described this very well, but it is rather
complicated. If anyone has any insights on this, I'm all ears. Otherwise
I'll try and implement this with the phonon VolumeSlider first and test
that it works and then try and make the Amarok one work in the same way.



Colin Guthrie

Day Job:
  Tribalogic Limited []
Open Source:
  Mandriva Linux Contributor []
  PulseAudio Hacker []
  Trac Hacker []

More information about the kde-multimedia mailing list