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
23#include "AudioGraphBuffers.h"
24#include "AudioGraphTask.h"
25#include "EffectStage.h"
26#include "MixAndRender.h"
27#include "SampleTrackSource.h"
28#include "../SyncLock.h"
29#include "ViewInfo.h"
30#include "../WaveTrack.h"
31#include "../WaveTrackSink.h"
32
33AudioGraph::Sink::~Sink() = default;
34
36
38{
39 return mProcessor.Process(*this, settings);
40}
41
43 double, ChannelNames)
44{
45 return true;
46}
47
49{
50 return true;
51}
52
54
56{
57 return true;
58}
59
61{
62 return false;
63}
64
66 EffectInstance &instance, EffectSettings &settings) const
67{
68 auto pThis = const_cast<PerTrackEffect *>(this);
69 pThis->CopyInputTracks(true);
70 bool bGoodResult = true;
71 // mPass = 1;
72 if (DoPass1()) {
73 auto &myInstance = dynamic_cast<Instance&>(instance);
74 bGoodResult = pThis->ProcessPass(myInstance, settings);
75 // mPass = 2;
76 if (bGoodResult && DoPass2())
77 bGoodResult = pThis->ProcessPass(myInstance, settings);
78 }
79 pThis->ReplaceProcessedTracks(bGoodResult);
80 return bGoodResult;
81}
82
84{
85 const auto duration = settings.extra.GetDuration();
86 bool bGoodResult = true;
87 bool isGenerator = GetType() == EffectTypeGenerate;
88 bool isProcessor = GetType() == EffectTypeProcess;
89
90 Buffers inBuffers, outBuffers;
91 ChannelName map[3];
92 size_t prevBufferSize = 0;
93 int count = 0;
94 bool clear = false;
95
96 // It's possible that the number of channels the effect expects changed based on
97 // the parameters (the Audacity Reverb effect does when the stereo width is 0).
98 const auto numAudioIn = instance.GetAudioInCount();
99 const auto numAudioOut = instance.GetAudioOutCount();
100 if (numAudioOut < 1)
101 return false;
102
103 // Instances that can be reused in each loop pass
104 std::vector<std::shared_ptr<EffectInstance>> recycledInstances{
105 // First one is the given one; any others pushed onto here are
106 // discarded when we exit
107 std::dynamic_pointer_cast<EffectInstanceEx>(instance.shared_from_this())
108 };
109
110 const bool multichannel = numAudioIn > 1;
111 auto range = multichannel
112 ? mOutputTracks->Leaders()
113 : mOutputTracks->Any();
114 range.VisitWhile( bGoodResult,
115 [&](WaveTrack *pLeft, const Track::Fallthrough &fallthrough) {
116 // Track range visitor functions receive a pointer that is never null
117 auto &left = *pLeft;
118 if (!left.GetSelected())
119 return fallthrough();
120
121 sampleCount len = 0;
122 sampleCount start = 0;
123 WaveTrack *pRight{};
124
125 const auto numChannels =
126 AudioGraph::MakeChannelMap(left, multichannel, map);
127 if (multichannel) {
128 assert(numAudioIn > 1);
129 if (numChannels == 2) {
130 // TODO: more-than-two-channels
131 pRight = *TrackList::Channels(&left).rbegin();
132 clear = false;
133 }
134 }
135
136 if (!isGenerator) {
137 GetBounds(left, pRight, &start, &len);
138 mSampleCnt = len;
139 if (len > 0 && numAudioIn < 1) {
140 bGoodResult = false;
141 return;
142 }
143 }
144 else
145 mSampleCnt = left.TimeToLongSamples(duration);
146
147 const auto sampleRate = left.GetRate();
148
149 // Get the block size the client wants to use
150 auto max = left.GetMaxBlockSize() * 2;
151 const auto blockSize = instance.SetBlockSize(max);
152 if (blockSize == 0) {
153 bGoodResult = false;
154 return;
155 }
156
157 // Calculate the buffer size to be at least the max rounded up to the clients
158 // selected block size.
159 const auto bufferSize =
160 ((max + (blockSize - 1)) / blockSize) * blockSize;
161 if (bufferSize == 0) {
162 bGoodResult = false;
163 return;
164 }
165
166 // Always create the number of input buffers the client expects even
167 // if we don't have
168 // the same number of channels.
169 // (These resizes may do nothing after the first track)
170
171 if (len > 0)
172 assert(numAudioIn > 0); // checked above
173 inBuffers.Reinit(
174 // TODO fix this hack for making Generator progress work without
175 // assertion violations. Make a dummy Source class that doesn't
176 // care about the buffers.
177 std::max(1u, numAudioIn),
178 blockSize,
179 std::max<size_t>(1, bufferSize / blockSize));
180 if (len > 0)
181 // post of Reinit later satisfies pre of Source::Acquire()
182 assert(inBuffers.Channels() > 0);
183
184 if (prevBufferSize != bufferSize) {
185 // Buffer size has changed
186 // We won't be using more than the first 2 buffers,
187 // so clear the rest (if any)
188 for (size_t i = 2; i < numAudioIn; i++)
189 inBuffers.ClearBuffer(i, bufferSize);
190 }
191 prevBufferSize = bufferSize;
192
193 // Always create the number of output buffers the client expects
194 // even if we don't have the same number of channels.
195 // (These resizes may do nothing after the first track)
196 // Output buffers get an extra blockSize worth to give extra room if
197 // the plugin adds latency -- PRL: actually not important to do
198 assert(numAudioOut > 0); // checked above
199 outBuffers.Reinit(numAudioOut, blockSize,
200 (bufferSize / blockSize) + 1);
201 // post of Reinit satisfies pre of ProcessTrack
202 assert(outBuffers.Channels() > 0);
203
204 // (Re)Set the input buffer positions
205 inBuffers.Rewind();
206
207 // Clear unused input buffers
208 if (!pRight && !clear && numAudioIn > 1) {
209 inBuffers.ClearBuffer(1, bufferSize);
210 clear = true;
211 }
212
213 const auto genLength = [this, &settings, &left, isGenerator](
214 ) -> std::optional<sampleCount> {
215 double genDur = 0;
216 if (isGenerator) {
217 const auto duration = settings.extra.GetDuration();
218 if (IsPreviewing()) {
219 gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &genDur, 6.0);
220 genDur = std::min(duration, CalcPreviewInputLength(settings, genDur));
221 }
222 else
223 genDur = duration;
224 // round to nearest sample
225 return sampleCount{ (left.GetRate() * genDur) + 0.5 };
226 }
227 else
228 return {};
229 }();
230
231 const auto pollUser = [this, numChannels, count, start,
232 length = (genLength ? *genLength : len).as_double()
233 ](sampleCount inPos){
234 if (numChannels > 1) {
236 count, (inPos - start).as_double() / length)
237 )
238 return false;
239 }
240 else {
241 if (TrackProgress(count, (inPos - start).as_double() / length))
242 return false;
243 }
244 return true;
245 };
246
247 // Assured above
248 assert(len == 0 || inBuffers.Channels() > 0);
249 // TODO fix this hack to make the time remaining of the generator
250 // progress dialog correct
251 if (len == 0 && genLength)
252 len = *genLength;
253 SampleTrackSource source{ left, pRight, start, len, pollUser };
254 // Assert source is safe to Acquire inBuffers
255 assert(source.AcceptsBuffers(inBuffers));
256 assert(source.AcceptsBlockSize(inBuffers.BlockSize()));
257
258 WaveTrackSink sink{ left, pRight, start, isGenerator, isProcessor,
260 };
261 assert(sink.AcceptsBuffers(outBuffers));
262
263 // Go process the track(s)
264 const auto factory =
265 [this, &recycledInstances, counter = 0]() mutable {
266 auto index = counter++;
267 if (index < recycledInstances.size())
268 return recycledInstances[index];
269 else
270 return recycledInstances.emplace_back(MakeInstance());
271 };
272 bGoodResult = ProcessTrack(multichannel, factory, settings, source, sink,
273 genLength, sampleRate, left,
274 inBuffers, outBuffers);
275 if (bGoodResult)
276 sink.Flush(outBuffers,
277 mT0, ViewInfo::Get(*FindProject()).selectedRegion.t1());
278 if (!bGoodResult)
279 return;
280 ++count;
281 },
282 [&](Track *t) {
284 t->SyncLockAdjust(mT1, mT0 + duration);
285 }
286 );
287
288 if (bGoodResult && GetType() == EffectTypeGenerate)
289 mT1 = mT0 + duration;
290
291 return bGoodResult;
292}
293
296 AudioGraph::Source &upstream, AudioGraph::Sink &sink,
297 std::optional<sampleCount> genLength,
298 const double sampleRate, const Track &track,
299 Buffers &inBuffers, Buffers &outBuffers)
300{
301 assert(upstream.AcceptsBuffers(inBuffers));
302 assert(sink.AcceptsBuffers(outBuffers));
303
304 const auto blockSize = inBuffers.BlockSize();
305 assert(upstream.AcceptsBlockSize(blockSize));
306 assert(blockSize == outBuffers.BlockSize());
307
308 auto pSource = AudioGraph::EffectStage::Create( multi, upstream, inBuffers,
309 factory, settings, sampleRate, genLength, track );
310 if (!pSource)
311 return false;
312 assert(pSource->AcceptsBlockSize(blockSize)); // post of ctor
313 assert(pSource->AcceptsBuffers(outBuffers));
314
315 AudioGraph::Task task{ *pSource, outBuffers, sink };
316 return task.RunLoop();
317}
wxT("CloseDown"))
int min(int a, int b)
@ EffectTypeGenerate
@ EffectTypeProcess
ChannelName
FileConfig * gPrefs
Definition: Prefs.cpp:70
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
static Settings & settings()
Definition: TrackInfo.cpp:87
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)
static std::unique_ptr< EffectStage > Create(bool multi, Source &upstream, Buffers &inBuffers, const Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength, const Track &track)
Satisfies postcondition of constructor or returns null.
Definition: EffectStage.cpp:87
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:109
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:107
bool IsPreviewing() const
Definition: EffectBase.h:85
double mT0
Definition: EffectBase.h:108
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:320
bool TrackGroupProgress(int whichGroup, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:637
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:677
double CalcPreviewInputLength(const EffectSettings &settings, double previewLength) const override
Default implementation returns previewLength
Definition: Effect.cpp:801
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:627
EffectType GetType() const override
Type determines how it behaves.
Definition: Effect.cpp:107
void GetBounds(const WaveTrack &track, const WaveTrack *pRight, sampleCount *start, sampleCount *len)
Definition: Effect.cpp:647
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.
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
bool ProcessPass(Instance &instance, EffectSettings &settings)
bool Process(EffectInstance &instance, EffectSettings &settings) const
~PerTrackEffect() override
bool DoPass1() const
std::function< std::shared_ptr< EffectInstance >()> Factory
static bool ProcessTrack(bool multi, const Factory &factory, EffectSettings &settings, AudioGraph::Source &source, AudioGraph::Sink &sink, std::optional< sampleCount > genLength, double sampleRate, const Track &track, Buffers &inBuffers, Buffers &outBuffers)
sampleCount mSampleCnt
Adapts SampleTrack to the interface AudioGraph::Source.
static bool IsSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:43
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:225
Continuation<> Fallthrough
Type of arguments passed as optional second parameter to TypeSwitch<void>() cases.
Definition: Track.h:541
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1541
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:234
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
AUDIO_GRAPH_API unsigned MakeChannelMap(const Track &track, bool multichannel, ChannelName map[3])
static RegisteredToolbarFactory factory
Copies from a Source to a Sink, mediated by Buffers.
Externalized state of a plug-in.