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,
80) : mNumChannels{ numOutChannels }
81 , mInputs{ move(inputs) }
83 , mApplyGain{ applyGain }
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;
128 mHasMixerSpec = pMixerSpec !=
nullptr;
131 mSources.reserve(nChannelsIn);
132 const auto nStages = std::accumulate(mInputs.begin(), mInputs.end(), 0,
133 [](
auto sum,
const auto &input){
134 return sum + input.stages.size() * input.pSequence->NChannels(); });
135 mSettings.reserve(nStages);
136 mStageBuffers.reserve(nStages);
139 for (
auto &input : mInputs) {
140 const auto &sequence = input.pSequence;
145 auto increment =
finally([&]{ i += sequence->NChannels(); });
147 auto &source = mSources.emplace_back(sequence,
BufferSize(), outRate,
148 warpOptions, highQuality, mayThrow, mTimesAndSpeed,
149 (pMixerSpec ? &pMixerSpec->mMap[i] :
nullptr));
151 for (
const auto &stage : input.stages) {
153 auto &
settings = mSettings.emplace_back(stage.settings);
158 auto &stageInput = mStageBuffers.emplace_back(3, mBufferSize, 1);
159 const auto &
factory = [&stage]{
161 return stage.mpFirstInstance
162 ? move(stage.mpFirstInstance)
165 auto &pNewDownstream =
167 *pDownstream, stageInput,
171 pDownstream = pNewDownstream.get();
175 mStageBuffers.pop_back();
176 mSettings.pop_back();
179 mDecoratedSources.emplace_back(Source{ source, *pDownstream });
183 std::tie(mNeedsDither, mEffectiveFormat) = NeedsDither(needsDither, outRate);
188std::pair<bool, sampleFormat>
203 for (
const auto &input :
mSources) {
204 auto &sequence = input.GetSequence();
206 if (sequence.GetRate() != rate)
217 for (
auto c : {0, 1}) {
218 const auto gain = sequence.GetChannelGain(c);
219 if (!(gain == 0.0 || gain == 1.0))
228 if (!sequence.HasTrivialEnvelope())
231 auto effectiveFormat = sequence.WidestEffectiveFormat();
236 widestEffectiveFormat =
237 std::max(widestEffectiveFormat, effectiveFormat);
245 assert(widestEffectiveFormat <=
mFormat);
246 return {
false, widestEffectiveFormat };
252 for (
auto &buffer:
mTemp)
253 std::fill(buffer.begin(), buffer.end(), 0);
257 const unsigned char *channelFlags,
const float *gains,
258 const float &src, std::vector<std::vector<float>> &dests,
int len)
260 const auto pSrc = &src;
261 for (
unsigned int c = 0; c < numChannels; c++) {
262 if (!channelFlags[c])
264 for (
int j = 0; j < len; ++j)
265 dests[c][j] += pSrc[j] * gains[c];
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();
335 gains[c] = sequence.GetChannelGain(c);
337 gains[c] = sequence.GetChannelGain(j);
340 gains[0] /=
static_cast<float>(limit);
344 findChannelFlags(upstream.MixerSpec(j), sequence, j);
348 downstream.Release();
354 mTime = std::clamp(mTime, mT1, oldTime);
356 mTime = std::clamp(mTime, oldTime, mT1);
374 assert(maxOut <= maxToProcess);
403 const bool backwards = (mT1 < mT0);
405 mTime = std::clamp(mTime, mT1, mT0);
407 mTime = std::clamp(mTime, mT0, mT1);
410 source.Reposition(mTime, bSkipping);
415 wxASSERT(std::isfinite(speed));
419 mSpeed = fabs(speed);
425 wxASSERT(std::isfinite(speed));
429 if ((speed > 0.0 && mT1 < mT0) || (speed < 0.0 && mT1 > mT0)) {
433 if (speed > 0.0 && mT1 < mT0) {
435 mT1 = std::numeric_limits<double>::max();
438 mT0 = std::numeric_limits<double>::max();
445 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 ApplyGain mApplyGain
const std::vector< SampleBuffer > mBuffer
std::vector< Input > Inputs
void SetSpeedForKeyboardScrubbing(double speed, double startTime)
std::vector< Source > mDecoratedSources
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, ApplyGain applyGain=ApplyGain::MapChannels)
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.
size_t FindBufferSize(const Mixer::Inputs &inputs, size_t bufferSize)
std::vector< std::vector< T > > initVector(size_t dim1, size_t dim2)
const char * end(const char *str) noexcept
void copy(const T *src, T *dst, int32_t n)
Immutable structure is an argument to Mixer's constructor.