Ejemplo n.º 1
0
bool LV2Effect::Process()
{
   CopyInputTracks();
   bool bGoodResult = true;

   TrackListIterator iter(mOutputTracks);
   int count = 0;
   Track *left = iter.First();
   Track *right = NULL;
   while (left)
   {
      sampleCount lstart = 0, rstart = 0;
      sampleCount len;
      GetSamples((WaveTrack *)left, &lstart, &len);
      
      right = NULL;
      if (left->GetLinked() && mAudioInputs.GetCount() > 1)
      {
         right = iter.Next();         
         GetSamples((WaveTrack *)right, &rstart, &len);
      }

      if (mAudioInputs.GetCount() < 2 && right)
      {
         // If the effect is mono, apply to each channel separately

         bGoodResult = ProcessStereo(count, (WaveTrack *)left, NULL,
                                     lstart, 0, len) && 
            ProcessStereo(count, (WaveTrack *)right, NULL,
                          rstart, 0, len);
      }
      else
      {
         bGoodResult = ProcessStereo(count,
                                     (WaveTrack *)left, (WaveTrack *)right,
                                      lstart, rstart, len);
      }

      if (!bGoodResult)
      {
         break;
      }
   
      left = iter.Next();
      count++;
   }

   ReplaceProcessedTracks(bGoodResult);

   return bGoodResult;
}
Ejemplo n.º 2
0
bool EffectTruncSilence::DoRemoval
(const RegionList &silences, unsigned iGroup, unsigned nGroups, Track *firstTrack, Track *lastTrack,
 double &totalCutLen)
{
   //
   // Now remove the silent regions from all selected / sync-lock selected tracks.
   //

   // Loop over detected regions in reverse (so cuts don't change time values
   // down the line)
   int whichReg = 0;
   RegionList::const_reverse_iterator rit;
   for (rit = silences.rbegin(); rit != silences.rend(); ++rit)
   {
      const Region &region = *rit;
      const Region *const r = &region;

      // Progress dialog and cancellation. Do additional cleanup before return.
      const double frac = detectFrac +
         (1 - detectFrac) * (iGroup + whichReg / double(silences.size())) / nGroups;
      if (TotalProgress(frac))
      {
         ReplaceProcessedTracks(false);
         return false;
      }

      // Intersection may create regions smaller than allowed; ignore them.
      // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
      if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
         continue;

      // Find NEW silence length as requested
      double inLength = r->end - r->start;
      double outLength;

      switch (mActionIndex)
      {
      case kTruncate:
         outLength = std::min(mTruncLongestAllowedSilence, inLength);
         break;
      case kCompress:
         outLength = mInitialAllowedSilence +
                        (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
         break;
      default: // Not currently used.
         outLength = std::min(mInitialAllowedSilence +
                              (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
                           mTruncLongestAllowedSilence);
      }

      double cutLen = inLength - outLength;
      totalCutLen += cutLen;

      TrackListIterator iterOut(mOutputTracks);
      bool lastSeen = false;
      for (Track *t = iterOut.StartWith(firstTrack); t && !lastSeen; t = iterOut.Next())
      {
         lastSeen = (t == lastTrack);
         if (!(t->GetSelected() || t->IsSyncLockSelected()))
            continue;

         // Don't waste time past the end of a track
         if (t->GetEndTime() < r->start)
            continue;

         double cutStart = (r->start + r->end - cutLen) / 2;
         double cutEnd = cutStart + cutLen;
         if (t->GetKind() == Track::Wave)
         {
            // In WaveTracks, clear with a cross-fade
            WaveTrack *const wt = static_cast<WaveTrack*>(t);
            sampleCount blendFrames = mBlendFrameCount;
            // Round start/end times to frame boundaries
            cutStart = wt->LongSamplesToTime(wt->TimeToLongSamples(cutStart));
            cutEnd = wt->LongSamplesToTime(wt->TimeToLongSamples(cutEnd));

            // Make sure the cross-fade does not affect non-silent frames
            if (wt->LongSamplesToTime(blendFrames) > inLength)
            {
               blendFrames = wt->TimeToLongSamples(inLength);
            }

            // Perform cross-fade in memory
            float *buf1 = new float[blendFrames];
            float *buf2 = new float[blendFrames];
            sampleCount t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
            sampleCount t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;

            wt->Get((samplePtr)buf1, floatSample, t1, blendFrames);
            wt->Get((samplePtr)buf2, floatSample, t2, blendFrames);

            for (sampleCount i = 0; i < blendFrames; ++i)
            {
               buf1[i] = ((blendFrames-i) * buf1[i] + i * buf2[i]) /
                         (double)blendFrames;
            }

            // Perform the cut
            wt->Clear(cutStart, cutEnd);

            // Write cross-faded data
            wt->Set((samplePtr)buf1, floatSample, t1, blendFrames);

            delete [] buf1;
            delete [] buf2;
         }
         else
            // Non-wave tracks: just do a sync-lock adjust
            t->SyncLockAdjust(cutEnd, cutStart);
      }
      ++whichReg;
   }

   return true;
}
Ejemplo n.º 3
0
bool EffectChangeSpeed::Process()
{
   // Similar to EffectSoundTouch::Process()

   // Iterate over each track.
   // Track::All is needed because this effect needs to introduce 
   // silence in the sync-lock group tracks to keep sync
   this->CopyInputTracks(Track::All); // Set up mOutputTracks.
   bool bGoodResult = true;

   TrackListIterator iter(mOutputTracks);
   Track* t;
   mCurTrackNum = 0;
   mMaxNewLength = 0.0;

   mFactor = 100.0 / (100.0 + mPercentChange);

   t = iter.First();
   while (t != NULL)
   {
      if (t->GetKind() == Track::Label) {
         if (t->GetSelected() || t->IsSyncLockSelected())
         {
            if (!ProcessLabelTrack(t)) {
               bGoodResult = false;
               break;
            }
         }
      }
      else if (t->GetKind() == Track::Wave && t->GetSelected())
      {
         WaveTrack *pOutWaveTrack = (WaveTrack*)t;
         //Get start and end times from track
         mCurT0 = pOutWaveTrack->GetStartTime();
         mCurT1 = pOutWaveTrack->GetEndTime();

         //Set the current bounds to whichever left marker is
         //greater and whichever right marker is less:
         mCurT0 = wxMax(mT0, mCurT0);
         mCurT1 = wxMin(mT1, mCurT1);

         // Process only if the right marker is to the right of the left marker
         if (mCurT1 > mCurT0) {
            //Transform the marker timepoints to samples
            sampleCount start = pOutWaveTrack->TimeToLongSamples(mCurT0);
            sampleCount end = pOutWaveTrack->TimeToLongSamples(mCurT1);

            //ProcessOne() (implemented below) processes a single track
            if (!ProcessOne(pOutWaveTrack, start, end))
            {
               bGoodResult = false;
               break;
            }
         }
         mCurTrackNum++;
      }
      else if (t->IsSyncLockSelected())
      {
         t->SyncLockAdjust(mT1, mT0 + (mT1 - mT0) * mFactor);
      }

      //Iterate to the next track
      t=iter.Next();
   }

   if (bGoodResult)
      ReplaceProcessedTracks(bGoodResult);

   mT1 = mT0 + mMaxNewLength; // Update selection.

   return bGoodResult;
}
Ejemplo n.º 4
0
bool EffectAutoDuck::Process()
{
   sampleCount i;

   if (GetNumWaveTracks() == 0 || !mControlTrack)
      return false;

   bool cancel = false;

   sampleCount start =
      mControlTrack->TimeToLongSamples(mT0 + mOuterFadeDownLen);
   sampleCount end =
      mControlTrack->TimeToLongSamples(mT1 - mOuterFadeUpLen);

   if (end <= start)
      return false;

   // the minimum number of samples we have to wait until the maximum
   // pause has been exceeded
   double maxPause = mMaximumPause;

   // We don't fade in until we have time enough to actually fade out again
   if (maxPause < mOuterFadeDownLen + mOuterFadeUpLen)
      maxPause = mOuterFadeDownLen + mOuterFadeUpLen;

   sampleCount minSamplesPause =
      mControlTrack->TimeToLongSamples(maxPause);

   double threshold = DB_TO_LINEAR(mThresholdDb);

   // adjust the threshold so we can compare it to the rmsSum value
   threshold = threshold * threshold * kRMSWindowSize;

   int rmsPos = 0;
   float rmsSum = 0;
   float *rmsWindow = new float[kRMSWindowSize];
   for (i = 0; i < kRMSWindowSize; i++)
      rmsWindow[i] = 0;

   float *buf = new float[kBufSize];

   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;
   sampleCount pos = start;

   while (pos < end)
   {
      sampleCount len = end - pos;
      if (len > kBufSize)
         len = kBufSize;

      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) % kRMSWindowSize;

         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 - mOuterFadeDownLen,
                              duckRegionEnd + mOuterFadeUpLen));

               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 - mOuterFadeDownLen,
                     duckRegionEnd + mOuterFadeUpLen));
   }

   delete[] buf;
   delete[] rmsWindow;

   if (!cancel)
   {
      CopyInputTracks(); // Set up mOutputTracks.
      SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks);
      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++;
      }
   }

   ReplaceProcessedTracks(!cancel);
   return !cancel;
}
Ejemplo n.º 5
0
bool EffectSBSMS::Process()
{
   bool bGoodResult = true;

   //Iterate over each track
   //Track::All is needed because this effect needs to introduce silence in the group tracks to keep sync
   this->CopyInputTracks(Track::All); // Set up mOutputTracks.
   TrackListIterator iter(mOutputTracks);
   Track* t;
   mCurTrackNum = 0;

   double maxDuration = 0.0;

   // Must sync if selection length will change
   bool mustSync = (rateStart != rateEnd);
   Slide rateSlide(rateSlideType,rateStart,rateEnd);
   Slide pitchSlide(pitchSlideType,pitchStart,pitchEnd);
   mTotalStretch = rateSlide.getTotalStretch();

   t = iter.First();
   while (t != NULL) {
      if (t->GetKind() == Track::Label &&
            (t->GetSelected() || (mustSync && t->IsSyncLockSelected())) )
      {
         if (!ProcessLabelTrack(t)) {
            bGoodResult = false;
            break;
         }
      }
      else if (t->GetKind() == Track::Wave && t->GetSelected() )
      {
         WaveTrack* leftTrack = (WaveTrack*)t;

         //Get start and end times from track
         mCurT0 = leftTrack->GetStartTime();
         mCurT1 = leftTrack->GetEndTime();

         //Set the current bounds to whichever left marker is
         //greater and whichever right marker is less
         mCurT0 = wxMax(mT0, mCurT0);
         mCurT1 = wxMin(mT1, mCurT1);

         // Process only if the right marker is to the right of the left marker
         if (mCurT1 > mCurT0) {
            sampleCount start;
            sampleCount end;
            start = leftTrack->TimeToLongSamples(mCurT0);
            end = leftTrack->TimeToLongSamples(mCurT1);

            WaveTrack* rightTrack = NULL;
            if (leftTrack->GetLinked()) {
               double t;
               rightTrack = (WaveTrack*)(iter.Next());

               //Adjust bounds by the right tracks markers
               t = rightTrack->GetStartTime();
               t = wxMax(mT0, t);
               mCurT0 = wxMin(mCurT0, t);
               t = rightTrack->GetEndTime();
               t = wxMin(mT1, t);
               mCurT1 = wxMax(mCurT1, t);

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

               mCurTrackNum++; // Increment for rightTrack, too.
            }
            sampleCount trackStart = leftTrack->TimeToLongSamples(leftTrack->GetStartTime());
            sampleCount trackEnd = leftTrack->TimeToLongSamples(leftTrack->GetEndTime());

            // SBSMS has a fixed sample rate - we just convert to its sample rate and then convert back
            float srTrack = leftTrack->GetRate();
            float srProcess = bLinkRatePitch?srTrack:44100.0;

            // the resampler needs a callback to supply its samples
            ResampleBuf rb;
            sampleCount maxBlockSize = leftTrack->GetMaxBlockSize();
            rb.blockSize = maxBlockSize;
            rb.buf = (audio*)calloc(rb.blockSize,sizeof(audio));
            rb.leftTrack = leftTrack;
            rb.rightTrack = rightTrack?rightTrack:leftTrack;
            rb.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float));
            rb.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float));

            // Samples in selection
            sampleCount samplesIn = end-start;

            // Samples for SBSMS to process after resampling
            sampleCount samplesToProcess = (sampleCount) ((float)samplesIn*(srProcess/srTrack));

            SlideType outSlideType;
            SBSMSResampleCB outResampleCB;

            sampleCount processPresamples = 0;
            sampleCount trackPresamples = 0;

            if(bLinkRatePitch) {
              rb.bPitch = true;
              outSlideType = rateSlideType;
              outResampleCB = resampleCB;
              rb.offset = start;
              rb.end = end;
              rb.iface = new SBSMSInterfaceSliding(&rateSlide,&pitchSlide,
                                                       bPitchReferenceInput,
                                                       samplesToProcess,0,
                                                       NULL);
               
             
            } else {
              rb.bPitch = false;
              outSlideType = (srProcess==srTrack?SlideIdentity:SlideConstant);
              outResampleCB = postResampleCB;
              rb.ratio = srProcess/srTrack;
              rb.quality = new SBSMSQuality(&SBSMSQualityStandard);
              rb.resampler = new Resampler(resampleCB, &rb, srProcess==srTrack?SlideIdentity:SlideConstant);
              rb.sbsms = new SBSMS(rightTrack?2:1,rb.quality,true);
              rb.SBSMSBlockSize = rb.sbsms->getInputFrameSize();
              rb.SBSMSBuf = (audio*)calloc(rb.SBSMSBlockSize,sizeof(audio));

              processPresamples = wxMin(rb.quality->getMaxPresamples(),
                                        (long)((float)(start-trackStart)*(srProcess/srTrack)));
              trackPresamples = wxMin(start-trackStart,
                                      (long)((float)(processPresamples)*(srTrack/srProcess)));
              rb.offset = start - trackPresamples;
              rb.end = trackEnd;
              rb.iface = new SBSMSEffectInterface(rb.resampler,
                                                      &rateSlide,&pitchSlide,
                                                      bPitchReferenceInput,
                                                      samplesToProcess,processPresamples,
                                                      rb.quality);
            }
            
            Resampler resampler(outResampleCB,&rb,outSlideType);

            audio outBuf[SBSMSOutBlockSize];
            float outBufLeft[2*SBSMSOutBlockSize];
            float outBufRight[2*SBSMSOutBlockSize];

            // Samples in output after SBSMS
            sampleCount samplesToOutput = rb.iface->getSamplesToOutput();

            // Samples in output after resampling back
            sampleCount samplesOut = (sampleCount) ((float)samplesToOutput * (srTrack/srProcess));

            // Duration in track time
            double duration =  (mCurT1-mCurT0) * mTotalStretch;

            if(duration > maxDuration)
               maxDuration = duration;

            TimeWarper *warper = createTimeWarper(mCurT0,mCurT1,maxDuration,rateStart,rateEnd,rateSlideType);
            SetTimeWarper(warper);

            rb.outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat(),
                                                        leftTrack->GetRate());
            if(rightTrack)
               rb.outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat(),
                                                            rightTrack->GetRate());
            long pos = 0;
            long outputCount = -1;

            // process
            while(pos<samplesOut && outputCount) {
               long frames;
               if(pos+SBSMSOutBlockSize>samplesOut) {
                  frames = samplesOut - pos;
               } else {
                  frames = SBSMSOutBlockSize;
               }
               outputCount = resampler.read(outBuf,frames);
               for(int i = 0; i < outputCount; i++) {
                  outBufLeft[i] = outBuf[i][0];
                  if(rightTrack)
                     outBufRight[i] = outBuf[i][1];
               }
               pos += outputCount;
               rb.outputLeftTrack->Append((samplePtr)outBufLeft, floatSample, outputCount);
               if(rightTrack)
                  rb.outputRightTrack->Append((samplePtr)outBufRight, floatSample, outputCount);

               double frac = (double)pos/(double)samplesOut;
               int nWhichTrack = mCurTrackNum;
               if(rightTrack) {
                  nWhichTrack = 2*(mCurTrackNum/2);
                  if (frac < 0.5)
                     frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once.
                  else {
                     nWhichTrack++;
                     frac -= 0.5;
                     frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once.
                  }
               }
               if (TrackProgress(nWhichTrack, frac))
                  return false;
            }
            rb.outputLeftTrack->Flush();
            if(rightTrack)
               rb.outputRightTrack->Flush();

            bool bResult =
               leftTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputLeftTrack,
                                          true, false, GetTimeWarper());
            wxASSERT(bResult); // TO DO: Actually handle this.
            wxUnusedVar(bResult);

            if(rightTrack)
            {
               bResult =
                  rightTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputRightTrack,
                                             true, false, GetTimeWarper());
               wxASSERT(bResult); // TO DO: Actually handle this.
            }
         }
         mCurTrackNum++;
      }
      else if (mustSync && t->IsSyncLockSelected())
      {
         t->SyncLockAdjust(mCurT1, mCurT0 + (mCurT1 - mCurT0) * mTotalStretch);
      }
      //Iterate to the next track
      t = iter.Next();
   }

   if (bGoodResult)
      ReplaceProcessedTracks(bGoodResult);

   // Update selection
   mT0 = mCurT0;
   mT1 = mCurT0 + maxDuration;

   return bGoodResult;
}
Ejemplo n.º 6
0
bool VSTEffect::Process()
{
   CopyInputTracks();
   bool bGoodResult = true;

   mInBuffer = NULL;
   mOutBuffer = NULL;

   TrackListIterator iter(mOutputTracks);
   int count = 0;
   bool clear = false;
   WaveTrack *left = (WaveTrack *) iter.First();
   while (left) {
      WaveTrack *right;
      sampleCount len;
      sampleCount lstart;
      sampleCount rstart;

      GetSamples(left, &lstart, &len);

      mChannels = 1;

      right = NULL;
      rstart = 0;
      if (left->GetLinked() && mInputs > 1) {
         right = (WaveTrack *) iter.Next();         
         GetSamples(right, &rstart, &len);
         clear = false;
         mChannels = 2;
      }

      if (mBlockSize == 0) {
         mBlockSize = left->GetMaxBlockSize() * 2;

         // Some VST effects (Antress Modern is an example), do not like
         // overly large block sizes.  Unfortunately, I have not found a
         // way to determine if the effect has a maximum it will support,
         // so just limit to small value for now.  This will increase
         // processing time and, it's a shame, because most plugins seem
         // to be able to handle much larger sizes.
         if (mBlockSize > 8192) { // The Antress limit
            mBlockSize = 8192;
         }

         mInBuffer = new float *[mInputs];
         for (int i = 0; i < mInputs; i++) {
            mInBuffer[i] = new float[mBlockSize];
         }

         mOutBuffer = new float *[mOutputs];
         for (int i = 0; i < mOutputs; i++) {
            mOutBuffer[i] = new float[mBlockSize];
         }

         // Turn the power off
         callDispatcher(effMainsChanged, 0, 0, NULL, 0.0);

         // Set processing parameters
         callDispatcher(effSetSampleRate, 0, 0, NULL, left->GetRate());
         callDispatcher(effSetBlockSize, 0, mBlockSize, NULL, 0.0);
      }

      // Clear unused input buffers
      if (!right && !clear) {
         for (int i = 1; i < mInputs; i++) {
            for (int j = 0; j < mBlockSize; j++) {
               mInBuffer[i][j] = 0.0;
            }
         }
         clear = true;
      }

      bGoodResult = ProcessStereo(count, left, right, lstart, rstart, len);
      if (!bGoodResult) {
         break;
      }

      left = (WaveTrack *) iter.Next();
      count++;
   }

   if (mOutBuffer) {
      for (int i = 0; i < mOutputs; i++) {
         delete mOutBuffer[i];
      }
      delete [] mOutBuffer;
      mOutBuffer = NULL;
   }

   if (mInBuffer) {
      for (int i = 0; i < mInputs; i++) {
         delete mInBuffer[i];
      }
      delete [] mInBuffer;
      mInBuffer = NULL;
   }

   ReplaceProcessedTracks(bGoodResult); 
   return bGoodResult;
}
Ejemplo n.º 7
0
bool EffectSoundTouch::Process()
{
   // Assumes that mSoundTouch has already been initialized
   // by the subclass for subclass-specific parameters. The
   // time warper should also be set.

   // Check if this effect will alter the selection length; if so, we need
   // to operate on sync-lock selected tracks.
   bool mustSync = true;
   if (mT1 == GetTimeWarper()->Warp(mT1)) {
      mustSync = false;
   }

   //Iterate over each track
   // Needs Track::All for sync-lock grouping.
   this->CopyInputTracks(Track::All);
   bool bGoodResult = true;

   TrackListIterator iter(mOutputTracks);
   Track* t;
   mCurTrackNum = 0;
   m_maxNewLength = 0.0;

   t = iter.First();
   while (t != NULL) {
      if (t->GetKind() == Track::Label &&
            (t->GetSelected() || (mustSync && t->IsSyncLockSelected())) )
      {
         if (!ProcessLabelTrack(t))
         {
            bGoodResult = false;
            break;
         }
      }
#ifdef USE_MIDI
      else if (t->GetKind() == Track::Note &&
               (t->GetSelected() || (mustSync && t->IsSyncLockSelected())))
      {
         if (!ProcessNoteTrack(t))
         {
            bGoodResult = false;
            break;
         }
      }
#endif
      else if (t->GetKind() == Track::Wave && t->GetSelected())
      {
         WaveTrack* leftTrack = (WaveTrack*)t;
         //Get start and end times from track
         mCurT0 = leftTrack->GetStartTime();
         mCurT1 = leftTrack->GetEndTime();

         //Set the current bounds to whichever left marker is
         //greater and whichever right marker is less
         mCurT0 = wxMax(mT0, mCurT0);
         mCurT1 = wxMin(mT1, mCurT1);

         // Process only if the right marker is to the right of the left marker
         if (mCurT1 > mCurT0) {
            sampleCount start, end;

            if (leftTrack->GetLinked()) {
               double t;
               WaveTrack* rightTrack = (WaveTrack*)(iter.Next());

               //Adjust bounds by the right tracks markers
               t = rightTrack->GetStartTime();
               t = wxMax(mT0, t);
               mCurT0 = wxMin(mCurT0, t);
               t = rightTrack->GetEndTime();
               t = wxMin(mT1, t);
               mCurT1 = wxMax(mCurT1, t);

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

               //Inform soundtouch there's 2 channels
               mSoundTouch->setChannels(2);

               //ProcessStereo() (implemented below) processes a stereo track
               if (!ProcessStereo(leftTrack, rightTrack, start, end))
               {
                  bGoodResult = false;
                  break;
               }
               mCurTrackNum++; // Increment for rightTrack, too.
            } else {
               //Transform the marker timepoints to samples
               start = leftTrack->TimeToLongSamples(mCurT0);
               end = leftTrack->TimeToLongSamples(mCurT1);

               //Inform soundtouch there's a single channel
               mSoundTouch->setChannels(1);

               //ProcessOne() (implemented below) processes a single track
               if (!ProcessOne(leftTrack, start, end))
               {
                  bGoodResult = false;
                  break;
               }
            }
         }
         mCurTrackNum++;
      }
      else if (mustSync && t->IsSyncLockSelected()) {
         t->SyncLockAdjust(mT1, GetTimeWarper()->Warp(mT1));
      }

      //Iterate to the next track
      t = iter.Next();
   }

   if (bGoodResult)
      ReplaceProcessedTracks(bGoodResult);

   delete mSoundTouch;
   mSoundTouch = NULL;

//   mT0 = mCurT0;
//   mT1 = mCurT0 + m_maxNewLength; // Update selection.

   return bGoodResult;
}
Ejemplo n.º 8
0
bool EffectRepeat::Process()
{
   // Set up mOutputTracks.
   // This effect needs Track::All for sync-lock grouping.
   CopyInputTracks(Track::All);

   int nTrack = 0;
   bool bGoodResult = true;
   double maxDestLen = 0.0; // used to change selection to generated bit

   TrackListIterator iter(mOutputTracks);

   for (Track *t = iter.First(); t && bGoodResult; t = iter.Next())
   {
      if (t->GetKind() == Track::Label)
      {
         if (t->GetSelected() || t->IsSyncLockSelected())
         {
            LabelTrack* track = (LabelTrack*)t;

            if (!track->Repeat(mT0, mT1, repeatCount))
            {
               bGoodResult = false;
               break;
            }
         }
      }
      else if (t->GetKind() == Track::Wave && t->GetSelected())
      {
         WaveTrack* track = (WaveTrack*)t;

         sampleCount start = track->TimeToLongSamples(mT0);
         sampleCount end = track->TimeToLongSamples(mT1);
         sampleCount len = (sampleCount)(end - start);
         double tLen = track->LongSamplesToTime(len);
         double tc = mT0 + tLen;

         if (len <= 0)
         {
            continue;
         }

         auto dest = track->Copy(mT0, mT1);
         for(int j=0; j<repeatCount; j++)
         {
            if (!track->Paste(tc, dest.get()) ||
                  TrackProgress(nTrack, j / repeatCount)) // TrackProgress returns true on Cancel.
            {
               bGoodResult = false;
               break;
            }
            tc += tLen;
         }
         if (tc > maxDestLen)
            maxDestLen = tc;
         nTrack++;
      }
      else if (t->IsSyncLockSelected())
      {
         t->SyncLockAdjust(mT1, mT1 + (mT1 - mT0) * repeatCount);
      }
   }

   if (bGoodResult)
   {
      // Select the NEW bits + original bit
      mT1 = maxDestLen;
   }

   ReplaceProcessedTracks(bGoodResult);
   return bGoodResult;
}
Ejemplo n.º 9
0
bool EffectTruncSilence::Process()
{
   // Typical fraction of total time taken by detection (better to guess low)
   const double detectFrac = .4;

   // Copy tracks
   this->CopyInputTracks(Track::All);

   // Lower bound on the amount of silence to find at a time -- this avoids
   // detecting silence repeatedly in low-frequency sounds.
   const double minTruncMs = 0.001;
   double truncDbSilenceThreshold = Enums::Db2Signal[mTruncDbChoiceIndex];

   // Master list of silent regions; it is responsible for deleting them.
   // This list should always be kept in order.
   RegionList silences;
   silences.DeleteContents(true);

   // Start with the whole selection silent
   Region *sel = new Region;
   sel->start = mT0;
   sel->end = mT1;
   silences.push_back(sel);

   // Remove non-silent regions in each track
   SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
   int whichTrack = 0;
   for (Track *t = iter.First(); t; t = iter.Next())
   {
      WaveTrack *wt = (WaveTrack *)t;

      // Smallest silent region to detect in frames
      sampleCount minSilenceFrames =
            sampleCount(wxMax( mInitialAllowedSilence, minTruncMs) *
                  wt->GetRate());

      //
      // Scan the track for silences
      //
      RegionList trackSilences;
      trackSilences.DeleteContents(true);
      sampleCount blockLen = wt->GetMaxBlockSize();
      sampleCount start = wt->TimeToLongSamples(mT0);
      sampleCount end = wt->TimeToLongSamples(mT1);

      // Allocate buffer
      float *buffer = new float[blockLen];

      sampleCount index = start;
      sampleCount silentFrames = 0;
      bool cancelled = false;

      // Keep position in overall silences list for optimization
      RegionList::iterator rit(silences.begin());

      while (index < end) {
         // Show progress dialog, test for cancellation
         cancelled = TotalProgress(
               detectFrac * (whichTrack + index / (double)end) /
               (double)GetNumWaveTracks());
         if (cancelled)
            break;

         //
         // Optimization: if not in a silent region skip ahead to the next one
         //
         double curTime = wt->LongSamplesToTime(index);
         for ( ; rit != silences.end(); ++rit)
         {
            // Find the first silent region ending after current time
            if ((*rit)->end >= curTime)
               break;
         }

         if (rit == silences.end()) {
            // No more regions -- no need to process the rest of the track
            break;
         }
         else if ((*rit)->start > curTime) {
            // End current silent region, skip ahead
            if (silentFrames >= minSilenceFrames) {
               Region *r = new Region;
               r->start = wt->LongSamplesToTime(index - silentFrames);
               r->end = wt->LongSamplesToTime(index);
               trackSilences.push_back(r);
            }
            silentFrames = 0;

            index = wt->TimeToLongSamples((*rit)->start);
         }
         //
         // End of optimization
         //

         // Limit size of current block if we've reached the end
         sampleCount count = blockLen;
         if ((index + count) > end) {
            count = end - index;
         }

         // Fill buffer
         wt->Get((samplePtr)(buffer), floatSample, index, count);

         // Look for silences in current block
         for (sampleCount i = 0; i < count; ++i) {
            if (fabs(buffer[i]) < truncDbSilenceThreshold) {
               ++silentFrames;
            }
            else {
               if (silentFrames >= minSilenceFrames)
               {
                  // Record the silent region
                  Region *r = new Region;
                  r->start = wt->LongSamplesToTime(index + i - silentFrames);
                  r->end = wt->LongSamplesToTime(index + i);
                  trackSilences.push_back(r);
               }
               silentFrames = 0;
            }
         }

         // Next block
         index += count;
      }

      delete [] buffer;

      // Buffer has been freed, so we're OK to return if cancelled
      if (cancelled)
      {
         ReplaceProcessedTracks(false);
         return false;
      }

      if (silentFrames >= minSilenceFrames)
      {
         // Track ended in silence -- record region
         Region *r = new Region;
         r->start = wt->LongSamplesToTime(index - silentFrames);
         r->end = wt->LongSamplesToTime(index);
         trackSilences.push_back(r);
      }

      // Intersect with the overall silent region list
      Intersect(silences, trackSilences);
      whichTrack++;
   }

   //
   // Now remove the silent regions from all selected / sync-lock selected tracks.
   //

   // Loop over detected regions in reverse (so cuts don't change time values
   // down the line)
   int whichReg = 0;
   RegionList::reverse_iterator rit;
   double totalCutLen = 0.0;  // For cutting selection at the end
   for (rit = silences.rbegin(); rit != silences.rend(); ++rit) {
      Region *r = *rit;

      // Progress dialog and cancellation. Do additional cleanup before return.
      if (TotalProgress(detectFrac + (1 - detectFrac) * whichReg / (double)silences.size()))
      {
         ReplaceProcessedTracks(false);
         return false;
      }

      // Intersection may create regions smaller than allowed; ignore them.
      // Allow one nanosecond extra for consistent results with exact milliseconds of allowed silence.
      if ((r->end - r->start) < (mInitialAllowedSilence - 0.000000001))
         continue;

      // Find new silence length as requested
      double inLength = r->end - r->start;
      double outLength;

      switch (mProcessIndex) {
      case 0:
         outLength = wxMin(mTruncLongestAllowedSilence, inLength);
         break;
      case 1:
         outLength = mInitialAllowedSilence +
                        (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0;
         break;
      default: // Not currently used.
         outLength = wxMin(mInitialAllowedSilence +
                              (inLength - mInitialAllowedSilence) * mSilenceCompressPercent / 100.0,
                           mTruncLongestAllowedSilence);
      }

      double cutLen = inLength - outLength;
      totalCutLen += cutLen;

      TrackListIterator iterOut(mOutputTracks);
      for (Track *t = iterOut.First(); t; t = iterOut.Next())
      {
         // Don't waste time past the end of a track
         if (t->GetEndTime() < r->start)
            continue;

         if (t->GetKind() == Track::Wave && (
                  t->GetSelected() || t->IsSyncLockSelected()))
         {
            // In WaveTracks, clear with a cross-fade
            WaveTrack *wt = (WaveTrack *)t;
            sampleCount blendFrames = mBlendFrameCount;
            double cutStart = (r->start + r->end - cutLen) / 2;
            double cutEnd = cutStart + cutLen;
            // Round start/end times to frame boundaries
            cutStart = wt->LongSamplesToTime(wt->TimeToLongSamples(cutStart));
            cutEnd = wt->LongSamplesToTime(wt->TimeToLongSamples(cutEnd));

            // Make sure the cross-fade does not affect non-silent frames
            if (wt->LongSamplesToTime(blendFrames) > inLength) {
               blendFrames = wt->TimeToLongSamples(inLength);
            }

            // Perform cross-fade in memory
            float *buf1 = new float[blendFrames];
            float *buf2 = new float[blendFrames];
            sampleCount t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
            sampleCount t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;

            wt->Get((samplePtr)buf1, floatSample, t1, blendFrames);
            wt->Get((samplePtr)buf2, floatSample, t2, blendFrames);

            for (sampleCount i = 0; i < blendFrames; ++i) {
               buf1[i] = ((blendFrames-i) * buf1[i] + i * buf2[i]) /
                         (double)blendFrames;
            }

            // Perform the cut
            wt->Clear(cutStart, cutEnd);

            // Write cross-faded data
            wt->Set((samplePtr)buf1, floatSample, t1, blendFrames);

            delete [] buf1;
            delete [] buf2;
         }
         else if (t->GetSelected() || t->IsSyncLockSelected())
         {
            // Non-wave tracks: just do a sync-lock adjust
            double cutStart = (r->start + r->end - cutLen) / 2;
            double cutEnd = cutStart + cutLen;
            t->SyncLockAdjust(cutEnd, cutStart);
         }
      }
      ++whichReg;
   }

   mT1 -= totalCutLen;

   ReplaceProcessedTracks(true);

   return true;
}
Ejemplo n.º 10
0
bool EffectSBSMS::Process()
{
   if(!bInit) {
      sbsms_init(4096);
      bInit = TRUE;
   }
   
   bool bGoodResult = true;
   
   //Iterate over each track
   //Track::All is needed because this effect needs to introduce silence in the group tracks to keep sync
   this->CopyInputTracks(Track::All); // Set up mOutputTracks.
   TrackListIterator iter(mOutputTracks);
   Track* t;
   mCurTrackNum = 0;

   double maxDuration = 0.0;

   if(rateStart == rateEnd)
      mTotalStretch = 1.0/rateStart;
   else
      mTotalStretch = 1.0/(rateEnd-rateStart)*log(rateEnd/rateStart);

   // Must sync if selection length will change
   bool mustSync = (mTotalStretch != 1.0);

   t = iter.First();
   while (t != NULL) {
      if (t->GetKind() == Track::Label && 
            (t->GetSelected() || (mustSync && t->IsSynchroSelected())) )
      {
         if (!ProcessLabelTrack(t)) {
            bGoodResult = false;
            break;
         }
      }
      else if (t->GetKind() == Track::Wave && t->GetSelected() )
      {
         WaveTrack* leftTrack = (WaveTrack*)t;

         //Get start and end times from track
         mCurT0 = leftTrack->GetStartTime();
         mCurT1 = leftTrack->GetEndTime();
         
         //Set the current bounds to whichever left marker is
         //greater and whichever right marker is less
         mCurT0 = wxMax(mT0, mCurT0);
         mCurT1 = wxMin(mT1, mCurT1);
         
         // Process only if the right marker is to the right of the left marker
         if (mCurT1 > mCurT0) {
            sampleCount start;
            sampleCount end;
            start = leftTrack->TimeToLongSamples(mCurT0);
            end = leftTrack->TimeToLongSamples(mCurT1);
            
            WaveTrack* rightTrack = NULL;
            if (leftTrack->GetLinked()) {
               double t;
               rightTrack = (WaveTrack*)(iter.Next());
               
               //Adjust bounds by the right tracks markers
               t = rightTrack->GetStartTime();
               t = wxMax(mT0, t);
               mCurT0 = wxMin(mCurT0, t);
               t = rightTrack->GetEndTime();
               t = wxMin(mT1, t);
               mCurT1 = wxMax(mCurT1, t);
               
               //Transform the marker timepoints to samples
               start = leftTrack->TimeToLongSamples(mCurT0);
               end = leftTrack->TimeToLongSamples(mCurT1);
               
               mCurTrackNum++; // Increment for rightTrack, too.	
            }
            
            sampleCount trackEnd = leftTrack->TimeToLongSamples(leftTrack->GetEndTime());

            // SBSMS has a fixed sample rate - we just convert to its sample rate and then convert back
            float srIn = leftTrack->GetRate();
            float srSBSMS = 44100.0;
            
            // the resampler needs a callback to supply its samples
            resampleBuf rb;
            sampleCount maxBlockSize = leftTrack->GetMaxBlockSize();
            rb.block = maxBlockSize;
            rb.buf = (audio*)calloc(rb.block,sizeof(audio));
            rb.leftTrack = leftTrack;
            rb.rightTrack = rightTrack?rightTrack:leftTrack;
            rb.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float));
            rb.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float));
            rb.offset = start;
            rb.end = trackEnd;
            rb.ratio = srSBSMS/srIn;
            rb.resampler = new Resampler(resampleCB, &rb);
            
            // Samples in selection
            sampleCount samplesIn = end-start;
            
            // Samples for SBSMS to process after resampling
            sampleCount samplesToProcess = (sampleCount) ((real)samplesIn*(srSBSMS/srIn));
            
            // Samples in output after resampling back
            sampleCount samplesToGenerate = (sampleCount) ((real)samplesToProcess * mTotalStretch);
            sampleCount samplesOut = (sampleCount) ((real)samplesIn * mTotalStretch);
            double duration =  (mCurT1-mCurT0) * mTotalStretch;

            if(duration > maxDuration)
               maxDuration = duration;

            TimeWarper *warper = NULL;
            if (rateStart == rateEnd)
            {
               warper = new LinearTimeWarper(mCurT0, mCurT0,
                                             mCurT1, mCurT0+maxDuration);
            } else
            {
               warper = new LogarithmicTimeWarper(mCurT0, mCurT1,
                                                  rateStart, rateEnd);
            }
            SetTimeWarper(warper);
            
            sbsmsInfo si;
            si.rs = rb.resampler;
            si.samplesToProcess = samplesToProcess;
            si.samplesToGenerate = samplesToGenerate;
            si.stretch0 = rateStart;
            si.stretch1 = rateEnd;
            si.ratio0 = pitchStart;
            si.ratio1 = pitchEnd;
            
            rb.sbsmser = sbsms_create(&samplesCB,&stretchCB,&ratioCB,rightTrack?2:1,quality,bPreAnalyze,true);
            rb.pitch = pitch_create(rb.sbsmser,&si,srIn/srSBSMS);
            
            rb.outputLeftTrack = mFactory->NewWaveTrack(leftTrack->GetSampleFormat(),
                                                        leftTrack->GetRate());
            if(rightTrack)
               rb.outputRightTrack = mFactory->NewWaveTrack(rightTrack->GetSampleFormat(),
                                                            rightTrack->GetRate());
            
            
            sampleCount blockSize = SBSMS_FRAME_SIZE[quality];
            rb.outBuf = (audio*)calloc(blockSize,sizeof(audio));
            rb.outputLeftBuffer = (float*)calloc(blockSize*2,sizeof(float));
            if(rightTrack)
               rb.outputRightBuffer = (float*)calloc(blockSize*2,sizeof(float));
            
            long pos = 0;
            long outputCount = -1;
            
            // pre analysis
            real fracPre = 0.0f;
            if(bPreAnalyze) {
               fracPre = 0.05f;
               resampleBuf rbPre;
               rbPre.block = maxBlockSize;
               rbPre.buf = (audio*)calloc(rb.block,sizeof(audio));
               rbPre.leftTrack = leftTrack;
               rbPre.rightTrack = rightTrack?rightTrack:leftTrack;
               rbPre.leftBuffer = (float*)calloc(maxBlockSize,sizeof(float));
               rbPre.rightBuffer = (float*)calloc(maxBlockSize,sizeof(float));
               rbPre.offset = start;
               rbPre.end = end;
               rbPre.ratio = srSBSMS/srIn;
               rbPre.resampler = new Resampler(resampleCB, &rbPre);
               si.rs = rbPre.resampler;
               
               long pos = 0;
               long lastPos = 0;
               long ret = 0;
               while(lastPos<samplesToProcess) {
                  ret = sbsms_pre_analyze(&samplesCB,&si,rb.sbsmser);
                  lastPos = pos;
                  pos += ret;
                  real completion = (real)lastPos/(real)samplesToProcess;
                  if (TrackProgress(0,fracPre*completion))
                     return false;
               }
               sbsms_pre_analyze_complete(rb.sbsmser);
               sbsms_reset(rb.sbsmser);
               si.rs = rb.resampler;
            }
            
            // process
            while(pos<samplesOut && outputCount) {
               long frames;
               if(pos+blockSize>samplesOut) {
                  frames = samplesOut - pos;
               } else {
                  frames = blockSize;
               }
               
               outputCount = pitch_process(rb.outBuf, frames, rb.pitch);
               for(int i = 0; i < outputCount; i++) {
                  rb.outputLeftBuffer[i] = rb.outBuf[i][0];
                  if(rightTrack)
                     rb.outputRightBuffer[i] = rb.outBuf[i][1];
               }
               pos += outputCount;
               rb.outputLeftTrack->Append((samplePtr)rb.outputLeftBuffer, floatSample, outputCount);
               if(rightTrack)
                  rb.outputRightTrack->Append((samplePtr)rb.outputRightBuffer, floatSample, outputCount);
               
               double frac = (double)pos/(double)samplesOut;
               int nWhichTrack = mCurTrackNum;
               if(rightTrack) {
                  nWhichTrack = 2*(mCurTrackNum/2);
                  if (frac < 0.5)
                     frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once. 
                  else {
                     nWhichTrack++;
                     frac -= 0.5;
                     frac *= 2.0; // Show twice as far for each track, because we're doing 2 at once. 
                  }
               }
               if (TrackProgress(nWhichTrack, fracPre + (1.0-fracPre)*frac))
                  return false;
            }
            rb.outputLeftTrack->Flush();
            if(rightTrack)
               rb.outputRightTrack->Flush();
            
            leftTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputLeftTrack,
                  true, false, GetTimeWarper());

            if(rightTrack) {
               rightTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputRightTrack,
                     true, false, GetTimeWarper());
            }
         }
         mCurTrackNum++;
      }
      else if (mustSync && t->IsSynchroSelected())
      {
         t->SyncAdjust(mCurT1, mCurT0 + (mCurT1 - mCurT0) * mTotalStretch);
      }
      //Iterate to the next track
      t = iter.Next();
   }
   
   if (bGoodResult)
      ReplaceProcessedTracks(bGoodResult); 

   // Update selection
   mT0 = mCurT0;
   mT1 = mCurT0 + maxDuration;
   
   return bGoodResult;
}
Ejemplo n.º 11
0
bool EffectAutoDuck::Process()
{
   if (GetNumWaveTracks() == 0 || !mControlTrack)
      return false;

   bool cancel = false;

   auto start =
      mControlTrack->TimeToLongSamples(mT0 + mOuterFadeDownLen);
   auto end =
      mControlTrack->TimeToLongSamples(mT1 - mOuterFadeUpLen);

   if (end <= start)
      return false;

   // the minimum number of samples we have to wait until the maximum
   // pause has been exceeded
   double maxPause = mMaximumPause;

   // We don't fade in until we have time enough to actually fade out again
   if (maxPause < mOuterFadeDownLen + mOuterFadeUpLen)
      maxPause = mOuterFadeDownLen + mOuterFadeUpLen;

   auto minSamplesPause =
      mControlTrack->TimeToLongSamples(maxPause);

   double threshold = DB_TO_LINEAR(mThresholdDb);

   // adjust the threshold so we can compare it to the rmsSum value
   threshold = threshold * threshold * kRMSWindowSize;

   int rmsPos = 0;
   float rmsSum = 0;
   // to make the progress bar appear more natural, we first look for all
   // duck regions and apply them all at once afterwards
   std::vector<AutoDuckRegion> regions;
   bool inDuckRegion = false;
   {
      Floats rmsWindow{ kRMSWindowSize, true };

      Floats buf{ kBufSize };

      // initialize the following two variables to prevent compiler warning
      double duckRegionStart = 0;
      sampleCount curSamplesPause = 0;

      auto pos = start;

      while (pos < end)
      {
         const auto len = limitSampleBufferSize( kBufSize, end - pos );
         
         mControlTrack->Get((samplePtr)buf.get(), floatSample, pos, len);

         for (auto i = pos; i < pos + len; i++)
         {
            rmsSum -= rmsWindow[rmsPos];
            // i - pos is bounded by len:
            auto index = ( i - pos ).as_size_t();
            rmsWindow[rmsPos] = buf[ index ] * buf[ index ];
            rmsSum += rmsWindow[rmsPos];
            rmsPos = (rmsPos + 1) % kRMSWindowSize;

            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.push_back(AutoDuckRegion(
                     duckRegionStart - mOuterFadeDownLen,
                     duckRegionEnd + mOuterFadeUpLen));

                  inDuckRegion = false;
               }
            }
         }

         pos += len;

         if (TotalProgress(
            (pos - start).as_double() /
            (end - start).as_double() /
            (GetNumWaveTracks() + 1)
         ))
         {
            cancel = true;
            break;
         }
      }

      // apply last duck fade, if any
      if (inDuckRegion)
      {
         double duckRegionEnd =
            mControlTrack->LongSamplesToTime(end - curSamplesPause);
         regions.push_back(AutoDuckRegion(
            duckRegionStart - mOuterFadeDownLen,
            duckRegionEnd + mOuterFadeUpLen));
      }
   }

   if (!cancel)
   {
      CopyInputTracks(); // Set up mOutputTracks.
      SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks.get());
      Track *iterTrack = iter.First();

      int trackNum = 0;

      while (iterTrack)
      {
         WaveTrack* t = (WaveTrack*)iterTrack;

         for (size_t i = 0; i < regions.size(); i++)
         {
            const AutoDuckRegion& region = regions[i];
            if (ApplyDuckFade(trackNum, t, region.t0, region.t1))
            {
               cancel = true;
               break;
            }
         }

         if (cancel)
            break;

         iterTrack = iter.Next();
         trackNum++;
      }
   }

   ReplaceProcessedTracks(!cancel);
   return !cancel;
}