Audacity 3.2.0
Mix.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Mix.cpp
6
7 Dominic Mazzoni
8 Markus Meyer
9 Vaughan Johnson
10
11*******************************************************************//*******************************************************************/
17#include "Mix.h"
18#include "MixerSource.h"
19
20#include <cmath>
21#include "EffectStage.h"
22#include "Dither.h"
23#include "Resample.h"
24#include "WideSampleSequence.h"
25#include "float_cast.h"
26#include <numeric>
27
28#include "DownmixSource.h"
29
30namespace {
31template<typename T, typename F> std::vector<T>
32initVector(size_t dim1, const F &f)
33{
34 std::vector<T> result( dim1 );
35 for (auto &row : result)
36 f(row);
37 return result;
38}
39
40template<typename T> std::vector<std::vector<T>>
41initVector(size_t dim1, size_t dim2)
42{
43 return initVector<std::vector<T>>(dim1,
44 [dim2](auto &row){ row.resize(dim2); });
45}
46}
47
48namespace
49{
50
51
52void ConsiderStages(const Mixer::Stages& stages, size_t& blockSize)
53{
54 for (const auto& stage : stages)
55 {
56 // Need an instance to query acceptable block size
57 const auto pInstance = stage.factory();
58 if (pInstance)
59 blockSize = std::min(blockSize, pInstance->SetBlockSize(blockSize));
60 // Cache the first factory call
61 stage.mpFirstInstance = move(pInstance);
62 }
63}
64
65// Find a block size acceptable to all stages; side-effects on instances
67 const Mixer::Inputs& inputs,
68 const std::optional<Mixer::Stages>& masterEffects, size_t bufferSize)
69{
70 size_t blockSize = bufferSize;
71 for (const auto &input : inputs) {
72 const auto sequence = input.pSequence.get();
73 const auto nInChannels = sequence->NChannels();
74 if (!sequence) {
75 assert(false);
76 break;
77 }
78 ConsiderStages(input.stages, blockSize);
79 }
80 if (masterEffects)
81 ConsiderStages(*masterEffects, blockSize);
82
83 return blockSize;
84}
85
87 return spec.mpFirstInstance && spec.mpFirstInstance->NeedsDither();
88};
89
90} // namespace
91
93 Inputs inputs, std::optional<Stages> masterEffects, const bool mayThrow,
94 const WarpOptions& warpOptions, const double startTime,
95 const double stopTime, const unsigned numOutChannels,
96 const size_t outBufferSize, const bool outInterleaved, double outRate,
97 sampleFormat outFormat, const bool highQuality, MixerSpec* const mixerSpec,
98 ApplyVolume applyVolume)
99 : mNumChannels { numOutChannels }
100 , mInputs { move(inputs) }
101 , mMasterEffects { move(masterEffects) }
102 , mBufferSize { FindBufferSize(mInputs, mMasterEffects, outBufferSize) }
103 , mApplyVolume { applyVolume }
104 , mHighQuality { highQuality }
105 , mFormat { outFormat }
106 , mInterleaved { outInterleaved }
107 , mTimesAndSpeed { std::make_shared<TimesAndSpeed>(TimesAndSpeed {
108 startTime, stopTime, warpOptions.initialSpeed, startTime }) }
109
110 // non-interleaved
111 , mTemp { mNumChannels, mBufferSize, 1, 1 }
112 , mBuffer { initVector<SampleBuffer>(
113 mInterleaved ? 1 : mNumChannels,
114 [format = mFormat,
115 size = mBufferSize * (mInterleaved ? mNumChannels : 1)](
116 auto& buffer) { buffer.Allocate(size, format); }) }
117 , mEffectiveFormat { floatSample }
118{
119 assert(BufferSize() <= outBufferSize);
120 const auto nChannelsIn =
121 std::accumulate(mInputs.begin(), mInputs.end(), size_t{},
122 [](auto sum, const auto &input){
123 return sum + input.pSequence->NChannels(); });
124
125 // Examine the temporary instances that were made in FindBufferSize
126 // This finds a sufficient, but not necessary, condition to do dithering
127 bool needsDither = std::any_of(mInputs.begin(), mInputs.end(),
128 [](const Input &input){
129 return std::any_of(input.stages.begin(), input.stages.end(), NeedsDitherPred); }
130 );
131 if (mMasterEffects)
132 needsDither |= std::any_of(
133 mMasterEffects->begin(), mMasterEffects->end(), NeedsDitherPred);
134
135 auto pMixerSpec = ( mixerSpec &&
136 mixerSpec->GetNumChannels() == mNumChannels &&
137 mixerSpec->GetNumTracks() == nChannelsIn
138 ) ? mixerSpec : nullptr;
139 mHasMixerSpec = pMixerSpec != nullptr;
140
141 // Reserve vectors first so we can take safe references to pushed elements
142 mSources.reserve(nChannelsIn);
143 // One stereo-capable stage per effect.
144 const auto nMasterStages = mMasterEffects ? mMasterEffects->size() : 0;
145 const auto nStages =
146 std::accumulate(
147 mInputs.begin(), mInputs.end(), 0,
148 [](auto sum, const auto& input) {
149 return sum + input.stages.size();
150 }) +
151 nMasterStages;
152 mSettings.reserve(nStages);
153 mStageBuffers.reserve(nStages);
154
155 size_t i = 0;
156 std::vector<std::unique_ptr<DownmixSource>> downmixSources;
157 for (auto &input : mInputs) {
158 const auto &sequence = input.pSequence;
159 if (!sequence) {
160 assert(false);
161 break;
162 }
163
164 auto &source = mSources.emplace_back(sequence, BufferSize(), outRate,
165 warpOptions, highQuality, mayThrow, mTimesAndSpeed);
166 AudioGraph::Source *pDownstream = &source;
167 for (const auto &stage : input.stages)
168 if (
169 auto& pNewDownstream =
170 RegisterEffectStage(*pDownstream, sequence->NChannels(), stage, outRate))
171 {
172 pDownstream = pNewDownstream.get();
173 }
174 downmixSources.emplace_back(
175 std::make_unique<SequenceDownmixSource>(
176 *pDownstream,
177 *sequence,
178 pMixerSpec ? &pMixerSpec->mMap[i] : nullptr
179 )
180 );
181
182 i += sequence->NChannels();
183 }
184
185 if (mMasterEffects && !mMasterEffects->empty())
186 {
187 mDownmixStage = std::make_unique<DownmixStage>(
188 std::move(downmixSources), mNumChannels, mBufferSize, ApplyVolume::MapChannels
189 );
190
191 AudioGraph::Source* pDownstream = mDownmixStage.get();
192 for (const auto& stage : *mMasterEffects)
193 {
194 if (
195 auto& pNewDownstream =
196 RegisterEffectStage(*pDownstream, mNumChannels, stage, outRate))
197 {
198 pDownstream = pNewDownstream.get();
199 }
200 }
201
202 //Effect stage could have more than mNumChannels output
203 //and expect at least 3 float buffers input to work correctly...
204 std::vector<std::unique_ptr<DownmixSource>> masterDownmixSources;
205 masterDownmixSources.push_back(std::make_unique<SimpleDonwmixSource>(*pDownstream, mNumChannels));
206 mMasterDownmixStage = std::make_unique<DownmixStage>(
207 std::move(masterDownmixSources), mNumChannels, mBufferSize, ApplyVolume::Mixdown);
208 mDownstream = mMasterDownmixStage.get();
209 }
210 else
211 {
212 mDownmixStage = std::make_unique<DownmixStage>(
213 std::move(downmixSources),
214 mNumChannels,
215 mBufferSize,
216 mApplyVolume
217 );
218 mDownstream = mDownmixStage.get();
219 }
220
221 // Decide once at construction time
222 std::tie(mNeedsDither, mEffectiveFormat) = NeedsDither(needsDither, outRate);
223}
224
225Mixer::~Mixer() = default;
226
227std::pair<bool, sampleFormat>
228Mixer::NeedsDither(bool needsDither, double rate) const
229{
230 // This will accumulate the widest effective format of any input
231 // clip
232 auto widestEffectiveFormat = narrowestSampleFormat;
233
234 // needsDither may already be given as true.
235 // There are many other possible disqualifiers for the avoidance of dither.
236 if (std::any_of(mSources.begin(), mSources.end(),
237 std::mem_fn(&MixerSource::VariableRates))
238 )
239 // We will call MixVariableRates(), so we need nontrivial resampling
240 needsDither = true;
241
242 for (const auto &input : mSources) {
243 auto &sequence = input.GetSequence();
244
245 if (sequence.GetRate() != rate)
246 // Also leads to MixVariableRates(), needs nontrivial resampling
247 needsDither = true;
249 !mHasMixerSpec &&
250 sequence.NChannels() > 1 && mNumChannels == 1)
251 {
252 needsDither = true;
253 }
256 for (auto c : {0, 1}) {
257 const auto volume = sequence.GetChannelVolume(c);
258 if (!(volume == 0.0 || volume == 1.0))
259 // Fractional volume may be applied even in MixSameRate
260 needsDither = true;
261 }
262 }
263 // Examine all tracks. (This ignores the time bounds for the mixer.
264 // If it did not, we might avoid dither in more cases. But if we fix
265 // that, remember that some mixers change their time bounds after
266 // construction, as when scrubbing.)
267 if (!sequence.HasTrivialEnvelope())
268 // Varying or non-unit volume may be applied even in MixSameRate
269 needsDither = true;
270 auto effectiveFormat = sequence.WidestEffectiveFormat();
271 if (effectiveFormat > mFormat)
272 // Real, not just nominal, precision loss would happen in at
273 // least one clip
274 needsDither = true;
275 widestEffectiveFormat =
276 std::max(widestEffectiveFormat, effectiveFormat);
277 }
278
279 if (needsDither)
280 // Results will be dithered to width mFormat
281 return { true, mFormat };
282 else {
283 // Results will not be dithered
284 assert(widestEffectiveFormat <= mFormat);
285 return { false, widestEffectiveFormat };
286 }
287}
288
290{
291 for (auto c = 0; c < mTemp.Channels(); ++c)
293}
294
295size_t Mixer::Process(const size_t maxToProcess)
296{
297 assert(maxToProcess <= BufferSize());
298
299 // MB: this is wrong! mT represented warped time, and mTime is too inaccurate to use
300 // it here. It's also unnecessary I think.
301 //if (mT >= mT1)
302 // return 0;
303
304 auto &[mT0, mT1, _, mTime] = *mTimesAndSpeed;
305 auto oldTime = mTime;
306 // backwards (as possibly in scrubbing)
307 const auto backwards = (mT0 > mT1);
308
309 Clear();
310
311 std::optional<size_t> maxOut;
312
313 maxOut = mDownstream->Acquire(mTemp, maxToProcess);
315
316 if (!maxOut)
317 return 0;
318
319 if (backwards)
320 mTime = std::clamp(mTime, mT1, oldTime);
321 else
322 mTime = std::clamp(mTime, oldTime, mT1);
323
324 const auto dstStride = (mInterleaved ? mNumChannels : 1);
325 auto ditherType = mNeedsDither
328 for (size_t c = 0; c < mNumChannels; ++c)
331 ? mBuffer[0].ptr() + (c * SAMPLE_SIZE(mFormat))
332 : mBuffer[c].ptr()
333 ),
334 mFormat, *maxOut, ditherType,
335 1, dstStride);
336
337 // MB: this doesn't take warping into account, replaced with code based on mSamplePos
338 //mT += (maxOut / mRate);
339
340 assert(*maxOut <= maxToProcess);
341 return *maxOut;
342}
343
345{
346 return mBuffer[0].ptr();
347}
348
350{
351 return mBuffer[channel].ptr();
352}
353
355{
356 return mEffectiveFormat;
357}
358
360{
361 return mTimesAndSpeed->mTime;
362}
363
364void Mixer::Reposition(double t, bool bSkipping)
365{
366 const auto &[mT0, mT1, _, __] = *mTimesAndSpeed;
367 auto &mTime = mTimesAndSpeed->mTime;
368 mTime = t;
369 const bool backwards = (mT1 < mT0);
370 if (backwards)
371 mTime = std::clamp(mTime, mT1, mT0);
372 else
373 mTime = std::clamp(mTime, mT0, mT1);
374
375 for (auto &source : mSources)
376 source.Reposition(mTime, bSkipping);
377}
378
379void Mixer::SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping)
380{
381 wxASSERT(std::isfinite(speed));
382 auto &[mT0, mT1, mSpeed, _] = *mTimesAndSpeed;
383 mT0 = t0;
384 mT1 = t1;
385 mSpeed = fabs(speed);
386 Reposition(t0, bSkipping);
387}
388
389void Mixer::SetSpeedForKeyboardScrubbing(double speed, double startTime)
390{
391 wxASSERT(std::isfinite(speed));
392 auto &[mT0, mT1, mSpeed, _] = *mTimesAndSpeed;
393
394 // Check if the direction has changed
395 if ((speed > 0.0 && mT1 < mT0) || (speed < 0.0 && mT1 > mT0)) {
396 // It's safe to use 0 and std::numeric_limits<double>::max(),
397 // because Mixer::MixVariableRates() doesn't sample past the start
398 // or end of the audio in a track.
399 if (speed > 0.0 && mT1 < mT0) {
400 mT0 = 0;
401 mT1 = std::numeric_limits<double>::max();
402 }
403 else {
404 mT0 = std::numeric_limits<double>::max();
405 mT1 = 0;
406 }
407
408 Reposition(startTime, true);
409 }
410
411 mSpeed = fabs(speed);
412}
413
414std::unique_ptr<EffectStage>& Mixer::RegisterEffectStage(
415 AudioGraph::Source& upstream, size_t numChannels,
417 double outRate)
418{
419 // Make a mutable copy of stage.settings
420 auto& settings = mSettings.emplace_back(stage.settings);
421 // TODO: more-than-two-channels
422 // Like mFloatBuffers but padding not needed for soxr
423 // Allocate one extra buffer to hold dummy zero inputs
424 // (Issue 3854)
425 auto& stageInput = mStageBuffers.emplace_back(3, mBufferSize, 1);
426 const auto& factory = [&stage] {
427 // Avoid unnecessary repeated calls to the factory
428 return stage.mpFirstInstance ? move(stage.mpFirstInstance) :
429 stage.factory();
430 };
431 auto& pNewDownstream = mStages.emplace_back(EffectStage::Create(
432 -1, numChannels, upstream, stageInput, factory, settings, outRate,
433 std::nullopt));
434 if (!pNewDownstream)
435 {
436 // Just omit the failed stage from rendering
437 // TODO propagate the error?
438 mStageBuffers.pop_back();
439 mSettings.pop_back();
440 }
441 return pNewDownstream;
442}
constexpr int BufferSize
static RegisteredToolbarFactory factory
int min(int a, int b)
@ none
Definition: Dither.h:20
#define _(s)
Definition: Internat.h:73
static const AttachedProjectObjects::RegisteredFactory masterEffects
DitherType gLowQualityDither
These global variables are assigned at application startup or after change of preferences.
DitherType gHighQualityDither
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, size_t len, DitherType ditherType, unsigned int srcStride, unsigned int dstStride)
Copy samples from any format to any other format; apply dithering only if narrowing the format.
constexpr sampleFormat floatSample
Definition: SampleFormat.h:45
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
const char * constSamplePtr
Definition: SampleFormat.h:58
static Settings & settings()
Definition: TrackInfo.cpp:51
unsigned Channels() const
void ClearBuffer(unsigned iChannel, size_t n)
constSamplePtr GetReadPosition(unsigned iChannel) const
Get accumulated data for one channel.
Upstream producer of sample streams, taking Buffers as external context.
virtual std::optional< size_t > Acquire(Buffers &data, size_t bound)=0
Occupy vacant space in Buffers with some data.
virtual bool Release()=0
Caller is done examining last Acquire()d positions.
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
std::unique_ptr< EffectStage > & RegisterEffectStage(AudioGraph::Source &upstream, size_t numChannels, const MixerOptions::StageSpecification &stage, double outRate)
Definition: Mix.cpp:414
AudioGraph::Source * mDownstream
Definition: Mix.h:171
std::vector< EffectSettings > mSettings
Definition: Mix.h:166
const std::vector< SampleBuffer > mBuffer
Definition: Mix.h:163
std::vector< Input > Inputs
Definition: Mix.h:49
std::vector< std::unique_ptr< EffectStage > > mStages
Definition: Mix.h:168
void SetSpeedForKeyboardScrubbing(double speed, double startTime)
Definition: Mix.cpp:389
sampleFormat EffectiveFormat() const
Deduce the effective width of the output, which may be narrower than the stored format.
Definition: Mix.cpp:354
const sampleFormat mFormat
Definition: Mix.h:145
AudioGraph::Buffers mTemp
Definition: Mix.h:160
const ApplyVolume mApplyVolume
Definition: Mix.h:143
std::pair< bool, sampleFormat > NeedsDither(bool needsDither, double rate) const
Definition: Mix.cpp:228
const unsigned mNumChannels
Definition: Mix.h:130
sampleFormat mEffectiveFormat
Definition: Mix.h:149
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
Definition: Mix.cpp:344
std::vector< MixerOptions::StageSpecification > Stages
Definition: Mix.h:37
const bool mInterleaved
Definition: Mix.h:146
const size_t mBufferSize
Definition: Mix.h:135
void Clear()
Definition: Mix.cpp:289
bool mNeedsDither
Definition: Mix.h:150
Mixer(Inputs inputs, std::optional< Stages > masterEffects, bool mayThrow, const WarpOptions &warpOptions, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, bool highQuality=true, MixerSpec *mixerSpec=nullptr, ApplyVolume applyVolume=ApplyVolume::MapChannels)
Definition: Mix.cpp:92
std::vector< AudioGraph::Buffers > mStageBuffers
Definition: Mix.h:167
double MixGetCurrentTime()
Current time in seconds (unwarped, i.e. always between startTime and stopTime)
Definition: Mix.cpp:359
const bool mHighQuality
Definition: Mix.h:144
std::vector< MixerSource > mSources
Definition: Mix.h:165
size_t Process()
Definition: Mix.h:98
size_t BufferSize() const
Definition: Mix.h:79
void Reposition(double t, bool bSkipping=false)
Reposition processing to absolute time next time Process() is called.
Definition: Mix.cpp:364
void SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping=false)
Used in scrubbing and other nonuniform playback policies.
Definition: Mix.cpp:379
const std::shared_ptr< TimesAndSpeed > mTimesAndSpeed
Definition: Mix.h:153
bool mHasMixerSpec
Definition: Mix.h:151
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
bool VariableRates() const
Definition: MixerSource.h:61
void ConsiderStages(const Mixer::Stages &stages, size_t &blockSize)
Definition: Mix.cpp:52
auto NeedsDitherPred(const MixerOptions::StageSpecification &spec)
Definition: Mix.cpp:86
size_t FindBufferSize(const Mixer::Inputs &inputs, const std::optional< Mixer::Stages > &masterEffects, size_t bufferSize)
Definition: Mix.cpp:66
std::vector< std::vector< T > > initVector(size_t dim1, size_t dim2)
Definition: Mix.cpp:41
STL namespace.
std::shared_ptr< EffectInstance > mpFirstInstance
Definition: MixerOptions.h:110
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:56