/**********************************************************************

  Audacity: A Digital Audio Editor

  Normalize.cpp

  Dominic Mazzoni
  Vaughan Johnson (Preview)
  Max Maisel (Loudness)

*******************************************************************//**

\class EffectNormalize
\brief An Effect to bring the peak level up to a chosen level.

*//*******************************************************************/


#include "../Audacity.h" // for rint from configwin.h
#include "Normalize.h"

#include "../Experimental.h"

#include <math.h>

#include <wx/checkbox.h>
#include <wx/intl.h>
#include <wx/stattext.h>
#include <wx/valgen.h>

#include "../Prefs.h"
#include "../ProjectFileManager.h"
#include "../Shuttle.h"
#include "../ShuttleGui.h"
#include "../WaveTrack.h"
#include "../widgets/valnum.h"
#include "../widgets/ProgressDialog.h"

// Define keys, defaults, minimums, and maximums for the effect parameters
//
//     Name         Type     Key                        Def      Min      Max   Scale
Param( PeakLevel,   double,  wxT("PeakLevel"),           -1.0,    -145.0,  0.0,  1  );
Param( RemoveDC,    bool,    wxT("RemoveDcOffset"),      true,    false,   true, 1  );
Param( ApplyGain,   bool,    wxT("ApplyGain"),           true,    false,   true, 1  );
Param( StereoInd,   bool,    wxT("StereoIndependent"),   false,   false,   true, 1  );
#ifdef EXPERIMENTAL_R128_NORM
Param( LUFSLevel,   double,  wxT("LUFSLevel"),           -23.0,   -145.0,  0.0,  1  );
Param( UseLoudness, bool,    wxT("UseLoudness"),         false,   false,   true, 1  );
#endif

BEGIN_EVENT_TABLE(EffectNormalize, wxEvtHandler)
   EVT_CHECKBOX(wxID_ANY, EffectNormalize::OnUpdateUI)
   EVT_TEXT(wxID_ANY, EffectNormalize::OnUpdateUI)
END_EVENT_TABLE()

EffectNormalize::EffectNormalize()
{
   mPeakLevel = DEF_PeakLevel;
   mDC = DEF_RemoveDC;
   mGain = DEF_ApplyGain;
   mStereoInd = DEF_StereoInd;
#ifdef EXPERIMENTAL_R128_NORM
   mLUFSLevel = DEF_LUFSLevel;
   mUseLoudness = DEF_UseLoudness;
#endif

   SetLinearEffectFlag(false);
}

EffectNormalize::~EffectNormalize()
{
}

// ComponentInterface implementation

ComponentInterfaceSymbol EffectNormalize::GetSymbol()
{
   return NORMALIZE_PLUGIN_SYMBOL;
}

wxString EffectNormalize::GetDescription()
{
#ifdef EXPERIMENTAL_R128_NORM
   return _("Sets the peak amplitude or loudness of one or more tracks");
#else
   return _("Sets the peak amplitude of one or more tracks");
#endif
}

wxString EffectNormalize::ManualPage()
{
   return wxT("Normalize");
}

// EffectDefinitionInterface implementation

EffectType EffectNormalize::GetType()
{
   return EffectTypeProcess;
}

// EffectClientInterface implementation
bool EffectNormalize::DefineParams( ShuttleParams & S ){
   S.SHUTTLE_PARAM( mPeakLevel, PeakLevel );
   S.SHUTTLE_PARAM( mGain, ApplyGain );
   S.SHUTTLE_PARAM( mDC, RemoveDC );
   S.SHUTTLE_PARAM( mStereoInd, StereoInd );
#ifdef EXPERIMENTAL_R128_NORM
   S.SHUTTLE_PARAM( mLUFSLevel, LUFSLevel );
   S.SHUTTLE_PARAM( mUseLoudness, UseLoudness );
#endif
   return true;
}

bool EffectNormalize::GetAutomationParameters(CommandParameters & parms)
{
   parms.Write(KEY_PeakLevel, mPeakLevel);
   parms.Write(KEY_ApplyGain, mGain);
   parms.Write(KEY_RemoveDC, mDC);
   parms.Write(KEY_StereoInd, mStereoInd);
#ifdef EXPERIMENTAL_R128_NORM
   parms.Write(KEY_LUFSLevel, mLUFSLevel);
   parms.Write(KEY_UseLoudness, mUseLoudness);
#endif

   return true;
}

bool EffectNormalize::SetAutomationParameters(CommandParameters & parms)
{
   ReadAndVerifyDouble(PeakLevel);
   ReadAndVerifyBool(ApplyGain);
   ReadAndVerifyBool(RemoveDC);
   ReadAndVerifyBool(StereoInd);
#ifdef EXPERIMENTAL_R128_NORM
   ReadAndVerifyDouble(LUFSLevel);
   ReadAndVerifyBool(UseLoudness);
#endif

   mPeakLevel = PeakLevel;
   mGain = ApplyGain;
   mDC = RemoveDC;
   mStereoInd = StereoInd;
#ifdef EXPERIMENTAL_R128_NORM
   mLUFSLevel = LUFSLevel;
   mUseLoudness = UseLoudness;
#endif

   return true;
}

// Effect implementation

bool EffectNormalize::CheckWhetherSkipEffect()
{
   return ((mGain == false) && (mDC == false));
}

bool EffectNormalize::Startup()
{
   wxString base = wxT("/Effects/Normalize/");

   // Migrate settings from 2.1.0 or before

   // Already migrated, so bail
   if (gPrefs->Exists(base + wxT("Migrated")))
   {
      return true;
   }

   // Load the old "current" settings
   if (gPrefs->Exists(base))
   {
      int boolProxy = gPrefs->Read(base + wxT("RemoveDcOffset"), 1);
      mDC = (boolProxy == 1);
      boolProxy = gPrefs->Read(base + wxT("Normalize"), 1);
      mGain = (boolProxy == 1);
      gPrefs->Read(base + wxT("Level"), &mPeakLevel, -1.0);
      if(mPeakLevel > 0.0)  // this should never happen
         mPeakLevel = -mPeakLevel;
      boolProxy = gPrefs->Read(base + wxT("StereoIndependent"), 0L);
      mStereoInd = (boolProxy == 1);
#ifdef EXPERIMENTAL_R128_NORM
      mUseLoudness = false;
      mLUFSLevel = DEF_LUFSLevel;
#endif

      SaveUserPreset(GetCurrentSettingsGroup());

      // Do not migrate again
      gPrefs->Write(base + wxT("Migrated"), true);
      gPrefs->Flush();
   }

   return true;
}

bool EffectNormalize::Process()
{
   if (mGain == false && mDC == false)
      return true;

   float ratio;
   if( mGain )
   {
#ifdef EXPERIMENTAL_R128_NORM
      if(mUseLoudness) {
         // LU use 10*log10(...) instead of 20*log10(...)
         // so multiply level by 2 and use standard DB_TO_LINEAR macro.
         ratio = DB_TO_LINEAR(TrapDouble(mLUFSLevel*2, MIN_LUFSLevel, MAX_LUFSLevel));
      }
      else {
         // same value used for all tracks
         ratio = DB_TO_LINEAR(TrapDouble(mPeakLevel, MIN_PeakLevel, MAX_PeakLevel));
      }
#else
      // same value used for all tracks
      ratio = DB_TO_LINEAR(TrapDouble(mPeakLevel, MIN_PeakLevel, MAX_PeakLevel));
#endif
   }
   else {
      ratio = 1.0;
   }

   //Iterate over each track
   this->CopyInputTracks(); // Set up mOutputTracks.
   bool bGoodResult = true;
   double progress = 0;
   wxString topMsg;
   if(mDC && mGain)
      topMsg = _("Removing DC offset and Normalizing...\n");
   else if(mDC && !mGain)
      topMsg = _("Removing DC offset...\n");
   else if(!mDC && mGain)
      topMsg = _("Normalizing without removing DC offset...\n");
   else if(!mDC && !mGain)
      topMsg = _("Not doing anything...\n");   // shouldn't get here

   for ( auto track : mOutputTracks->Selected< WaveTrack >()
            + ( mStereoInd ? &Track::Any : &Track::IsLeader ) ) {
      //Get start and end times from track
      // PRL:  No accounting for multiple channels?
      double trackStart = track->GetStartTime();
      double trackEnd = track->GetEndTime();

      //Set the current bounds to whichever left marker is
      //greater and whichever right marker is less:
      mCurT0 = mT0 < trackStart? trackStart: mT0;
      mCurT1 = mT1 > trackEnd? trackEnd: mT1;

      auto range = mStereoInd
         ? TrackList::SingletonRange(track)
         : TrackList::Channels(track);

      // Process only if the right marker is to the right of the left marker
      if (mCurT1 > mCurT0) {
         wxString trackName = track->GetName();

         float extent;
#ifdef EXPERIMENTAL_R128_NORM
         if (mUseLoudness)
            // Loudness: use sum of both tracks.
            // As a result, stereo tracks appear about 3 LUFS louder,
            // as specified.
            extent = 0;
         else
#endif
            // Will compute a maximum
            extent = std::numeric_limits<float>::lowest();
         std::vector<float> offsets;

         wxString msg;
         if (range.size() == 1)
            // mono or 'stereo tracks independently'
            msg = topMsg +
               wxString::Format( _("Analyzing: %s"), trackName );
         else
            msg = topMsg +
               // TODO: more-than-two-channels-message
               wxString::Format( _("Analyzing first track of stereo pair: %s"), trackName);
         
         // Analysis loop over channels collects offsets and extent
         for (auto channel : range) {
            float offset = 0;
            float extent2 = 0;
            bGoodResult =
               AnalyseTrack( channel, msg, progress, offset, extent2 );
            if ( ! bGoodResult )
               goto break2;
#ifdef EXPERIMENTAL_R128_NORM
            if (mUseLoudness)
               extent += extent2;
            else
#endif
               extent = std::max( extent, extent2 );
            offsets.push_back(offset);
            // TODO: more-than-two-channels-message
            msg = topMsg +
               wxString::Format( _("Analyzing second track of stereo pair: %s"), trackName );
         }

         // Compute the multiplier using extent
         if( (extent > 0) && mGain ) {
            mMult = ratio / extent;
#ifdef EXPERIMENTAL_R128_NORM
            if(mUseLoudness) {
               // PRL:  See commit 9cbb67a for the origin of the next line,
               // which has no effect because mMult is again overwritten.  What
               // was the intent?

               // LUFS is defined as -0.691 dB + 10*log10(sum(channels))
               mMult /= 0.8529037031;
               // LUFS are related to square values so the multiplier must be the root.
               mMult = sqrt(ratio / extent);
            }
#endif
         }
         else
            mMult = 1.0;

         if (range.size() == 1) {
            if (TrackList::Channels(track).size() == 1)
               // really mono
               msg = topMsg +
                  wxString::Format( _("Processing: %s"), trackName );
            else
               //'stereo tracks independently'
               // TODO: more-than-two-channels-message
               msg = topMsg +
                  wxString::Format( _("Processing stereo channels independently: %s"), trackName);
         }
         else
            msg = topMsg +
               // TODO: more-than-two-channels-message
               wxString::Format( _("Processing first track of stereo pair: %s"), trackName);

         // Use multiplier in the second, processing loop over channels
         auto pOffset = offsets.begin();
         for (auto channel : range) {
            if (false ==
                (bGoodResult = ProcessOne(channel, msg, progress, *pOffset++)) )
               goto break2;
            // TODO: more-than-two-channels-message
            msg = topMsg +
               wxString::Format( _("Processing second track of stereo pair: %s"), trackName);
         }
      }
   }

   break2:

   this->ReplaceProcessedTracks(bGoodResult);
   return bGoodResult;
}

void EffectNormalize::PopulateOrExchange(ShuttleGui & S)
{
   mCreating = true;

   S.StartVerticalLay(0);
   {
      S.StartMultiColumn(2, wxALIGN_CENTER);
      {
         S.StartVerticalLay(false);
         {
            mDCCheckBox = S.AddCheckBox(_("Remove DC offset (center on 0.0 vertically)"),
                                        mDC);
            mDCCheckBox->SetValidator(wxGenericValidator(&mDC));

            S.StartHorizontalLay(wxALIGN_LEFT, false);
            {
               // The checkbox needs to be sized for the longer prompt, and
               // which that is will depend on translation.  So decide that here.
               // (strictly we should count pixels, not characters).
               wxString prompt1 = _("Normalize peak amplitude to");
#ifdef EXPERIMENTAL_R128_NORM
               wxString prompt2 = _("Normalize loudness to");
               wxString longerPrompt = ((prompt1.length() > prompt2.length()) ? prompt1 : prompt2) + "   ";
#else
               wxString longerPrompt = prompt1 + "   ";
#endif
               // Now make the checkbox.
               mGainCheckBox = S.AddCheckBox(longerPrompt,
                                             mGain);
               mGainCheckBox->SetValidator(wxGenericValidator(&mGain));
               mGainCheckBox->SetMinSize( mGainCheckBox->GetSize());

               FloatingPointValidator<double> vldLevel(2, &mPeakLevel,
                                                       NumValidatorStyle::ONE_TRAILING_ZERO);
               vldLevel.SetRange( MIN_PeakLevel, MAX_PeakLevel);

               mLevelTextCtrl = S.AddTextBox( {}, wxT(""), 10);
               mLevelTextCtrl->SetName( _("Peak amplitude dB"));
               mLevelTextCtrl->SetValidator(vldLevel);
               mLeveldB = S.AddVariableText(_("dB"), false,
                                            wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
               mWarning = S.AddVariableText( {}, false,
                                            wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
            }
            S.EndHorizontalLay();
#ifdef EXPERIMENTAL_R128_NORM
            mUseLoudnessCheckBox = S.AddCheckBox(_("Use loudness instead of peak amplitude"),
                                                 mUseLoudness);
            mUseLoudnessCheckBox->SetValidator(wxGenericValidator(&mGUIUseLoudness));
#endif
            mStereoIndCheckBox = S.AddCheckBox(_("Normalize stereo channels independently"),
                                               mStereoInd);
            mStereoIndCheckBox->SetValidator(wxGenericValidator(&mStereoInd));
         }
         S.EndVerticalLay();
      }
      S.EndMultiColumn();
   }
   S.EndVerticalLay();
#ifdef EXPERIMENTAL_R128_NORM
   // To ensure that the UpdateUI on creation sets the prompts correctly.
   mUseLoudness = !mGUIUseLoudness;
#endif
   mCreating = false;
}

bool EffectNormalize::TransferDataToWindow()
{
   if (!mUIParent->TransferDataToWindow())
   {
      return false;
   }

   UpdateUI();

   return true;
}

bool EffectNormalize::TransferDataFromWindow()
{
   if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
   {
      return false;
   }

   return true;
}

// EffectNormalize implementation

bool EffectNormalize::AnalyseTrack(const WaveTrack * track, const wxString &msg,
                                   double &progress, float &offset, float &extent)
{
   bool result = true;
   float min, max;

   if(mGain)
   {
#ifdef EXPERIMENTAL_R128_NORM
      if(mUseLoudness)
      {
         CalcEBUR128HPF(track->GetRate());
         CalcEBUR128HSF(track->GetRate());
         if(mDC)
         {
            result = AnalyseTrackData(track, msg, progress, ANALYSE_LOUDNESS_DC, offset);
         }
         else
         {
            result = AnalyseTrackData(track, msg, progress, ANALYSE_LOUDNESS, offset);
            offset = 0.0;
         }

         // EBU R128: z_i = mean square without root
         extent = mSqSum / mCount.as_double();
      }
      else
      {
#endif
         // Since we need complete summary data, we need to block until the OD tasks are done for this track
         // This is needed for track->GetMinMax
         // TODO: should we restrict the flags to just the relevant block files (for selections)
         while (ProjectFileManager::GetODFlags( *track )) {
            // update the gui
            if (ProgressResult::Cancelled == mProgress->Update(
               0, _("Waiting for waveform to finish computing...")) )
               return false;
            wxMilliSleep(100);
         }

         // set mMin, mMax.  No progress bar here as it's fast.
         auto pair = track->GetMinMax(mCurT0, mCurT1); // may throw
         min = pair.first, max = pair.second;

         if(mDC)
         {
            result = AnalyseTrackData(track, msg, progress, ANALYSE_DC, offset);
            min += offset;
            max += offset;
         }
#ifdef EXPERIMENTAL_R128_NORM
      }
#endif
   }
   else if(mDC)
   {
      min = -1.0, max = 1.0;   // sensible defaults?
      result = AnalyseTrackData(track, msg, progress, ANALYSE_DC, offset);
      min += offset;
      max += offset;
   }
   else
   {
      wxFAIL_MSG("Analysing Track when nothing to do!");
      min = -1.0, max = 1.0;   // sensible defaults?
      offset = 0.0;
   }
#ifdef EXPERIMENTAL_R128_NORM
   if(!mUseLoudness)
#endif
      extent = fmax(fabs(min), fabs(max));

   return result;
}

//AnalyseTrackData() takes a track, transforms it to bunch of buffer-blocks,
//and executes selected AnalyseOperation on it...
bool EffectNormalize::AnalyseTrackData(const WaveTrack * track, const wxString &msg,
                                double &progress, AnalyseOperation op, float &offset)
{
   bool rc = true;

   //Transform the marker timepoints to samples
   auto start = track->TimeToLongSamples(mCurT0);
   auto end = track->TimeToLongSamples(mCurT1);

   //Get the length of the buffer (as double). len is
   //used simply to calculate a progress meter, so it is easier
   //to make it a double now than it is to do it later
   auto len = (end - start).as_double();

   //Initiate a processing buffer.  This buffer will (most likely)
   //be shorter than the length of the track being processed.
   Floats buffer{ track->GetMaxBlockSize() };

   mSum   = 0.0; // dc offset inits
   mCount = 0;
#ifdef EXPERIMENTAL_R128_NORM
   mSqSum = 0.0; // rms init
#endif

   sampleCount blockSamples;
   sampleCount totalSamples = 0;

   //Go through the track one buffer at a time. s counts which
   //sample the current buffer starts at.
   auto s = start;
   while (s < end) {
      //Get a block of samples (smaller than the size of the buffer)
      //Adjust the block size if it is the final block in the track
      const auto block = limitSampleBufferSize(
         track->GetBestBlockSize(s),
         end - s
      );

      //Get the samples from the track and put them in the buffer
      track->Get((samplePtr) buffer.get(), floatSample, s, block, fillZero, true, &blockSamples);
      totalSamples += blockSamples;

      //Process the buffer.
      if(op == ANALYSE_DC)
         AnalyseDataDC(buffer.get(), block);
#ifdef EXPERIMENTAL_R128_NORM
      else if(op == ANALYSE_LOUDNESS)
         AnalyseDataLoudness(buffer.get(), block);
      else if(op == ANALYSE_LOUDNESS_DC)
         AnalyseDataLoudnessDC(buffer.get(), block);
#endif

      //Increment s one blockfull of samples
      s += block;

      //Update the Progress meter
      if (TotalProgress(progress +
                        ((s - start).as_double() / len)/double(2*GetNumWaveTracks()), msg)) {
         rc = false; //lda .. break, not return, so that buffer is deleted
         break;
      }
   }
   if( totalSamples > 0 )
      offset = -mSum / totalSamples.as_double();  // calculate actual offset (amount that needs to be added on)
   else
      offset = 0.0;

   progress += 1.0/double(2*GetNumWaveTracks());
   //Return true because the effect processing succeeded ... unless cancelled
   return rc;
}

//ProcessOne() takes a track, transforms it to bunch of buffer-blocks,
//and executes ProcessData, on it...
// uses mMult and offset to normalize a track.
// mMult must be set before this is called
bool EffectNormalize::ProcessOne(
   WaveTrack * track, const wxString &msg, double &progress, float offset)
{
   bool rc = true;

   //Transform the marker timepoints to samples
   auto start = track->TimeToLongSamples(mCurT0);
   auto end = track->TimeToLongSamples(mCurT1);

   //Get the length of the buffer (as double). len is
   //used simply to calculate a progress meter, so it is easier
   //to make it a double now than it is to do it later
   auto len = (end - start).as_double();

   //Initiate a processing buffer.  This buffer will (most likely)
   //be shorter than the length of the track being processed.
   Floats buffer{ track->GetMaxBlockSize() };

   //Go through the track one buffer at a time. s counts which
   //sample the current buffer starts at.
   auto s = start;
   while (s < end) {
      //Get a block of samples (smaller than the size of the buffer)
      //Adjust the block size if it is the final block in the track
      const auto block = limitSampleBufferSize(
         track->GetBestBlockSize(s),
         end - s
      );

      //Get the samples from the track and put them in the buffer
      track->Get((samplePtr) buffer.get(), floatSample, s, block);

      //Process the buffer.
      ProcessData(buffer.get(), block, offset);

      //Copy the newly-changed samples back onto the track.
      track->Set((samplePtr) buffer.get(), floatSample, s, block);

      //Increment s one blockfull of samples
      s += block;

      //Update the Progress meter
      if (TotalProgress(progress +
                        ((s - start).as_double() / len)/double(2*GetNumWaveTracks()), msg)) {
         rc = false; //lda .. break, not return, so that buffer is deleted
         break;
      }
   }
   progress += 1.0/double(2*GetNumWaveTracks());

   //Return true because the effect processing succeeded ... unless cancelled
   return rc;
}

/// @see AnalyseDataLoudnessDC
void EffectNormalize::AnalyseDataDC(float *buffer, size_t len)
{
   for(decltype(len) i = 0; i < len; i++)
      mSum += (double)buffer[i];
   mCount += len;
}

#ifdef EXPERIMENTAL_R128_NORM
/// @see AnalyseDataLoudnessDC
void EffectNormalize::AnalyseDataLoudness(float *buffer, size_t len)
{
   float value;
   for(decltype(len) i = 0; i < len; i++)
   {
      value = mR128HSF.ProcessOne(buffer[i]);
      value = mR128HPF.ProcessOne(value);
      mSqSum += ((double)value) * ((double)value);
   }
   mCount += len;
}

/// Calculates sample sum (for DC) and EBU R128 weighted square sum
/// (for loudness). This function has variants which only calculate
/// sum or square sum for performance improvements if only one of those
/// values is required.
/// @see AnalyseDataLoudness
/// @see AnalyseDataDC
void EffectNormalize::AnalyseDataLoudnessDC(float *buffer, size_t len)
{
   float value;
   for(decltype(len) i = 0; i < len; i++)
   {
      mSum += (double)buffer[i];
      value = mR128HSF.ProcessOne(buffer[i]);
      value = mR128HPF.ProcessOne(value);
      mSqSum += ((double)value) * ((double)value);
   }
   mCount += len;
}
#endif

void EffectNormalize::ProcessData(float *buffer, size_t len, float offset)
{
   for(decltype(len) i = 0; i < len; i++) {
      float adjFrame = (buffer[i] + offset) * mMult;
      buffer[i] = adjFrame;
   }
}

#ifdef EXPERIMENTAL_R128_NORM
// EBU R128 parameter sampling rate adaption after
// Mansbridge, Stuart, Saoirse Finn, and Joshua D. Reiss.
// "Implementation and Evaluation of Autonomous Multi-track Fader Control."
// Paper presented at the 132nd Audio Engineering Society Convention,
// Budapest, Hungary, 2012."
void EffectNormalize::CalcEBUR128HPF(float fs)
{
   double f0 = 38.13547087602444;
   double Q  =  0.5003270373238773;
   double K  = tan(M_PI * f0 / fs);

   mR128HPF.Reset();

   mR128HPF.fNumerCoeffs[Biquad::B0] =  1.0;
   mR128HPF.fNumerCoeffs[Biquad::B1] = -2.0;
   mR128HPF.fNumerCoeffs[Biquad::B2] =  1.0;

   mR128HPF.fDenomCoeffs[Biquad::A1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K);
   mR128HPF.fDenomCoeffs[Biquad::A2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K);
}

// EBU R128 parameter sampling rate adaption after
// Mansbridge, Stuart, Saoirse Finn, and Joshua D. Reiss.
// "Implementation and Evaluation of Autonomous Multi-track Fader Control."
// Paper presented at the 132nd Audio Engineering Society Convention,
// Budapest, Hungary, 2012."
void EffectNormalize::CalcEBUR128HSF(float fs)
{
   double db =    3.999843853973347;
   double f0 = 1681.974450955533;
   double Q  =    0.7071752369554196;
   double K  = tan(M_PI * f0 / fs);

   double Vh = pow(10.0, db / 20.0);
   double Vb = pow(Vh, 0.4996667741545416);

   double a0 = 1.0 + K / Q + K * K;

   mR128HSF.Reset();

   mR128HSF.fNumerCoeffs[Biquad::B0] = (Vh + Vb * K / Q + K * K) / a0;
   mR128HSF.fNumerCoeffs[Biquad::B1] =       2.0 * (K * K -  Vh) / a0;
   mR128HSF.fNumerCoeffs[Biquad::B2] = (Vh - Vb * K / Q + K * K) / a0;

   mR128HSF.fDenomCoeffs[Biquad::A1] =   2.0 * (K * K - 1.0) / a0;
   mR128HSF.fDenomCoeffs[Biquad::A2] = (1.0 - K / Q + K * K) / a0;
}
#endif

void EffectNormalize::OnUpdateUI(wxCommandEvent & WXUNUSED(evt))
{
   UpdateUI();
}

void EffectNormalize::UpdateUI()
{

   if (!mUIParent->TransferDataFromWindow())
   {
      mWarning->SetLabel(_("(Maximum 0dB)"));
      EnableApply(false);
      return;
   }
   mWarning->SetLabel(wxT(""));

#ifdef EXPERIMENTAL_R128_NORM
   // Changing the prompts causes an unwanted UpdateUI event.  
   // This 'guard' stops that becoming an infinite recursion.
   if (mUseLoudness != mGUIUseLoudness)
   {
      mUseLoudness = mGUIUseLoudness;
      if (mUseLoudness)
      {
         FloatingPointValidator<double> vldLevel(2, &mLUFSLevel, NumValidatorStyle::ONE_TRAILING_ZERO);
         vldLevel.SetRange(MIN_LUFSLevel, MAX_LUFSLevel);
         mLevelTextCtrl->SetValidator(vldLevel);
         /* i18n-hint: LUFS is a particular method for measuring loudnesss */
         mLevelTextCtrl->SetName(_("Loudness LUFS"));
         mLevelTextCtrl->SetValue(wxString::FromDouble(mLUFSLevel));
         /* i18n-hint: LUFS is a particular method for measuring loudnesss */
         mLeveldB->SetLabel(_("LUFS"));
         mGainCheckBox->SetLabelText(_("Normalize loudness to"));
      }
      else
      {
         FloatingPointValidator<double> vldLevel(2, &mPeakLevel, NumValidatorStyle::ONE_TRAILING_ZERO);
         vldLevel.SetRange(MIN_PeakLevel, MAX_PeakLevel);
         mLevelTextCtrl->SetValidator(vldLevel);
         mLevelTextCtrl->SetName(_("Peak amplitude dB"));
         mLevelTextCtrl->SetValue(wxString::FromDouble(mPeakLevel));
         mLeveldB->SetLabel(_("dB"));
         mGainCheckBox->SetLabelText(_("Normalize peak amplitude to"));
      }
   }
#endif

   // Disallow level stuff if not normalizing
   mLevelTextCtrl->Enable(mGain);
   mLeveldB->Enable(mGain);
   mStereoIndCheckBox->Enable(mGain);
#ifdef EXPERIMENTAL_R128_NORM
   mUseLoudnessCheckBox->Enable(mGain);
#endif

   // Disallow OK/Preview if doing nothing
   EnableApply(mGain || mDC);
}
