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; }
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; }