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