How to getRMSLevel() of a dsp::AudioBlock?

dsp_module
#1

How to getRMSLevel() of a dsp::AudioBlock? It’s a member function of AudioBuffer, not directly available for AudioBlock. (Hopefully requiring no copying of samples.)

0 Likes

#2

DIY :wink: I didn’t test this, but I basically just rewrote the function to work with a block.

/** Returns the root mean squared level for a region of a channel. */
template<typename T>
T getRMSLevel (dsp::Audioblock<T> block, int channel, int startSample, int numSamples) const noexcept
{
    jassert (isPositiveAndBelow (channel, block.getNumChannels()));
    jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= block.getNumSamples());

    if (numSamples <= 0 || channel < 0 || channel >= block.getNumChannels() || block.getNumSamples() == 0)
        return T(0);

    auto* data = block.getChannelPointer(channel) + startSample;
    double sum = 0.0;

    for (int i = 0; i < numSamples; ++i)
    {
        auto sample = data[i];
        sum += sample * sample;
    }

    return static_cast<T> (std::sqrt (sum / numSamples));
}
0 Likes

#3

Thank you. Also been trying to figure out how to “cast” or “extract” an AudioBuffer given an AudioBlock. There’s a hint in your line

auto* data = block.getChannelPointer(channel) + startSample;

but I’ve yet to find the correct incantation. Any ideas?

0 Likes

#4

I’m not sure what you mean, what kind of magic are you after? :smile:

0 Likes

#5

Hi All,

Sometime ago I also was trying to convert AudioBlock to AudioBuffer, because I had some processor based on dsp module and I want to use it as a regular AudioProcessor regular AudioProcessors and I wanted to use them in processors based on dsp module. But I did not find a “clean, nice looking” way to do it. So after reading some posts on forum I rethink my design and I did it the other way around. :stuck_out_tongue:

It could be easy if AudioBlock would have channels with public access. Then wrapping AudioBlock in AudioBuffer would be as easy as wrapping a part of AudioBuffer in AudioBlock. Both with no allocation/copying.
This is doable:

dsp::AudioBlock<SampleType> wrapperBlock(audioBuffer.getArrayOfWritePointers(), audioBuffer.getNumChannels(), audioBuffer.getNumSamples());

But this is not:

AudioBuffer<SampleType> wrapperBuffer(audioBlock.getChannels(), audioBlock.getNumChannels(), audioBlock.getNumSamples());

(or am I missing somethnig?)
Probably it was intentional.

For getting RMS level of AudioBlock, proposition from @gustav-scholda looks the best for me.
It would be nice to have such function in dsp_module :slight_smile:

Best regards,
Mateusz

0 Likes

#6

I’m not sure what you mean by “regular AudioProcessor” but it IS intentional! Audioblock doesn’t contain any data and that’s for good. You don’t ever need to convert it to an AudioBuffer.
Also it’s good to keep the Audioblock as light-weight as possible - if you need more functions just create static methods and include them.

0 Likes

#7

Nothing without exception to the rule:
There is a way to have an AudioBuffer, that is referring to external data:
AudioBuffer<Type>::AudioBuffer (Type *const * dataToReferTo, int numChannelsToUse, int numSamples).

The only thing that’s missing is to return the array of pointers to the block, but you can assemble them yourself, using float* ptrs [numChannels].

However, I would still go with @gustav-scholda’s advice and use my own. I was hoping to find something in FloatVectorOperations, but unfortunately, there is no RMS method (still a good place to look at for this kind of block based computations IMHO).

BTW, thanks for the heads up, I have to add it on the backlog for my metering module :wink:

0 Likes

#8

True! Having a getArrayOfChannelPointers() function would be helpful!

0 Likes

#9

The most important is that I agree with you. :slight_smile: Maybe I did not describe it well.
And I lied a bit in my previous post :(. I have edited it now.

By “regular AudioProcessor” I mean juce::AudioProcessor, which processBlock method takes AudioBuffer.

I was building a plugin based on AudioProcessorGraph tutorial, so I had for example chorus inherited from juce::AudioProcessor. Then I changed juce::AudioProcessorGraph to juce::dsp::ProcessorChain, because my plugin do not need runtime connection changes.

So I want to do a quick wrapper and use already implemented Chorus in ProcessorChain, which is working with dsp::ProcessContext and dsp::AudioBlock. But this would force me to “cast” AudioBlock to AudioBuffer. Which is a bad idea.
The correct way is the other way around and it is also shown in AudioProcessorGraph tutorial :smiley:

I am pretty sure that it was already mention somewhere that AudioBuffer has some useful methods, which are missing for AudioBlock (out of the box), but I am too lazy to look for it now.

Sorry if this explanation is out of topic.

0 Likes

#10

There are several years (even decades?) between AudioBuffer and AudioBlock. The thinking of AudioBuffer was still very OO, adding member functions for each functionality.

The AudioBlock tries to be the opposite, rather a slick interface for algorithms. That’s the reason, why it chooses not to own any data, so it is safe to be created and removed on the audio thread.

So I would write something like this:

namespace Mesuring
{
    template<typename SampleType>
    static inline SampleType getRMS (const dsp::AudioBlock<SampleType>& block)
    {
        ...
    }
}

auto rms = Measuring::getRMS (block);
0 Likes