Commit f90557da authored by Jonathan Hyde's avatar Jonathan Hyde

Add DC offset adjust and level metering.

parent b0409205
This diff is collapsed.
......@@ -32,6 +32,7 @@ private:
TextButton m_mute;
TextButton m_pad;
TextButton m_DCoffset;
TextButton m_phaseL;
TextButton m_phaseR;
TextButton m_muteL;
......@@ -58,6 +59,23 @@ private:
OtherLookAndFeel3 otherLookAndFeel3;
OtherLookAndFeel4 otherLookAndFeel4;
ScopedPointer<LevelMeter> meterIn;
ScopedPointer<LevelMeter> meterOut;
ScopedPointer<LevelMeterLookAndFeel> lnfIn;
ScopedPointer<LevelMeterLookAndFeel> lnfOut;
Label m_lgainDB;
Label m_lPan;
Label m_lWidth;
Label m_lLowpass;
Label m_lHighpass;
void getGainDB();
void getPanText();
void getWidthText();
int horShift = 100;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UtilityAudioProcessorEditor)
};
......
......@@ -21,6 +21,7 @@ UtilityAudioProcessor::UtilityAudioProcessor()
addParameter(p_input = new AudioParameterInt("input", "Input", 1, 4, 1));
addParameter(p_mute = new AudioParameterBool("mute", "Mute", false));
addParameter(p_pad = new AudioParameterBool("pad", "Pad", false));
addParameter(p_DCoffset = new AudioParameterBool("dcOffset", "DCOffset", false));
addParameter(p_gain = new AudioParameterFloat("gain", "Gain", Decibels::decibelsToGain(-35.0f), Decibels::decibelsToGain(35.0f), 1.0f));
addParameter(p_pan = new AudioParameterFloat("pan", "Pan", 0.0f, 1.0f, 0.5f));
addParameter(p_gainL = new AudioParameterFloat("gainL", "GainL", Decibels::decibelsToGain(-35.0f), Decibels::decibelsToGain(35.0f), 1.0f));
......@@ -113,8 +114,8 @@ void UtilityAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBloc
m_sampleRate = sampleRate;
lowPassCutoff.reset(sampleRate, 0.001);
highPassCutoff.reset(sampleRate, 0.001);
lowPassCutoff.reset(sampleRate, 0.0005);
highPassCutoff.reset(sampleRate, 0.0005);
m_prevHighPass = 0.0f;
m_prevLowPass = 0.0f;
......@@ -158,6 +159,8 @@ void UtilityAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer&
for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, numSamples);
meterSourceIn.measureBlock(buffer);
if (*p_lowPass != m_prevLowPass)
{
lowPassCutoff.setValue(*p_lowPass);
......@@ -190,6 +193,9 @@ void UtilityAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer&
convertMono(buffer);
}
if (*p_DCoffset)
dcOffset(buffer);
//we apply two gains to each channel, one for immediate changes and one for ramped changes
float rampGainL = *p_gainL * *p_gain;
......@@ -246,6 +252,8 @@ void UtilityAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer&
if (*p_hardClip || *p_softClip)
clipSamples(buffer);
meterSourceOut.measureBlock(buffer);
}
//==============================================================================
......@@ -391,3 +399,48 @@ void UtilityAudioProcessor::clipSamples(AudioSampleBuffer & buffer)
}
}
}
void UtilityAudioProcessor::dcOffset(AudioSampleBuffer & buffer)
{
int numSamples = buffer.getNumSamples();
int numChans = buffer.getNumChannels();
if (m_sampleRate == 44100.0) ////use eps = 2*pi*f/fs where f is the cutoff and fs is sample rate, storing for faster lookup
{
m_eps = 0.00142475857;
}
else if (m_sampleRate == 48000.0)
{
m_eps = 0.00130899693;
}
else if (m_sampleRate == 88200.0)
{
m_eps = 0.00071237928;
}
else if (m_sampleRate == 96000.0)
{
m_eps = 0.00065449846;
}
else
{
m_eps = 0.00142475857;
}
double sampleL = 0.0;
double sampleR = 0.0;
float *bufferDataL = buffer.getWritePointer(0);
float *bufferDataR = buffer.getWritePointer(1);
for (int i = 0; i < numSamples; i++)
{
sampleL = bufferDataL[i];
bufferDataL[i] = (1 - m_eps) * m_prevOutL + sampleL - m_prevInL;
m_prevOutL = bufferDataL[i];
m_prevInL = sampleL;
sampleR = bufferDataR[i];
bufferDataR[i] = (1 - m_eps) * m_prevOutR + sampleR - m_prevInR;
m_prevOutR = bufferDataR[i];
m_prevInR = sampleR;
}
}
......@@ -49,6 +49,8 @@ public:
AudioParameterInt *p_input;
AudioParameterBool *p_mute;
AudioParameterBool *p_pad;
AudioParameterBool *p_DCoffset;
AudioParameterFloat *p_gain;
AudioParameterFloat *p_pan;
......@@ -74,6 +76,15 @@ public:
AudioParameterBool *p_hardClip;
AudioParameterBool *p_softClip;
LevelMeterSource* getMeterSourceIn()
{
return &meterSourceIn;
}
LevelMeterSource* getMeterSourceOut()
{
return &meterSourceOut;
}
private:
//==============================================================================
float m_prevGain = 1.0f;
......@@ -85,6 +96,7 @@ private:
void convertMono(AudioSampleBuffer &buffer);
void midSideProcess(AudioSampleBuffer &buffer);
void clipSamples(AudioSampleBuffer &buffer);
void dcOffset(AudioSampleBuffer &buffer);
AudioSampleBuffer m_midBuffer;
AudioSampleBuffer m_sideBuffer;
......@@ -100,6 +112,15 @@ private:
LinearSmoothedValue<float> lowPassCutoff;
LinearSmoothedValue<float> highPassCutoff;
double m_prevInL = 0.0f;
double m_prevOutL = 0.0f;
double m_prevInR = 0.0f;
double m_prevOutR = 0.0f;
double m_eps;
LevelMeterSource meterSourceIn;
LevelMeterSource meterSourceOut;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UtilityAudioProcessor)
};
......
This diff is collapsed.
==============================================================================
Copyright (c) 2017, Foleys Finest Audio UG - Daniel Walz
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
==============================================================================
ff_meters
=========
by Daniel Walz / Foleys Finest Audio UG (haftungsbeschraenkt)
Published under the BSD License (3 clause)
The ff_meters provide an easy to use Component to display a level reading for an
AudioBuffer. It is to be used in the audio framework JUCE (www.juce.com).
Usage
=====
To use it create a LevelMeterSource instance next to the AudioBuffer you want to
display. To update the meter, call LevelMeterSource::measureBlock (buffer) in your
processBlock or getNextAudioBuffer method.
On the Component use LevelMeter::setMeterSource to link to the LevelMeterSource
instance. The number of channels will be updated automatically.
You can pull the drawing into your LookAndFeel by inheriting LevelMeter::LookAndFeelMethods
and inlining the default implementation from LevelMeterLookAndFeel in
ff_meters_LookAndFeelMethods.h into a public section of your class declaration. To
setup the default colour scheme, call setupDefaultMeterColours() in your constructor.
Or you can use the LevelMeterLookAndFeel directly because it inherits from juce::LookAndFeel_V3
for your convenience. You can set it as default LookAndFeel, if you used the default,
or set it only to the meters, if you don't want it to interfere.
// In your Editor
public:
PluginEditor () {
lnf = new LevelMeterLookAndFeel();
// adjust the colours to how you like them
lnf->setColour (LevelMeter::lmMeterGradientLowColour, juce::Colours::green);
meter = new LevelMeter (LevelMeter::VerticalMeters);
meter->setLookAndFeel (lnf);
meter->setMeterSource (processor.getMeterSource());
addAndMakeVisible (meter);
// ...
}
private:
ScopedPointer<LevelMeter> meter;
ScopedPointer<LevelMeterLookAndFeel> lnf;
// and in the processor:
public:
LevelMeterSource* getMeterSource ()
{
return &meterSource;
}
void processBlock (AudioSampleBuffer& buffer, MidiBuffer&) override
{
meterSource.measureBlock (buffer);
// ...
}
private:
LevelMeterSource meterSource;
********************************************************************************
We hope it is of any use, let us know of any problems or improvements you may
come up with...
Brighton, 2nd March 2017
********************************************************************************
/*
==============================================================================
Copyright (c) 2017 Filmstro Ltd. / Foleys Finest Audio UG - Daniel Walz
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
==============================================================================
BEGIN_JUCE_MODULE_DECLARATION
ID: ff_meters
vendor: Foleys Finest Audio UG / Filmstro Ltd.
version: 0.9.0
name: Meters with GUI and LookAndFeel
description: Audio helper classes of general purpose
dependencies: juce_audio_basics, juce_audio_formats, juce_audio_devices, juce_audio_processors
website: http://www.foleysfinest.com/
license: BSD V2 3-clause
END_JUCE_MODULE_DECLARATION
==============================================================================
*/
#ifndef FF_AUDIO_METERS_INCLUDED_H
#define FF_AUDIO_METERS_INCLUDED_H
#ifndef USE_FF_AUDIO_METERS
#define USE_FF_AUDIO_METERS 1
#endif
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_formats/juce_audio_formats.h>
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <ff_meters/ff_meters_LevelMeterSource.h>
//#include <ff_meters/ff_meters_LookAndFeelVertical.h>
//#include <ff_meters/ff_meters_LookAndFeelHorizontal.h>
//#include <ff_meters/ff_meters_LookAndFeelVintage.h>
#include <ff_meters/ff_meters_LevelMeter.h>
#include <ff_meters/ff_meters_LookAndFeel.h>
#endif /* FF_AUDIO_METERS_INCLUDED_H */
/*
==============================================================================
Copyright (c) 2017 Filmstro Ltd. / Foleys Finest Audio UG - Daniel Walz
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
==============================================================================
ff_meters_LevelMeter.cpp
Created: 5 Apr 2016 9:49:54am
Author: Daniel Walz
==============================================================================
*/
#include "../JuceLibraryCode/JuceHeader.h"
//==============================================================================
LevelMeter::LevelMeter (const MeterFlags type)
: source (nullptr),
selectedChannel (-1),
meterType (type),
refreshRate (30),
useBackgroundImage (false),
backgroundNeedsRepaint (true)
{
startTimerHz (refreshRate);
}
LevelMeter::~LevelMeter()
{
stopTimer();
}
void LevelMeter::setMeterSource (LevelMeterSource* src)
{
source = src;
}
void LevelMeter::setSelectedChannel (const int c)
{
selectedChannel = c;
}
void LevelMeter::setRefreshRateHz (const int newRefreshRate)
{
refreshRate = newRefreshRate;
startTimerHz (refreshRate);
}
void LevelMeter::paint (Graphics& g)
{
Graphics::ScopedSaveState saved (g);
LookAndFeel& l = getLookAndFeel();
if (LookAndFeelMethods* lnf = dynamic_cast<LookAndFeelMethods*> (&l)) {
const juce::Rectangle<float> bounds = getLocalBounds().toFloat();
if (useBackgroundImage) {
// This seems to only speed up, if you use complex drawings on the background. For
// "normal" vector graphics the image approach seems actually slower
if (backgroundNeedsRepaint) {
backgroundImage = Image (Image::ARGB, getWidth(), getHeight(), true);
Graphics backGraphics (backgroundImage);
lnf->drawBackground (backGraphics, meterType, bounds);
lnf->drawMeterBarsBackground (backGraphics, meterType, bounds, source ? source->getNumChannels() : 1);
backgroundNeedsRepaint = false;
}
g.drawImageAt (backgroundImage, 0, 0);
lnf->drawMeterBars (g, meterType, bounds, source, selectedChannel);
}
else {
lnf->drawBackground (g, meterType, bounds);
lnf->drawMeterBarsBackground (g, meterType, bounds, source ? source->getNumChannels() : 1);
lnf->drawMeterBars (g, meterType, bounds, source, selectedChannel);
}
}
else {
// This LookAndFeel is missing the LevelMeter::LookAndFeelMethods.
// If you work with the default LookAndFeel, set an instance of
// LevelMeterLookAndFeel as LookAndFeel of this component.
//
// If you write a LookAndFeel from scratch, inherit also
// LevelMeter::LookAndFeelMethods
jassertfalse;
}
}
void LevelMeter::resized ()
{
backgroundNeedsRepaint = true;
}
void LevelMeter::timerCallback ()
{
repaint();
}
void LevelMeter::clearClipIndicator (const int channel)
{
if (source) {
if (channel < 0) {
source->clearAllClipFlags();
}
else {
source->clearClipFlag (channel);
}
}
}
void LevelMeter::clearMaxLevelDisplay (const int channel)
{
if (source) {
if (channel < 0) {
source->clearAllMaxNums();
}
else {
source->clearMaxNum (channel);
}
}
}
void LevelMeter::mouseDown (const juce::MouseEvent &event)
{
if (LookAndFeelMethods* lnf = dynamic_cast<LookAndFeelMethods*> (&getLookAndFeel())) {
const juce::Rectangle<float> innerBounds = lnf->getMeterInnerBounds (getLocalBounds().toFloat(),
meterType);
if (event.mods.isLeftButtonDown() && source) {
int hit = lnf->hitTestClipIndicator (event.getPosition(),
meterType,
innerBounds,
source);
if (hit >= 0) {
listeners.call (&LevelMeter::Listener::clipLightClicked, hit);
}
hit = lnf->hitTestMaxNumber (event.getPosition(),
meterType,
innerBounds,
source);
if (hit >= 0) {
listeners.call (&LevelMeter::Listener::maxLevelClicked, hit);
}
}
}
}
void LevelMeter::addListener (LevelMeter::Listener* listener)
{
listeners.add (listener);
}
void LevelMeter::removeListener (LevelMeter::Listener* listener)
{
listeners.remove (listener);
}
This diff is collapsed.
/*
==============================================================================
Copyright (c) 2017 Filmstro Ltd. / Foleys Finest Audio UG - Daniel Walz
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
==============================================================================
ff_meters_LevelMeterSource.h
Created: 5 Apr 2016 9:49:54am
Author: Daniel Walz
==============================================================================
*/
#ifndef FF_METER_LEVEL_METER_SOURCE_H_INCLUDED
#define FF_METER_LEVEL_METER_SOURCE_H_INCLUDED
#include <atomic>
#include <vector>
/**
\class LevelMeterSource
To get a nice meter GUI create a LevelMeterSource in your audioProcessor
or whatever instance processes an AudioBuffer.
Then call LevelMeterSource::measureBlock (AudioBuffer<float>& buf) to
create the readings.
*/
class LevelMeterSource
{
private:
class ChannelData {
public:
ChannelData (const int rmsWindow = 8) :
max (),
maxOverall (),
clip (false),
reduction (-1.0f),
hold (0),
rmsHistory (rmsWindow, 0.0),
rmsSum (0.0),
rmsPtr (0)
{}
ChannelData (const ChannelData& other) :
max (other.max.load() ),
maxOverall(other.maxOverall.load() ),
clip (other.clip.load() ),
reduction (other.reduction.load()),
hold (other.hold.load()),
rmsHistory (8, 0.0),
rmsSum (0.0),
rmsPtr (0)
{}
std::atomic<float> max;
std::atomic<float> maxOverall;
std::atomic<bool> clip;
std::atomic<float> reduction;
float getAvgRMS () const {
if (rmsHistory.size() > 0) {
return sqrtf (rmsSum / rmsHistory.size());
}
return sqrtf (rmsSum);
}
void setLevels (const juce::int64 time, const float newMax, const float newRms, const juce::int64 holdMSecs)
{
if (newMax > 1.0 || newRms > 1.0) {
clip = true;
}
maxOverall = fmaxf (maxOverall, newMax);
if (newMax >= max) {
max = std::min (1.0f, newMax);
hold = time + holdMSecs;
}
else if (time > hold) {
max = std::min (1.0f, newMax);
}
pushNextRMS (newRms);
}
void setRMSsize (const int numBlocks) {
rmsHistory.resize (numBlocks);
rmsPtr %= rmsHistory.size();
}
private:
void pushNextRMS (const float newRMS) {
const float squaredRMS = std::min (newRMS * newRMS, 1.0f);
if (rmsHistory.size() > 0) {
float oldRMS = rmsSum - rmsHistory [rmsPtr];
rmsSum = oldRMS + squaredRMS;
rmsHistory [rmsPtr] = squaredRMS;
rmsPtr = ++rmsPtr % rmsHistory.size();
}
else {
rmsSum = squaredRMS;
}
}
std::atomic<juce::int64> hold;
std::vector<float> rmsHistory;
std::atomic<float> rmsSum;
int rmsPtr;
};
public:
LevelMeterSource () :
holdMSecs (500),
suspended (false)
{}
~LevelMeterSource ()
{
masterReference.clear();
}
void resize (const int channels, const int rmsWindow) {
levels.resize (channels, ChannelData (rmsWindow));
for (ChannelData& l : levels) {
l.setRMSsize (rmsWindow);
}
}
template<typename FloatType>
void measureBlock (const juce::AudioBuffer<FloatType>& buffer)
{
if (! suspended) {
const juce::int64 time = juce::Time::currentTimeMillis();
const int numChannels = buffer.getNumChannels ();
const int numSamples = buffer.getNumSamples ();
levels.resize (numChannels);
for (int channel=0; channel < numChannels; ++channel) {
levels [channel].setLevels (time,
buffer.getMagnitude (channel, 0, numSamples),
buffer.getRMSLevel (channel, 0, numSamples),
holdMSecs);
}
}
}
void setReductionLevel (const int channel, const float reduction)
{
if (juce::isPositiveAndBelow (channel, static_cast<int> (levels.size ())))
levels [channel].reduction = reduction;
}
void setMaxHoldMS (const juce::int64 millis)
{
holdMSecs = millis;
}
float getReductionLevel (const int channel) const
{
if (juce::isPositiveAndBelow (channel, static_cast<int> (levels.size ())))
return levels [channel].reduction;
return -1.0f;
}
float getMaxLevel (const int channel) const
{
return levels.at (channel).max;
}
float getMaxOverallLevel (const int channel) const
{
return levels.at (channel).maxOverall;
}
float getRMSLevel (const int channel) const
{
return levels.at (channel).getAvgRMS();
}
bool getClipFlag (const int channel) const
{
return levels.at (channel).clip;
}
void clearClipFlag (const int channel)
{
levels.at (channel).clip = false;
}
void clearAllClipFlags ()
{
for (ChannelData& l : levels) {
l.clip = false;