bool EffectSoundTouch::Process() { // Assumes that mSoundTouch has already been initialized // by the subclass for subclass-specific parameters. //Iterate over each track TrackListIterator iter(mWaveTracks); WaveTrack* leftTrack = (WaveTrack*)(iter.First()); WaveTrack* rightTrack = NULL; mCurTrackNum = 0; m_maxNewLength = 0.0; while (leftTrack) { //Get start and end times from track double trackStart = leftTrack->GetStartTime(); double trackEnd = leftTrack->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { //Transform the marker timepoints to samples longSampleCount start = leftTrack->TimeToLongSamples(mCurT0); longSampleCount end = leftTrack->TimeToLongSamples(mCurT1); rightTrack = NULL; if (leftTrack->GetLinked()) { rightTrack = (WaveTrack*)(iter.Next()); mSoundTouch->setChannels(2); if (!ProcessStereo(leftTrack, rightTrack, start, end)) return false; mCurTrackNum++; // Increment for rightTrack, too. } else { mSoundTouch->setChannels(1); //ProcessOne() (implemented below) processes a single track if (!ProcessOne(leftTrack, start, end)) return false; } } //Iterate to the next track leftTrack = (WaveTrack*)(iter.Next()); mCurTrackNum++; } delete mSoundTouch; mSoundTouch = NULL; mT1 = mT0 + m_maxNewLength; // Update selection. return true; }
//TODO: There are a lot of places where a track is being checked // to see if it is stereo. Consolidate these bool EffectStereoToMono::CheckWhetherSkipEffect() { TrackListIterator iter(mWaveTracks); WaveTrack *t = (WaveTrack*)iter.First(); while (t) { if (t->GetLinked()) { return false; } t = (WaveTrack *)iter.Next(); } return true; }
bool VSTEffect::Init() { if (!mAEffect) { Load(); } if (!mAEffect) { return false; } mBlockSize = 0; TrackListIterator iter(mOutputTracks); WaveTrack *left = (WaveTrack *) iter.First(); while (left) { sampleCount lstart; sampleCount llen; GetSamples(left, &lstart, &llen); if (left->GetLinked()) { WaveTrack *right = (WaveTrack *) iter.Next(); sampleCount rstart; sampleCount rlen; GetSamples(right, &rstart, &rlen); if (left->GetRate() != right->GetRate()) { wxMessageBox(_("Both channels of a stereo track must be the same sample rate.")); return false; } if (llen != rlen) { wxMessageBox(_("Both channels of a stereo track must be the same length.")); return false; } } left = (WaveTrack *) iter.Next(); } return true; }
bool VampEffect::Init() { Vamp::HostExt::PluginLoader *loader = Vamp::HostExt::PluginLoader::getInstance(); delete mPlugin; mPlugin = 0; TrackListIterator iter(mWaveTracks); WaveTrack *left = (WaveTrack *)iter.First(); mRate = 0.0; while (left) { if (mRate == 0.0) mRate = left->GetRate(); if (left->GetLinked()) { WaveTrack *right = (WaveTrack *)iter.Next(); if (left->GetRate() != right->GetRate()) { wxMessageBox(_("Sorry, Vamp Plug-ins cannot be run on stereo tracks where the individual channels of the track do not match.")); return false; } } left = (WaveTrack *)iter.Next(); } if (mRate <= 0.0) mRate = mProjectRate; mPlugin = loader->loadPlugin (mKey, mRate, Vamp::HostExt::PluginLoader::ADAPT_ALL); if (!mPlugin) { wxMessageBox(_("Sorry, failed to load Vamp Plug-in.")); return false; } return true; }
bool EffectNormalize::Process() { if (mGain == false && mDC == false) return true; float ratio; if( mGain ) ratio = pow(10.0,TrapDouble(mLevel, // same value used for all tracks NORMALIZE_DB_MIN, NORMALIZE_DB_MAX)/20.0); else ratio = 1.0; //Iterate over each track this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); WaveTrack *prevTrack; prevTrack = track; mCurTrackNum = 0; wxString topMsg; if(mDC & mGain) topMsg = _("Removing DC offset and Normalizing...\n"); else if(mDC & !mGain) topMsg = _("Removing DC offset...\n"); else if(!mDC & mGain) topMsg = _("Normalizing without removing DC offset...\n"); else if(!mDC & !mGain) topMsg = wxT("Not doing anything)...\n"); // shouldn't get here while (track) { //Get start and end times from track double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { wxString msg; wxString trackName = track->GetName(); if(!track->GetLinked() || mStereoInd) msg = topMsg + _("Analyzing: ") + trackName; else msg = topMsg + _("Analyzing first track of stereo pair: ") + trackName; AnalyseTrack(track, msg); // sets mOffset and offset-adjusted mMin and mMax if(!track->GetLinked() || mStereoInd) { // mono or 'stereo tracks independently' float extent = wxMax(fabs(mMax), fabs(mMin)); if( (extent > 0) && mGain ) mMult = ratio / extent; else mMult = 1.0; msg = topMsg + _("Processing: ") + trackName; if(track->GetLinked() || prevTrack->GetLinked()) // only get here if there is a linked track but we are processing independently msg = topMsg + _("Processing stereo channels independently: ") + trackName; if (!ProcessOne(track, msg)) { bGoodResult = false; break; } } else { // we have a linked stereo track // so we need to find it's min, max and offset // as they are needed to calc the multiplier for both tracks float offset1 = mOffset; // remember ones from first track float min1 = mMin; float max1 = mMax; track = (WaveTrack *) iter.Next(); // get the next one mCurTrackNum++; // keeps progress bar correct msg = topMsg + _("Analyzing second track of stereo pair: ") + trackName; AnalyseTrack(track, msg); // sets mOffset and offset-adjusted mMin and mMax float offset2 = mOffset; // ones for second track float min2 = mMin; float max2 = mMax; float extent = wxMax(fabs(min1), fabs(max1)); extent = wxMax(extent, fabs(min2)); extent = wxMax(extent, fabs(max2)); if( (extent > 0) && mGain ) mMult = ratio / extent; // we need to use this for both linked tracks else mMult = 1.0; mOffset = offset1; track = (WaveTrack *) iter.Prev(); // go back to the first linked one mCurTrackNum--; // keeps progress bar correct msg = topMsg + _("Processing first track of stereo pair: ") + trackName; if (!ProcessOne(track, msg)) { bGoodResult = false; break; } mOffset = offset2; track = (WaveTrack *) iter.Next(); // go to the second linked one mCurTrackNum++; // keeps progress bar correct msg = topMsg + _("Processing second track of stereo pair: ") + trackName; if (!ProcessOne(track, msg)) { bGoodResult = false; break; } } } //Iterate to the next track prevTrack = track; track = (WaveTrack *) iter.Next(); mCurTrackNum++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }
bool VampEffect::Process() { if (!mPlugin) return false; TrackListIterator iter(mWaveTracks); int count = 0; WaveTrack *left = (WaveTrack *)iter.First(); bool multiple = false; int prevTrackChannels = 0; TrackListIterator scooter(iter); if (left->GetLinked()) scooter.Next(); if (scooter.Next()) { // if there is another track beyond this one and any linked one, // then we're processing more than one track. That means we // should use the originating track name in each new label // track's name, to make clear which is which multiple = true; } while (left) { sampleCount lstart, rstart; sampleCount len; GetSamples(left, &lstart, &len); WaveTrack *right = NULL; int channels = 1; if (left->GetLinked()) { right = (WaveTrack *)iter.Next(); channels = 2; GetSamples(right, &rstart, &len); } size_t step = mPlugin->getPreferredStepSize(); size_t block = mPlugin->getPreferredBlockSize(); bool initialiseRequired = true; if (block == 0) { if (step != 0) block = step; else block = 1024; } if (step == 0) { step = block; } if (prevTrackChannels > 0) { // Plugin has already been initialised, so if the number of // channels remains the same, we only need to do a reset. // Otherwise we need to re-construct the whole plugin, // because a Vamp plugin can't be re-initialised. if (prevTrackChannels == channels) { mPlugin->reset(); initialiseRequired = false; } else { //!!! todo: retain parameters previously set Init(); } } if (initialiseRequired) { if (!mPlugin->initialise(channels, step, block)) { wxMessageBox(_("Sorry, Vamp Plug-in failed to initialize.")); return false; } } LabelTrack *ltrack = mFactory->NewLabelTrack(); if (!multiple) { ltrack->SetName(GetEffectName()); } else { ltrack->SetName(wxString::Format(wxT("%s: %s"), left->GetName().c_str(), GetEffectName().c_str())); } mTracks->Add(ltrack); float **data = new float*[channels]; for (int c = 0; c < channels; ++c) data[c] = new float[block]; sampleCount originalLen = len; sampleCount ls = lstart; sampleCount rs = rstart; while (len) { int request = block; if (request > len) request = len; if (left) left->Get((samplePtr)data[0], floatSample, ls, request); if (right) right->Get((samplePtr)data[1], floatSample, rs, request); if (request < (int)block) { for (int c = 0; c < channels; ++c) { for (int i = request; i < (int)block; ++i) { data[c][i] = 0.f; } } } Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime (ls, (int)(mRate + 0.5)); Vamp::Plugin::FeatureSet features = mPlugin->process(data, timestamp); AddFeatures(ltrack, features); if (len > (int)step) len -= step; else len = 0; ls += step; rs += step; if (channels > 1) { if (TrackGroupProgress(count, (ls - lstart) / double(originalLen))) return false; } else { if (TrackProgress(count, (ls - lstart) / double(originalLen))) return false; } } Vamp::Plugin::FeatureSet features = mPlugin->getRemainingFeatures(); AddFeatures(ltrack, features); prevTrackChannels = channels; left = (WaveTrack *)iter.Next(); } return true; }
bool ContrastDialog::GetDB(float &dB) { float rms = float(0.0); int numberSelecteTracks = 0; // For stereo tracks: sqrt((mean(L)+mean(R))/2) bool isStereo = false; double meanSq = 0.0; AudacityProject *p = GetActiveProject(); SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); WaveTrack *t = (WaveTrack *) iter.First(); while (t) { numberSelecteTracks++; if (numberSelecteTracks > 1 && !isStereo) { AudacityMessageDialog m(NULL, _("You can only measure one track at a time."), _("Error"), wxOK); m.ShowModal(); return false; } isStereo = t->GetLinked(); wxASSERT(mT0 <= mT1); // Ignore whitespace beyond ends of track. if(mT0 < t->GetStartTime()) mT0 = t->GetStartTime(); if(mT1 > t->GetEndTime()) mT1 = t->GetEndTime(); auto SelT0 = t->TimeToLongSamples(mT0); auto SelT1 = t->TimeToLongSamples(mT1); if(SelT0 > SelT1) { AudacityMessageDialog m(NULL, _("Invalid audio selection.\nPlease ensure that audio is selected."), _("Error"), wxOK); m.ShowModal(); return false; } if(SelT0 == SelT1) { AudacityMessageDialog m(NULL, _("Nothing to measure.\nPlease select a section of a track."), _("Error"), wxOK); m.ShowModal(); return false; } // Don't throw in this analysis dialog rms = ((WaveTrack *)t)->GetRMS(mT0, mT1, false); meanSq += rms * rms; t = (WaveTrack *) iter.Next(); } // TODO: This works for stereo, provided the audio clips are in both channels. // We should really count gaps between clips as silence. rms = (meanSq > 0.0)? sqrt(meanSq/(double)numberSelecteTracks) : 0.0; if(numberSelecteTracks == 0) { AudacityMessageDialog m(NULL, _("Please select an audio track."), _("Error"), wxOK); m.ShowModal(); return false; } // Gives warning C4056, Overflow in floating-point constant arithmetic // -INFINITY is intentional here. // Looks like we are stuck with this warning, as // #pragma warning( disable : 4056) // even around the whole function does not disable it successfully. dB = (rms == 0.0)? -INFINITY : LINEAR_TO_DB(rms); return true; }
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 ContrastDialog::GetDB(float &dB) { float rms = float(0.0); int numberSelecteTracks = 0; // For stereo tracks: sqrt((mean(L)+mean(R))/2) bool isStereo = false; double meanSq = 0.0; AudacityProject *p = GetActiveProject(); SelectedTrackListOfKindIterator iter(Track::Wave, p->GetTracks()); WaveTrack *t = (WaveTrack *) iter.First(); while (t) { numberSelecteTracks++; if (numberSelecteTracks > 1 && !isStereo) { wxMessageDialog m(NULL, _("You can only measure one track at a time."), _("Error"), wxOK); m.ShowModal(); return false; } isStereo = t->GetLinked(); wxASSERT(mT0 <= mT1); // Ignore whitespace beyond ends of track. if(mT0 < t->GetStartTime()) mT0 = t->GetStartTime(); if(mT1 > t->GetEndTime()) mT1 = t->GetEndTime(); sampleCount SelT0 = t->TimeToLongSamples(mT0); sampleCount SelT1 = t->TimeToLongSamples(mT1); if(SelT0 > SelT1) { wxMessageDialog m(NULL, _("Invalid audio selection.\nPlease ensure that audio is selected."), _("Error"), wxOK); m.ShowModal(); return false; } if(SelT0 == SelT1) { wxMessageDialog m(NULL, _("Nothing to measure.\nPlease select a section of a track."), _("Error"), wxOK); m.ShowModal(); return false; } ((WaveTrack *)t)->GetRMS(&rms, mT0, mT1); meanSq += rms * rms; t = (WaveTrack *) iter.Next(); } // TODO: This works for stereo, provided the audio clips are in both channels. // We should really count gaps between clips as silence. rms = (meanSq > 0.0)? sqrt(meanSq/(double)numberSelecteTracks) : 0.0; if(numberSelecteTracks == 0) { wxMessageDialog m(NULL, _("Please select an audio track."), _("Error"), wxOK); m.ShowModal(); return false; } dB = (rms == 0.0)? -INFINITY : LINEAR_TO_DB(rms); return true; }
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 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 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 EffectNormalize::Process() { bool wasLinked = false; // set when a track has a linked (stereo) track if (mGain == false && mDC == false) return true; //Iterate over each track this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); WaveTrack *track = (WaveTrack *) iter.First(); mCurTrackNum = 0; while (track) { //Get start and end times from track double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); //Set the current bounds to whichever left marker is //greater and whichever right marker is less: mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; // 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 = track->TimeToLongSamples(mCurT0); sampleCount end = track->TimeToLongSamples(mCurT1); //Get the track rate and samples mCurRate = track->GetRate(); mCurChannel = track->GetChannel(); if(mStereoInd) // do stereo tracks independently (the easy way) track->GetMinMax(&mMin, &mMax, mCurT0, mCurT1); else { if(!wasLinked) // new mono track or first of a stereo pair { track->GetMinMax(&mMin, &mMax, mCurT0, mCurT1); if(track->GetLinked()) { wasLinked = true; // so we use these values for the next (linked) track track = (WaveTrack *) iter.Next(); // get the next one for the max/min float min, max; track->GetMinMax(&min, &max, mCurT0, mCurT1); mMin = min < mMin ? min : mMin; mMax = max > mMax ? max : mMax; track = (WaveTrack *) iter.Prev(); // back to the one we are on } } else wasLinked = false; // second of the stereo pair, next one is mono or first } //ProcessOne() (implemented below) processes a single track if (!ProcessOne(track, start, end)) { bGoodResult = false; break; } } //Iterate to the next track track = (WaveTrack *) iter.Next(); mCurTrackNum++; } this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; }