Best Practice for Getting RMS/Peak on an audio buffer
-
Hello everyone,
I was wondering what the updated best practice is for getting RMS and peak values of an audio signal for HISE are? The goal is to use them for an "input trim" knob on an audio effect plugin, however the only examples I have seen are ones for peak/vu meters.
Any help on the issue would be much appreciated? I have checked out the relevant API documentation except for some reason Buffer.getRMSLevel has not been working. I keep getting an unknown function error based on this input:
var RMS = Buffer.getRMSLevel(0,128);
I have tested it also using Engine.getBlockSize() too. Right now I have it implemented in a timer callback.
Best,
Noah -
@Noahdeetz Did you figure out how to do this?
-
I think there is a working post on this. If you look for rms in the docs there are some instructions. If you need to get rms from a specific point in scriptnode, I can give you one of my c++ nodes for scriptnode which has a mod output for rms.
-
@griffinboy ooh yes— that’s exactly what I need!
-
Create a file called Griffin_Rms.h and paste the following code into it. Place the file in your Hise project under:
DspNetworks > ThridPartyOpen your hise project and under export, choose 'compile dsp networks as dll' Upon completion you can restart hise and you will be able to use the node in scriptnode, find it under the 'projects' category when you open a new node in scriptnode.
Feel free to experiment with the code, most of the functionality should be self explanatory, and if you give chat gpt the code, you can ask it to explain each part.
#pragma once #include <JuceHeader.h> #include <cmath> namespace project { using namespace juce; using namespace hise; using namespace scriptnode; template <int NV> struct Griffin_Rms : public data::base { // Metadata Definitions ------------------------------------------------------------------------ SNEX_NODE(Griffin_Rms); struct MetadataClass { SN_NODE_ID("Griffin_Rms"); }; // Node Configuration ------------------------------------------------------------------------ static constexpr bool isModNode() { return true; } static constexpr bool isPolyphonic() { return NV > 1; } static constexpr bool hasTail() { return false; } static constexpr bool isSuspendedOnSilence() { return true; } static constexpr int getFixChannelAmount() { return NV; } static constexpr int NumTables = 0; static constexpr int NumSliderPacks = 0; static constexpr int NumAudioFiles = 0; static constexpr int NumFilters = 0; static constexpr int NumDisplayBuffers = 0; // Constants ------------------------------------------------------------------------------------ static constexpr double SILENCE_THRESHOLD_RMS = 1e-5; // Threshold for detecting silence static constexpr double MIN_RMS = 1e-8; // Minimum RMS value to prevent log(0) // External Parameters ----------------------------------------------------------------------- double blockDuration = 0.1; // Block size in seconds (RMS window duration) // Internal Variables ------------------------------------------------------------------------ double sampleRate = 44100.0; // Default sample rate double rmsFilterCoefficient = 0.0; // Filter coefficient for the smoothing filter double currentMeanSquare = 0.0; // The current mean square value ModValue modValue; // Modulation value handler // Preparation and Reset ---------------------------------------------------------------------- void prepare(PrepareSpecs prepareSpecs) { sampleRate = prepareSpecs.sampleRate; updateFilterCoefficient(); currentMeanSquare = 0.0; } void reset() { currentMeanSquare = 0.0; } // Helper Functions --------------------------------------------------------------------------- void updateFilterCoefficient() { if (blockDuration <= 0.0) blockDuration = 0.1; // Prevent invalid block duration rmsFilterCoefficient = std::exp(-1.0 / (sampleRate * blockDuration)); } // Processing --------------------------------------------------------------------------------- template <typename ProcessDataType> void process(ProcessDataType& data) { auto& fixData = data.as<ProcessData<getFixChannelAmount()>>(); auto audioBlock = fixData.toAudioBlock(); int numSamples = audioBlock.getNumSamples(); int numChannels = audioBlock.getNumChannels(); for (int i = 0; i < numSamples; ++i) { double sumSquares = 0.0; for (int ch = 0; ch < numChannels; ++ch) { double sample = static_cast<double>(audioBlock.getSample(ch, i)); sumSquares += sample * sample; } // Compute mean square for current sample across all channels double meanSquare = sumSquares / numChannels; // Update the EMA of the mean square (RMS squared) currentMeanSquare = rmsFilterCoefficient * currentMeanSquare + (1.0 - rmsFilterCoefficient) * meanSquare; // Compute RMS from mean square double rmsValue = std::sqrt(currentMeanSquare); // Silence detection bool isSilent = 0; // bool isSilent = rmsValue < SILENCE_THRESHOLD_RMS; // ^ uncomment this line to enable silence detection for rms // Use MIN_RMS to prevent log(0) double safeRMS = isSilent ? MIN_RMS : rmsValue; // Convert own RMS to dB double ownRMS_dB = 20.0 * std::log10(safeRMS); // Update modulation value with the RMS in dB modValue.setModValue(static_cast<float>(ownRMS_dB)); } } // Modulation Handling ------------------------------------------------------------------------ int handleModulation(double& value) { return modValue.getChangedValue(value); } // External Data and Events -------------------------------------------------------------------- void setExternalData(const ExternalData& data, int index) {} void handleHiseEvent(HiseEvent& e) {} // Frame Processing ---------------------------------------------------------------------------- template <typename T> void processFrame(T& data) noexcept {} // Parameter Setting --------------------------------------------------------------------------- template <int P> void setParameter(double v) { if (P == 0) { // Block Duration parameter blockDuration = v; updateFilterCoefficient(); reset(); } } // Create Parameters on the GUI ----------------------------------------------------------------- void createParameters(ParameterDataList& data) { { parameter::data p("Block Size (s)", { 0.01, 3.0, 0.01 }); registerCallback<0>(p); p.setDefaultValue(0.02); data.add(std::move(p)); } } }; }