Update of /cvsroot/audacity/audacity-src/src/effects
In directory sc8-pr-cvs11.sourceforge.net:/tmp/cvs-serv28214/effects

Modified Files:
        LoadEffects.cpp 
Added Files:
        AutoDuck.cpp AutoDuck.h 
Log Message:
Implement Auto Duck effect

Index: LoadEffects.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/LoadEffects.cpp,v
retrieving revision 1.54
retrieving revision 1.55
diff -u -d -r1.54 -r1.55
--- LoadEffects.cpp     17 Jan 2007 23:48:19 -0000      1.54
+++ LoadEffects.cpp     26 Jan 2007 11:06:17 -0000      1.55
@@ -16,6 +16,7 @@
 
 #include "Amplify.h"
 // #include "AvcCompressor.h"
+#include "AutoDuck.h"
 #include "BassBoost.h"
 #include "ChangeSpeed.h"
 #include "ClickRemoval.h"
@@ -90,6 +91,8 @@
    // In this list, designating an effect as 'SIMPLE_EFFECT' just means
    // that it should be included in even the most basic of menus.
    // This was introduced for CleanSpeech mode.
+   
+   Effect::RegisterEffect(new EffectAutoDuck());
    Effect::RegisterEffect(new EffectBassBoost());
    Effect::RegisterEffect(new EffectChangeSpeed());
        #ifdef USE_SOUNDTOUCH

--- NEW FILE: AutoDuck.cpp ---
/**********************************************************************

  Audacity: A Digital Audio Editor

  AutoDuck.cpp

  Markus Meyer

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

\class EffectAutoDuck
\brief Implements the Auto Ducking effect

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

#include <math.h>
#include <wx/sizer.h>
#include <wx/dynarray.h>
#include <wx/dcclient.h>
#include <wx/dcmemory.h>

#include "../Audacity.h"
#include "../Prefs.h"
#include "../Internat.h"
#include "../Theme.h"
#include "../AllThemeResources.h"
#include "../AColor.h"

#include "AutoDuck.h"

#include <wx/arrimpl.cpp>

/*
 * Default values for effect params
 */
#define PARAM_DEFAULT_DUCK_AMOUNT_DB -12.0
#define PARAM_DEFAULT_FADE_DOWN_LEN 0.5
#define PARAM_DEFAULT_FADE_UP_LEN 0.5
#define PARAM_DEFAULT_THRESHOLD_DB -30.0
#define PARAM_DEFAULT_MAXIMUM_PAUSE 0

/*
 * Common constants
 */

#define BUF_SIZE 4096 // number of samples to process at once
#define RMS_WINDOW_SIZE 100 // samples in circular RMS window buffer

/*
 * A auto duck region and an array of auto duck regions
 */

struct AutoDuckRegion
{
   AutoDuckRegion(double t0, double t1)
   {
      this->t0 = t0;
      this->t1 = t1;
   }
   
   double t0;
   double t1;
};

WX_DECLARE_OBJARRAY(AutoDuckRegion, AutoDuckRegionArray);
WX_DEFINE_OBJARRAY(AutoDuckRegionArray);

/*
 * Effect implementation
 */

EffectAutoDuck::EffectAutoDuck()
{
   SetEffectFlags(BUILTIN_EFFECT | PROCESS_EFFECT | ADVANCED_EFFECT);
   
   mControlTrack = NULL;
}

bool EffectAutoDuck::Init()
{
   gPrefs->Read(wxT("/Effects/AutoDuck/DuckAmountDb"),
      &mDuckAmountDb, PARAM_DEFAULT_DUCK_AMOUNT_DB);
   gPrefs->Read(wxT("/Effects/AutoDuck/FadeDownLen"),
      &mFadeDownLen, PARAM_DEFAULT_FADE_DOWN_LEN);
   gPrefs->Read(wxT("/Effects/AutoDuck/FadeUpLen"),
      &mFadeUpLen, PARAM_DEFAULT_FADE_UP_LEN);
   gPrefs->Read(wxT("/Effects/AutoDuck/ThresholdDb"),
      &mThresholdDb, PARAM_DEFAULT_THRESHOLD_DB);
   gPrefs->Read(wxT("/Effects/AutoDuck/MaximumPause"),
      &mMaximumPause, PARAM_DEFAULT_MAXIMUM_PAUSE);

   mControlTrack = NULL;
   
   TrackListIterator iter(mTracks);
   Track *t = iter.First();
   
   bool lastWasSelectedWaveTrack = false;
   WaveTrack *controlTrackCandidate = NULL;
   
   while(t)
   {
      if (lastWasSelectedWaveTrack && !t->GetSelected() &&
          t->GetKind() == Track::Wave)
      {
         // This could be the control track, so remember it
         controlTrackCandidate = (WaveTrack*)t;
      }

      lastWasSelectedWaveTrack = false;
      
      if (t->GetSelected())
      {
         if (t->GetKind() == Track::Wave)
         {
            lastWasSelectedWaveTrack = true;
         } else
         {
            wxMessageBox(
               _("You selected a track which does not contain audio. AutoDuck 
can only process audio tracks."),
               _("AutoDuck"), wxICON_ERROR, mParent);
            return false;
         }
      }

      t = iter.Next();
   }
   
   if (!controlTrackCandidate)
   {
      wxMessageBox(
         _("AutoDuck needs a control track which must be placed below the 
selected track(s)."),
         _("AutoDuck"), wxICON_ERROR, mParent);
      return false;
   }
   
   mControlTrack = controlTrackCandidate;
   wxASSERT(mWaveTracks);

   return true;
}

bool EffectAutoDuck::TransferParameters( Shuttle & shuttle )
{
   shuttle.TransferDouble(wxT("DuckAmountDb"), mDuckAmountDb,
      PARAM_DEFAULT_DUCK_AMOUNT_DB);
   shuttle.TransferDouble(wxT("FadeDownLen"), mFadeDownLen,
      PARAM_DEFAULT_FADE_DOWN_LEN);
   shuttle.TransferDouble(wxT("FadeUpLen"), mFadeUpLen,
      PARAM_DEFAULT_FADE_UP_LEN);
   shuttle.TransferDouble(wxT("ThresholdDb"), mThresholdDb,
      PARAM_DEFAULT_THRESHOLD_DB);
   shuttle.TransferDouble(wxT("MaximumPause"), mMaximumPause,
      PARAM_DEFAULT_MAXIMUM_PAUSE);

   return true;
}

bool EffectAutoDuck::CheckWhetherSkipEffect()
{
   return false;
}

void EffectAutoDuck::End()
{
   mControlTrack = NULL;
}

bool EffectAutoDuck::PromptUser()
{
   EffectAutoDuckDialog dlog(this, mParent);
   
   if (dlog.ShowModal() != wxID_OK)
      return false; // user cancelled dialog
      
   gPrefs->Write(wxT("/Effects/AutoDuck/DuckAmountDb"), mDuckAmountDb);
   gPrefs->Write(wxT("/Effects/AutoDuck/FadeDownLen"), mFadeDownLen);
   gPrefs->Write(wxT("/Effects/AutoDuck/FadeUpLen"), mFadeUpLen);
   gPrefs->Write(wxT("/Effects/AutoDuck/ThresholdDb"), mThresholdDb);
   gPrefs->Write(wxT("/Effects/AutoDuck/MaximumPause"), mMaximumPause);

   return true;
}

bool EffectAutoDuck::Process()
{
   int i;
   
   if (!mWaveTracks || !mControlTrack)
      return false;

   bool cancel = false;

   longSampleCount start =
      mControlTrack->TimeToLongSamples(mT0 + mFadeDownLen);
   longSampleCount end =
      mControlTrack->TimeToLongSamples(mT1 - mFadeUpLen);
   
   if (end <= start)
      return false;

   // the minimum number of samples we have to wait until the maximum
   // pause has been exceeded
   double maxPause = mMaximumPause;
   if (maxPause < mFadeDownLen + mFadeUpLen)
      maxPause = mFadeDownLen + mFadeUpLen;
   longSampleCount minSamplesPause =
      mControlTrack->TimeToLongSamples(maxPause);

   double threshold = pow(10.0, mThresholdDb/20);
      
   // adjust the threshold so we can compare it to the rmsSum value
   threshold = threshold * threshold * RMS_WINDOW_SIZE;

   int rmsPos = 0;
   float rmsSum = 0;
   float *rmsWindow = new float[RMS_WINDOW_SIZE];
   for (i = 0; i < RMS_WINDOW_SIZE; i++)
      rmsWindow[i] = 0;
   
   float *buf = new float[BUF_SIZE];
   
   bool inDuckRegion = false;

   // initialize the following two variables to prevent compiler warning
   double duckRegionStart = 0;
   sampleCount curSamplesPause = 0;
   
   // to make the progress bar appear more natural, we first look for all
   // duck regions and apply them all at once afterwards
   AutoDuckRegionArray regions;
   longSampleCount pos = start;
   
   while (pos < end)
   {
      longSampleCount len = end - pos;
      if (len > BUF_SIZE)
         len = BUF_SIZE;
      
      mControlTrack->Get((samplePtr)buf, floatSample, pos, (sampleCount)len);
      
      for (i = pos; i < pos + len; i++)
      {
         rmsSum -= rmsWindow[rmsPos];
         rmsWindow[rmsPos] = buf[i - pos] * buf[i - pos];
         rmsSum += rmsWindow[rmsPos];
         rmsPos = (rmsPos + 1) % RMS_WINDOW_SIZE;

         bool thresholdExceeded = rmsSum > threshold;
         
         if (thresholdExceeded)
         {
            // everytime the threshold is exceeded, reset our count for
            // the number of pause samples
            curSamplesPause = 0;
            
            if (!inDuckRegion)
            {
               // the threshold has been exceeded for the first time, so
               // let the duck region begin here
               inDuckRegion = true;
               duckRegionStart = mControlTrack->LongSamplesToTime(i);
            }
         }
         
         if (!thresholdExceeded && inDuckRegion)
         {
            // the threshold has not been exceeded and we are in a duck
            // region, but only fade in if the maximum pause has been
            // exceeded
            curSamplesPause += 1;
            
            if (curSamplesPause >= minSamplesPause)
            {
               // do the actual duck fade and reset all values
               double duckRegionEnd =
                  mControlTrack->LongSamplesToTime(i - curSamplesPause);
                  
               regions.Add(AutoDuckRegion(
                              duckRegionStart - mFadeDownLen,
                              duckRegionEnd + mFadeUpLen));

               inDuckRegion = false;
            }
         }
      }
      
      pos += len;

      if (TotalProgress( ((double)(pos-start)) / (end-start) /
                         (GetNumWaveTracks() + 1) ))
      {
         cancel = true;
         break;
      }
   }
   
   // apply last duck fade, if any
   if (inDuckRegion)
   {
      double duckRegionEnd =
         mControlTrack->LongSamplesToTime(end - curSamplesPause);
      regions.Add(AutoDuckRegion(
                     duckRegionStart - mFadeDownLen,
                     duckRegionEnd + mFadeUpLen));
   }
   
   delete[] buf;
   delete[] rmsWindow;

   if (!cancel)
   {
      TrackListIterator iter(mWaveTracks);
      Track *iterTrack = iter.First();
      
      int trackNumber = 0;
      
      while (iterTrack)
      {
         wxASSERT(iterTrack->GetKind() == Track::Wave);
         
         WaveTrack* t = (WaveTrack*)iterTrack;
      
         for (i = 0; i < (int)regions.GetCount(); i++)
         {
            const AutoDuckRegion& region = regions[i];
            if (ApplyDuckFade(trackNumber, t, region.t0, region.t1))
            {
               cancel = true;
               break;
            }
         }
         
         if (cancel)
            break;

         iterTrack = iter.Next();
         trackNumber++;
      }
   }
   
   return !cancel;
}

// this currently does an exponential fade
bool EffectAutoDuck::ApplyDuckFade(int trackNumber, WaveTrack* t,
                                   double t0, double t1)
{
   bool cancel = false;
   
   int start = t->TimeToLongSamples(t0);
   int end = t->TimeToLongSamples(t1);
   
   float *buf = new float[BUF_SIZE];
   int pos = start;
   
   int fadeDownSamples = t->TimeToLongSamples(mFadeDownLen);
   if (fadeDownSamples < 1)
      fadeDownSamples = 1;

   int fadeUpSamples = t->TimeToLongSamples(mFadeUpLen);
   if (fadeUpSamples < 1)
      fadeUpSamples = 1;
      
   float fadeDownStep = mDuckAmountDb / fadeDownSamples;
   float fadeUpStep = mDuckAmountDb / fadeUpSamples;

   while (pos < end)
   {
      longSampleCount len = end - pos;
      if (len > BUF_SIZE)
         len = BUF_SIZE;

      t->Get((samplePtr)buf, floatSample, pos, (sampleCount)len);
      
      for (int i = pos; i < pos + len; i++)
      {
         float gainDown = fadeDownStep * (i - start);
         float gainUp = fadeUpStep * (end - i);;
         
         float gain;
         if (gainDown > gainUp)
            gain = gainDown;
         else
            gain = gainUp;
         if (gain < mDuckAmountDb)
            gain = mDuckAmountDb;

         buf[i - pos] *= pow(10.0, gain / 20.0);
      }
      
      t->Set((samplePtr)buf, floatSample, pos, (sampleCount)len);

      pos += len;
      
      float curTime = t->LongSamplesToTime(pos);
      float fractionFinished = (curTime - mT0) / (mT1 - mT0);
      if (TotalProgress( (trackNumber + 1 + fractionFinished) /
                         (GetNumWaveTracks() + 1) ))
      {
         cancel = true;
         break;
      }
   }

   return cancel;
}

/*
 * Effect dialog implementation
 */
 
#define ID_DUCK_AMOUNT_DB  10001
#define ID_THRESHOLD_DB    10002
#define ID_FADE_DOWN_LEN   10003
#define ID_FADE_UP_LEN     10004
#define ID_MAXIMUM_PAUSE   10005
#define ID_PANEL           10006

BEGIN_EVENT_TABLE(EffectAutoDuckDialog, wxDialog)
   EVT_BUTTON(wxID_OK, EffectAutoDuckDialog::OnOk)
   EVT_BUTTON(wxID_OK, EffectAutoDuckDialog::OnCancel)
   EVT_TEXT(ID_DUCK_AMOUNT_DB, EffectAutoDuckDialog::OnValueChanged)
   EVT_TEXT(ID_THRESHOLD_DB, EffectAutoDuckDialog::OnValueChanged)
   EVT_TEXT(ID_FADE_DOWN_LEN, EffectAutoDuckDialog::OnValueChanged)
   EVT_TEXT(ID_FADE_UP_LEN, EffectAutoDuckDialog::OnValueChanged)
   EVT_TEXT(ID_MAXIMUM_PAUSE, EffectAutoDuckDialog::OnValueChanged)
END_EVENT_TABLE()

EffectAutoDuckDialog::EffectAutoDuckDialog(EffectAutoDuck* effect,
   wxWindow *parent) : wxDialog(parent, -1, _("Auto Duck"))
{
   mEffect = effect;

   ShuttleGui S(this, eIsCreating);
   
   S.SetBorder(5);
   S.StartVerticalLay(true);
   {
      S.StartHorizontalLay(wxCENTER, false);
      {
         S.AddTitle(_("Auto Duck by Markus Meyer"));
      }
      S.EndHorizontalLay();

      S.StartHorizontalLay(wxCENTER, false);
      {
         // Add a little space
      }
      S.EndHorizontalLay();
      
      mPanel = (EffectAutoDuckPanel*)
         S.AddWindow(new EffectAutoDuckPanel(this, ID_PANEL));

      S.StartHorizontalLay(wxCENTER, false);
      {
         // Add a little space
      }
      S.EndHorizontalLay();

      S.StartMultiColumn(6, wxCENTER);
      {
         mDuckAmountDbBox = S.Id(ID_DUCK_AMOUNT_DB).AddTextBox(
            _("Duck Amount:"),
            Internat::ToDisplayString(mEffect->mDuckAmountDb), 10);
         S.AddUnits(_("db"));
         
         mMaximumPauseBox = S.Id(ID_MAXIMUM_PAUSE).AddTextBox(
            _("Maximum Pause:"),
            Internat::ToDisplayString(mEffect->mMaximumPause), 10);
         S.AddUnits(_("seconds"));

         mFadeDownLenBox = S.Id(ID_FADE_DOWN_LEN).AddTextBox(
            _("Fade Down Length:"),
            Internat::ToDisplayString(mEffect->mFadeDownLen), 10);
         S.AddUnits(_("seconds"));
         
         mThresholdDbBox = S.Id(ID_THRESHOLD_DB).AddTextBox(
            _("Threshold:"),
            Internat::ToDisplayString(mEffect->mThresholdDb), 10);
         S.AddUnits(_("db"));

         mFadeUpLenBox = S.Id(ID_FADE_UP_LEN).AddTextBox(
            _("Fade Up Length:"),
            Internat::ToDisplayString(mEffect->mFadeUpLen), 10);
         S.AddUnits(_("seconds"));

      }
      S.EndMultiColumn();

      S.StartHorizontalLay(wxALIGN_CENTER, false);
      {
#if defined(__WXGTK20__) || defined(__WXMAC__)
         S.Id(wxID_CANCEL).AddButton(_("&Cancel"));
         S.Id(wxID_OK).AddButton(_("&OK"))->SetDefault();
#else
         S.Id(wxID_OK).AddButton(_("&OK"))->SetDefault();
         S.Id(wxID_CANCEL).AddButton(_("&Cancel"));
#endif
      }
      S.EndHorizontalLay();
   }
   S.EndVerticalLay();

   GetSizer()->AddSpacer(5);
   Layout();
   Fit();
   SetMinSize(GetSize());
   Center();
}

void EffectAutoDuckDialog::OnOk(wxCommandEvent& evt)
{
   double duckAmountDb = 0, thresholdDb = 0, fadeDownLen = 0;
   double fadeUpLen = 0, maximumPause = 0;
   
   bool success = 
      mDuckAmountDbBox->GetValue().ToDouble(&duckAmountDb) &&
      mEffect->mDuckAmountDb > -100 &&
      mEffect->mDuckAmountDb < 0 &&
      mThresholdDbBox->GetValue().ToDouble(&thresholdDb) &&
      mEffect->mThresholdDb > -100 &&
      mEffect->mThresholdDb < 0 &&
      mFadeDownLenBox->GetValue().ToDouble(&fadeDownLen) &&
      mEffect->mFadeDownLen >= 0 &&
      mEffect->mFadeDownLen < 1000 &&
      mFadeUpLenBox->GetValue().ToDouble(&fadeUpLen) &&
      mEffect->mFadeUpLen >= 0 &&
      mEffect->mFadeUpLen < 1000 &&
      mMaximumPauseBox->GetValue().ToDouble(&maximumPause) &&
      mEffect->mMaximumPause >= 0 &&
      mEffect->mMaximumPause < 1000;
      
   if (!success)
   {
      wxMessageBox(_("Please enter valid values."), _("Auto Duck"),
         wxICON_ERROR, this);
      return;
   }
   
   mEffect->mDuckAmountDb = duckAmountDb;
   mEffect->mThresholdDb = thresholdDb;
   mEffect->mFadeDownLen = fadeDownLen;
   mEffect->mFadeUpLen = fadeUpLen;
   mEffect->mMaximumPause = maximumPause;

   EndModal(wxID_OK);
}

void EffectAutoDuckDialog::OnCancel(wxCommandEvent& evt)
{
   EndModal(wxID_CANCEL);
}

void EffectAutoDuckDialog::OnValueChanged(wxCommandEvent& evt)
{
   mPanel->Refresh(false);
}

/*
 * Effect dialog panel implementation
 */

#define CONTROL_POINT_REGION 10 // pixel distance to click on a control point
#define CONTROL_POINT_MIN_MOVE 5 // min mouse move until value is changed

#define TEXT_DISTANCE 15 // pixel distance text <-> center of control point

#define FADE_DOWN_START 50 // x coordinate
#define FADE_UP_START 550 // x coordinate
#define DUCK_AMOUNT_START 50 // y coordinate

#define MAX_DUCK_AMOUNT 0 // db
#define MIN_DUCK_AMOUNT -24 // db

#define MIN_FADE 0 // seconds
#define MAX_FADE 4.5 // seconds

#define FADE_SCALE 50 // scale factor for second -> pixel conversion
#define DUCK_AMOUNT_SCALE 8 // scale factor for db -> pixel conversion

static int GetDistance(const wxPoint& first, const wxPoint& second)
{
   int distanceX = abs(first.x - second.x);
   int distanceY = abs(first.y - second.y);
   if (distanceX > distanceY)
      return distanceX;
   else
      return distanceY;
}

BEGIN_EVENT_TABLE(EffectAutoDuckPanel, wxPanel)
   EVT_PAINT(EffectAutoDuckPanel::OnPaint)
   EVT_MOUSE_CAPTURE_CHANGED(EffectAutoDuckPanel::OnMouseCaptureChanged)
   EVT_LEFT_DOWN(EffectAutoDuckPanel::OnLeftDown)
   EVT_LEFT_UP(EffectAutoDuckPanel::OnLeftUp)
   EVT_MOTION(EffectAutoDuckPanel::OnMotion)
END_EVENT_TABLE()

EffectAutoDuckPanel::EffectAutoDuckPanel(EffectAutoDuckDialog* parent,
   wxWindowID id) : wxPanel(parent, id, wxDefaultPosition, wxSize(600, 300))
{
   mParent = parent;
   mCurrentControlPoint = none;
   mBackgroundBitmap = NULL;
   
   ResetControlPoints();
}

EffectAutoDuckPanel::~EffectAutoDuckPanel()
{
   if (mBackgroundBitmap)
      delete mBackgroundBitmap;
}

void EffectAutoDuckPanel::ResetControlPoints()
{
   mControlPoints[fadeDown] = wxPoint(-100,-100);
   mControlPoints[fadeUp] = wxPoint(-100,-100);
   mControlPoints[duckAmount] = wxPoint(-100,-100);
}

void EffectAutoDuckPanel::OnPaint(wxPaintEvent& evt)
{
   int clientWidth, clientHeight;
   GetSize(&clientWidth, &clientHeight);
   
   if (!mBackgroundBitmap || mBackgroundBitmap->GetWidth() != clientWidth ||
       mBackgroundBitmap->GetHeight() != clientHeight)
   {
      if (mBackgroundBitmap)
         delete mBackgroundBitmap;
      mBackgroundBitmap = new wxBitmap(clientWidth, clientHeight);
   }
   
   wxMemoryDC dc;
   dc.SelectObject(*mBackgroundBitmap);

   dc.SetBrush(*wxWHITE_BRUSH);
   dc.SetPen(*wxBLACK_PEN);
   dc.DrawRectangle(0, 0, clientWidth, clientHeight);
   
   dc.SetFont(wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
                     wxFONTWEIGHT_NORMAL));
   dc.SetTextForeground(*wxBLACK);
   dc.SetTextBackground(*wxWHITE);

   double duckAmountDb = 0;
   double fadeDownLen = 0;
   double fadeUpLen = 0;
   mParent->mDuckAmountDbBox->GetValue().ToDouble(&duckAmountDb);
   mParent->mFadeDownLenBox->GetValue().ToDouble(&fadeDownLen);
   mParent->mFadeUpLenBox->GetValue().ToDouble(&fadeUpLen);
   
   if (fadeDownLen < MIN_FADE || fadeDownLen > MAX_FADE ||
       fadeUpLen < MIN_FADE || fadeUpLen > MAX_FADE ||
       duckAmountDb < MIN_DUCK_AMOUNT || duckAmountDb > MAX_DUCK_AMOUNT)
   {
      // values are out of range, no preview available
      wxString message = wxString::Format(_("Preview not available"));
      int textWidth = 0, textHeight = 0;
      dc.GetTextExtent(message, &textWidth, &textHeight);
      dc.DrawText(message, (clientWidth - textWidth) / 2,
                           (clientHeight - textHeight) / 2);

      ResetControlPoints();
   } else
   {
      // draw preview
      dc.SetBrush(*wxTRANSPARENT_BRUSH);
      dc.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxSOLID));
      
      wxPoint points[6];
      
      points[0].x = FADE_DOWN_START - 30;
      points[0].y = DUCK_AMOUNT_START;
      
      points[1].x = FADE_DOWN_START;
      points[1].y = DUCK_AMOUNT_START;
      
      points[2].x = FADE_DOWN_START + (int)(fadeDownLen * FADE_SCALE);
      points[2].y = DUCK_AMOUNT_START -
         (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
      
      points[3].x = FADE_UP_START - (int)(fadeUpLen * FADE_SCALE);
      points[3].y = DUCK_AMOUNT_START -
         (int)(duckAmountDb * DUCK_AMOUNT_SCALE);
      
      points[4].x = FADE_UP_START;
      points[4].y = DUCK_AMOUNT_START;
      
      points[5].x = FADE_UP_START + 30;
      points[5].y = DUCK_AMOUNT_START;
      
      dc.DrawLines(6, points);
      
      dc.SetPen(AColor::envelopePen);
      dc.SetBrush(*wxWHITE_BRUSH);
      
      mControlPoints[fadeDown] = wxPoint(points[2].x - 3, points[2].y - 2);
      mControlPoints[fadeUp] = wxPoint(points[3].x - 3, points[3].y - 2);
      mControlPoints[duckAmount] = wxPoint(
         (points[2].x + points[3].x) / 2 - 3, points[2].y - 3);
         
      for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
      {
         int digits;
         float value;
         
         if (i == (int)fadeDown)
         {
            value = fadeDownLen;
            digits = 2;
         }
         else if (i == (int)fadeUp)
         {
            value = fadeUpLen;
            digits = 2;
         }
         else
         {
            value = duckAmountDb;
            digits = 3;
         }

         wxString valueStr = Internat::ToDisplayString(value, digits);
         valueStr += wxT(" ");
         
         if (i == (int)duckAmount)
            valueStr += wxT("db"); // i18n-hint: short form of 'decibels'
         else
            valueStr += _("s"); // i18n-hint: short form of 'seconds'
   
         int textWidth = 0, textHeight = 0;
         GetTextExtent(valueStr, &textWidth, &textHeight);
         
         int textPosX = mControlPoints[i].x - textWidth / 2;
         int textPosY = mControlPoints[i].y;
         
         if (i == (int)duckAmount)
            textPosY -= TEXT_DISTANCE + textHeight;
         else
            textPosY += TEXT_DISTANCE;
            
         dc.DrawText(valueStr, textPosX, textPosY);

         dc.DrawEllipse(mControlPoints[i].x,
                        mControlPoints[i].y, 6, 6);
      }
   }

   // copy background buffer to paint dc
   wxPaintDC paintDC(this);
   paintDC.Blit(0, 0, clientWidth, clientHeight, &dc, 0, 0);

   // clean up: necessary to free resources on Windows
   dc.SetPen(wxNullPen);
   dc.SetBrush(wxNullBrush);
   dc.SetFont(wxNullFont);
   dc.SelectObject(wxNullBitmap);
}

void EffectAutoDuckPanel::OnMouseCaptureChanged(
   wxMouseCaptureChangedEvent &evt)
{
   SetCursor(wxNullCursor);
   mCurrentControlPoint = none;
}

EffectAutoDuckPanel::EControlPoint
   EffectAutoDuckPanel::GetNearestControlPoint(const wxPoint& pt)
{
   int dist[AUTO_DUCK_PANEL_NUM_CONTROL_POINTS];
   int i;
   
   for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
      dist[i] = GetDistance(pt, mControlPoints[i]);
      
   int curMinimum = 0;
   for (i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
      if (dist[i] < dist[curMinimum])
         curMinimum = i;
   
   if (dist[curMinimum] <= CONTROL_POINT_REGION)
      return (EControlPoint)curMinimum;
   else
      return none;
}

void EffectAutoDuckPanel::OnLeftDown(wxMouseEvent &evt)
{
   EControlPoint nearest = GetNearestControlPoint(evt.GetPosition());
   
   if (nearest != none)
   {
      // this control point has been clicked
      mMouseDownPoint = evt.GetPosition();
      
      mCurrentControlPoint = nearest;
      mControlPointMoveActivated = false;
      
      for (int i = 0; i < AUTO_DUCK_PANEL_NUM_CONTROL_POINTS; i++)
         mMoveStartControlPoints[i] = mControlPoints[i];
         
      CaptureMouse();
   }
}

void EffectAutoDuckPanel::OnLeftUp(wxMouseEvent &evt)
{
   if (mCurrentControlPoint != none)
   {
      mCurrentControlPoint = none;
      ReleaseMouse();
   }
}

void EffectAutoDuckPanel::OnMotion(wxMouseEvent &evt)
{
   switch (GetNearestControlPoint(evt.GetPosition()))
   {
   case none:
      SetCursor(wxNullCursor);
      break;
   case fadeDown:
   case fadeUp:
      SetCursor(wxCursor(wxCURSOR_SIZEWE));
      break;
   case duckAmount:
      SetCursor(wxCursor(wxCURSOR_SIZENS));
      break;
   }
   
   if (mCurrentControlPoint != none)
   {
      if (!mControlPointMoveActivated)
      {
         int dist;
         
         if (mCurrentControlPoint == duckAmount)
            dist = abs(evt.GetY() - mMouseDownPoint.y);
         else
            dist = abs(evt.GetX() - mMouseDownPoint.x);

         if (dist >= CONTROL_POINT_MIN_MOVE)
            mControlPointMoveActivated = true;
      }

      if (mControlPointMoveActivated)
      {
         int dist;
         
         if (mCurrentControlPoint == duckAmount)
            dist = abs(evt.GetY() -
               mMoveStartControlPoints[mCurrentControlPoint].y);
         else
            dist = abs(evt.GetX() -
               mMoveStartControlPoints[mCurrentControlPoint].x);

         float newValue;
         
         switch (mCurrentControlPoint)
         {
         case fadeDown:
            newValue = ((double)(evt.GetX() - FADE_DOWN_START)) / FADE_SCALE;
            if (newValue < MIN_FADE)
               newValue = MIN_FADE;
            if (newValue > MAX_FADE)
               newValue = MAX_FADE;
            mParent->mFadeDownLenBox->SetValue(
               Internat::ToDisplayString(newValue));
            break;
         case fadeUp:
            newValue = ((double)(FADE_UP_START - evt.GetX())) / FADE_SCALE;
            if (newValue < MIN_FADE)
               newValue = MIN_FADE;
            if (newValue > MAX_FADE)
               newValue = MAX_FADE;
            mParent->mFadeUpLenBox->SetValue(
               Internat::ToDisplayString(newValue));
            break;
         case duckAmount:
            newValue = ((double)(DUCK_AMOUNT_START - evt.GetY())) /
                           DUCK_AMOUNT_SCALE;
            if (newValue < MIN_DUCK_AMOUNT)
               newValue = MIN_DUCK_AMOUNT;
            if (newValue > MAX_DUCK_AMOUNT)
               newValue = MAX_DUCK_AMOUNT;
            mParent->mDuckAmountDbBox->SetValue(
               Internat::ToDisplayString(newValue));
            break;
         case none:
            wxASSERT(false); // should not happen
         }
         
         Refresh(false);
      }
   }
}

--- NEW FILE: AutoDuck.h ---
/**********************************************************************

  Audacity: A Digital Audio Editor

  AutoDuck.h

  Markus Meyer

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

#ifndef __AUDACITY_EFFECT_AUTODUCK__
#define __AUDACITY_EFFECT_AUTODUCK__

#include <wx/dialog.h>
#include <wx/panel.h>
#include <wx/textctrl.h>

#include "Effect.h"

class EffectAutoDuck;
class EffectAutoDuckPanel;

class EffectAutoDuckDialog: public wxDialog
{
public:
   EffectAutoDuckDialog(EffectAutoDuck* effect, wxWindow* parent);

private:
   friend class EffectAutoDuckPanel;

   void OnOk(wxCommandEvent& evt);
   void OnCancel(wxCommandEvent& evt);
   void OnValueChanged(wxCommandEvent& evt);
   
   EffectAutoDuck* mEffect;
   
   wxTextCtrl* mDuckAmountDbBox;
   wxTextCtrl* mFadeDownLenBox;
   wxTextCtrl* mFadeUpLenBox;
   wxTextCtrl* mThresholdDbBox;
   wxTextCtrl* mMaximumPauseBox;
   EffectAutoDuckPanel* mPanel;
   
   DECLARE_EVENT_TABLE()
};

#define AUTO_DUCK_PANEL_NUM_CONTROL_POINTS 3

class EffectAutoDuckPanel: public wxPanel
{
public:
   EffectAutoDuckPanel(EffectAutoDuckDialog* parent, wxWindowID id);
   virtual ~EffectAutoDuckPanel();
   
private:
   enum EControlPoint
   {
      fadeDown = 0,
      fadeUp,
      duckAmount,
      none = 99,
   };

   void OnPaint(wxPaintEvent& evt);
   void OnMouseCaptureChanged(wxMouseCaptureChangedEvent &evt);
   void OnLeftDown(wxMouseEvent &evt);
   void OnLeftUp(wxMouseEvent &evt);
   void OnMotion(wxMouseEvent &evt);
   
   void ResetControlPoints();
   EControlPoint GetNearestControlPoint(const wxPoint& pt);
   
   EffectAutoDuckDialog* mParent;
   wxBitmap* mBackgroundBitmap;
   EControlPoint mCurrentControlPoint;
   wxPoint mControlPoints[AUTO_DUCK_PANEL_NUM_CONTROL_POINTS];
   wxPoint mMoveStartControlPoints[AUTO_DUCK_PANEL_NUM_CONTROL_POINTS];
   wxPoint mMouseDownPoint;
   bool mControlPointMoveActivated;
   
   DECLARE_EVENT_TABLE()
};

class EffectAutoDuck: public Effect
{
   friend class EffectAutoDuckDialog;

public:
   EffectAutoDuck();
   
   virtual wxString GetEffectName()
   {
      return wxString(_("Auto Duck..."));
   }
   
   virtual wxString GetEffectAction()
   {
      return wxString(_("Processing Auto Duck..."));
   }
   
   virtual bool PromptUser();
   virtual bool TransferParameters(Shuttle & shuttle);

   virtual bool Init();
   virtual void End();
   virtual bool CheckWhetherSkipEffect();
   virtual bool Process();
   
private:
   bool ApplyDuckFade(int trackNumber, WaveTrack* t, double t0, double t1);
   
   double mDuckAmountDb;
   double mFadeDownLen;
   double mFadeUpLen;
   double mThresholdDb;
   double mMaximumPause;

   WaveTrack* mControlTrack;
};

#endif


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Audacity-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/audacity-cvs

Reply via email to