Improve Volume Wheel Daemon (Bounty!)


It's the exact same volume wheel that the Pandora uses. So yeah, it's a poti with absolute values and an ADC.

We discussed that a long time ago - and users here disliked an encoder but wanted to have a classic volume wheel. So that you can turn the volume fully down without looking.

Also, encoders need steps. Set then to rough and users are missing steps in between. Set then too fine and you need to turn the wheel forever and ever to make bigger changes to the volume.
 
The CPU enjoys a long, lazy sleep until the encoder moves.
But does the encoder sleep? Its a slippery slope which chip does need your energy in the end.
Even multi-turn pots can only be rotated so many times in each direction.
In a simple form it usually is just a contact grinding over a resistance wire. On a wheel you can of course just overturn this contact from the end point to the start point again.
If you can actually buy this as an electronic part is another question because when used traditionally such poti would be horrible.
 
Suppose the volume daemon were written to only check the volume wheel when audio is playing.

How long would it take to notice a new audio playback stream, read the ADC, and set the volume? If it were fast - taking only a few tens of ms, you'd only get a faint click in the worst case.

In fact, that problem could be eliminated by muting audio when none is playing.

I haven't timed it, but my pandora does have a noticeable (and audible) process of firing-up the audio hardware. It isn't a problem.
 
It's the exact same volume wheel that the Pandora uses. So yeah, it's a poti with absolute values and an ADC.

We discussed that a long time ago - and users here disliked an encoder but wanted to have a classic volume wheel. So that you can turn the volume fully down without looking.

Also, encoders need steps. Set then to rough and users are missing steps in between. Set then too fine and you need to turn the wheel forever and ever to make bigger changes to the volume.

It's important to note that the poti has end stops (it can only turns some limited amount in each direction). An advantage is, at suspend/resume, or a reboot, the poti remembers its absolute position. And you can feel with your fingers when you've reached the minimum/maximum.

A disadvantage is, it is harder to use the volume wheel for some other function, e.g. hold Fn-key to change brightness. Because the wheel will be in a different place after you let go of the Fn-key.

I wonder what happens when the poti gets scratched - would the ADC values go very high or very low at the position of the scratch? Anyway, I suppose this won't be an issue in the first couple of years :)
Post automatically merged:

I wonder, as an alternative, could a PulseAudio plugin read the wheel position? Instead of some external daemon setting the PulseAudio or ALSA volume.

Maybe that way, when PulseAudio suspends audio playback, it stops polling too.

Another optimisation: define a dead zone so that very small movements (random noise) don't result in any volume change.

Also: if the volume wheel was already moved in the last 5 seconds (or the rate of change of position is high), poll faster. The rest of the time, poll slower?
 
  • Like
Reactions: rSl
I wonder, as an alternative, could a PulseAudio plugin read the wheel position? Instead of some external daemon setting the PulseAudio or ALSA volume.
Alternative? No this is exactly what it is about.I am looking into making a pulse connection already. But pulse is a bitch to deal with because its very poorly documented.
I am not entirely sure if that makes sense yet, you need to have a constant connection to the pulse server open and it needs to react on anything it does. How much overhead will that add?
It is in fact the very code that the volume icon runs anyway tho. One could just slam "read this file" into the icons code and we have all that.

Another optimisation: define a dead zone so that very small movements (random noise) don't result in any volume change.

Also: if the volume wheel was already moved in the last 5 seconds (or the rate of change of position is high), poll faster. The rest of the time, poll slower?
Could you ppl please read the thread before posting? The default code can already deal with noise and i implemented a variable polling time in my first test script and concluded it sucks.
 
It's difficult for me to follow this conversation.

If I understood correctly, it is impossible to turn down/up the volume before the Pyra is turned on, right ?
 
If I understood correctly, it is impossible to turn down/up the volume before the Pyra is turned on, right ?
In the current situation the daemon keeps it in sync at all times. And if its out there will be no sound. So no, you can set it just fine before turning it on.
 
If I understood correctly, it is impossible to turn down/up the volume before the Pyra is turned on, right ?

The physical wheel has an absolute, physical position. You can turn it down to minimum, and up to maximum, or somewhere in the middle, regardless of what the software is doing.

Where the Pandora and many older devices have the wheel directly wired into the audio circuit, the Pyra has it connected to the CPU. There is then a magic file you can read which contains the numerical position of the wheel. You then need a programme that reads said "file", and gives the value to the audio transport software to set the volume.

You could write a script that does that in a tight loop thousands of times a second from boot to shutdown. The question is how much cleverer and more efficient can we be about it.
 
How is control of the volume currently shared between GUI volume controls and the wheel?

What happens if you open Kmix/whatever, and move the master volume slider?
 
We have different volume sliders.
Alsa master, several alsa software channels, pulse sink and of course every application has its own pulse channel.
Currently the wheel changes an alsa software channel i think.
The volume icon changes the pulse sink. They wont collide but simply substract each other.

Question is, what do we want to control in the end? Alsa or pulse? And there, which channel?
For users i agree with ed here, the most practical solution is to have a fixed alsa state and control the rest via pulse. Then again pulse is a horrible piece of crap...
 
Its polling once a second, you should learn to read better.

Could you ppl please read the thread before posting? The default code can already deal with noise and i implemented a variable polling time [...]

Then please, next time, describe in your post what the code does! Don't assume we can all immediately see that from reading the code.

[...] and concluded it sucks.
Can you be more specific? It sucked because it was too slow to respond to changes? Because it made the code unnecessarily complex? Because it used too much CPU whilst moving the wheel? What?

I am looking into making a pulse connection already.

That's not what I was trying to suggest. The PulseAudio server has support for modules. These are started automatically when the PulseAudio daemon starts, and they stay running the whole time PulseAudio is running. See examples in /etc/pulse/default.pa
This is just a suggestion - I don't even like PulseAudio - and I have no problem with controlling the ALSA volume instead.
 
I don't understand why the code is consuming so many cycles. I guess iio_channel_attr_read_double() is inefficient somehow.
 
Don't assume we can all immediately see that from reading the code.
But thats exactly what i do. And your user pic is the debian logo, you should be able to read a simple while loop.
[...] and concluded it sucks.
The simple explanation was that a potential 1s lag between turning and result is inacceptable.
That's not what I was trying to suggest. The PulseAudio server has support for modules.
Brilliant idea, we dont need any code for it, we simply use a pulse module. (This is sarcasm, ok?)
I don't understand why the code is consuming so many cycles. I guess iio_channel_attr_read_double() is inefficient somehow.
Ive noticed that the cpu usage of my script seems to change depending on where the wheel is set. There might be a serious bug in how the output is scaled.
Its gone when i read from the unscaled raw output. But that doesnt mean the raw is bugfree.

But the big question is if this is really cpu usage. Cpu usage measuring is tricky to begin with, its more of an estimation.
There are a few different ways to calculate it, eg: get the percentage of duty time vs total time or free time vs duty time.
We also have a wait time. And this may be a possible explanation, wait time causes no cpu usage is however counted as duty time.
In other words the script may be completely idle but the waiting time till the io voltage readout comes in is wrongly counted as cpu usage.
Depending on which tool you ask of course...
If you are interested in this topic take a look at /proc/stat.
If one could decipher /proc/DAEMONPID/stat we could see for sure which of these two theories apply.
 
Note that i saw this wrongly counted wait time firsthand already when i wondered why some tool reported 100% cpu usage while i copied from a slow usb drive and it took me quite some time to find out why.
This is not a crude theory but something you really encounter in some tools in the wild.

Ah and btw, i just had a splendid idea, i think i found a way to not only automatically detect if sound is played without polling or binding to pulse, but also to detect which channel is used and automatically switch the daemon to control this.
I am just absolutely not sure if that works....
 
Last edited:
now i really wish i had a pyra..

a few things that can go wrong:

volume != pv always returning true. but you said you accounted for flickering so rule this out. could be due to int vs long.

usleep() busy spinning. google says it doesn't do this. is there a more efficient way to schedule? aside from an device interrupt of course.

that leaves iio_channel_attr_read_double() the main suspect.

i like the idea about dynamic sleep times. i would think normally it could be 1 second.

i suggest adding more printf's, timing iio_channel_attr_read_double() and printing when it calls snd_mixer_selem_set_playback_volume_all().
 
We have ruled all this things out already. The shown high usage is ONLY caused by the read. Which you can simply check by deleting the setting line from the code and end up with no cpu difference.
 
I still wonder if it would be better to use Pipewire instead of Pulseaudio.
If someone was willing to maintain an updated version to replace the Debian packages that would make sense. The ones in the main repo are way too old, the PulseAudio support was not really in a usable state at that point.
 
If someone was willing to maintain an updated version to replace the Debian packages that would make sense. The ones in the main repo are way too old, the PulseAudio support was not really in a usable state at that point.
Oh yeah, that sucks.
 
Back
Top