bool EffectCompressor::InitPass1() { mMax=0.0; if (!mNormalize) DisableSecondPass(); // Find the maximum block length required for any track size_t maxlen = 0; SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); WaveTrack *track = (WaveTrack *) iter.First(); while (track) { maxlen = std::max(maxlen, track->GetMaxBlockSize()); //Iterate to the next track track = (WaveTrack *) iter.Next(); } mFollow1.reset(); mFollow2.reset(); // Allocate buffers for the envelope if(maxlen > 0) { mFollow1.reinit(maxlen); mFollow2.reinit(maxlen); } mFollowLen = maxlen; return true; }
double EffectTruncSilence::CalcPreviewInputLength(double /* previewLength */) { double inputLength = mT1 - mT0; double minInputLength = inputLength; // Master list of silent regions RegionList silences; // Start with the whole selection silent silences.push_back(Region(mT0, mT1)); SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); int whichTrack = 0; for (Track *t = iter.First(); t; t = iter.Next()) { WaveTrack *const wt = static_cast<WaveTrack *>(t); RegionList trackSilences; auto index = wt->TimeToLongSamples(mT0); sampleCount silentFrame = 0; // length of the current silence Analyze(silences, trackSilences, wt, &silentFrame, &index, whichTrack, &inputLength, &minInputLength); whichTrack++; } return inputLength; }
bool EffectScienFilter::Init() { int selcount = 0; double rate = 0.0; TrackListOfKindIterator iter(Track::Wave, inputTracks()); WaveTrack *t = (WaveTrack *) iter.First(); mNyquist = (t ? t->GetRate() : GetActiveProject()->GetRate()) / 2.0; while (t) { if (t->GetSelected()) { if (selcount == 0) { rate = t->GetRate(); } else { if (t->GetRate() != rate) { Effect::MessageBox(_("To apply a filter, all selected tracks must have the same sample rate.")); return false; } } selcount++; } t = (WaveTrack *) iter.Next(); } return true; }
// Deduce m_FromFrequency from the samples at the beginning of // the selection. Then set some other params accordingly. void EffectChangePitch::DeduceFrequencies() { // As a neat trick, attempt to get the frequency of the note at the // beginning of the selection. SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); WaveTrack *track = (WaveTrack *) iter.First(); if (track) { double rate = track->GetRate(); // Auto-size window -- high sample rates require larger windowSize. // Aim for around 2048 samples at 44.1 kHz (good down to about 100 Hz). // To detect single notes, analysis period should be about 0.2 seconds. // windowSize must be a power of 2. const size_t windowSize = // windowSize < 256 too inaccurate std::max(256, wxRound(pow(2.0, floor((log(rate / 20.0)/log(2.0)) + 0.5)))); // we want about 0.2 seconds to catch the first note. // number of windows rounded to nearest integer >= 1. const unsigned numWindows = std::max(1, wxRound((double)(rate / (5.0f * windowSize)))); double trackStart = track->GetStartTime(); double t0 = mT0 < trackStart? trackStart: mT0; auto start = track->TimeToLongSamples(t0); auto analyzeSize = windowSize * numWindows; Floats buffer{ analyzeSize }; Floats freq{ windowSize / 2 }; Floats freqa{ windowSize / 2, true }; track->Get((samplePtr) buffer.get(), floatSample, start, analyzeSize); for(unsigned i = 0; i < numWindows; i++) { ComputeSpectrum(buffer.get() + i * windowSize, windowSize, windowSize, rate, freq.get(), true); for(size_t j = 0; j < windowSize / 2; j++) freqa[j] += freq[j]; } size_t argmax = 0; for(size_t j = 1; j < windowSize / 2; j++) if (freqa[j] > freqa[argmax]) argmax = j; auto lag = (windowSize / 2 - 1) - argmax; m_dStartFrequency = rate / lag; } double dFromMIDInote = FreqToMIDInote(m_dStartFrequency); double dToMIDInote = dFromMIDInote + m_dSemitonesChange; m_nFromPitch = PitchIndex(dFromMIDInote); m_nFromOctave = PitchOctave(dFromMIDInote); m_nToPitch = PitchIndex(dToMIDInote); m_nToOctave = PitchOctave(dToMIDInote); m_FromFrequency = m_dStartFrequency; Calc_PercentChange(); Calc_ToFrequency(); }
bool EffectAutoDuck::Init() { mControlTrack = NULL; TrackListIterator iter(inputTracks()); Track *t = iter.First(); bool lastWasSelectedWaveTrack = false; const WaveTrack *controlTrackCandidate = NULL; while(t) { if (lastWasSelectedWaveTrack && !t->GetSelected() && t->GetKind() == Track::Wave) { // This could be the control track, so remember it controlTrackCandidate = (WaveTrack*)t; } lastWasSelectedWaveTrack = false; if (t->GetSelected()) { if (t->GetKind() == Track::Wave) { lastWasSelectedWaveTrack = true; } else { Effect::MessageBox( _("You selected a track which does not contain audio. AutoDuck can only process audio tracks."), /* i18n-hint: Auto duck is the name of an effect that 'ducks' (reduces the volume) * of the audio automatically when there is sound on another track. Not as * in 'Donald-Duck'!*/ wxICON_ERROR); return false; } } t = iter.Next(); } if (!controlTrackCandidate) { Effect::MessageBox( _("Auto Duck needs a control track which must be placed below the selected track(s)."), wxICON_ERROR); return false; } mControlTrack = controlTrackCandidate; return true; }
bool EffectTruncSilence::ProcessAll() { // Copy tracks CopyInputTracks(Track::All); // Master list of silent regions. // This list should always be kept in order. RegionList silences; SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); if (FindSilences(silences, inputTracks(), iter.First(), iter.Last())) { TrackListIterator iterOut(mOutputTracks.get()); double totalCutLen = 0.0; Track *const first = iterOut.First(); if (DoRemoval(silences, 0, 1, first, iterOut.Last(), totalCutLen)) { mT1 -= totalCutLen; return true; } } return false; }
bool VampEffect::Init() { mRate = 0.0; // PRL: this loop checked that channels of a track have the same rate, // but there was no check that all tracks have one rate, and only the first // is remembered in mRate. Is that correct? for (auto leader : inputTracks()->Leaders<const WaveTrack>()) { auto channelGroup = TrackList::Channels( leader ); auto rate = (*channelGroup.first++) -> GetRate(); for(auto channel : channelGroup) { if (rate != channel->GetRate()) // PRL: Track rate might not match individual clip rates. // So is this check not adequate? { // TODO: more-than-two-channels-message Effect::MessageBox(_("Sorry, Vamp Plug-ins cannot be run on stereo tracks where the individual channels of the track do not match.")); return false; } } if (mRate == 0.0) mRate = rate; } if (mRate <= 0.0) { mRate = mProjectRate; } // The plugin must be reloaded to allow changing parameters Vamp::HostExt::PluginLoader *loader = Vamp::HostExt::PluginLoader::getInstance(); mPlugin.reset(loader->loadPlugin(mKey, mRate, Vamp::HostExt::PluginLoader::ADAPT_ALL)); if (!mPlugin) { Effect::MessageBox(_("Sorry, failed to load Vamp Plug-in.")); return false; } return true; }
bool EffectAmplify::Init() { mPeak = 0.0; SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); for (Track *t = iter.First(); t; t = iter.Next()) { auto pair = ((WaveTrack *)t)->GetMinMax(mT0, mT1); // may throw const float min = pair.first, max = pair.second; float newpeak = (fabs(min) > fabs(max) ? fabs(min) : fabs(max)); if (newpeak > mPeak) { mPeak = newpeak; } } return true; }
bool EffectTruncSilence::ProcessIndependently() { unsigned nGroups = 0; const bool syncLock = ::GetActiveProject()->IsSyncLocked(); // Check if it's permissible { SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks()); for (Track *track = iter.First(); track; track = iter.Next(true) // skip linked tracks ) { if (syncLock) { Track *const link = track->GetLink(); SyncLockedTracksIterator syncIter(inputTracks()); for (Track *track2 = syncIter.StartWith(track); track2; track2 = syncIter.Next()) { if (track2->GetKind() == Track::Wave && !(track2 == track || track2 == link) && track2->GetSelected()) { ::wxMessageBox(_("When truncating independently, there may only be one selected audio track in each Sync-Locked Track Group.")); return false; } } } ++nGroups; } } if (nGroups == 0) // nothing to do return true; // Now do the work // Copy tracks CopyInputTracks(Track::All); double newT1 = 0.0; { unsigned iGroup = 0; SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks.get()); for (Track *track = iter.First(); track; ++iGroup, track = iter.Next(true) // skip linked tracks ) { Track *const link = track->GetLink(); Track *const last = link ? link : track; RegionList silences; if (!FindSilences(silences, mOutputTracks.get(), track, last)) return false; // Treat tracks in the sync lock group only Track *groupFirst, *groupLast; if (syncLock) { SyncLockedTracksIterator syncIter(mOutputTracks.get()); groupFirst = syncIter.StartWith(track); groupLast = syncIter.Last(); } else { groupFirst = track; groupLast = last; } double totalCutLen = 0.0; if (!DoRemoval(silences, iGroup, nGroups, groupFirst, groupLast, totalCutLen)) return false; newT1 = std::max(newT1, mT1 - totalCutLen); } } mT1 = newT1; return true; }
bool VampEffect::Process() { if (!mPlugin) { return false; } int count = 0; bool multiple = false; unsigned prevTrackChannels = 0; if (GetNumWaveGroups() > 1) { // 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; } std::vector<std::shared_ptr<Effect::AddedAnalysisTrack>> addedTracks; for (auto leader : inputTracks()->Leaders<const WaveTrack>()) { auto channelGroup = TrackList::Channels(leader); auto left = *channelGroup.first++; sampleCount lstart, rstart = 0; sampleCount len; GetSamples(left, &lstart, &len); unsigned channels = 1; // channelGroup now contains all but the first channel const WaveTrack *right = channelGroup.size() ? *channelGroup.first++ : nullptr; if (right) { channels = 2; GetSamples(right, &rstart, &len); } // TODO: more-than-two-channels 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)) { Effect::MessageBox(_("Sorry, Vamp Plug-in failed to initialize.")); return false; } } const auto effectName = GetSymbol().Translation(); addedTracks.push_back(AddAnalysisTrack( multiple ? wxString::Format( _("%s: %s"), left->GetName(), effectName ) : effectName )); LabelTrack *ltrack = addedTracks.back()->get(); FloatBuffers data{ channels, block }; auto originalLen = len; auto ls = lstart; auto rs = rstart; while (len != 0) { const auto request = limitSampleBufferSize( block, len ); if (left) { left->Get((samplePtr)data[0].get(), floatSample, ls, request); } if (right) { right->Get((samplePtr)data[1].get(), floatSample, rs, request); } if (request < block) { for (unsigned int c = 0; c < channels; ++c) { for (decltype(block) i = request; i < block; ++i) { data[c][i] = 0.f; } } } // UNSAFE_SAMPLE_COUNT_TRUNCATION // Truncation in case of very long tracks! Vamp::RealTime timestamp = Vamp::RealTime::frame2RealTime( long( ls.as_long_long() ), (int)(mRate + 0.5) ); Vamp::Plugin::FeatureSet features = mPlugin->process( reinterpret_cast< float** >( data.get() ), 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).as_double() / originalLen.as_double() )) { return false; } } else { if (TrackProgress(count, (ls - lstart).as_double() / originalLen.as_double() )) { return false; } } } Vamp::Plugin::FeatureSet features = mPlugin->getRemainingFeatures(); AddFeatures(ltrack, features); prevTrackChannels = channels; } // All completed without cancellation, so commit the addition of tracks now for (auto &addedTrack : addedTracks) addedTrack->Commit(); return true; }