HISE Logo Forum
    • Categories
    • Register
    • Login

    Best Practice for Getting RMS/Peak on an audio buffer

    Scheduled Pinned Locked Moved General Questions
    5 Posts 3 Posters 371 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • N
      Noahdeetz
      last edited by

      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

      mmprodM 1 Reply Last reply Reply Quote 1
      • mmprodM
        mmprod @Noahdeetz
        last edited by

        @Noahdeetz Did you figure out how to do this?

        For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.
        John 3:16

        griffinboyG 1 Reply Last reply Reply Quote 0
        • griffinboyG
          griffinboy @mmprod
          last edited by griffinboy

          @mmprod

          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.

          mmprodM 1 Reply Last reply Reply Quote 0
          • mmprodM
            mmprod @griffinboy
            last edited by

            @griffinboy ooh yes— that’s exactly what I need!

            For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.
            John 3:16

            griffinboyG 1 Reply Last reply Reply Quote 0
            • griffinboyG
              griffinboy @mmprod
              last edited by griffinboy

              @mmprod

              Create a file called Griffin_Rms.h and paste the following code into it. Place the file in your Hise project under:
              DspNetworks > ThridParty

              Open 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));
                          }
                      }
                  };
              }
              
              1 Reply Last reply Reply Quote 1
              • First post
                Last post

              60

              Online

              1.7k

              Users

              11.7k

              Topics

              101.7k

              Posts