PeppyMeter

I need to clarify if event context will be the same in SDL running in ALSA plugin and
SDL used by Python. If not then named pipe is another option. In this case you can have
full control on pipe and can make it non-blocking.

I'm still not sure how that issue with different signal format was resolved in those meters.
Probably the API which you mentioned does that.
 
I decided to go named pipe route. In this case the solution will be universal -
it will work for any language. Also I will need to make not so many changes
in my code as it works with pipes right now.

So I'll create additional output device for 'pivumeter'. It will just output
VU meter signal to a named pipe. In this sense it will be similar to the
'file' ALSA plugin but it will overcome such issues as blocking issue and
different signal format issue.

Also I hope to add such configuration parameters to .asoundrc for that
output device: rate - how often data should be updated in fifo, max
value - define maximum signal value.

As data in pipe is like a stream there is the need to distinguish between left
and right channels. The only way I can think of is some separator for example
-1 which will be sent before left/right channels or combine channels and send
32 bits.

I started playing with C code. Here is the named pipe writer program
which overcomes the issues with no readers at startup and disconnecting
readers. It can be compiled as gcc test.c -o test, started as ./test and the reader
can be started in another terminal, for example 'cat myfifo'.

Code:
 #include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

int main() {
    // ignore signal when reader disconnected
    sigaction(SIGPIPE, &(struct sigaction){SIG_IGN}, NULL);
    
    char *mypipe = "/home/pi/myfifo";
    int fifo = -1;
    int n = 22;     
    mkfifo(mypipe, 0666); // create fifo if doesn't exist
    
    while(1) {
        if(fifo == -1) {
            fifo = open(mypipe, O_WRONLY | O_NONBLOCK);
            if(fifo == -1) { // no readers at startup
                printf("no readers connected\n");
            } else {
                printf("created fifo:%d\n", fifo);
            }            
        } else {        
            if(write(fifo, &n, sizeof(n)) == -1) {
                printf("no readers connected\n");
            } else {
                printf("write successful\n");
            }
        }        
        usleep(500000);
    }    
    close(fifo);
}
 
Last edited:
Hopefully that should also make it more efficient because the current Python implementation works with PCM data directly and that's pretty large amount of data and processing that data in Python takes a lot of CPU cycles. As I mentioned already GUI for UI Meter should be updated using the same rate as display update rate. That's usually 60-100 Hz which is almost 1000 times less than the regular PCM rate 44100 Hz.
 
I'm not sure I got your question. If the volume value doesn't change there is no need to update UI. But to define if it was changed or not you need to recalculate.

BTW, as far as I understand PCM signal changes from -32767 to +32767 for 16-bit signal.
But I've never seen in any code that negative values are taken into account. Usually the level value is just divided by 32767. But that can be negative.
 
I'm not sure I got your question. If the volume value doesn't change there is no need to update UI. But to define if it was changed or not you need to recalculate.

I see, every incoming sample must be checked. But cannot you re-calculate some moving average from samples stored in some array only every e.g. 50ms?

BTW, as far as I understand PCM signal changes from -32767 to +32767 for 16-bit signal.

I think it depends on representation. Perhaps they are using unsigned int16? Zero would correspond to middle of the scale (32767isch). I have seen that being used in some audio calculations.
 
That's right, the value which will be sent to the fifo will be either average calculated from several previous values or just current value. That can be configured in .asoundrc file for that output device. Another property in that file can define how often fifo should be updated. That will simplify the Python reader side. In this case there is no need in any calculations there.

As for negative/positive stuff, here is code snippet from that 'pivumeter' plugin, they use signed int and ignore the sign:

Code:
int bar = (meter_level / 32767.0f) * (brightness * (NUM_PIXELS/2));
int offset = meter * 8;

if(bar < 0) {bar = 0;}
if(bar > (brightness*(NUM_PIXELS/2))) {bar = (brightness*(NUM_PIXELS/2));}
Probably only positive amplitude should be taken into account.
 
Last edited:
So, each negative volume level will be converted into positive. Hmm, is that correct
from the common sense point of view? If one sample was -123 and another +123
according to that logic there is no difference between them. How that can be translated
to the speaker cone movement? Does it move towards the listener when value is positive
and backwards when negative?
 
If you consider it as a sine waveform you can say that maximum amplitude is 32K (either positive or negative). Though the range is 64K. My confusion was that we need to show the whole range 64K. For that we could add to each sample 32K to move waveform to the positive half. In this case -32K becomes minimum volume level -> 0 and +32K becomes maximum level -> 64K. But in this case the silence (no sound) will be shown in VU Meter as the middle level :)
 
Last edited:
If the sample is given in signed int16, -32k is the maximum cone displacement backward, 0 is middle (silence), +32k maximum cone displacement forward. Intensity is the amplitude - absolute value of the sample. Hence the calculation of absolute value/rectification:

Code:
if sample < 0 { = -sample;}

Where is the problem? :)