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; }
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 ®ion = *rit; const Region *const r = ®ion; // 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; }
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; }
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 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; }
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; }
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; }
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; }
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; }
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; }
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; }