Audacity 3.2.0
PerTrackEffect.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 PerTrackEffect.cpp
6
7 Dominic Mazzoni
8 Vaughan Johnson
9 Martyn Shaw
10
11 Paul Licameli split from Effect.cpp
12
13*******************************************************************//*******************************************************************/
19
20
21#include "PerTrackEffect.h"
22#include "EffectOutputTracks.h"
23
24#include "AudioGraphBuffers.h"
25#include "AudioGraphTask.h"
26#include "EffectStage.h"
27#include "SyncLock.h"
28#include "TimeWarper.h"
29#include "ViewInfo.h"
30#include "WaveTrack.h"
31#include "WaveTrackSink.h"
32#include "WideSampleSource.h"
33
35
37{
38 return mProcessor.Process(*this, settings);
39}
40
42 double, ChannelNames)
43{
44 return true;
45}
46
48{
49 return true;
50}
51
53
55{
56 return true;
57}
58
60{
61 return false;
62}
63
65 EffectInstance &instance, EffectSettings &settings) const
66{
67 auto pThis = const_cast<PerTrackEffect *>(this);
68
69 // Destroy any pre-formed output tracks when done
70 auto pOutputs = mpOutputTracks.get();
71
72 std::optional<EffectOutputTracks> outputs;
73 if (!pOutputs)
74 pOutputs = &outputs.emplace(*mTracks, GetType(),
76
77 bool bGoodResult = true;
78 // mPass = 1;
79 if (DoPass1()) {
80 auto &myInstance = dynamic_cast<Instance&>(instance);
81 bGoodResult = pThis->ProcessPass(pOutputs->Get(), myInstance, settings);
82 // mPass = 2;
83 if (bGoodResult && DoPass2())
84 bGoodResult = pThis->ProcessPass(pOutputs->Get(), myInstance, settings);
85 }
86 if (bGoodResult)
87 pOutputs->Commit();
89 return bGoodResult;
90}
91
94{
95 const auto duration = settings.extra.GetDuration();
96 bool bGoodResult = true;
97 bool isGenerator = GetType() == EffectTypeGenerate;
98 bool isProcessor = GetType() == EffectTypeProcess;
99
100 Buffers inBuffers, outBuffers;
101 ChannelName map[3];
102 size_t prevBufferSize = 0;
103 int count = 0;
104 bool clear = false;
105
106 // It's possible that the number of channels the effect expects changed based on
107 // the parameters (the Audacity Reverb effect does when the stereo width is 0).
108 const auto numAudioIn = instance.GetAudioInCount();
109 const auto numAudioOut = instance.GetAudioOutCount();
110 if (numAudioOut < 1)
111 return false;
112
113 // Instances that can be reused in each loop pass
114 std::vector<std::shared_ptr<EffectInstance>> recycledInstances{
115 // First one is the given one; any others pushed onto here are
116 // discarded when we exit
117 std::dynamic_pointer_cast<EffectInstanceEx>(instance.shared_from_this())
118 };
119
120 const bool multichannel = numAudioIn > 1;
121 int iChannel = 0;
122 TrackListHolder results;
123 const auto waveTrackVisitor =
124 [&](WaveTrack &wt, WaveChannel &chan, bool isFirst) {
125 if (isFirst)
126 iChannel = 0;
127
128 sampleCount len = 0;
129 sampleCount start = 0;
130 WaveChannel *pRight{};
131
132 const int channel = (multichannel ? -1 : iChannel++);
133 const auto numChannels = MakeChannelMap(wt.NChannels(), channel, map);
134 if (multichannel) {
135 assert(numAudioIn > 1);
136 if (numChannels == 2) {
137 // TODO: more-than-two-channels
138 pRight = (*wt.Channels().rbegin()).get();
139 clear = false;
140 }
141 }
142
143 if (!isGenerator) {
144 GetBounds(wt, &start, &len);
145 mSampleCnt = len;
146 if (len > 0 && numAudioIn < 1) {
147 bGoodResult = false;
148 return;
149 }
150 }
151 else
152 mSampleCnt = wt.TimeToLongSamples(duration);
153
154 const auto sampleRate = wt.GetRate();
155
156 // Get the block size the client wants to use
157 auto max = wt.GetMaxBlockSize() * 2;
158 const auto blockSize = instance.SetBlockSize(max);
159 if (blockSize == 0) {
160 bGoodResult = false;
161 return;
162 }
163
164 // Calculate the buffer size to be at least the max rounded up to the clients
165 // selected block size.
166 const auto bufferSize =
167 ((max + (blockSize - 1)) / blockSize) * blockSize;
168 if (bufferSize == 0) {
169 bGoodResult = false;
170 return;
171 }
172
173 // Always create the number of input buffers the client expects even
174 // if we don't have
175 // the same number of channels.
176 // (These resizes may do nothing after the first track)
177
178 if (len > 0)
179 assert(numAudioIn > 0); // checked above
180 inBuffers.Reinit(
181 // TODO fix this hack for making Generator progress work without
182 // assertion violations. Make a dummy Source class that doesn't
183 // care about the buffers.
184 std::max(1u, numAudioIn),
185 blockSize,
186 std::max<size_t>(1, bufferSize / blockSize));
187 if (len > 0)
188 // post of Reinit later satisfies pre of Source::Acquire()
189 assert(inBuffers.Channels() > 0);
190
191 if (prevBufferSize != bufferSize) {
192 // Buffer size has changed
193 // We won't be using more than the first 2 buffers,
194 // so clear the rest (if any)
195 for (size_t i = 2; i < numAudioIn; i++)
196 inBuffers.ClearBuffer(i, bufferSize);
197 }
198 prevBufferSize = bufferSize;
199
200 // Always create the number of output buffers the client expects
201 // even if we don't have the same number of channels.
202 // (These resizes may do nothing after the first track)
203 // Output buffers get an extra blockSize worth to give extra room if
204 // the plugin adds latency -- PRL: actually not important to do
205 assert(numAudioOut > 0); // checked above
206 outBuffers.Reinit(numAudioOut, blockSize,
207 (bufferSize / blockSize) + 1);
208 // post of Reinit satisfies pre of ProcessTrack
209 assert(outBuffers.Channels() > 0);
210
211 // (Re)Set the input buffer positions
212 inBuffers.Rewind();
213
214 // Clear unused input buffers
215 if (!pRight && !clear && numAudioIn > 1) {
216 inBuffers.ClearBuffer(1, bufferSize);
217 clear = true;
218 }
219
220 const auto genLength = [this, &settings, &wt, isGenerator](
221 ) -> std::optional<sampleCount> {
222 double genDur = 0;
223 if (isGenerator) {
224 const auto duration = settings.extra.GetDuration();
225 if (IsPreviewing()) {
226 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &genDur, 6.0);
227 genDur = std::min(duration, CalcPreviewInputLength(settings, genDur));
228 }
229 else
230 genDur = duration;
231 // round to nearest sample
232 return sampleCount{ (wt.GetRate() * genDur) + 0.5 };
233 }
234 else
235 return {};
236 }();
237
238 const auto pollUser = [this, numChannels, count, start,
239 length = (genLength ? *genLength : len).as_double()
240 ](sampleCount inPos){
241 if (numChannels > 1) {
243 count, (inPos - start).as_double() / length)
244 )
245 return false;
246 }
247 else {
248 if (TrackProgress(count, (inPos - start).as_double() / length))
249 return false;
250 }
251 return true;
252 };
253
254 // Assured above
255 assert(len == 0 || inBuffers.Channels() > 0);
256 // TODO fix this hack to make the time remaining of the generator
257 // progress dialog correct
258 if (len == 0 && genLength)
259 len = *genLength;
260 WideSampleSequence *pSeq = &chan;
261 if (pRight)
262 pSeq = &wt;
263 WideSampleSource source{
264 *pSeq, size_t(pRight ? 2 : 1), start, len, pollUser };
265 // Assert source is safe to Acquire inBuffers
266 assert(source.AcceptsBuffers(inBuffers));
267 assert(source.AcceptsBlockSize(inBuffers.BlockSize()));
268
269 // Make "wide" or "narrow" copy of the track if generating
270 // Old generator code may still proceed "interval-major" and later
271 // join mono into stereo
272 auto wideTrack =
273 (pRight && isGenerator) ? wt.EmptyCopy() : nullptr;
274 auto narrowTrack =
275 (!pRight && isGenerator) ? wt.EmptyCopy(1) : nullptr;
276 const auto pGenerated = wideTrack
277 ? wideTrack
278 : narrowTrack;
279
280 WaveTrackSink sink{ chan, pRight, pGenerated.get(), start, isProcessor,
282 };
283 assert(sink.AcceptsBuffers(outBuffers));
284
285 // Go process the track(s)
286 const auto factory =
287 [this, &recycledInstances, counter = 0]() mutable {
288 auto index = counter++;
289 if (index < recycledInstances.size())
290 return recycledInstances[index];
291 else
292 return recycledInstances.emplace_back(MakeInstance());
293 };
294 bGoodResult = ProcessTrack(channel, factory, settings, source, sink,
295 genLength, sampleRate, wt, inBuffers, outBuffers);
296 if (bGoodResult) {
297 sink.Flush(outBuffers);
298 bGoodResult = sink.IsOk();
299 if (bGoodResult && pGenerated) {
300 if (!results)
301 results = TrackList::Temporary(nullptr, pGenerated);
302 else {
303 results->Add(pGenerated);
304 if (!multichannel && !isFirst && narrowTrack) {
305 // Generated a stereo track, in channel-major fashion.
306 // Get the last track but one -- generated in the previous
307 // pass
308 const auto pLast =
309 static_cast<WaveTrack*>(*std::next(results->rbegin()));
310 pLast->ZipClips();
311 }
312 }
313 }
314 }
315 if (!bGoodResult)
316 return;
317 ++count;
318 };
319 const auto defaultTrackVisitor =
320 [&](Track &t) {
322 t.SyncLockAdjust(mT1, mT0 + duration);
323 };
324
325 outputs.Any().VisitWhile(bGoodResult,
326 [&](auto &&fallthrough){ return [&](WaveTrack &wt) {
327 if (!wt.GetSelected())
328 return fallthrough();
329 const auto channels = wt.Channels();
330 if (multichannel)
331 waveTrackVisitor(wt, **channels.begin(), true);
332 else {
333 bool first = true;
334 for (const auto pChannel : channels) {
335 waveTrackVisitor(wt, *pChannel, first);
336 first = false;
337 }
338 }
339 if (results) {
340 const auto t1 = ViewInfo::Get(*FindProject()).selectedRegion.t1();
341 PasteTimeWarper warper { t1,
342 mT0 + (*results->begin())->GetEndTime() };
343 wt.ClearAndPaste(mT0, t1,
344 static_cast<WaveTrack&>(*results->DetachFirst()),
345 true, true, &warper);
346 results.reset();
347 }
348 }; },
349 defaultTrackVisitor
350 );
351
352 if (bGoodResult && GetType() == EffectTypeGenerate)
353 mT1 = mT0 + duration;
354
355 return bGoodResult;
356}
357
360 AudioGraph::Source &upstream, AudioGraph::Sink &sink,
361 std::optional<sampleCount> genLength,
362 const double sampleRate, const SampleTrack &wt,
363 Buffers &inBuffers, Buffers &outBuffers)
364{
365 assert(upstream.AcceptsBuffers(inBuffers));
366 assert(sink.AcceptsBuffers(outBuffers));
367
368 const auto blockSize = inBuffers.BlockSize();
369 assert(upstream.AcceptsBlockSize(blockSize));
370 assert(blockSize == outBuffers.BlockSize());
371
372 auto pSource = EffectStage::Create(
373 channel, static_cast<const WideSampleSequence&>(wt).NChannels(), upstream,
374 inBuffers, factory, settings, sampleRate, genLength);
375 if (!pSource)
376 return false;
377 assert(pSource->AcceptsBlockSize(blockSize)); // post of ctor
378 assert(pSource->AcceptsBuffers(outBuffers));
379
380 AudioGraph::Task task{ *pSource, outBuffers, sink };
381 return task.RunLoop();
382}
383
384std::shared_ptr<EffectOutputTracks> PerTrackEffect::MakeOutputTracks()
385{
386 return mpOutputTracks =
387 std::make_shared<EffectOutputTracks>(*mTracks, GetType(),
389}
390
392{
393 mpOutputTracks.reset();
394}
wxT("CloseDown"))
static RegisteredToolbarFactory factory
int min(int a, int b)
@ EffectTypeGenerate
@ EffectTypeProcess
ChannelName
unsigned MakeChannelMap(int nChannels, int channel, ChannelName map[3])
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:42
static Settings & settings()
Definition: TrackInfo.cpp:51
Accumulates (non-interleaved) data during effect processing.
size_t BlockSize() const
void Rewind()
Reset positions to starts of buffers.
void Reinit(unsigned nChannels, size_t blockSize, size_t nBlocks, size_t padding=0)
unsigned Channels() const
void ClearBuffer(unsigned iChannel, size_t n)
Downstream receiver of sample streams, taking Buffers as external context.
virtual bool AcceptsBuffers(const Buffers &buffers) const =0
Upstream producer of sample streams, taking Buffers as external context.
virtual bool AcceptsBlockSize(size_t blockSize) const =0
virtual bool AcceptsBuffers(const Buffers &buffers) const =0
double mT1
Definition: EffectBase.h:123
bool IsPreviewing() const
Definition: EffectBase.h:100
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:116
double mT0
Definition: EffectBase.h:122
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:220
virtual EffectType GetType() const =0
Type determines how it behaves.
bool TrackGroupProgress(int whichGroup, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:353
void GetBounds(const WaveTrack &track, sampleCount *start, sampleCount *len)
Definition: Effect.cpp:363
double CalcPreviewInputLength(const EffectSettings &settings, double previewLength) const override
Default implementation returns previewLength
Definition: Effect.cpp:384
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:343
virtual std::shared_ptr< EffectInstance > MakeInstance() const =0
Make an object maintaining short-term state of an Effect.
Performs effect computation.
virtual unsigned GetAudioInCount() const =0
How many input buffers to allocate at once.
virtual size_t SetBlockSize(size_t maxBlockSize)=0
virtual bool NeedsDither() const
virtual unsigned GetAudioOutCount() const =0
How many output buffers to allocate at once.
std::pair< double, double > TimeInterval
static std::unique_ptr< EffectStage > Create(int channel, int nInputChannels, Source &upstream, Buffers &inBuffers, const Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength)
Satisfies postcondition of constructor or returns null.
Definition: EffectStage.cpp:79
double t1() const
Definition: ViewInfo.h:36
Unit slope but with either a jump (pasting more) or a flat interval (pasting less)
Definition: TimeWarper.h:181
bool ProcessFinalize() noexcept override
bool ProcessInitialize(EffectSettings &settings, double sampleRate, ChannelNames chanMap) override
bool Process(EffectSettings &settings) final
Uses the other virtual functions of this class.
const PerTrackEffect & mProcessor
Base class for many of the effects in Audacity.
bool DoPass2() const
std::shared_ptr< EffectOutputTracks > mpOutputTracks
void DestroyOutputTracks() const
bool ProcessPass(TrackList &outputs, Instance &instance, EffectSettings &settings)
std::shared_ptr< EffectOutputTracks > MakeOutputTracks()
bool Process(EffectInstance &instance, EffectSettings &settings) const
~PerTrackEffect() override
static bool ProcessTrack(int channel, const Factory &factory, EffectSettings &settings, AudioGraph::Source &source, AudioGraph::Sink &sink, std::optional< sampleCount > genLength, double sampleRate, const SampleTrack &wt, Buffers &inBuffers, Buffers &outBuffers)
bool DoPass1() const
std::function< std::shared_ptr< EffectInstance >()> Factory
sampleCount mSampleCnt
static bool IsSyncLockSelected(const Track &track)
Definition: SyncLock.cpp:80
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:78
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:850
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:950
static TrackListHolder Temporary(AudacityProject *pProject, const Track::Holder &pTrack={})
Definition: Track.cpp:858
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
auto Channels()
Definition: WaveTrack.h:263
void ZipClips(bool mustAlign=true)
Definition: WaveTrack.cpp:3317
double GetRate() const override
Definition: WaveTrack.cpp:821
size_t NChannels() const override
A constant property.
Definition: WaveTrack.cpp:530
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2279
void ClearAndPaste(double t0, double t1, const WaveTrack &src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=nullptr, bool clearByTrimming=false)
Definition: WaveTrack.cpp:1219
sampleCount TimeToLongSamples(double t0) const
Adapts WideSampleSequence to the interface AudioGraph::Source.
virtual bool Read(const wxString &key, bool *value) const =0
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
Copies from a Source to a Sink, mediated by Buffers.
Externalized state of a plug-in.