Get RMS and Peak level on the same call?

Started by rgomez,

rgomez

Hello,

I am playing with a waveform drawer and would like to have the RMS and peak level for the period in the same call. I am using BASS_ChannelGetLevelEx, but what I have to do now is have two (decoding) channels and process them in the same cycle. This effectively doubles the processing time, for what I can see... and of course, it makes sense. I am trying to avoid this...

I wonder then if there is a way to obtain this on the same call? I am working with lengthy audios (usually 24 hours long)  so the period I am using is not very fine: 660ms and 20 seconds

Thanks!

Ian @ un4seen

It isn't possible to get both levels in a single BASS_ChannelGetLevelEx call, but instead of using a separate decoder for each level, you could use a push stream for both: get data from the source, feed it to the push stream and get the peak level, feed it to the push stream again and get the RMS level.

BASS_CHANNELINFO info;
BASS_ChannelGetInfo(decoder, &info); // get source info
levelstream = BASS_StreamCreate(info.freq, info.chans, info.flags | BASS_STEAM_DECODE, STREAMPROC_PUSH, 0); // create push stream with same format
...
got = BASS_ChannelGetData(decoder, buffer, bufsize); // get data from source
BASS_StreamPutData(levelstream, buffer, got); // feed data to the level stream
BASS_ChannelGetLevelEx(levelstream, &peaklevel, 1, BASS_LEVEL_MONO); // get the peak level
BASS_StreamPutData(levelstream, buffer, got); // feed data to the level stream again
BASS_ChannelGetLevelEx(levelstream, &rmslevel, 1, BASS_LEVEL_MONO | BASS_LEVEL_RMS); // get the RMS level

Make sure the BASS_ChannelGetLevelEx "length" parameter at least matches (or exceeds) the BASS_ChannelGetData "length" parameter, to avoid a build-up of data in the push stream.

Ian @ un4seen

The code above will work with both playback and decoding channels, but if you're only dealing with decoding channels, then another (possibly simpler) option is to use splitters, ie. a splitter for each level. Something like this:

peakstream = BASS_Split_StreamCreate(decoder, BASS_STREAM_DECODE, NULL); // create splitter for peak levels
rmsstream = BASS_Split_StreamCreate(decoder, BASS_STREAM_DECODE | BASS_SPLIT_SLAVE, NULL); // create "slave" splitter for RMS levels
...
BASS_ChannelGetLevelEx(peakstream, &peaklevel, length, BASS_LEVEL_MONO); // get the peak level
BASS_ChannelGetLevelEx(rmsstream, &rmslevel, length, BASS_LEVEL_MONO | BASS_LEVEL_RMS); // get the RMS level

Note BASS_ChannelGetLevelEx needs to be called on the non-slave splitter (peakstream) first. Please see the BASS_Split_StreamCreate documentation for details.

rgomez

Hello Ian,

The original code is working fine. I wonder, using splitter streams, if I understand correctly, I would not need to call BASS_ChannelGetData first? It would be just the BASS_ChannelGetLevelEx calls on the two splitter streams, and that's it?

Ian @ un4seen

That's correct, there would be no need for you to call BASS_ChannelGetData. BASS_ChannelGetLevelEx itself uses BASS_ChannelGetData internally to get the data that it measures.

rgomez

Thank you Ian,

This solution is simpler code-wise and works great.