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 "SampleTrack.h"
23#include "SampleTrackCache.h"
24#include "Resample.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 const auto nTracks = inputs.size();
52 for (size_t i = 0; i < nTracks;) {
53 const auto &input = inputs[i];
54 const auto leader = input.pTrack.get();
55 const auto nInChannels = TrackList::Channels(leader).size();
56 if (!leader || i + nInChannels > nTracks) {
57 assert(false);
58 break;
59 }
60 auto increment = finally([&]{ i += nInChannels; });
61 for (const auto &stage : input.stages) {
62 // Need an instance to query acceptable block size
63 const auto pInstance = stage.factory();
64 if (pInstance)
65 blockSize = std::min(blockSize, pInstance->SetBlockSize(blockSize));
66 // Cache the first factory call
67 stage.mpFirstInstance = move(pInstance);
68 }
69 }
70 return blockSize;
71}
72}
73
75 const bool mayThrow,
76 const WarpOptions &warpOptions,
77 const double startTime, const double stopTime,
78 const unsigned numOutChannels,
79 const size_t outBufferSize, const bool outInterleaved,
80 double outRate, sampleFormat outFormat,
81 const bool highQuality, MixerSpec *const mixerSpec,
82 const bool applyTrackGains
83) : mNumChannels{ numOutChannels }
84 , mInputs{ move(inputs) }
85 , mBufferSize{ FindBufferSize(mInputs, outBufferSize) }
86 , mApplyTrackGains{ applyTrackGains }
87 , mHighQuality{ highQuality }
88 , mFormat{ outFormat }
89 , mInterleaved{ outInterleaved }
90
91 , mTimesAndSpeed{ std::make_shared<TimesAndSpeed>( TimesAndSpeed{
92 startTime, stopTime, warpOptions.initialSpeed, startTime
93 } ) }
94
95 // PRL: Bug2536: see other comments below for the last, padding argument
96 // TODO: more-than-two-channels
97 // Issue 3565 workaround: allocate one extra buffer when applying a
98 // GVerb effect stage. It is simply discarded
99 // See also issue 3854, when the number of out channels expected by the
100 // plug-in is yet larger
101 , mFloatBuffers{ 3, mBufferSize, 1, 1 }
102
103 // non-interleaved
104 , mTemp{ initVector<float>(mNumChannels, mBufferSize) }
105 , mBuffer{ initVector<SampleBuffer>(mInterleaved ? 1 : mNumChannels,
106 [format = mFormat,
107 size = mBufferSize * (mInterleaved ? mNumChannels : 1)
108 ](auto &buffer){ buffer.Allocate(size, format); }
109 )}
110{
111 assert(BufferSize() <= outBufferSize);
112 const auto nTracks = mInputs.size();
113
114 auto pMixerSpec = ( mixerSpec &&
115 mixerSpec->GetNumChannels() == mNumChannels &&
116 mixerSpec->GetNumTracks() == nTracks
117 ) ? mixerSpec : nullptr;
118
119 // Reserve vectors first so we can take safe references to pushed elements
120 mSources.reserve(nTracks);
121 auto nStages = std::accumulate(mInputs.begin(), mInputs.end(), 0,
122 [](auto sum, auto &input){ return sum + input.stages.size(); });
123 mSettings.reserve(nStages);
124 mStageBuffers.reserve(nStages);
125
126 for (size_t i = 0; i < nTracks;) {
127 const auto &input = mInputs[i];
128 const auto leader = input.pTrack.get();
129 const auto nInChannels = TrackList::Channels(leader).size();
130 if (!leader || i + nInChannels > nTracks) {
131 assert(false);
132 break;
133 }
134 auto increment = finally([&]{ i += nInChannels; });
135
136 auto &source = mSources.emplace_back( *leader, BufferSize(), outRate,
137 warpOptions, highQuality, mayThrow, mTimesAndSpeed,
138 (pMixerSpec ? &pMixerSpec->mMap[i] : nullptr));
139 AudioGraph::Source *pDownstream = &source;
140 for (const auto &stage : input.stages) {
141 // Make a mutable copy of stage.settings
142 auto &settings = mSettings.emplace_back(stage.settings);
143 // TODO: more-than-two-channels
144 // Like mFloatBuffers but padding not needed for soxr
145 // Allocate one extra buffer to hold dummy zero inputs
146 // (Issue 3854)
147 auto &stageInput = mStageBuffers.emplace_back(3, mBufferSize, 1);
148 const auto &factory = [&stage]{
149 // Avoid unnecessary repeated calls to the factory
150 return stage.mpFirstInstance
151 ? move(stage.mpFirstInstance)
152 : stage.factory();
153 };
154 auto &pNewDownstream =
155 mStages.emplace_back(AudioGraph::EffectStage::Create(true,
156 *pDownstream, stageInput,
157 factory, settings, outRate, std::nullopt, *leader
158 ));
159 if (pNewDownstream)
160 pDownstream = pNewDownstream.get();
161 else {
162 // Just omit the failed stage from rendering
163 // TODO propagate the error?
164 mStageBuffers.pop_back();
165 mSettings.pop_back();
166 }
167 }
168 mDecoratedSources.emplace_back(Source{ source, *pDownstream });
169 }
170}
171
173{
174}
175
177{
178 for (auto &buffer: mTemp)
179 std::fill(buffer.begin(), buffer.end(), 0);
180}
181
182static void MixBuffers(unsigned numChannels,
183 const unsigned char *channelFlags, const float *gains,
184 const float &src, std::vector<std::vector<float>> &dests, int len)
185{
186 const auto pSrc = &src;
187 for (unsigned int c = 0; c < numChannels; c++) {
188 if (!channelFlags[c])
189 continue;
190 float *dest = dests[c].data();
191 float gain = gains[c];
192 for (int j = 0; j < len; ++j)
193 *dest++ += pSrc[j] * gain; // the actual mixing process
194 }
195}
196
197#define stackAllocate(T, count) static_cast<T*>(alloca(count * sizeof(T)))
198
199size_t Mixer::Process(const size_t maxToProcess)
200{
201 assert(maxToProcess <= BufferSize());
202
203 // MB: this is wrong! mT represented warped time, and mTime is too inaccurate to use
204 // it here. It's also unnecessary I think.
205 //if (mT >= mT1)
206 // return 0;
207
208 size_t maxOut = 0;
209 const auto channelFlags = stackAllocate(unsigned char, mNumChannels);
210 const auto gains = stackAllocate(float, mNumChannels);
211 if (!mApplyTrackGains)
212 std::fill(gains, gains + mNumChannels, 1.0f);
213
214 // Decides which output buffers an input channel accumulates into
215 auto findChannelFlags = [&channelFlags, numChannels = mNumChannels]
216 (const bool *map, Track::ChannelType channel){
217 const auto end = channelFlags + numChannels;
218 std::fill(channelFlags, end, 0);
219 if (map)
220 // ignore left and right when downmixing is customized
221 std::copy(map, map + numChannels, channelFlags);
222 else switch(channel) {
224 default:
225 std::fill(channelFlags, end, 1);
226 break;
228 channelFlags[0] = 1;
229 break;
231 if (numChannels >= 2)
232 channelFlags[1] = 1;
233 else
234 channelFlags[0] = 1;
235 break;
236 }
237 return channelFlags;
238 };
239
240 auto &[mT0, mT1, _, mTime] = *mTimesAndSpeed;
241 auto oldTime = mTime;
242 // backwards (as possibly in scrubbing)
243 const auto backwards = (mT0 > mT1);
244
245 Clear();
246 // TODO: more-than-two-channels
247 auto maxChannels = std::max(2u, mFloatBuffers.Channels());
248
249 for (auto &[ upstream, downstream ] : mDecoratedSources) {
250 auto oResult = downstream.Acquire(mFloatBuffers, maxToProcess);
251 if (!oResult)
252 return 0;
253 auto result = *oResult;
254 maxOut = std::max(maxOut, result);
255
256 // Insert effect stages here! Passing them all channels of the track
257
258 const auto limit = std::min<size_t>(upstream.Channels(), maxChannels);
259 for (size_t j = 0; j < limit; ++j) {
260 const auto pFloat = (const float *)mFloatBuffers.GetReadPosition(j);
261 const auto track = upstream.GetChannel(j);
263 for (size_t c = 0; c < mNumChannels; ++c)
264 gains[c] = track->GetChannelGain(c);
265 const auto flags =
266 findChannelFlags(upstream.MixerSpec(j), track->GetChannel());
267 MixBuffers(mNumChannels, flags, gains, *pFloat, mTemp, result);
268 }
269
270 downstream.Release();
271 mFloatBuffers.Advance(result);
273 }
274
275 if (backwards)
276 mTime = std::clamp(mTime, mT1, oldTime);
277 else
278 mTime = std::clamp(mTime, oldTime, mT1);
279
280 const auto dstStride = (mInterleaved ? mNumChannels : 1);
281 for (size_t c = 0; c < mNumChannels; ++c)
284 ? mBuffer[0].ptr() + (c * SAMPLE_SIZE(mFormat))
285 : mBuffer[c].ptr()
286 ),
287 mFormat, maxOut,
289 1, dstStride);
290
291 // MB: this doesn't take warping into account, replaced with code based on mSamplePos
292 //mT += (maxOut / mRate);
293
294 assert(maxOut <= maxToProcess);
295 return maxOut;
296}
297
299{
300 return mBuffer[0].ptr();
301}
302
304{
305 return mBuffer[channel].ptr();
306}
307
309{
310 return mTimesAndSpeed->mTime;
311}
312
313#if 0
314// Was used before 3.1.0 whenever looping play restarted
315// No longer used
316void Mixer::Restart()
317{
318 mTime = mT0;
319
320 for(size_t i=0; i<mNumInputTracks; i++)
321 mSamplePos[i] = mInputTrack[i].GetTrack()->TimeToLongSamples(mT0);
322
323 for(size_t i=0; i<mNumInputTracks; i++) {
324 mQueueStart[i] = 0;
325 mQueueLen[i] = 0;
326 }
327
328 // Bug 1887: libsoxr 0.1.3, first used in Audacity 2.3.0, crashes with
329 // constant rate resampling if you try to reuse the resampler after it has
330 // flushed. Should that be considered a bug in sox? This works around it:
331 MakeResamplers();
332}
333#endif
334
335void Mixer::Reposition(double t, bool bSkipping)
336{
337 auto &[mT0, mT1, _, mTime] = *mTimesAndSpeed;
338 mTime = t;
339 const bool backwards = (mT1 < mT0);
340 if (backwards)
341 mTime = std::clamp(mTime, mT1, mT0);
342 else
343 mTime = std::clamp(mTime, mT0, mT1);
344
345 for (auto &source : mSources)
346 source.Reposition(mTime, bSkipping);
347}
348
349void Mixer::SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping)
350{
351 wxASSERT(std::isfinite(speed));
352 auto &[mT0, mT1, mSpeed, _] = *mTimesAndSpeed;
353 mT0 = t0;
354 mT1 = t1;
355 mSpeed = fabs(speed);
356 Reposition(t0, bSkipping);
357}
358
359void Mixer::SetSpeedForKeyboardScrubbing(double speed, double startTime)
360{
361 wxASSERT(std::isfinite(speed));
362 auto &[mT0, mT1, mSpeed, _] = *mTimesAndSpeed;
363
364 // Check if the direction has changed
365 if ((speed > 0.0 && mT1 < mT0) || (speed < 0.0 && mT1 > mT0)) {
366 // It's safe to use 0 and std::numeric_limits<double>::max(),
367 // because Mixer::MixVariableRates() doesn't sample past the start
368 // or end of the audio in a track.
369 if (speed > 0.0 && mT1 < mT0) {
370 mT0 = 0;
371 mT1 = std::numeric_limits<double>::max();
372 }
373 else {
374 mT0 = std::numeric_limits<double>::max();
375 mT1 = 0;
376 }
377
378 Reposition(startTime, true);
379 }
380
381 mSpeed = fabs(speed);
382}
constexpr int BufferSize
int min(int a, int b)
int format
Definition: ExportPCM.cpp:56
#define _(s)
Definition: Internat.h:75
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:182
#define stackAllocate(T, count)
Definition: Mix.cpp:197
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.
sampleFormat
Definition: SampleFormat.h:29
@ floatSample
Definition: SampleFormat.h:34
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
const char * constSamplePtr
Definition: SampleFormat.h:50
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:140
const std::vector< SampleBuffer > mBuffer
Definition: Mix.h:148
std::vector< Input > Inputs
Definition: Mix.h:43
void SetSpeedForKeyboardScrubbing(double speed, double startTime)
Definition: Mix.cpp:359
std::vector< Source > mDecoratedSources
Definition: Mix.h:156
const bool mApplyTrackGains
Definition: Mix.h:128
const sampleFormat mFormat
Definition: Mix.h:130
virtual ~Mixer()
Definition: Mix.cpp:172
std::vector< std::vector< float > > mTemp
Definition: Mix.h:145
void Restart()
const unsigned mNumChannels
Definition: Mix.h:121
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
Definition: Mix.cpp:298
const bool mInterleaved
Definition: Mix.h:131
void Clear()
Definition: Mix.cpp:176
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:74
double MixGetCurrentTime()
Definition: Mix.cpp:308
const bool mHighQuality
Definition: Mix.h:129
std::vector< MixerSource > mSources
Definition: Mix.h:150
size_t Process()
Definition: Mix.h:89
size_t BufferSize() const
Definition: Mix.h:70
void Reposition(double t, bool bSkipping=false)
Definition: Mix.cpp:335
void SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping=false)
Definition: Mix.cpp:349
const std::shared_ptr< TimesAndSpeed > mTimesAndSpeed
Definition: Mix.h:135
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
ChannelType
Definition: Track.h:281
@ LeftChannel
Definition: Track.h:282
@ RightChannel
Definition: Track.h:283
@ MonoChannel
Definition: Track.h:284
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1541
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
STL namespace.
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:54