29template <
typename Element>
33 explicit Queue (
int size)
34 : fifo (size), storage (
static_cast<size_t> (size)) {}
36 bool push (Element& element)
noexcept
41 const auto writer = fifo.
write (1);
43 if (writer.blockSize1 != 0)
44 storage[
static_cast<size_t> (writer.startIndex1)] = std::move (element);
45 else if (writer.blockSize2 != 0)
46 storage[
static_cast<size_t> (writer.startIndex2)] = std::move (element);
51 template <
typename Fn>
52 void pop (
Fn&& fn) { popN (1, std::forward<Fn> (fn)); }
54 template <
typename Fn>
55 void popAll (
Fn&& fn) { popN (fifo.
getNumReady(), std::forward<Fn> (fn)); }
57 bool hasPendingMessages()
const noexcept {
return fifo.
getNumReady() > 0; }
60 template <
typename Fn>
61 void popN (
int n,
Fn&& fn)
65 fn (storage[
static_cast<size_t> (index)]);
77 :
Thread (
"Convolution background loader"), queue (entries)
80 using IncomingCommand = FixedSizeFunction<400,
void()>;
85 bool push (IncomingCommand& command) {
return queue.push (command); }
90 queue.popAll ([] (IncomingCommand& command) { command(); command =
nullptr; });
105 if (! queue.hasPendingMessages())
108 queue.pop ([] (IncomingCommand& command) { command(); command =
nullptr;});
125 using BackgroundMessageQueue::BackgroundMessageQueue;
133 : pimpl (
std::make_unique<
Impl> (entries))
135 pimpl->startThread();
138ConvolutionMessageQueue::~ConvolutionMessageQueue() noexcept
140 pimpl->stopThread (-1);
144ConvolutionMessageQueue& ConvolutionMessageQueue::
operator= (ConvolutionMessageQueue&&) noexcept =
default;
153 fftSize (blockSize > 128 ? 2 * blockSize : 4 * blockSize),
155 numSegments (numSamples / (fftSize - blockSize) + 1u),
156 numInputSegments ((blockSize > 128 ? numSegments : 3 * numSegments)),
157 bufferInput (1,
static_cast<int> (fftSize)),
158 bufferOutput (1,
static_cast<int> (fftSize * 2)),
159 bufferTempOutput (1,
static_cast<int> (fftSize * 2)),
160 bufferOverlap (1,
static_cast<int> (fftSize))
162 bufferOutput.clear();
174 segments.push_back ({ 1,
static_cast<int> (fftSize * 2) });
184 for (
auto&
buf : buffersImpulseSegments)
188 auto* impulseResponse =
buf.getWritePointer (0);
190 if (&
buf == &buffersImpulseSegments.front())
191 impulseResponse[0] = 1.0f;
193 FloatVectorOperations::copy (impulseResponse,
195 static_cast<int> (
jmin (fftSize - blockSize, numSamples -
currentPtr)));
197 FFTTempObject->performRealOnlyForwardTransform (impulseResponse);
198 prepareForConvolution (impulseResponse);
209 bufferOverlap.clear();
210 bufferTempOutput.clear();
211 bufferOutput.clear();
213 for (
auto&
buf : buffersInputSegments)
220 void processSamples (
const float* input,
float* output,
size_t numSamples)
225 auto indexStep = numInputSegments / numSegments;
227 auto*
inputData = bufferInput.getWritePointer (0);
229 auto*
outputData = bufferOutput.getWritePointer (0);
230 auto*
overlapData = bufferOverlap.getWritePointer (0);
239 auto*
inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0);
248 FloatVectorOperations::fill (
outputTempData, 0,
static_cast<int> (fftSize + 1));
250 auto index = currentSegment;
252 for (
size_t i = 1; i < numSegments; ++i)
256 if (index >= numInputSegments)
257 index -= numInputSegments;
259 convolutionProcessingAndAccumulate (buffersInputSegments[index].getWritePointer (0),
260 buffersImpulseSegments[i].getWritePointer (0),
268 buffersImpulseSegments.front().getWritePointer (0),
271 updateSymmetricFrequencyDomainData (
outputData);
272 fftObject->performRealOnlyInverseTransform (
outputData);
280 if (inputDataPos == blockSize)
283 FloatVectorOperations::fill (
inputData, 0.0f,
static_cast<int> (fftSize));
288 FloatVectorOperations::add (&(
outputData[blockSize]), &(
overlapData[blockSize]),
static_cast<int> (fftSize - 2 * blockSize));
291 FloatVectorOperations::copy (
overlapData, &(
outputData[blockSize]),
static_cast<int> (fftSize - blockSize));
293 currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1);
300 void processSamplesWithAddedLatency (
const float* input,
float* output,
size_t numSamples)
305 auto indexStep = numInputSegments / numSegments;
307 auto*
inputData = bufferInput.getWritePointer (0);
309 auto*
outputData = bufferOutput.getWritePointer (0);
310 auto*
overlapData = bufferOverlap.getWritePointer (0);
324 if (inputDataPos == blockSize)
327 auto*
inputSegmentData = buffersInputSegments[currentSegment].getWritePointer (0);
334 FloatVectorOperations::fill (
outputTempData, 0,
static_cast<int> (fftSize + 1));
336 auto index = currentSegment;
338 for (
size_t i = 1; i < numSegments; ++i)
342 if (index >= numInputSegments)
343 index -= numInputSegments;
345 convolutionProcessingAndAccumulate (buffersInputSegments[index].getWritePointer (0),
346 buffersImpulseSegments[i].getWritePointer (0),
353 buffersImpulseSegments.front().getWritePointer (0),
356 updateSymmetricFrequencyDomainData (
outputData);
357 fftObject->performRealOnlyInverseTransform (
outputData);
363 FloatVectorOperations::fill (
inputData, 0.0f,
static_cast<int> (fftSize));
366 FloatVectorOperations::add (&(
outputData[blockSize]), &(
overlapData[blockSize]),
static_cast<int> (fftSize - 2 * blockSize));
369 FloatVectorOperations::copy (
overlapData, &(
outputData[blockSize]),
static_cast<int> (fftSize - blockSize));
371 currentSegment = (currentSegment > 0) ? (currentSegment - 1) : (numInputSegments - 1);
379 void prepareForConvolution (
float *
samples)
noexcept
393 void convolutionProcessingAndAccumulate (
const float *input,
const float *
impulse,
float *output)
397 FloatVectorOperations::addWithMultiply (output, input,
impulse,
static_cast<int> (
FFTSizeDiv2));
403 output[fftSize] += input[fftSize] *
impulse[fftSize];
409 void updateSymmetricFrequencyDomainData (
float*
samples)
noexcept
429 const size_t blockSize;
430 const size_t fftSize;
432 const size_t numSegments;
433 const size_t numInputSegments;
434 size_t currentSegment = 0, inputDataPos = 0;
436 AudioBuffer<float> bufferInput, bufferOutput, bufferTempOutput, bufferOverlap;
449 : tailBuffer (1, maxBlockSize),
450 latency (isZeroDelayIn ? 0 : maxBufferSize),
452 blockSize (maxBlockSize),
453 isZeroDelay (isZeroDelayIn)
455 constexpr auto numChannels = 2;
457 const auto makeEngine = [&] (
int channel,
int offset,
int length,
uint32 thisBlockSize)
461 static_cast<size_t> (thisBlockSize));
464 if (headSizeIn.headSizeInSamples == 0)
466 for (
int i = 0; i < numChannels; ++i)
467 head.emplace_back (makeEngine (i, 0, buf.
getNumSamples(),
static_cast<uint32> (maxBufferSize)));
473 for (
int i = 0; i < numChannels; ++i)
474 head.emplace_back (makeEngine (i, 0, size,
static_cast<uint32> (maxBufferSize)));
476 const auto tailBufferSize =
static_cast<uint32> (headSizeIn.headSizeInSamples + (isZeroDelay ? 0 : maxBufferSize));
479 for (
int i = 0; i < numChannels; ++i)
480 tail.emplace_back (makeEngine (i, size, buf.
getNumSamples() - size, tailBufferSize));
486 for (
const auto& e : head)
489 for (
const auto& e : tail)
499 const auto tailBlock = fullTailBlock.
getSubBlock (0, (
size_t) numSamples);
501 const auto isUniform = tail.empty();
503 for (
size_t channel = 0; channel < numChannels; ++channel)
506 tail[channel]->processSamplesWithAddedLatency (input.
getChannelPointer (channel),
507 tailBlock.getChannelPointer (0),
515 head[channel]->processSamplesWithAddedLatency (input.
getChannelPointer (channel),
525 for (
auto i = numChannels; i < numOutputChannels; ++i)
529 int getIRSize()
const noexcept {
return irSize; }
530 int getLatency()
const noexcept {
return latency; }
531 int getBlockSize()
const noexcept {
return blockSize; }
540 const bool isZeroDelay;
545 const auto numChannels =
jmin (
buf.getNumChannels(), stereo == Convolution::Stereo::yes ? 2 : 1);
550 for (
auto channel = 0; channel != numChannels; ++channel)
551 result.copyFrom (channel, 0,
buf.getReadPointer (channel), numSamples);
553 if (result.getNumSamples() == 0 || result.getNumChannels() == 0)
555 result.setSize (1, 1);
556 result.setSample (0, 0, 1.0f);
562static AudioBuffer<float> trimImpulseResponse (
const AudioBuffer<float>&
buf)
566 const auto numChannels =
buf.getNumChannels();
567 const auto numSamples =
buf.getNumSamples();
572 for (
auto channel = 0; channel < numChannels; ++channel)
594 auto result = AudioBuffer<float> (numChannels, 1);
601 AudioBuffer<float> result (numChannels,
newLength);
603 for (
auto channel = 0; channel < numChannels; ++channel)
605 result.copyFrom (channel,
608 result.getNumSamples());
622static void normaliseImpulseResponse (AudioBuffer<float>&
buf)
624 const auto numChannels =
buf.getNumChannels();
625 const auto numSamples =
buf.getNumSamples();
644static AudioBuffer<float> resampleImpulseResponse (
const AudioBuffer<float>&
buf,
661 AudioBuffer<float> result (
buf.getNumChannels(),
finalSize);
662 resamplingSource.getNextAudioBlock ({ &result, 0, result.getNumSamples() });
668template <
typename Element>
681 return lock.isLocked() ? std::move (ptr) :
nullptr;
694 : buffer (std::move (bufferIn)), sampleRate (sampleRateIn) {}
697 double sampleRate = 0.0;
712 BufferWithSampleRate result { {
jlimit (1, 2,
static_cast<int> (
formatReader->numChannels)),
716 formatReader->read (result.buffer.getArrayOfWritePointers(),
717 result.buffer.getNumChannels(),
719 result.buffer.getNumSamples());
733 : latency { (requiredLatency.latencyInSamples <= 0) ? 0 :
jmax (64,
nextPowerOfTwo (requiredLatency.latencyInSamples)) },
734 headSize { (requiredHeadSize.headSizeInSamples <= 0) ? 0 :
jmax (64,
nextPowerOfTwo (requiredHeadSize.headSizeInSamples)) },
735 shouldBeZeroLatency (requiredLatency.latencyInSamples == 0)
745 engine.set (makeEngine());
751 Convolution::Stereo stereo,
752 Convolution::Trim trim,
753 Convolution::Normalise normalise)
756 wantsNormalise = normalise;
757 originalSampleRate = buf.sampleRate;
759 impulseResponse = [&]
761 auto corrected = fixNumChannels (buf.buffer, stereo);
762 return trim == Convolution::Trim::yes ? trimImpulseResponse (corrected) : corrected;
765 engine.set (makeEngine());
778 auto resampled = resampleImpulseResponse (impulseResponse, originalSampleRate, processSpec.sampleRate);
780 if (wantsNormalise == Convolution::Normalise::yes)
781 normaliseImpulseResponse (resampled);
783 resampled.applyGain ((
float) (originalSampleRate / processSpec.sampleRate));
785 const auto currentLatency =
jmax (processSpec.maximumBlockSize, (
uint32) latency.latencyInSamples);
786 const auto maxBufferSize = shouldBeZeroLatency ?
static_cast<int> (processSpec.maximumBlockSize)
789 return std::make_unique<MultichannelEngine> (resampled,
790 processSpec.maximumBlockSize,
793 shouldBeZeroLatency);
805 double originalSampleRate = processSpec.sampleRate;
806 Convolution::Normalise wantsNormalise = Convolution::Normalise::no;
809 const bool shouldBeZeroLatency;
819 Convolution::Stereo stereo,
820 Convolution::Trim trim,
822 Convolution::Normalise normalise)
824 factory.setImpulseResponse (loadStreamToBuffer (std::make_unique<MemoryInputStream> (
sourceData,
sourceDataSize,
false), size),
825 stereo, trim, normalise);
828static void setImpulseResponse (ConvolutionEngineFactory& factory,
830 Convolution::Stereo stereo,
831 Convolution::Trim trim,
833 Convolution::Normalise normalise)
835 factory.setImpulseResponse (loadStreamToBuffer (std::make_unique<FileInputStream> (
fileImpulseResponse), size),
836 stereo, trim, normalise);
852 : messageQueue (queue), factory (latencyIn, headSizeIn) {}
856 Convolution::Stereo stereo,
857 Convolution::Trim trim,
858 Convolution::Normalise normalise)
862 f.setImpulseResponse ({ std::move (b), sr }, stereo, trim, normalise);
866 void loadImpulseResponse (
const void* sourceData,
867 size_t sourceDataSize,
868 Convolution::Stereo stereo,
869 Convolution::Trim trim,
871 Convolution::Normalise normalise)
875 setImpulseResponse (f, sourceData, sourceDataSize, stereo, trim, size, normalise);
879 void loadImpulseResponse (
const File& fileImpulseResponse,
880 Convolution::Stereo stereo,
881 Convolution::Trim trim,
883 Convolution::Normalise normalise)
887 setImpulseResponse (f, fileImpulseResponse, stereo, trim, size, normalise);
893 factory.setProcessSpec (spec);
899 void postPendingCommand()
901 if (pendingCommand ==
nullptr)
904 if (messageQueue.push (pendingCommand))
905 pendingCommand =
nullptr;
911 template <
typename Fn>
912 void callLater (
Fn&& fn)
916 pendingCommand = [weak = weakFromThis(), callback = std::forward<Fn> (fn)]()
mutable
918 if (
auto t = weak.lock())
919 callback (t->factory);
922 postPendingCommand();
929 BackgroundMessageQueue::IncomingCommand pendingCommand;
937 smoother.setCurrentAndTargetValue (1.0f);
948 template <
typename ProcessCurrent,
typename ProcessPrevious,
typename NotifyDone>
951 ProcessCurrent&& current,
952 ProcessPrevious&& previous,
953 NotifyDone&& notifyDone)
955 if (smoother.isSmoothing())
957 const auto numSamples =
static_cast<int> (input.
getNumSamples());
959 for (
auto sample = 0; sample != numSamples; ++sample)
960 smootherBuffer.setSample (0, sample, smoother.getNextValue());
964 previous (input, mixBlock);
966 for (
size_t channel = 0; channel != output.
getNumChannels(); ++channel)
969 smootherBuffer.getReadPointer (0),
973 FloatVectorOperations::multiply (smootherBuffer.getWritePointer (0), -1.0f, numSamples);
974 FloatVectorOperations::add (smootherBuffer.getWritePointer (0), 1.0f, numSamples);
976 current (input, output);
978 for (
size_t channel = 0; channel != output.
getNumChannels(); ++channel)
981 smootherBuffer.getReadPointer (0),
988 if (! smoother.isSmoothing())
993 current (input, output);
997 void beginTransition()
999 smoother.setCurrentAndTargetValue (1.0f);
1000 smoother.setTargetValue (0.0f);
1017 : messageQueue (std::move (queue)),
1018 engineQueue (std::make_shared<ConvolutionEngineQueue> (*messageQueue->pimpl,
1027 if (currentEngine !=
nullptr)
1028 currentEngine->reset();
1030 destroyPreviousEngine();
1035 messageQueue->pimpl->popAll();
1036 mixer.prepare (spec);
1037 engineQueue->prepare (spec);
1039 if (
auto newEngine = engineQueue->getEngine())
1040 currentEngine = std::move (newEngine);
1042 previousEngine =
nullptr;
1043 jassert (currentEngine !=
nullptr);
1048 engineQueue->postPendingCommand();
1050 if (previousEngine ==
nullptr)
1051 installPendingEngine();
1053 mixer.processSamples (input,
1057 currentEngine->processSamples (in, out);
1061 if (previousEngine !=
nullptr)
1062 previousEngine->processSamples (in, out);
1066 [
this] { destroyPreviousEngine(); });
1069 int getCurrentIRSize()
const {
return currentEngine !=
nullptr ? currentEngine->getIRSize() : 0; }
1071 int getLatency()
const {
return currentEngine !=
nullptr ? currentEngine->getLatency() : 0; }
1074 double originalSampleRate,
1077 Normalise normalise)
1079 engineQueue->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise);
1082 void loadImpulseResponse (
const void* sourceData,
1083 size_t sourceDataSize,
1087 Normalise normalise)
1089 engineQueue->loadImpulseResponse (sourceData, sourceDataSize, stereo, trim, size, normalise);
1092 void loadImpulseResponse (
const File& fileImpulseResponse,
1096 Normalise normalise)
1098 engineQueue->loadImpulseResponse (fileImpulseResponse, stereo, trim, size, normalise);
1102 void destroyPreviousEngine()
1105 BackgroundMessageQueue::IncomingCommand command = [p = std::move (previousEngine)]()
mutable { p =
nullptr; };
1106 messageQueue->pimpl->push (command);
1111 destroyPreviousEngine();
1112 previousEngine = std::move (currentEngine);
1113 currentEngine = std::move (newEngine);
1114 mixer.beginTransition();
1117 void installPendingEngine()
1119 if (
auto newEngine = engineQueue->getEngine())
1120 installNewEngine (std::move (newEngine));
1132 for (
auto&
dry : volumeDry)
1133 dry.reset (
spec.sampleRate, 0.05);
1135 for (
auto&
wet : volumeWet)
1136 wet.reset (
spec.sampleRate, 0.05);
1138 sampleRate =
spec.sampleRate;
1142 spec.maximumBlockSize);
1146template <
typename ProcessWet>
1152 const auto numChannels =
jmin (input.getNumChannels(), volumeDry.size());
1153 const auto numSamples =
jmin (input.getNumSamples(), output.getNumSamples());
1155 auto dry = dryBlock.getSubsetChannelBlock (0, numChannels);
1157 if (volumeDry[0].isSmoothing())
1159 dry.copyFrom (input);
1161 for (
size_t channel = 0; channel < numChannels; ++channel)
1162 volumeDry[channel].applyGain (
dry.getChannelPointer (channel), (
int) numSamples);
1166 for (
size_t channel = 0; channel < numChannels; ++channel)
1167 volumeWet[channel].applyGain (output.getChannelPointer (channel), (
int) numSamples);
1173 if (! currentIsBypassed)
1176 if (isBypassed != currentIsBypassed)
1178 currentIsBypassed = isBypassed;
1180 for (
size_t channel = 0; channel < numChannels; ++channel)
1182 volumeDry[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
1183 volumeDry[channel].reset (sampleRate, 0.05);
1184 volumeDry[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
1186 volumeWet[channel].setTargetValue (isBypassed ? 1.0f : 0.0f);
1187 volumeWet[channel].reset (sampleRate, 0.05);
1188 volumeWet[channel].setTargetValue (isBypassed ? 0.0f : 1.0f);
1194void Convolution::Mixer::reset() { dryBlock.clear(); }
1197Convolution::Convolution()
1208 OptionalQueue { std::make_unique<ConvolutionMessageQueue>() })
1214 OptionalQueue { std::make_unique<ConvolutionMessageQueue>() })
1227 OptionalQueue&& queue)
1231Convolution::~Convolution() noexcept =
default;
1238 Normalise normalise)
1247 Normalise normalise)
1253 double originalSampleRate,
1256 Normalise normalise)
1258 pimpl->loadImpulseResponse (std::move (buffer), originalSampleRate, stereo, trim, normalise);
1263 mixer.prepare (
spec);
1264 pimpl->prepare (
spec);
1276 bool isBypassed)
noexcept
1281 jassert (input.getNumChannels() == output.getNumChannels());
1284 mixer.processSamples (input, output, isBypassed, [
this] (
const auto& in,
auto& out)
1286 pimpl->processSamples (in, out);
void forEach(FunctionToApply &&func) const
Calls the passed function with each index that was deemed valid for the current read/write operation.
Encapsulates the logic required to implement a lock-free FIFO.
ScopedRead read(int numToRead) noexcept
Replaces prepareToRead/finishedRead with a single function.
int getFreeSpace() const noexcept
Returns the number of items that can currently be added to the buffer without it overflowing.
ScopedWrite write(int numToWrite) noexcept
Replaces prepareToWrite/finishedWrite with a single function.
int getNumReady() const noexcept
Returns the number of items that can currently be read from the buffer.
A multi-channel buffer containing floating point audio samples.
int getNumChannels() const noexcept
Returns the number of channels of audio data that this buffer contains.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer's channels.
void setSample(int destChannel, int destSample, Type newValue) noexcept
Sets a sample in the buffer.
const Type * getReadPointer(int channelNumber) const noexcept
Returns a pointer to an array of read-only samples in one of the buffer's channels.
static Type decibelsToGain(Type decibels, Type minusInfinityDb=Type(defaultMinusInfinitydB))
Converts a dBFS value to its equivalent gain level.
Represents a local file or directory.
Automatically locks and unlocks a mutex object.
Automatically locks and unlocks a mutex object.
A utility class for values that need smoothing to avoid audio glitches.
A simple spin-lock class that can be used as a simple, low-overhead mutex for uncontended situations.
bool startThread()
Attempts to start a new thread with default ('Priority::normal') priority.
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
bool stopThread(int timeOutMilliseconds)
Attempts to stop the thread running.
Minimal and lightweight data-structure which contains a list of pointers to channels containing some ...
AudioBlock getSubBlock(size_t newOffset, size_t newLength) const noexcept
Return a new AudioBlock pointing to a sub-block inside this block.
constexpr size_t getNumChannels() const noexcept
Returns the number of channels referenced by this block.
SampleType * getChannelPointer(size_t channel) const noexcept
Returns a raw pointer into one of the channels in this block.
AudioBlock & copyFrom(const AudioBlock< OtherSampleType > &src) noexcept
Copies the values in src to this block.
AudioBlock getSingleChannelBlock(size_t channel) const noexcept
Returns an AudioBlock that represents one of the channels in this block.
constexpr size_t getNumSamples() const noexcept
Returns the number of samples referenced by this block.
AudioBlock & clear() noexcept
Clears the memory referenced by this AudioBlock.
Used by the Convolution to dispatch engine-update messages on a background thread.
ConvolutionMessageQueue()
Initialises the queue to a default size.
Performs stereo partitioned convolution of an input signal with an impulse response in the frequency ...
int getCurrentIRSize() const
This function returns the size of the current IR in samples.
Convolution()
Initialises an object for performing convolution in the frequency domain.
int getLatency() const
This function returns the current latency of the process in samples.
void prepare(const ProcessSpec &)
Must be called before first calling process.
void loadImpulseResponse(const void *sourceData, size_t sourceDataSize, Stereo isStereo, Trim requiresTrimming, size_t size, Normalise requiresNormalisation=Normalise::yes)
This function loads an impulse response audio file from memory, added in a JUCE project with the Proj...
void reset() noexcept
Resets the processing pipeline ready to start a new stream of data.
Contains configuration information for a convolution with a fixed latency.
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
T make_reverse_iterator(T... args)
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
int nextPowerOfTwo(int n) noexcept
Returns the smallest power-of-two which is equal to or greater than the given integer.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
RangedDirectoryIterator begin(const RangedDirectoryIterator &it)
Returns the iterator that was passed in.
This structure is passed into a DSP algorithm's prepare() method, and contains information about vari...
uint32 numChannels
The number of channels that the process() method will be expected to handle.
double sampleRate
The sample rate that will be used for the data that is sent to the processor.
uint32 maximumBlockSize
The maximum number of samples that will be in the blocks sent to process() method.