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 "SampleTrack.h"
24#include "SampleTrackCache.h"
25#include "Resample.h"
26#include "float_cast.h"
27#include <numeric>
28
29namespace {
30template<typename T, typename F> std::vector<T>
31initVector(size_t dim1, const F &f)
32{
33 std::vector<T> result( dim1 );
34 for (auto &row : result)
35 f(row);
36 return result;
37}
38
39template<typename T> std::vector<std::vector<T>>
40initVector(size_t dim1, size_t dim2)
41{
42 return initVector<std::vector<T>>(dim1,
43 [dim2](auto &row){ row.resize(dim2); });
44}
45}
46
47namespace {
48// Find a block size acceptable to all stages; side-effects on instances
49size_t FindBufferSize(const Mixer::Inputs &inputs, size_t bufferSize)
50{
51 size_t blockSize = bufferSize;
52 const auto nTracks = inputs.size();
53 for (size_t i = 0; i < nTracks;) {
54 const auto &input = inputs[i];
55 const auto leader = input.pTrack.get();
56 const auto nInChannels = TrackList::Channels(leader).size();
57 if (!leader || i + nInChannels > nTracks) {
58 assert(false);
59 break;
60 }
61 auto increment = finally([&]{ i += nInChannels; });
62 for (const auto &stage : input.stages) {
63 // Need an instance to query acceptable block size
64 const auto pInstance = stage.factory();
65 if (pInstance)
66 blockSize = std::min(blockSize, pInstance->SetBlockSize(blockSize));
67 // Cache the first factory call
68 stage.mpFirstInstance = move(pInstance);
69 }
70 }
71 return blockSize;
72}
73}
74
76 const bool mayThrow,
77 const WarpOptions &warpOptions,
78 const double startTime, const double stopTime,
79 const unsigned numOutChannels,
80 const size_t outBufferSize, const bool outInterleaved,
81 double outRate, sampleFormat outFormat,
82 const bool highQuality, MixerSpec *const mixerSpec,
83 const bool applyTrackGains
84) : mNumChannels{ numOutChannels }
85 , mInputs{ move(inputs) }
86 , mBufferSize{ FindBufferSize(mInputs, outBufferSize) }
87 , mApplyTrackGains{ applyTrackGains }
88 , mHighQuality{ highQuality }
89 , mFormat{ outFormat }
90 , mInterleaved{ outInterleaved }
91
92 , mTimesAndSpeed{ std::make_shared<TimesAndSpeed>( TimesAndSpeed{
93 startTime, stopTime, warpOptions.initialSpeed, startTime
94 } ) }
95
96 // PRL: Bug2536: see other comments below for the last, padding argument
97 // TODO: more-than-two-channels
98 // Issue 3565 workaround: allocate one extra buffer when applying a
99 // GVerb effect stage. It is simply discarded
100 // See also issue 3854, when the number of out channels expected by the
101 // plug-in is yet larger
102 , mFloatBuffers{ 3, mBufferSize, 1, 1 }
103
104 // non-interleaved
105 , mTemp{ initVector<float>(mNumChannels, mBufferSize) }
106 , mBuffer{ initVector<SampleBuffer>(mInterleaved ? 1 : mNumChannels,
107 [format = mFormat,
108 size = mBufferSize * (mInterleaved ? mNumChannels : 1)
109 ](auto &buffer){ buffer.Allocate(size, format); }
110 )}
111 , mEffectiveFormat{ floatSample }
112{
113 assert(BufferSize() <= outBufferSize);
114 const auto nTracks = mInputs.size();
115
116 // Examine the temporary instances that were made in FindBufferSize
117 // This finds a sufficient, but not necessary, condition to do dithering
118 bool needsDither = std::any_of(mInputs.begin(), mInputs.end(),
119 [](const Input &input){
120 return std::any_of(input.stages.begin(), input.stages.end(),
121 [](const MixerOptions::StageSpecification &spec){
122 return spec.mpFirstInstance &&
123 spec.mpFirstInstance->NeedsDither(); } ); } );
124
125 auto pMixerSpec = ( mixerSpec &&
126 mixerSpec->GetNumChannels() == mNumChannels &&
127 mixerSpec->GetNumTracks() == nTracks
128 ) ? mixerSpec : nullptr;
129
130 // Reserve vectors first so we can take safe references to pushed elements
131 mSources.reserve(nTracks);
132 auto nStages = std::accumulate(mInputs.begin(), mInputs.end(), 0,
133 [](auto sum, auto &input){ return sum + input.stages.size(); });
134 mSettings.reserve(nStages);
135 mStageBuffers.reserve(nStages);
136
137 for (size_t i = 0; i < nTracks;) {
138 const auto &input = mInputs[i];
139 const auto leader = input.pTrack.get();
140 const auto nInChannels = TrackList::Channels(leader).size();
141 if (!leader || i + nInChannels > nTracks) {
142 assert(false);
143 break;
144 }
145 auto increment = finally([&]{ i += nInChannels; });
146
147 auto &source = mSources.emplace_back( *leader, BufferSize(), outRate,
148 warpOptions, highQuality, mayThrow, mTimesAndSpeed,
149 (pMixerSpec ? &pMixerSpec->mMap[i] : nullptr));
150 AudioGraph::Source *pDownstream = &source;
151 for (const auto &stage : input.stages) {
152 // Make a mutable copy of stage.settings
153 auto &settings = mSettings.emplace_back(stage.settings);
154 // TODO: more-than-two-channels
155 // Like mFloatBuffers but padding not needed for soxr
156 // Allocate one extra buffer to hold dummy zero inputs
157 // (Issue 3854)
158 auto &stageInput = mStageBuffers.emplace_back(3, mBufferSize, 1);
159 const auto &factory = [&stage]{
160 // Avoid unnecessary repeated calls to the factory
161 return stage.mpFirstInstance
162 ? move(stage.mpFirstInstance)
163 : stage.factory();
164 };
165 auto &pNewDownstream =
166 mStages.emplace_back(AudioGraph::EffectStage::Create(true,
167 *pDownstream, stageInput,
168 factory, settings, outRate, std::nullopt, *leader
169 ));
170 if (pNewDownstream)
171 pDownstream = pNewDownstream.get();
172 else {
173 // Just omit the failed stage from rendering
174 // TODO propagate the error?
175 mStageBuffers.pop_back();
176 mSettings.pop_back();
177 }
178 }
179 mDecoratedSources.emplace_back(Source{ source, *pDownstream });
180 }
181
182 // Decide once at construction time
183 std::tie(mNeedsDither, mEffectiveFormat) = NeedsDither(needsDither, outRate);
184}
185
187{
188}
189
190std::pair<bool, sampleFormat>
191Mixer::NeedsDither(bool needsDither, double rate) const
192{
193 // This will accumulate the widest effective format of any input
194 // clip
195 auto widestEffectiveFormat = narrowestSampleFormat;
196
197 // needsDither may already be given as true.
198 // There are many other possible disqualifiers for the avoidance of dither.
199 if (std::any_of(mSources.begin(), mSources.end(),
200 std::mem_fn(&MixerSource::VariableRates))
201 )
202 // We will call MixVariableRates(), so we need nontrivial resampling
203 needsDither = true;
204
205 for (const auto &input : mInputs) {
206 auto &pTrack = input.pTrack;
207 if (!pTrack)
208 continue;
209 auto &track = *pTrack;
210 if (track.GetRate() != rate)
211 // Also leads to MixVariableRates(), needs nontrivial resampling
212 needsDither = true;
213 if (mApplyTrackGains) {
215 for (auto c : {0, 1}) {
216 const auto gain = track.GetChannelGain(c);
217 if (!(gain == 0.0 || gain == 1.0))
218 // Fractional gain may be applied even in MixSameRate
219 needsDither = true;
220 }
221 }
222
223 // Examine all tracks. (This ignores the time bounds for the mixer.
224 // If it did not, we might avoid dither in more cases. But if we fix
225 // that, remember that some mixers change their time bounds after
226 // construction, as when scrubbing.)
227 if (!track.HasTrivialEnvelope())
228 // Varying or non-unit gain may be applied even in MixSameRate
229 needsDither = true;
230 auto effectiveFormat = track.WidestEffectiveFormat();
231 if (effectiveFormat > mFormat)
232 // Real, not just nominal, precision loss would happen in at
233 // least one clip
234 needsDither = true;
235 widestEffectiveFormat =
236 std::max(widestEffectiveFormat, effectiveFormat);
237 }
238
239 if (needsDither)
240 // Results will be dithered to width mFormat
241 return { true, mFormat };
242 else {
243 // Results will not be dithered
244 assert(widestEffectiveFormat <= mFormat);
245 return { false, widestEffectiveFormat };
246 }
247}
248
250{
251 for (auto &buffer: mTemp)
252 std::fill(buffer.begin(), buffer.end(), 0);
253}
254
255static void MixBuffers(unsigned numChannels,
256 const unsigned char *channelFlags, const float *gains,
257 const float &src, std::vector<std::vector<float>> &dests, int len)
258{
259 const auto pSrc = &src;
260 for (unsigned int c = 0; c < numChannels; c++) {
261 if (!channelFlags[c])
262 continue;
263 float *dest = dests[c].data();
264 float gain = gains[c];
265 for (int j = 0; j < len; ++j)
266 *dest++ += pSrc[j] * gain; // the actual mixing process
267 }
268}
269
270#define stackAllocate(T, count) static_cast<T*>(alloca(count * sizeof(T)))
271
272size_t Mixer::Process(const size_t maxToProcess)
273{
274 assert(maxToProcess <= BufferSize());
275
276 // MB: this is wrong! mT represented warped time, and mTime is too inaccurate to use
277 // it here. It's also unnecessary I think.
278 //if (mT >= mT1)
279 // return 0;
280
281 size_t maxOut = 0;
282 const auto channelFlags = stackAllocate(unsigned char, mNumChannels);
283 const auto gains = stackAllocate(float, mNumChannels);
284 if (!mApplyTrackGains)
285 std::fill(gains, gains + mNumChannels, 1.0f);
286
287 // Decides which output buffers an input channel accumulates into
288 auto findChannelFlags = [&channelFlags, numChannels = mNumChannels]
289 (const bool *map, Track::ChannelType channel){
290 const auto end = channelFlags + numChannels;
291 std::fill(channelFlags, end, 0);
292 if (map)
293 // ignore left and right when downmixing is customized
294 std::copy(map, map + numChannels, channelFlags);
295 else switch(channel) {
297 default:
298 std::fill(channelFlags, end, 1);
299 break;
301 channelFlags[0] = 1;
302 break;
304 if (numChannels >= 2)
305 channelFlags[1] = 1;
306 else
307 channelFlags[0] = 1;
308 break;
309 }
310 return channelFlags;
311 };
312
313 auto &[mT0, mT1, _, mTime] = *mTimesAndSpeed;
314 auto oldTime = mTime;
315 // backwards (as possibly in scrubbing)
316 const auto backwards = (mT0 > mT1);
317
318 Clear();
319 // TODO: more-than-two-channels
320 auto maxChannels = std::max(2u, mFloatBuffers.Channels());
321
322 for (auto &[ upstream, downstream ] : mDecoratedSources) {
323 auto oResult = downstream.Acquire(mFloatBuffers, maxToProcess);
324 // One of MixVariableRates or MixSameRate assigns into mTemp[*][*] which
325 // are the sources for the CopySamples calls, and they copy into
326 // mBuffer[*][*]
327 if (!oResult)
328 return 0;
329 auto result = *oResult;
330 maxOut = std::max(maxOut, result);
331
332 // Insert effect stages here! Passing them all channels of the track
333
334 const auto limit = std::min<size_t>(upstream.Channels(), maxChannels);
335 for (size_t j = 0; j < limit; ++j) {
336 const auto pFloat = (const float *)mFloatBuffers.GetReadPosition(j);
337 const auto track = upstream.GetChannel(j);
339 for (size_t c = 0; c < mNumChannels; ++c)
340 gains[c] = track->GetChannelGain(c);
341 const auto flags =
342 findChannelFlags(upstream.MixerSpec(j), track->GetChannel());
343 MixBuffers(mNumChannels, flags, gains, *pFloat, mTemp, result);
344 }
345
346 downstream.Release();
347 mFloatBuffers.Advance(result);
349 }
350
351 if (backwards)
352 mTime = std::clamp(mTime, mT1, oldTime);
353 else
354 mTime = std::clamp(mTime, oldTime, mT1);
355
356 const auto dstStride = (mInterleaved ? mNumChannels : 1);
357 auto ditherType = mNeedsDither
360 for (size_t c = 0; c < mNumChannels; ++c)
363 ? mBuffer[0].ptr() + (c * SAMPLE_SIZE(mFormat))
364 : mBuffer[c].ptr()
365 ),
366 mFormat, maxOut, ditherType,
367 1, dstStride);
368
369 // MB: this doesn't take warping into account, replaced with code based on mSamplePos
370 //mT += (maxOut / mRate);
371
372 assert(maxOut <= maxToProcess);
373 return maxOut;
374}
375
377{
378 return mBuffer[0].ptr();
379}
380
382{
383 return mBuffer[channel].ptr();
384}
385
387{
388 return mEffectiveFormat;
389}
390
392{
393 return mTimesAndSpeed->mTime;
394}
395
396#if 0
397// Was used before 3.1.0 whenever looping play restarted
398// No longer used
399void Mixer::Restart()
400{
401 mTime = mT0;
402
403 for(size_t i=0; i<mNumInputTracks; i++)
404 mSamplePos[i] = mInputTrack[i].GetTrack()->TimeToLongSamples(mT0);
405
406 for(size_t i=0; i<mNumInputTracks; i++) {
407 mQueueStart[i] = 0;
408 mQueueLen[i] = 0;
409 }
410
411 // Bug 1887: libsoxr 0.1.3, first used in Audacity 2.3.0, crashes with
412 // constant rate resampling if you try to reuse the resampler after it has
413 // flushed. Should that be considered a bug in sox? This works around it:
414 MakeResamplers();
415}
416#endif
417
418void Mixer::Reposition(double t, bool bSkipping)
419{
420 auto &[mT0, mT1, _, mTime] = *mTimesAndSpeed;
421 mTime = t;
422 const bool backwards = (mT1 < mT0);
423 if (backwards)
424 mTime = std::clamp(mTime, mT1, mT0);
425 else
426 mTime = std::clamp(mTime, mT0, mT1);
427
428 for (auto &source : mSources)
429 source.Reposition(mTime, bSkipping);
430}
431
432void Mixer::SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping)
433{
434 wxASSERT(std::isfinite(speed));
435 auto &[mT0, mT1, mSpeed, _] = *mTimesAndSpeed;
436 mT0 = t0;
437 mT1 = t1;
438 mSpeed = fabs(speed);
439 Reposition(t0, bSkipping);
440}
441
442void Mixer::SetSpeedForKeyboardScrubbing(double speed, double startTime)
443{
444 wxASSERT(std::isfinite(speed));
445 auto &[mT0, mT1, mSpeed, _] = *mTimesAndSpeed;
446
447 // Check if the direction has changed
448 if ((speed > 0.0 && mT1 < mT0) || (speed < 0.0 && mT1 > mT0)) {
449 // It's safe to use 0 and std::numeric_limits<double>::max(),
450 // because Mixer::MixVariableRates() doesn't sample past the start
451 // or end of the audio in a track.
452 if (speed > 0.0 && mT1 < mT0) {
453 mT0 = 0;
454 mT1 = std::numeric_limits<double>::max();
455 }
456 else {
457 mT0 = std::numeric_limits<double>::max();
458 mT1 = 0;
459 }
460
461 Reposition(startTime, true);
462 }
463
464 mSpeed = fabs(speed);
465}
constexpr int BufferSize
int min(int a, int b)
@ none
Definition: Dither.h:20
int format
Definition: ExportPCM.cpp:53
#define _(s)
Definition: Internat.h:73
static void MixBuffers(unsigned numChannels, const unsigned char *channelFlags, const float *gains, const float &src, std::vector< std::vector< float > > &dests, int len)
Definition: Mix.cpp:255
#define stackAllocate(T, count)
Definition: Mix.cpp:270
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:43
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:50
const char * constSamplePtr
Definition: SampleFormat.h:56
static Settings & settings()
Definition: TrackInfo.cpp:87
void Advance(size_t count)
Move the positions.
size_t Rotate()
Shift all data at and after the old position to position 0.
unsigned Channels() const
constSamplePtr GetReadPosition(unsigned iChannel) const
Get accumulated data for one channel.
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
Upstream producer of sample streams, taking Buffers as external context.
AudioGraph::Buffers mFloatBuffers
Definition: Mix.h:148
const std::vector< SampleBuffer > mBuffer
Definition: Mix.h:156
std::vector< Input > Inputs
Definition: Mix.h:43
void SetSpeedForKeyboardScrubbing(double speed, double startTime)
Definition: Mix.cpp:442
std::vector< Source > mDecoratedSources
Definition: Mix.h:164
const bool mApplyTrackGains
Definition: Mix.h:134
sampleFormat EffectiveFormat() const
Deduce the effective width of the output, which may be narrower than the stored format.
Definition: Mix.cpp:386
const sampleFormat mFormat
Definition: Mix.h:136
Inputs mInputs
Definition: Mix.h:123
virtual ~Mixer()
Definition: Mix.cpp:186
std::vector< std::vector< float > > mTemp
Definition: Mix.h:153
std::pair< bool, sampleFormat > NeedsDither(bool needsDither, double rate) const
Definition: Mix.cpp:191
void Restart()
Restart processing at beginning of buffer next time Process() is called.
const unsigned mNumChannels
Definition: Mix.h:122
sampleFormat mEffectiveFormat
Definition: Mix.h:140
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
Definition: Mix.cpp:376
const bool mInterleaved
Definition: Mix.h:137
void Clear()
Definition: Mix.cpp:249
bool mNeedsDither
Definition: Mix.h:141
Mixer(Inputs inputs, 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, bool applytTrackGains=true)
Definition: Mix.cpp:75
double MixGetCurrentTime()
Current time in seconds (unwarped, i.e. always between startTime and stopTime)
Definition: Mix.cpp:391
const bool mHighQuality
Definition: Mix.h:135
std::vector< MixerSource > mSources
Definition: Mix.h:158
size_t Process()
Definition: Mix.h:89
size_t BufferSize() const
Definition: Mix.h:70
void Reposition(double t, bool bSkipping=false)
Reposition processing to absolute time next time Process() is called.
Definition: Mix.cpp:418
void SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping=false)
Used in scrubbing and other nonuniform playback policies.
Definition: Mix.cpp:432
const std::shared_ptr< TimesAndSpeed > mTimesAndSpeed
Definition: Mix.h:143
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
bool VariableRates() const
Definition: MixerSource.h:61
ChannelType
Definition: Track.h:282
@ LeftChannel
Definition: Track.h:283
@ RightChannel
Definition: Track.h:284
@ MonoChannel
Definition: Track.h:285
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1544
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
size_t FindBufferSize(const Mixer::Inputs &inputs, size_t bufferSize)
Definition: Mix.cpp:49
std::vector< std::vector< T > > initVector(size_t dim1, size_t dim2)
Definition: Mix.cpp:40
static RegisteredToolbarFactory factory
STL namespace.
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:54