29template<
typename T,
typename F> std::vector<T>
32 std::vector<T> result( dim1 );
33 for (
auto &row : result)
38template<
typename T> std::vector<std::vector<T>>
41 return initVector<std::vector<T>>(dim1,
42 [dim2](
auto &row){ row.resize(dim2); });
50 size_t blockSize = bufferSize;
51 for (
const auto &input : inputs) {
52 const auto sequence = input.pSequence.get();
53 const auto nInChannels = sequence->NChannels();
58 for (
const auto &stage : input.stages) {
60 const auto pInstance = stage.factory();
62 blockSize =
std::min(blockSize, pInstance->SetBlockSize(blockSize));
64 stage.mpFirstInstance = move(pInstance);
74 const double startTime,
const double stopTime,
75 const unsigned numOutChannels,
76 const size_t outBufferSize,
const bool outInterleaved,
78 const bool highQuality,
MixerSpec *
const mixerSpec,
79 const bool applyTrackGains
80) : mNumChannels{ numOutChannels }
81 , mInputs{ move(inputs) }
83 , mApplyTrackGains{ applyTrackGains }
84 , mHighQuality{ highQuality }
85 , mFormat{ outFormat }
86 , mInterleaved{ outInterleaved }
89 startTime, stopTime, warpOptions.initialSpeed, startTime
98 , mFloatBuffers{ 3, mBufferSize, 1, 1 }
101 , mTemp{
initVector<float>(mNumChannels, mBufferSize) }
104 size = mBufferSize * (mInterleaved ? mNumChannels : 1)
105 ](auto &buffer){ buffer.Allocate(
size,
format); }
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(); });
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(); } ); } );
124 auto pMixerSpec = ( mixerSpec &&
125 mixerSpec->GetNumChannels() == mNumChannels &&
126 mixerSpec->GetNumTracks() == nChannelsIn
127 ) ? mixerSpec :
nullptr;
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);
138 for (
auto &input : mInputs) {
139 const auto &sequence = input.pSequence;
144 auto increment =
finally([&]{ i += sequence->NChannels(); });
146 auto &source = mSources.emplace_back(sequence,
BufferSize(), outRate,
147 warpOptions, highQuality, mayThrow, mTimesAndSpeed,
148 (pMixerSpec ? &pMixerSpec->mMap[i] :
nullptr));
150 for (
const auto &stage : input.stages) {
152 auto &
settings = mSettings.emplace_back(stage.settings);
157 auto &stageInput = mStageBuffers.emplace_back(3, mBufferSize, 1);
158 const auto &
factory = [&stage]{
160 return stage.mpFirstInstance
161 ? move(stage.mpFirstInstance)
164 auto &pNewDownstream =
166 *pDownstream, stageInput,
170 pDownstream = pNewDownstream.get();
174 mStageBuffers.pop_back();
175 mSettings.pop_back();
178 mDecoratedSources.emplace_back(Source{ source, *pDownstream });
182 std::tie(mNeedsDither, mEffectiveFormat) = NeedsDither(needsDither, outRate);
189std::pair<bool, sampleFormat>
204 for (
const auto &input :
mInputs) {
205 auto &pSequence = input.pSequence;
208 auto &sequence = *pSequence;
209 if (sequence.GetRate() != rate)
214 for (
auto c : {0, 1}) {
215 const auto gain = sequence.GetChannelGain(c);
216 if (!(gain == 0.0 || gain == 1.0))
226 if (!sequence.HasTrivialEnvelope())
229 auto effectiveFormat = sequence.WidestEffectiveFormat();
234 widestEffectiveFormat =
235 std::max(widestEffectiveFormat, effectiveFormat);
243 assert(widestEffectiveFormat <=
mFormat);
244 return {
false, widestEffectiveFormat };
250 for (
auto &buffer:
mTemp)
251 std::fill(buffer.begin(), buffer.end(), 0);
255 const unsigned char *channelFlags,
const float *gains,
256 const float &src, std::vector<std::vector<float>> &dests,
int len)
258 const auto pSrc = &src;
259 for (
unsigned int c = 0; c < numChannels; c++) {
260 if (!channelFlags[c])
262 float *dest = dests[c].data();
263 float gain = gains[c];
264 for (
int j = 0; j < len; ++j)
265 *dest++ += pSrc[j] * gain;
269#define stackAllocate(T, count) static_cast<T*>(alloca(count * sizeof(T)))
287 auto findChannelFlags = [&channelFlags, numChannels =
mNumChannels]
289 const auto end = channelFlags + numChannels;
290 std::fill(channelFlags,
end, 0);
293 std::copy(map, map + numChannels, channelFlags);
294 else if (
IsMono(sequence))
295 std::fill(channelFlags,
end, 1);
299 if (numChannels >= 2)
308 auto oldTime = mTime;
310 const auto backwards = (mT0 > mT1);
317 auto oResult = downstream.Acquire(
mFloatBuffers, maxToProcess);
323 auto result = *oResult;
324 maxOut = std::max(maxOut, result);
328 const auto limit = std::min<size_t>(upstream.Channels(), maxChannels);
329 for (
size_t j = 0; j < limit; ++j) {
331 auto &sequence = upstream.GetSequence();
334 gains[c] = sequence.GetChannelGain(c);
336 findChannelFlags(upstream.MixerSpec(j), sequence, j);
340 downstream.Release();
346 mTime = std::clamp(mTime, mT1, oldTime);
348 mTime = std::clamp(mTime, oldTime, mT1);
366 assert(maxOut <= maxToProcess);
395 const bool backwards = (mT1 < mT0);
397 mTime = std::clamp(mTime, mT1, mT0);
399 mTime = std::clamp(mTime, mT0, mT1);
402 source.Reposition(mTime, bSkipping);
407 wxASSERT(std::isfinite(speed));
411 mSpeed = fabs(speed);
417 wxASSERT(std::isfinite(speed));
421 if ((speed > 0.0 && mT1 < mT0) || (speed < 0.0 && mT1 > mT0)) {
425 if (speed > 0.0 && mT1 < mT0) {
427 mT1 = std::numeric_limits<double>::max();
430 mT0 = std::numeric_limits<double>::max();
437 mSpeed = fabs(speed);
static void MixBuffers(unsigned numChannels, const unsigned char *channelFlags, const float *gains, const float &src, std::vector< std::vector< float > > &dests, int len)
#define stackAllocate(T, count)
static Settings & settings()
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.
AudioGraph::Buffers mFloatBuffers
const std::vector< SampleBuffer > mBuffer
std::vector< Input > Inputs
void SetSpeedForKeyboardScrubbing(double speed, double startTime)
std::vector< Source > mDecoratedSources
const bool mApplyTrackGains
sampleFormat EffectiveFormat() const
Deduce the effective width of the output, which may be narrower than the stored format.
const sampleFormat mFormat
std::vector< std::vector< float > > mTemp
std::pair< bool, sampleFormat > NeedsDither(bool needsDither, double rate) const
const unsigned mNumChannels
sampleFormat mEffectiveFormat
constSamplePtr GetBuffer()
Retrieve the main buffer or the interleaved buffer.
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)
double MixGetCurrentTime()
Current time in seconds (unwarped, i.e. always between startTime and stopTime)
std::vector< MixerSource > mSources
size_t BufferSize() const
void Reposition(double t, bool bSkipping=false)
Reposition processing to absolute time next time Process() is called.
void SetTimesAndSpeed(double t0, double t1, double speed, bool bSkipping=false)
Used in scrubbing and other nonuniform playback policies.
const std::shared_ptr< TimesAndSpeed > mTimesAndSpeed
A matrix of booleans, one row per input channel, column per output.
bool VariableRates() const
bool IsMono(const Channel &channel)
Whether the channel is mono.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
size_t FindBufferSize(const Mixer::Inputs &inputs, size_t bufferSize)
std::vector< std::vector< T > > initVector(size_t dim1, size_t dim2)
static RegisteredToolbarFactory factory
void copy(const T *src, T *dst, int32_t n)
Immutable structure is an argument to Mixer's constructor.