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