git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
892
deps/juce/modules/juce_dsp/containers/juce_AudioBlock.h
vendored
Normal file
892
deps/juce/modules/juce_dsp/containers/juce_AudioBlock.h
vendored
Normal file
@ -0,0 +1,892 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
namespace SampleTypeHelpers // Internal classes needed for handling sample type classes
|
||||
{
|
||||
template <typename T, bool = std::is_floating_point<T>::value>
|
||||
struct ElementType
|
||||
{
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ElementType<T, false>
|
||||
{
|
||||
using Type = typename T::value_type;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Minimal and lightweight data-structure which contains a list of pointers to
|
||||
channels containing some kind of sample data.
|
||||
|
||||
This class doesn't own any of the data which it points to, it's simply a view
|
||||
into data that is owned elsewhere. You can construct one from some raw data
|
||||
that you've allocated yourself, or give it a HeapBlock to use, or give it
|
||||
an AudioBuffer which it can refer to, but in all cases the user is
|
||||
responsible for making sure that the data doesn't get deleted while there's
|
||||
still an AudioBlock using it.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename SampleType>
|
||||
class AudioBlock
|
||||
{
|
||||
private:
|
||||
template <typename OtherSampleType>
|
||||
using MayUseConvertingConstructor =
|
||||
std::enable_if_t<std::is_same<std::remove_const_t<SampleType>,
|
||||
std::remove_const_t<OtherSampleType>>::value
|
||||
&& std::is_const<SampleType>::value
|
||||
&& ! std::is_const<OtherSampleType>::value,
|
||||
int>;
|
||||
|
||||
public:
|
||||
//==============================================================================
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
//==============================================================================
|
||||
/** Create a zero-sized AudioBlock. */
|
||||
AudioBlock() noexcept = default;
|
||||
|
||||
/** Creates an AudioBlock from a pointer to an array of channels.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the memory is retained
|
||||
throughout the life-time of the AudioBlock and released when no longer needed.
|
||||
*/
|
||||
constexpr AudioBlock (SampleType* const* channelData,
|
||||
size_t numberOfChannels, size_t numberOfSamples) noexcept
|
||||
: channels (channelData),
|
||||
numChannels (static_cast<ChannelCountType> (numberOfChannels)),
|
||||
numSamples (numberOfSamples)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock from a pointer to an array of channels.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the memory is retained
|
||||
throughout the life-time of the AudioBlock and released when no longer needed.
|
||||
*/
|
||||
constexpr AudioBlock (SampleType* const* channelData, size_t numberOfChannels,
|
||||
size_t startSampleIndex, size_t numberOfSamples) noexcept
|
||||
: channels (channelData),
|
||||
numChannels (static_cast<ChannelCountType> (numberOfChannels)),
|
||||
startSample (startSampleIndex),
|
||||
numSamples (numberOfSamples)
|
||||
{
|
||||
}
|
||||
|
||||
/** Allocates a suitable amount of space in a HeapBlock, and initialises this object
|
||||
to point into it.
|
||||
The HeapBlock must of course not be freed or re-allocated while this object is still in
|
||||
use, because it will be referencing its data.
|
||||
*/
|
||||
AudioBlock (HeapBlock<char>& heapBlockToUseForAllocation,
|
||||
size_t numberOfChannels, size_t numberOfSamples,
|
||||
size_t alignmentInBytes = defaultAlignment) noexcept
|
||||
: numChannels (static_cast<ChannelCountType> (numberOfChannels)),
|
||||
numSamples (numberOfSamples)
|
||||
{
|
||||
auto roundedUpNumSamples = (numberOfSamples + elementMask) & ~elementMask;
|
||||
auto channelSize = sizeof (SampleType) * roundedUpNumSamples;
|
||||
auto channelListBytes = sizeof (SampleType*) * numberOfChannels;
|
||||
auto extraBytes = alignmentInBytes - 1;
|
||||
|
||||
heapBlockToUseForAllocation.malloc (channelListBytes + extraBytes + channelSize * numberOfChannels);
|
||||
|
||||
auto* chanArray = unalignedPointerCast<SampleType**> (heapBlockToUseForAllocation.getData());
|
||||
channels = chanArray;
|
||||
|
||||
auto* data = unalignedPointerCast<SampleType*> (addBytesToPointer (chanArray, channelListBytes));
|
||||
data = snapPointerToAlignment (data, alignmentInBytes);
|
||||
|
||||
for (ChannelCountType i = 0; i < numChannels; ++i)
|
||||
{
|
||||
chanArray[i] = data;
|
||||
data += roundedUpNumSamples;
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock that points to the data in an AudioBuffer.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the buffer is retained
|
||||
throughout the life-time of the AudioBlock without being modified.
|
||||
*/
|
||||
template <typename OtherSampleType>
|
||||
constexpr AudioBlock (AudioBuffer<OtherSampleType>& buffer) noexcept
|
||||
: channels (buffer.getArrayOfWritePointers()),
|
||||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
|
||||
numSamples (static_cast<size_t> (buffer.getNumSamples()))
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock that points to the data in an AudioBuffer.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the buffer is retained
|
||||
throughout the life-time of the AudioBlock without being modified.
|
||||
*/
|
||||
template <typename OtherSampleType>
|
||||
constexpr AudioBlock (const AudioBuffer<OtherSampleType>& buffer) noexcept
|
||||
: channels (buffer.getArrayOfReadPointers()),
|
||||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
|
||||
numSamples (static_cast<size_t> (buffer.getNumSamples()))
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an AudioBlock that points to the data in an AudioBuffer.
|
||||
AudioBlock does not copy nor own the memory pointed to by dataToUse.
|
||||
Therefore it is the user's responsibility to ensure that the buffer is retained
|
||||
throughout the life-time of the AudioBlock without being modified.
|
||||
*/
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock (AudioBuffer<OtherSampleType>& buffer, size_t startSampleIndex) noexcept
|
||||
: channels (buffer.getArrayOfWritePointers()),
|
||||
numChannels (static_cast<ChannelCountType> (buffer.getNumChannels())),
|
||||
startSample (startSampleIndex),
|
||||
numSamples (static_cast<size_t> (buffer.getNumSamples()) - startSampleIndex)
|
||||
{
|
||||
jassert (startSample < static_cast<size_t> (buffer.getNumSamples()));
|
||||
}
|
||||
|
||||
AudioBlock (const AudioBlock& other) noexcept = default;
|
||||
AudioBlock& operator= (const AudioBlock& other) noexcept = default;
|
||||
|
||||
template <typename OtherSampleType, MayUseConvertingConstructor<OtherSampleType> = 0>
|
||||
AudioBlock (const AudioBlock<OtherSampleType>& other) noexcept
|
||||
: channels { other.channels },
|
||||
numChannels { other.numChannels },
|
||||
startSample { other.startSample },
|
||||
numSamples { other.numSamples }
|
||||
{
|
||||
}
|
||||
|
||||
template <typename OtherSampleType, MayUseConvertingConstructor<OtherSampleType> = 0>
|
||||
AudioBlock& operator= (const AudioBlock<OtherSampleType>& other) noexcept
|
||||
{
|
||||
AudioBlock blockCopy { other };
|
||||
swap (blockCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap (AudioBlock& other) noexcept
|
||||
{
|
||||
std::swap (other.channels, channels);
|
||||
std::swap (other.numChannels, numChannels);
|
||||
std::swap (other.startSample, startSample);
|
||||
std::swap (other.numSamples, numSamples);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename OtherSampleType>
|
||||
constexpr bool operator== (const AudioBlock<OtherSampleType>& other) const noexcept
|
||||
{
|
||||
return std::equal (channels,
|
||||
channels + numChannels,
|
||||
other.channels,
|
||||
other.channels + other.numChannels)
|
||||
&& startSample == other.startSample
|
||||
&& numSamples == other.numSamples;
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
constexpr bool operator!= (const AudioBlock<OtherSampleType>& other) const noexcept
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of channels referenced by this block. */
|
||||
constexpr size_t getNumChannels() const noexcept { return static_cast<size_t> (numChannels); }
|
||||
|
||||
/** Returns the number of samples referenced by this block. */
|
||||
constexpr size_t getNumSamples() const noexcept { return numSamples; }
|
||||
|
||||
/** Returns a raw pointer into one of the channels in this block. */
|
||||
SampleType* getChannelPointer (size_t channel) const noexcept
|
||||
{
|
||||
jassert (channel < numChannels);
|
||||
jassert (numSamples > 0);
|
||||
return channels[channel] + startSample;
|
||||
}
|
||||
|
||||
/** Returns an AudioBlock that represents one of the channels in this block. */
|
||||
AudioBlock getSingleChannelBlock (size_t channel) const noexcept
|
||||
{
|
||||
jassert (channel < numChannels);
|
||||
return AudioBlock (channels + channel, 1, startSample, numSamples);
|
||||
}
|
||||
|
||||
/** Returns a subset of contiguous channels
|
||||
@param channelStart First channel of the subset
|
||||
@param numChannelsToUse Count of channels in the subset
|
||||
*/
|
||||
AudioBlock getSubsetChannelBlock (size_t channelStart, size_t numChannelsToUse) const noexcept
|
||||
{
|
||||
jassert (channelStart < numChannels);
|
||||
jassert ((channelStart + numChannelsToUse) <= numChannels);
|
||||
|
||||
return AudioBlock (channels + channelStart, numChannelsToUse, startSample, numSamples);
|
||||
}
|
||||
|
||||
/** Returns a sample from the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
SampleType getSample (int channel, int sampleIndex) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (channel, numChannels));
|
||||
jassert (isPositiveAndBelow (sampleIndex, numSamples));
|
||||
return channels[channel][(size_t) startSample + (size_t) sampleIndex];
|
||||
}
|
||||
|
||||
/** Modifies a sample in the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
void setSample (int destChannel, int destSample, SampleType newValue) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (isPositiveAndBelow (destSample, numSamples));
|
||||
channels[destChannel][(size_t) startSample + (size_t) destSample] = newValue;
|
||||
}
|
||||
|
||||
/** Adds a value to a sample in the buffer.
|
||||
The channel and index are not checked - they are expected to be in-range. If not,
|
||||
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
|
||||
territory.
|
||||
*/
|
||||
void addSample (int destChannel, int destSample, SampleType valueToAdd) const noexcept
|
||||
{
|
||||
jassert (isPositiveAndBelow (destChannel, numChannels));
|
||||
jassert (isPositiveAndBelow (destSample, numSamples));
|
||||
channels[destChannel][(size_t) startSample + (size_t) destSample] += valueToAdd;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the memory referenced by this AudioBlock. */
|
||||
AudioBlock& clear() noexcept { clearInternal(); return *this; }
|
||||
const AudioBlock& clear() const noexcept { clearInternal(); return *this; }
|
||||
|
||||
/** Fills the memory referenced by this AudioBlock with value. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE fill (NumericType value) noexcept { fillInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE fill (NumericType value) const noexcept { fillInternal (value); return *this; }
|
||||
|
||||
/** Copies the values in src to this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& copyFrom (const AudioBlock<OtherSampleType>& src) noexcept { copyFromInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& copyFrom (const AudioBlock<OtherSampleType>& src) const noexcept { copyFromInternal (src); return *this; }
|
||||
|
||||
/** Copy the values from an AudioBuffer to this block.
|
||||
|
||||
All indices and sizes are in this AudioBlock's units, i.e. if SampleType is a
|
||||
SIMDRegister then incrementing srcPos by one will increase the sample position
|
||||
in the AudioBuffer's units by a factor of SIMDRegister<SampleType>::SIMDNumElements.
|
||||
*/
|
||||
template <typename OtherNumericType>
|
||||
AudioBlock& copyFrom (const AudioBuffer<OtherNumericType>& src,
|
||||
size_t srcPos = 0, size_t dstPos = 0,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) { copyFromInternal (src, srcPos, dstPos, numElements); return *this; }
|
||||
template <typename OtherNumericType>
|
||||
const AudioBlock& copyFrom (const AudioBuffer<OtherNumericType>& src,
|
||||
size_t srcPos = 0, size_t dstPos = 0,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const { copyFromInternal (src, srcPos, dstPos, numElements); return *this; }
|
||||
|
||||
|
||||
/** Copies the values from this block to an AudioBuffer.
|
||||
|
||||
All indices and sizes are in this AudioBlock's units, i.e. if SampleType is a
|
||||
SIMDRegister then incrementing dstPos by one will increase the sample position
|
||||
in the AudioBuffer's units by a factor of SIMDRegister<SampleType>::SIMDNumElements.
|
||||
*/
|
||||
void copyTo (AudioBuffer<typename std::remove_const<NumericType>::type>& dst, size_t srcPos = 0, size_t dstPos = 0,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const
|
||||
{
|
||||
auto dstlen = static_cast<size_t> (dst.getNumSamples()) / sizeFactor;
|
||||
auto n = static_cast<int> (jmin (numSamples - srcPos, dstlen - dstPos, numElements) * sizeFactor);
|
||||
auto maxChannels = jmin (static_cast<size_t> (dst.getNumChannels()), static_cast<size_t> (numChannels));
|
||||
|
||||
for (size_t ch = 0; ch < maxChannels; ++ch)
|
||||
FloatVectorOperations::copy (dst.getWritePointer (static_cast<int> (ch),
|
||||
static_cast<int> (dstPos * sizeFactor)),
|
||||
getDataPointer (ch) + (srcPos * sizeFactor),
|
||||
n);
|
||||
}
|
||||
|
||||
/** Move memory within this block from the position srcPos to the position dstPos.
|
||||
If numElements is not specified then move will move the maximum amount of memory.
|
||||
*/
|
||||
AudioBlock& move (size_t srcPos, size_t dstPos,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) noexcept { moveInternal (srcPos, dstPos, numElements); return *this; }
|
||||
const AudioBlock& move (size_t srcPos, size_t dstPos,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const noexcept { moveInternal (srcPos, dstPos, numElements); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Return a new AudioBlock pointing to a sub-block inside this block. This
|
||||
function does not copy the memory and you must ensure that the original memory
|
||||
pointed to by the receiver remains valid through-out the life-time of the
|
||||
returned sub-block.
|
||||
|
||||
@param newOffset The index of an element inside the receiver which will
|
||||
will become the first element of the return value.
|
||||
@param newLength The number of elements of the newly created sub-block.
|
||||
*/
|
||||
AudioBlock getSubBlock (size_t newOffset, size_t newLength) const noexcept
|
||||
{
|
||||
jassert (newOffset < numSamples);
|
||||
jassert (newOffset + newLength <= numSamples);
|
||||
|
||||
return AudioBlock (channels, numChannels, startSample + newOffset, newLength);
|
||||
}
|
||||
|
||||
/** Return a new AudioBlock pointing to a sub-block inside this block. This
|
||||
function does not copy the memory and you must ensure that the original memory
|
||||
pointed to by the receiver remains valid through-out the life-time of the
|
||||
returned sub-block.
|
||||
|
||||
@param newOffset The index of an element inside the block which will
|
||||
will become the first element of the return value.
|
||||
The return value will include all subsequent elements
|
||||
of the receiver.
|
||||
*/
|
||||
AudioBlock getSubBlock (size_t newOffset) const noexcept
|
||||
{
|
||||
return getSubBlock (newOffset, getNumSamples() - newOffset);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a fixed value to the elements in this block. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE add (NumericType value) noexcept { addInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE add (NumericType value) const noexcept { addInternal (value); return *this; }
|
||||
|
||||
/** Adds the elements in the src block to the elements in this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& add (AudioBlock<OtherSampleType> src) noexcept { addInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& add (AudioBlock<OtherSampleType> src) const noexcept { addInternal (src); return *this; }
|
||||
|
||||
/** Adds a fixed value to each source value and replaces the contents of this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithSumOf (AudioBlock<OtherSampleType> src, NumericType value) noexcept { replaceWithSumOfInternal (src, value); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithSumOf (AudioBlock<OtherSampleType> src, NumericType value) const noexcept { replaceWithSumOfInternal (src, value); return *this; }
|
||||
|
||||
/** Adds each source1 value to the corresponding source2 value and replaces the contents of this block. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithSumOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithSumOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithSumOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithSumOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Subtracts a fixed value from the elements in this block. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE subtract (NumericType value) noexcept { subtractInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE subtract (NumericType value) const noexcept { subtractInternal (value); return *this; }
|
||||
|
||||
/** Subtracts the source values from the elements in this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& subtract (AudioBlock<OtherSampleType> src) noexcept { subtractInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& subtract (AudioBlock<OtherSampleType> src) const noexcept { subtractInternal (src); return *this; }
|
||||
|
||||
/** Subtracts a fixed value from each source value and replaces the contents of this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithDifferenceOf (AudioBlock<OtherSampleType> src, NumericType value) noexcept { replaceWithDifferenceOfInternal (src, value); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithDifferenceOf (AudioBlock<OtherSampleType> src, NumericType value) const noexcept { replaceWithDifferenceOfInternal (src, value); return *this; }
|
||||
|
||||
/** Subtracts each source2 value from the corresponding source1 value and replaces the contents of this block. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithDifferenceOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithDifferenceOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithDifferenceOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithDifferenceOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies the elements in this block by a fixed value. */
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE multiplyBy (NumericType value) noexcept { multiplyByInternal (value); return *this; }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE multiplyBy (NumericType value) const noexcept { multiplyByInternal (value); return *this; }
|
||||
|
||||
/** Multiplies the elements in this block by the elements in the src block */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& multiplyBy (AudioBlock<OtherSampleType> src) noexcept { multiplyByInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& multiplyBy (AudioBlock<OtherSampleType> src) const noexcept { multiplyByInternal (src); return *this; }
|
||||
|
||||
/** Replaces the elements in this block with the product of the elements in the source src block and a fixed value. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithProductOf (AudioBlock<OtherSampleType> src, NumericType value) noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE replaceWithProductOf (AudioBlock<OtherSampleType> src, NumericType value) const noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
|
||||
/** Replaces the elements in this block with the product of the elements in the src1 and scr2 blocks. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithProductOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithProductOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies each channels of this block by a smoothly changing value. */
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
AudioBlock& multiplyBy (SmoothedValue<OtherSampleType, SmoothingType>& value) noexcept { multiplyByInternal (value); return *this; }
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
const AudioBlock& multiplyBy (SmoothedValue<OtherSampleType, SmoothingType>& value) const noexcept { multiplyByInternal (value); return *this; }
|
||||
|
||||
/** Replaces each channel of this block with the product of the src block and a smoothed value. */
|
||||
template <typename BlockSampleType, typename SmootherSampleType, typename SmoothingType>
|
||||
AudioBlock& replaceWithProductOf (AudioBlock<BlockSampleType> src, SmoothedValue<SmootherSampleType, SmoothingType>& value) noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
template <typename BlockSampleType, typename SmootherSampleType, typename SmoothingType>
|
||||
const AudioBlock& replaceWithProductOf (AudioBlock<BlockSampleType> src, SmoothedValue<SmootherSampleType, SmoothingType>& value) const noexcept { replaceWithProductOfInternal (src, value); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies each value in src by a fixed value and adds the result to this block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE addProductOf (AudioBlock<OtherSampleType> src, NumericType factor) noexcept { addProductOfInternal (src, factor); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE addProductOf (AudioBlock<OtherSampleType> src, NumericType factor) const noexcept { addProductOfInternal (src, factor); return *this; }
|
||||
|
||||
/** Multiplies each value in srcA with the corresponding value in srcB and adds the result to this block. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& addProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { addProductOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& addProductOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { addProductOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Negates each value of this block. */
|
||||
AudioBlock& negate() noexcept { negateInternal(); return *this; }
|
||||
const AudioBlock& negate() const noexcept { negateInternal(); return *this; }
|
||||
|
||||
/** Replaces the contents of this block with the negative of the values in the src block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& replaceWithNegativeOf (AudioBlock<OtherSampleType> src) noexcept { replaceWithNegativeOfInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& replaceWithNegativeOf (AudioBlock<OtherSampleType> src) const noexcept { replaceWithNegativeOfInternal (src); return *this; }
|
||||
|
||||
/** Replaces the contents of this block with the absolute values of the src block. */
|
||||
template <typename OtherSampleType>
|
||||
AudioBlock& replaceWithAbsoluteValueOf (AudioBlock<OtherSampleType> src) noexcept { replaceWithAbsoluteValueOfInternal (src); return *this; }
|
||||
template <typename OtherSampleType>
|
||||
const AudioBlock& replaceWithAbsoluteValueOf (AudioBlock<OtherSampleType> src) const noexcept { replaceWithAbsoluteValueOfInternal (src); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Replaces each element of this block with the minimum of the corresponding element of the source arrays. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithMinOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithMinOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithMinOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithMinOfInternal (src1, src2); return *this; }
|
||||
|
||||
/** Replaces each element of this block with the maximum of the corresponding element of the source arrays. */
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
AudioBlock& replaceWithMaxOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) noexcept { replaceWithMaxOfInternal (src1, src2); return *this; }
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
const AudioBlock& replaceWithMaxOf (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept { replaceWithMaxOfInternal (src1, src2); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Finds the minimum and maximum value of the buffer. */
|
||||
Range<typename std::remove_const<NumericType>::type> findMinAndMax() const noexcept
|
||||
{
|
||||
if (numChannels == 0)
|
||||
return {};
|
||||
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
auto minmax = FloatVectorOperations::findMinAndMax (getDataPointer (0), n);
|
||||
|
||||
for (size_t ch = 1; ch < numChannels; ++ch)
|
||||
minmax = minmax.getUnionWith (FloatVectorOperations::findMinAndMax (getDataPointer (ch), n));
|
||||
|
||||
return minmax;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Convenient operator wrappers.
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE operator+= (NumericType value) noexcept { return add (value); }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE operator+= (NumericType value) const noexcept { return add (value); }
|
||||
|
||||
AudioBlock& operator+= (AudioBlock src) noexcept { return add (src); }
|
||||
const AudioBlock& operator+= (AudioBlock src) const noexcept { return add (src); }
|
||||
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE operator-= (NumericType value) noexcept { return subtract (value); }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE operator-= (NumericType value) const noexcept { return subtract (value); }
|
||||
|
||||
AudioBlock& operator-= (AudioBlock src) noexcept { return subtract (src); }
|
||||
const AudioBlock& operator-= (AudioBlock src) const noexcept { return subtract (src); }
|
||||
|
||||
AudioBlock& JUCE_VECTOR_CALLTYPE operator*= (NumericType value) noexcept { return multiplyBy (value); }
|
||||
const AudioBlock& JUCE_VECTOR_CALLTYPE operator*= (NumericType value) const noexcept { return multiplyBy (value); }
|
||||
|
||||
AudioBlock& operator*= (AudioBlock src) noexcept { return multiplyBy (src); }
|
||||
const AudioBlock& operator*= (AudioBlock src) const noexcept { return multiplyBy (src); }
|
||||
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
AudioBlock& operator*= (SmoothedValue<OtherSampleType, SmoothingType>& value) noexcept { return multiplyBy (value); }
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
const AudioBlock& operator*= (SmoothedValue<OtherSampleType, SmoothingType>& value) const noexcept { return multiplyBy (value); }
|
||||
|
||||
//==============================================================================
|
||||
// This class can only be used with floating point types
|
||||
static_assert (std::is_same<std::remove_const_t<SampleType>, float>::value
|
||||
|| std::is_same<std::remove_const_t<SampleType>, double>::value
|
||||
#if JUCE_USE_SIMD
|
||||
|| std::is_same<std::remove_const_t<SampleType>, SIMDRegister<float>>::value
|
||||
|| std::is_same<std::remove_const_t<SampleType>, SIMDRegister<double>>::value
|
||||
#endif
|
||||
, "AudioBlock only supports single or double precision floating point types");
|
||||
|
||||
//==============================================================================
|
||||
/** Applies a function to each value in an input block, putting the result into an output block.
|
||||
The function supplied must take a SampleType as its parameter, and return a SampleType.
|
||||
The two blocks must have the same number of channels and samples.
|
||||
*/
|
||||
template <typename Src1SampleType, typename Src2SampleType, typename FunctionType>
|
||||
static void process (AudioBlock<Src1SampleType> inBlock, AudioBlock<Src2SampleType> outBlock, FunctionType&& function)
|
||||
{
|
||||
auto len = inBlock.getNumSamples();
|
||||
auto numChans = inBlock.getNumChannels();
|
||||
|
||||
jassert (len == outBlock.getNumSamples());
|
||||
jassert (numChans == outBlock.getNumChannels());
|
||||
|
||||
for (ChannelCountType c = 0; c < numChans; ++c)
|
||||
{
|
||||
auto* src = inBlock.getChannelPointer (c);
|
||||
auto* dst = outBlock.getChannelPointer (c);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
dst[i] = function (src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NumericType* getDataPointer (size_t channel) const noexcept
|
||||
{
|
||||
return reinterpret_cast<NumericType*> (getChannelPointer (channel));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_VECTOR_CALLTYPE clearInternal() const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::clear (getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
void JUCE_VECTOR_CALLTYPE fillInternal (NumericType value) const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::fill (getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void copyFromInternal (const AudioBlock<OtherSampleType>& src) const noexcept
|
||||
{
|
||||
auto maxChannels = jmin (src.numChannels, numChannels);
|
||||
auto n = static_cast<int> (jmin (src.numSamples * src.sizeFactor,
|
||||
numSamples * sizeFactor));
|
||||
|
||||
for (size_t ch = 0; ch < maxChannels; ++ch)
|
||||
FloatVectorOperations::copy (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherNumericType>
|
||||
void copyFromInternal (const AudioBuffer<OtherNumericType>& src, size_t srcPos, size_t dstPos, size_t numElements) const
|
||||
{
|
||||
auto srclen = static_cast<size_t> (src.getNumSamples()) / sizeFactor;
|
||||
auto n = static_cast<int> (jmin (srclen - srcPos, numSamples - dstPos, numElements) * sizeFactor);
|
||||
auto maxChannels = jmin (static_cast<size_t> (src.getNumChannels()), static_cast<size_t> (numChannels));
|
||||
|
||||
for (size_t ch = 0; ch < maxChannels; ++ch)
|
||||
FloatVectorOperations::copy (getDataPointer (ch) + (dstPos * sizeFactor),
|
||||
src.getReadPointer (static_cast<int> (ch),
|
||||
static_cast<int> (srcPos * sizeFactor)),
|
||||
n);
|
||||
}
|
||||
|
||||
void moveInternal (size_t srcPos, size_t dstPos,
|
||||
size_t numElements = std::numeric_limits<size_t>::max()) const noexcept
|
||||
{
|
||||
jassert (srcPos <= numSamples && dstPos <= numSamples);
|
||||
auto len = jmin (numSamples - srcPos, numSamples - dstPos, numElements) * sizeof (SampleType);
|
||||
|
||||
if (len != 0)
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
::memmove (getChannelPointer (ch) + dstPos,
|
||||
getChannelPointer (ch) + srcPos, len);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_VECTOR_CALLTYPE addInternal (NumericType value) const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void addInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE replaceWithSumOfInternal (AudioBlock<OtherSampleType> src, NumericType value) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), src.getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithSumOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::add (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
constexpr void JUCE_VECTOR_CALLTYPE subtractInternal (NumericType value) const noexcept
|
||||
{
|
||||
addInternal (value * static_cast<NumericType> (-1.0));
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void subtractInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::subtract (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE replaceWithDifferenceOfInternal (AudioBlock<OtherSampleType> src, NumericType value) const noexcept
|
||||
{
|
||||
replaceWithSumOfInternal (src, static_cast<NumericType> (-1.0) * value);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithDifferenceOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::subtract (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_VECTOR_CALLTYPE multiplyByInternal (NumericType value) const noexcept
|
||||
{
|
||||
auto n = static_cast<int> (numSamples * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void multiplyByInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE replaceWithProductOfInternal (AudioBlock<OtherSampleType> src, NumericType value) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), src.getDataPointer (ch), value, n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithProductOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::multiply (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType, typename SmoothingType>
|
||||
void multiplyByInternal (SmoothedValue<OtherSampleType, SmoothingType>& value) const noexcept
|
||||
{
|
||||
if (! value.isSmoothing())
|
||||
{
|
||||
multiplyByInternal ((NumericType) value.getTargetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < numSamples; ++i)
|
||||
{
|
||||
const auto scaler = (NumericType) value.getNextValue();
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
getDataPointer (ch)[i] *= scaler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BlockSampleType, typename SmootherSampleType, typename SmoothingType>
|
||||
void replaceWithProductOfInternal (AudioBlock<BlockSampleType> src, SmoothedValue<SmootherSampleType, SmoothingType>& value) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
|
||||
if (! value.isSmoothing())
|
||||
{
|
||||
replaceWithProductOfInternal (src, (NumericType) value.getTargetValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto n = jmin (numSamples, src.numSamples) * sizeFactor;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
const auto scaler = (NumericType) value.getNextValue();
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
getDataPointer (ch)[i] = scaler * src.getChannelPointer (ch)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename OtherSampleType>
|
||||
void JUCE_VECTOR_CALLTYPE addProductOfInternal (AudioBlock<OtherSampleType> src, NumericType factor) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::addWithMultiply (getDataPointer (ch), src.getDataPointer (ch), factor, n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void addProductOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src1.numSamples, src2.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::addWithMultiply (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
constexpr void negateInternal() const noexcept
|
||||
{
|
||||
multiplyByInternal (static_cast<NumericType> (-1.0));
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void replaceWithNegativeOfInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::negate (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename OtherSampleType>
|
||||
void replaceWithAbsoluteValueOfInternal (AudioBlock<OtherSampleType> src) const noexcept
|
||||
{
|
||||
jassert (numChannels == src.numChannels);
|
||||
auto n = static_cast<int> (jmin (numSamples, src.numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::abs (getDataPointer (ch), src.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithMinOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::min (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
template <typename Src1SampleType, typename Src2SampleType>
|
||||
void replaceWithMaxOfInternal (AudioBlock<Src1SampleType> src1, AudioBlock<Src2SampleType> src2) const noexcept
|
||||
{
|
||||
jassert (numChannels == src1.numChannels && src1.numChannels == src2.numChannels);
|
||||
auto n = static_cast<int> (jmin (src1.numSamples, src2.numSamples, numSamples) * sizeFactor);
|
||||
|
||||
for (size_t ch = 0; ch < numChannels; ++ch)
|
||||
FloatVectorOperations::max (getDataPointer (ch), src1.getDataPointer (ch), src2.getDataPointer (ch), n);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
using ChannelCountType = unsigned int;
|
||||
|
||||
//==============================================================================
|
||||
static constexpr size_t sizeFactor = sizeof (SampleType) / sizeof (NumericType);
|
||||
static constexpr size_t elementMask = sizeFactor - 1;
|
||||
static constexpr size_t byteMask = (sizeFactor * sizeof (NumericType)) - 1;
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
static constexpr size_t defaultAlignment = sizeof (SIMDRegister<NumericType>);
|
||||
#else
|
||||
static constexpr size_t defaultAlignment = sizeof (NumericType);
|
||||
#endif
|
||||
|
||||
SampleType* const* channels;
|
||||
ChannelCountType numChannels = 0;
|
||||
size_t startSample = 0, numSamples = 0;
|
||||
|
||||
template <typename OtherSampleType>
|
||||
friend class AudioBlock;
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
503
deps/juce/modules/juce_dsp/containers/juce_AudioBlock_test.cpp
vendored
Normal file
503
deps/juce/modules/juce_dsp/containers/juce_AudioBlock_test.cpp
vendored
Normal file
@ -0,0 +1,503 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
template <typename SampleType>
|
||||
class AudioBlockUnitTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
|
||||
|
||||
AudioBlockUnitTests()
|
||||
: UnitTest ("AudioBlock", UnitTestCategories::dsp)
|
||||
{
|
||||
for (auto v : { &data, &otherData })
|
||||
for (auto& channel : *v)
|
||||
channel = allocateAlignedMemory (numSamples);
|
||||
|
||||
block = { data.data(), data.size(), (size_t) numSamples };
|
||||
otherBlock = { otherData.data(), otherData.size(), (size_t) numSamples };
|
||||
|
||||
resetBlocks();
|
||||
}
|
||||
|
||||
~AudioBlockUnitTests() override
|
||||
{
|
||||
for (auto v : { &data, &otherData })
|
||||
for (auto channel : *v)
|
||||
deallocateAlignedMemory (channel);
|
||||
}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Equality");
|
||||
{
|
||||
expect (block == block);
|
||||
expect (block != otherBlock);
|
||||
}
|
||||
|
||||
beginTest ("Constructors");
|
||||
{
|
||||
expect (block == AudioBlock<SampleType> (data.data(), data.size(), numSamples));
|
||||
expect (block == AudioBlock<SampleType> (data.data(), data.size(), (size_t) 0, numSamples));
|
||||
expect (block == AudioBlock<SampleType> (block));
|
||||
|
||||
expect (block == AudioBlock<const SampleType> (data.data(), data.size(), numSamples));
|
||||
expect (block == AudioBlock<const SampleType> (data.data(), data.size(), (size_t) 0, numSamples));
|
||||
expect (block == AudioBlock<const SampleType> (block));
|
||||
}
|
||||
|
||||
beginTest ("Swap");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
expect (block != otherBlock);
|
||||
expect (block.getSample (0, 0) == SampleType (1.0));
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (otherBlock.getSample (0, 0) == SampleType (-1.0));
|
||||
expect (otherBlock.getSample (0, 3) == SampleType (-4.0));
|
||||
|
||||
block.swap (otherBlock);
|
||||
|
||||
expect (block != otherBlock);
|
||||
expect (otherBlock.getSample (0, 0) == SampleType (1.0));
|
||||
expect (otherBlock.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (0, 0) == SampleType (-1.0));
|
||||
expect (block.getSample (0, 3) == SampleType (-4.0));
|
||||
|
||||
block.swap (otherBlock);
|
||||
|
||||
expect (block.getSample (0, 0) == SampleType (1.0));
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (otherBlock.getSample (0, 0) == SampleType (-1.0));
|
||||
expect (otherBlock.getSample (0, 3) == SampleType (-4.0));
|
||||
}
|
||||
|
||||
beginTest ("Getters and setters");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
expectEquals ((int) block.getNumChannels(), (int) data.size());
|
||||
expectEquals ((int) block.getNumSamples(), numSamples);
|
||||
|
||||
expect (block.getChannelPointer (0)[2] == SampleType (3.0));
|
||||
block.getChannelPointer (0)[2] = SampleType (999.0);
|
||||
expect (block.getChannelPointer (0)[2] == SampleType (999.0));
|
||||
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (11.0));
|
||||
|
||||
expect (block.getSingleChannelBlock (1).getSample (0, 3) == block.getSample (1, 3));
|
||||
|
||||
expect (block.getSubsetChannelBlock (0, 2).getSample (1, 3) == block.getSample (1, 3));
|
||||
expect (block.getSubsetChannelBlock (1, 1).getSample (0, 3) == block.getSample (1, 3));
|
||||
|
||||
block.setSample (1, 1, SampleType (777.0));
|
||||
expect (block.getSample (1, 1) == SampleType (777.0));
|
||||
|
||||
block.addSample (1, 1, SampleType (1.0));
|
||||
expect (block.getSample (1, 1) == SampleType (778.0));
|
||||
}
|
||||
|
||||
beginTest ("Basic copying");
|
||||
{
|
||||
block.clear();
|
||||
expect (block.getSample (0, 2) == SampleType (0.0));
|
||||
expect (block.getSample (1, 4) == SampleType (0.0));
|
||||
|
||||
block.fill ((NumericType) 456.0);
|
||||
expect (block.getSample (0, 2) == SampleType (456.0));
|
||||
expect (block.getSample (1, 4) == SampleType (456.0));
|
||||
|
||||
block.copyFrom (otherBlock);
|
||||
expect (block != otherBlock);
|
||||
expect (block.getSample (0, 2) == otherBlock.getSample (0, 2));
|
||||
expect (block.getSample (1, 4) == otherBlock.getSample (1, 4));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
SampleType testSample1 = block.getSample (0, 2);
|
||||
SampleType testSample2 = block.getSample (1, 3);
|
||||
expect (testSample1 != block.getSample (0, 4));
|
||||
expect (testSample2 != block.getSample (1, 5));
|
||||
block.move (0, 2);
|
||||
expect (block.getSample (0, 4) == testSample1);
|
||||
expect (block.getSample (1, 5) == testSample2);
|
||||
}
|
||||
|
||||
beginTest ("Addition");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.add ((NumericType) 15.0);
|
||||
expect (block.getSample (0, 4) == SampleType (20.0));
|
||||
expect (block.getSample (1, 4) == SampleType (26.0));
|
||||
|
||||
block.add (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (15.0));
|
||||
expect (block.getSample (1, 4) == SampleType (15.0));
|
||||
|
||||
block.replaceWithSumOf (otherBlock, (NumericType) 9.0);
|
||||
expect (block.getSample (0, 4) == SampleType (4.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-2.0));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
block.replaceWithSumOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (0.0));
|
||||
expect (block.getSample (1, 4) == SampleType (0.0));
|
||||
}
|
||||
|
||||
beginTest ("Subtraction");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.subtract ((NumericType) 15.0);
|
||||
expect (block.getSample (0, 4) == SampleType (-10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-4.0));
|
||||
|
||||
block.subtract (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (7.0));
|
||||
|
||||
block.replaceWithDifferenceOf (otherBlock, (NumericType) 9.0);
|
||||
expect (block.getSample (0, 4) == SampleType (-14.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-20.0));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
block.replaceWithDifferenceOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (22.0));
|
||||
}
|
||||
|
||||
beginTest ("Multiplication");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.multiplyBy ((NumericType) 10.0);
|
||||
expect (block.getSample (0, 4) == SampleType (50.0));
|
||||
expect (block.getSample (1, 4) == SampleType (110.0));
|
||||
|
||||
block.multiplyBy (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-250.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-1210.0));
|
||||
|
||||
block.replaceWithProductOf (otherBlock, (NumericType) 3.0);
|
||||
expect (block.getSample (0, 4) == SampleType (-15.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-33.0));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
block.replaceWithProductOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-25.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-121.0));
|
||||
}
|
||||
|
||||
beginTest ("Multiply add");
|
||||
{
|
||||
resetBlocks();
|
||||
|
||||
block.addProductOf (otherBlock, (NumericType) -1.0);
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (22.0));
|
||||
|
||||
block.addProductOf (otherBlock, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (35.0));
|
||||
expect (block.getSample (1, 4) == SampleType (143.0));
|
||||
}
|
||||
|
||||
beginTest ("Negative abs min max");
|
||||
{
|
||||
resetBlocks();
|
||||
otherBlock.negate();
|
||||
|
||||
block.add (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (22.0));
|
||||
|
||||
block.replaceWithNegativeOf (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-11.0));
|
||||
|
||||
block.clear();
|
||||
otherBlock.negate();
|
||||
block.replaceWithAbsoluteValueOf (otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (11.0));
|
||||
|
||||
resetBlocks();
|
||||
block.replaceWithMinOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-11.0));
|
||||
|
||||
resetBlocks();
|
||||
block.replaceWithMaxOf (block, otherBlock);
|
||||
expect (block.getSample (0, 4) == SampleType (5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (11.0));
|
||||
|
||||
resetBlocks();
|
||||
auto range = block.findMinAndMax();
|
||||
expect (SampleType (range.getStart()) == SampleType (1.0));
|
||||
expect (SampleType (range.getEnd()) == SampleType (12.0));
|
||||
}
|
||||
|
||||
beginTest ("Operators");
|
||||
{
|
||||
resetBlocks();
|
||||
block += (NumericType) 10.0;
|
||||
expect (block.getSample (0, 4) == SampleType (15.0));
|
||||
expect (block.getSample (1, 4) == SampleType (21.0));
|
||||
block += otherBlock;
|
||||
expect (block.getSample (0, 4) == SampleType (10.0));
|
||||
expect (block.getSample (1, 4) == SampleType (10.0));
|
||||
|
||||
resetBlocks();
|
||||
block -= (NumericType) 10.0;
|
||||
expect (block.getSample (0, 4) == SampleType (-5.0));
|
||||
expect (block.getSample (1, 4) == SampleType (1.0));
|
||||
block -= otherBlock;
|
||||
expect (block.getSample (0, 4) == SampleType (0.0));
|
||||
expect (block.getSample (1, 4) == SampleType (12.0));
|
||||
|
||||
resetBlocks();
|
||||
block *= (NumericType) 10.0;
|
||||
expect (block.getSample (0, 4) == SampleType (50.0));
|
||||
expect (block.getSample (1, 4) == SampleType (110.0));
|
||||
block *= otherBlock;
|
||||
expect (block.getSample (0, 4) == SampleType (-250.0));
|
||||
expect (block.getSample (1, 4) == SampleType (-1210.0));
|
||||
}
|
||||
|
||||
beginTest ("Process");
|
||||
{
|
||||
resetBlocks();
|
||||
AudioBlock<SampleType>::process (block, otherBlock, [] (SampleType x) { return x + (NumericType) 1.0; });
|
||||
expect (otherBlock.getSample (0, 4) == SampleType (6.0));
|
||||
expect (otherBlock.getSample (1, 4) == SampleType (12.0));
|
||||
}
|
||||
|
||||
beginTest ("Copying");
|
||||
{
|
||||
resetBlocks();
|
||||
copyingTests();
|
||||
}
|
||||
|
||||
beginTest ("Smoothing");
|
||||
{
|
||||
resetBlocks();
|
||||
smoothedValueTests();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename T>
|
||||
using ScalarVoid = typename std::enable_if_t < std::is_scalar <T>::value, void>;
|
||||
|
||||
template <typename T>
|
||||
using SIMDVoid = typename std::enable_if_t <! std::is_scalar <T>::value, void>;
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SampleType>
|
||||
ScalarVoid<T> copyingTests()
|
||||
{
|
||||
auto unchangedElement1 = block.getSample (0, 4);
|
||||
auto unchangedElement2 = block.getSample (1, 1);
|
||||
|
||||
AudioBuffer<SampleType> otherBuffer (otherData.data(), (int) otherData.size(), numSamples);
|
||||
|
||||
block.copyFrom (otherBuffer, 1, 2, 2);
|
||||
|
||||
expectEquals (block.getSample (0, 4), unchangedElement1);
|
||||
expectEquals (block.getSample (1, 1), unchangedElement2);
|
||||
expectEquals (block.getSample (0, 2), otherBuffer.getSample (0, 1));
|
||||
expectEquals (block.getSample (1, 3), otherBuffer.getSample (1, 2));
|
||||
|
||||
resetBlocks();
|
||||
|
||||
unchangedElement1 = otherBuffer.getSample (0, 4);
|
||||
unchangedElement2 = otherBuffer.getSample (1, 3);
|
||||
|
||||
block.copyTo (otherBuffer, 2, 1, 2);
|
||||
|
||||
expectEquals (otherBuffer.getSample (0, 4), unchangedElement1);
|
||||
expectEquals (otherBuffer.getSample (1, 3), unchangedElement2);
|
||||
expectEquals (otherBuffer.getSample (0, 1), block.getSample (0, 2));
|
||||
expectEquals (otherBuffer.getSample (1, 2), block.getSample (1, 3));
|
||||
}
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
template <typename T = SampleType>
|
||||
SIMDVoid<T> copyingTests()
|
||||
{
|
||||
auto numSIMDElements = SIMDRegister<NumericType>::SIMDNumElements;
|
||||
AudioBuffer<NumericType> numericData ((int) block.getNumChannels(),
|
||||
(int) (block.getNumSamples() * numSIMDElements));
|
||||
|
||||
for (int c = 0; c < numericData.getNumChannels(); ++c)
|
||||
std::fill_n (numericData.getWritePointer (c), numericData.getNumSamples(), (NumericType) 1.0);
|
||||
|
||||
numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 0.127, (NumericType) 17.3);
|
||||
|
||||
auto lastUnchangedIndexBeforeCopiedRange = (int) ((numSIMDElements * 2) - 1);
|
||||
auto firstUnchangedIndexAfterCopiedRange = (int) ((numSIMDElements * 4) + 1);
|
||||
auto unchangedElement1 = numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange);
|
||||
auto unchangedElement2 = numericData.getSample (1, firstUnchangedIndexAfterCopiedRange);
|
||||
|
||||
block.copyTo (numericData, 1, 2, 2);
|
||||
|
||||
expectEquals (numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange), unchangedElement1);
|
||||
expectEquals (numericData.getSample (1, firstUnchangedIndexAfterCopiedRange), unchangedElement2);
|
||||
expect (SampleType (numericData.getSample (0, 2 * (int) numSIMDElements)) == block.getSample (0, 1));
|
||||
expect (SampleType (numericData.getSample (1, 3 * (int) numSIMDElements)) == block.getSample (1, 2));
|
||||
|
||||
numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 15.1, (NumericType) 0.7);
|
||||
|
||||
auto unchangedSIMDElement1 = block.getSample (0, 1);
|
||||
auto unchangedSIMDElement2 = block.getSample (1, 4);
|
||||
|
||||
block.copyFrom (numericData, 1, 2, 2);
|
||||
|
||||
expect (block.getSample (0, 1) == unchangedSIMDElement1);
|
||||
expect (block.getSample (1, 4) == unchangedSIMDElement2);
|
||||
expectEquals (block.getSample (0, 2).get (0), numericData.getSample (0, (int) numSIMDElements));
|
||||
expectEquals (block.getSample (1, 3).get (0), numericData.getSample (1, (int) (numSIMDElements * 2)));
|
||||
|
||||
if (numSIMDElements > 1)
|
||||
{
|
||||
expectEquals (block.getSample (0, 2).get (1), numericData.getSample (0, (int) (numSIMDElements + 1)));
|
||||
expectEquals (block.getSample (1, 3).get (1), numericData.getSample (1, (int) ((numSIMDElements * 2) + 1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
template <typename T = SampleType>
|
||||
ScalarVoid<T> smoothedValueTests()
|
||||
{
|
||||
block.fill ((SampleType) 1.0);
|
||||
SmoothedValue<SampleType> sv { (SampleType) 1.0 };
|
||||
sv.reset (1, 4);
|
||||
sv.setTargetValue ((SampleType) 0.0);
|
||||
|
||||
block.multiplyBy (sv);
|
||||
expect (block.getSample (0, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (1, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (0, 2) > (SampleType) 0.0);
|
||||
expect (block.getSample (1, 2) > (SampleType) 0.0);
|
||||
expectEquals (block.getSample (0, 5), (SampleType) 0.0);
|
||||
expectEquals (block.getSample (1, 5), (SampleType) 0.0);
|
||||
|
||||
sv.setCurrentAndTargetValue (-1.0f);
|
||||
sv.setTargetValue (0.0f);
|
||||
otherBlock.fill (-1.0f);
|
||||
block.replaceWithProductOf (otherBlock, sv);
|
||||
expect (block.getSample (0, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (1, 2) < (SampleType) 1.0);
|
||||
expect (block.getSample (0, 2) > (SampleType) 0.0);
|
||||
expect (block.getSample (1, 2) > (SampleType) 0.0);
|
||||
expectEquals (block.getSample (0, 5), (SampleType) 0.0);
|
||||
expectEquals (block.getSample (1, 5), (SampleType) 0.0);
|
||||
}
|
||||
|
||||
template <typename T = SampleType>
|
||||
SIMDVoid<T> smoothedValueTests() {}
|
||||
|
||||
//==============================================================================
|
||||
void resetBlocks()
|
||||
{
|
||||
auto value = SampleType (1.0);
|
||||
|
||||
for (size_t c = 0; c < block.getNumChannels(); ++c)
|
||||
{
|
||||
for (size_t i = 0; i < block.getNumSamples(); ++i)
|
||||
{
|
||||
block.setSample ((int) c, (int) i, value);
|
||||
value += SampleType (1.0);
|
||||
}
|
||||
}
|
||||
|
||||
otherBlock.replaceWithNegativeOf (block);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static SampleType* allocateAlignedMemory (int numSamplesToAllocate)
|
||||
{
|
||||
auto alignmentLowerBound = std::alignment_of<SampleType>::value;
|
||||
#if ! JUCE_WINDOWS
|
||||
alignmentLowerBound = jmax (sizeof (void*), alignmentLowerBound);
|
||||
#endif
|
||||
auto alignmentOrder = std::ceil (std::log2 (alignmentLowerBound));
|
||||
auto requiredAlignment = (size_t) std::pow (2, alignmentOrder);
|
||||
|
||||
auto size = (size_t) numSamplesToAllocate * sizeof (SampleType);
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
auto* memory = _aligned_malloc (size, requiredAlignment);
|
||||
#else
|
||||
void* memory;
|
||||
auto result = posix_memalign (&memory, requiredAlignment, size);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return static_cast<SampleType*> (memory);
|
||||
}
|
||||
|
||||
void deallocateAlignedMemory (void* address)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
_aligned_free (address);
|
||||
#else
|
||||
free (address);
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int numChannels = 2, numSamples = 6;
|
||||
std::array<SampleType*, numChannels> data, otherData;
|
||||
AudioBlock<SampleType> block, otherBlock;
|
||||
};
|
||||
|
||||
static AudioBlockUnitTests<float> audioBlockFloatUnitTests;
|
||||
static AudioBlockUnitTests<double> audioBlockDoubleUnitTests;
|
||||
|
||||
#if JUCE_USE_SIMD
|
||||
static AudioBlockUnitTests<SIMDRegister<float>> audioBlockSIMDFloatUnitTests;
|
||||
static AudioBlockUnitTests<SIMDRegister<double>> audioBlockSIMDDoubleUnitTests;
|
||||
#endif
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
242
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction.h
vendored
Normal file
242
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction.h
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename Ret, typename... Args>
|
||||
struct Vtable
|
||||
{
|
||||
using Storage = void*;
|
||||
|
||||
using Move = void (*) (Storage, Storage);
|
||||
using Call = Ret (*) (Storage, Args...);
|
||||
using Clear = void (*) (Storage);
|
||||
|
||||
constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept
|
||||
: move (moveIn), call (callIn), clear (clearIn) {}
|
||||
|
||||
Move move = nullptr;
|
||||
Call call = nullptr;
|
||||
Clear clear = nullptr;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void move (void* from, void* to)
|
||||
{
|
||||
new (to) Fn (std::move (*reinterpret_cast<Fn*> (from)));
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
(*reinterpret_cast<Fn*> (s)) (args...);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
typename std::enable_if<! std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
||||
{
|
||||
return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void clear (void* s)
|
||||
{
|
||||
auto& fn = *reinterpret_cast<Fn*> (s);
|
||||
fn.~Fn();
|
||||
// I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
|
||||
ignoreUnused (fn);
|
||||
}
|
||||
|
||||
template <typename Fn, typename Ret, typename... Args>
|
||||
constexpr Vtable<Ret, Args...> makeVtable()
|
||||
{
|
||||
return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <size_t len, typename T>
|
||||
class FixedSizeFunction;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
A type similar to `std::function` that holds a callable object.
|
||||
|
||||
Unlike `std::function`, the callable object will always be stored in
|
||||
a buffer of size `len` that is internal to the FixedSizeFunction instance.
|
||||
This in turn means that creating a FixedSizeFunction instance will never allocate,
|
||||
making FixedSizeFunctions suitable for use in realtime contexts.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <size_t len, typename Ret, typename... Args>
|
||||
class FixedSizeFunction<len, Ret (Args...)>
|
||||
{
|
||||
private:
|
||||
using Storage = typename std::aligned_storage<len>::type;
|
||||
|
||||
template <typename Item>
|
||||
using Decay = typename std::decay<Item>::type;
|
||||
|
||||
template <typename Item, typename Fn = Decay<Item>>
|
||||
using IntIfValidConversion = typename std::enable_if<sizeof (Fn) <= len
|
||||
&& alignof (Fn) <= alignof (Storage)
|
||||
&& ! std::is_same<FixedSizeFunction, Fn>::value,
|
||||
int>::type;
|
||||
|
||||
public:
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction() noexcept = default;
|
||||
|
||||
/** Create an empty function. */
|
||||
FixedSizeFunction (std::nullptr_t) noexcept
|
||||
: FixedSizeFunction() {}
|
||||
|
||||
FixedSizeFunction (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Forwards the passed Callable into the internal storage buffer. */
|
||||
template <typename Callable,
|
||||
typename Fn = Decay<Callable>,
|
||||
IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction (Callable&& callable)
|
||||
{
|
||||
static_assert (sizeof (Fn) <= len,
|
||||
"The requested function cannot fit in this FixedSizeFunction");
|
||||
static_assert (alignof (Fn) <= alignof (Storage),
|
||||
"FixedSizeFunction cannot accommodate the requested alignment requirements");
|
||||
|
||||
static constexpr auto vtableForCallable = detail::makeVtable<Fn, Ret, Args...>();
|
||||
vtable = &vtableForCallable;
|
||||
|
||||
auto* ptr = new (&storage) Fn (std::forward<Callable> (callable));
|
||||
jassertquiet ((void*) ptr == (void*) &storage);
|
||||
}
|
||||
|
||||
/** Move constructor. */
|
||||
FixedSizeFunction (FixedSizeFunction&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Converting constructor from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
: vtable (other.vtable)
|
||||
{
|
||||
move (std::move (other));
|
||||
}
|
||||
|
||||
/** Nulls this instance. */
|
||||
FixedSizeFunction& operator= (std::nullptr_t) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction();
|
||||
}
|
||||
|
||||
FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
|
||||
|
||||
/** Assigns a new callable to this instance. */
|
||||
template <typename Callable, IntIfValidConversion<Callable> = 0>
|
||||
FixedSizeFunction& operator= (Callable&& callable)
|
||||
{
|
||||
return *this = FixedSizeFunction (std::forward<Callable> (callable));
|
||||
}
|
||||
|
||||
/** Move assignment from smaller FixedSizeFunctions. */
|
||||
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
|
||||
FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
|
||||
{
|
||||
return *this = FixedSizeFunction (std::move (other));
|
||||
}
|
||||
|
||||
/** Move assignment operator. */
|
||||
FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
|
||||
{
|
||||
clear();
|
||||
vtable = other.vtable;
|
||||
move (std::move (other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~FixedSizeFunction() noexcept { clear(); }
|
||||
|
||||
/** If this instance is currently storing a callable object, calls that object,
|
||||
otherwise throws `std::bad_function_call`.
|
||||
*/
|
||||
Ret operator() (Args... args) const
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
return vtable->call (&storage, std::forward<Args> (args)...);
|
||||
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
/** Returns true if this instance currently holds a callable. */
|
||||
explicit operator bool() const noexcept { return vtable != nullptr; }
|
||||
|
||||
private:
|
||||
template <size_t, typename>
|
||||
friend class FixedSizeFunction;
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->clear (&storage);
|
||||
}
|
||||
|
||||
template <size_t otherLen, typename T>
|
||||
void move (FixedSizeFunction<otherLen, T>&& other) noexcept
|
||||
{
|
||||
if (vtable != nullptr)
|
||||
vtable->move (&other.storage, &storage);
|
||||
}
|
||||
|
||||
const detail::Vtable<Ret, Args...>* vtable = nullptr;
|
||||
mutable Storage storage;
|
||||
};
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
|
||||
|
||||
template <size_t len, typename T>
|
||||
bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
|
||||
|
||||
|
||||
}
|
||||
}
|
352
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction_test.cpp
vendored
Normal file
352
deps/juce/modules/juce_dsp/containers/juce_FixedSizeFunction_test.cpp
vendored
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_ENABLE_ALLOCATION_HOOKS
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this)
|
||||
#else
|
||||
#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
class ConstructCounts
|
||||
{
|
||||
auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); }
|
||||
|
||||
public:
|
||||
int constructions = 0;
|
||||
int copies = 0;
|
||||
int moves = 0;
|
||||
int calls = 0;
|
||||
int destructions = 0;
|
||||
|
||||
ConstructCounts withConstructions (int i) const noexcept { auto c = *this; c.constructions = i; return c; }
|
||||
ConstructCounts withCopies (int i) const noexcept { auto c = *this; c.copies = i; return c; }
|
||||
ConstructCounts withMoves (int i) const noexcept { auto c = *this; c.moves = i; return c; }
|
||||
ConstructCounts withCalls (int i) const noexcept { auto c = *this; c.calls = i; return c; }
|
||||
ConstructCounts withDestructions (int i) const noexcept { auto c = *this; c.destructions = i; return c; }
|
||||
|
||||
bool operator== (const ConstructCounts& other) const noexcept { return tie() == other.tie(); }
|
||||
bool operator!= (const ConstructCounts& other) const noexcept { return tie() != other.tie(); }
|
||||
};
|
||||
|
||||
String& operator<< (String& str, const ConstructCounts& c)
|
||||
{
|
||||
return str << "{ constructions: " << c.constructions
|
||||
<< ", copies: " << c.copies
|
||||
<< ", moves: " << c.moves
|
||||
<< ", calls: " << c.calls
|
||||
<< ", destructions: " << c.destructions
|
||||
<< " }";
|
||||
}
|
||||
|
||||
class FixedSizeFunctionTest : public UnitTest
|
||||
{
|
||||
static void toggleBool (bool& b) { b = ! b; }
|
||||
|
||||
struct ConstructCounter
|
||||
{
|
||||
explicit ConstructCounter (ConstructCounts& countsIn)
|
||||
: counts (countsIn) {}
|
||||
|
||||
ConstructCounter (const ConstructCounter& c)
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.copies += 1;
|
||||
}
|
||||
|
||||
ConstructCounter (ConstructCounter&& c) noexcept
|
||||
: counts (c.counts)
|
||||
{
|
||||
counts.moves += 1;
|
||||
}
|
||||
|
||||
~ConstructCounter() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
public:
|
||||
FixedSizeFunctionTest()
|
||||
: UnitTest ("Fixed Size Function", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Can be constructed and called from a lambda");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
const auto result = 5;
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return result; };
|
||||
|
||||
const FixedSizeFunction<sizeof (lambda), int()> fn (lambda);
|
||||
const auto out = fn();
|
||||
|
||||
expect (wasCalled);
|
||||
expectEquals (result, out);
|
||||
}
|
||||
|
||||
beginTest ("void fn can be constructed from function with return value");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool wasCalled = false;
|
||||
const auto lambda = [&] { wasCalled = true; return 5; };
|
||||
const FixedSizeFunction<sizeof (lambda), void()> fn (lambda);
|
||||
|
||||
fn();
|
||||
expect (wasCalled);
|
||||
}
|
||||
|
||||
beginTest ("Can be constructed and called from a function pointer");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
bool state = false;
|
||||
|
||||
const FixedSizeFunction<sizeof (void*), void (bool&)> fn (toggleBool);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
|
||||
fn (state);
|
||||
expect (! state);
|
||||
|
||||
fn (state);
|
||||
expect (state);
|
||||
}
|
||||
|
||||
beginTest ("Default constructed functions throw if called");
|
||||
{
|
||||
const auto a = FixedSizeFunction<8, void()>();
|
||||
expectThrowsType (a(), std::bad_function_call)
|
||||
|
||||
const auto b = FixedSizeFunction<8, void()> (nullptr);
|
||||
expectThrowsType (b(), std::bad_function_call)
|
||||
}
|
||||
|
||||
beginTest ("Functions can be moved");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (ConstructCounter { counts });
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed
|
||||
|
||||
a();
|
||||
expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
|
||||
|
||||
const auto b = std::move (a);
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
|
||||
|
||||
b();
|
||||
expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
|
||||
}
|
||||
|
||||
beginTest ("Functions are destructed properly");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
ConstructCounts counts;
|
||||
const ConstructCounter toCopy { counts };
|
||||
|
||||
{
|
||||
auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (toCopy);
|
||||
expectEquals (counts, ConstructCounts().withCopies (1));
|
||||
}
|
||||
|
||||
expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Avoid destructing functions that fail to construct");
|
||||
{
|
||||
struct BadConstructor
|
||||
{
|
||||
explicit BadConstructor (ConstructCounts& c)
|
||||
: counts (c)
|
||||
{
|
||||
counts.constructions += 1;
|
||||
throw std::runtime_error { "this was meant to happen" };
|
||||
}
|
||||
|
||||
~BadConstructor() noexcept { counts.destructions += 1; }
|
||||
|
||||
void operator()() const noexcept { counts.calls += 1; }
|
||||
|
||||
ConstructCounts& counts;
|
||||
};
|
||||
|
||||
ConstructCounts counts;
|
||||
|
||||
expectThrowsType ((FixedSizeFunction<sizeof (BadConstructor), void()> (BadConstructor { counts })),
|
||||
std::runtime_error)
|
||||
|
||||
expectEquals (counts, ConstructCounts().withConstructions (1));
|
||||
}
|
||||
|
||||
beginTest ("Equality checks work");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> a;
|
||||
expect (! bool (a));
|
||||
expect (a == nullptr);
|
||||
expect (nullptr == a);
|
||||
expect (! (a != nullptr));
|
||||
expect (! (nullptr != a));
|
||||
|
||||
FixedSizeFunction<8, void()> b ([] {});
|
||||
expect (bool (b));
|
||||
expect (b != nullptr);
|
||||
expect (nullptr != b);
|
||||
expect (! (b == nullptr));
|
||||
expect (! (nullptr == b));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be cleared");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
FixedSizeFunction<8, void()> fn ([] {});
|
||||
expect (bool (fn));
|
||||
|
||||
fn = nullptr;
|
||||
expect (! bool (fn));
|
||||
}
|
||||
|
||||
beginTest ("Functions can be assigned");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<8, void()>;
|
||||
|
||||
int numCallsA = 0;
|
||||
int numCallsB = 0;
|
||||
|
||||
Fn x;
|
||||
Fn y;
|
||||
expect (! bool (x));
|
||||
expect (! bool (y));
|
||||
|
||||
x = [&] { numCallsA += 1; };
|
||||
y = [&] { numCallsB += 1; };
|
||||
expect (bool (x));
|
||||
expect (bool (y));
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 0);
|
||||
|
||||
y();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x = std::move (y);
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCallsA, 1);
|
||||
expectEquals (numCallsB, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions may mutate internal state");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, void()>;
|
||||
|
||||
Fn x;
|
||||
expect (! bool (x));
|
||||
|
||||
int numCalls = 0;
|
||||
x = [&numCalls, counter = 0]() mutable { counter += 1; numCalls = counter; };
|
||||
expect (bool (x));
|
||||
|
||||
expectEquals (numCalls, 0);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 1);
|
||||
|
||||
x();
|
||||
expectEquals (numCalls, 2);
|
||||
}
|
||||
|
||||
beginTest ("Functions can sink move-only parameters");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using Fn = FixedSizeFunction<64, int (std::unique_ptr<int>)>;
|
||||
|
||||
auto value = 5;
|
||||
auto ptr = std::make_unique<int> (value);
|
||||
|
||||
Fn fn = [] (std::unique_ptr<int> p) { return *p; };
|
||||
|
||||
expect (value == fn (std::move (ptr)));
|
||||
}
|
||||
|
||||
beginTest ("Functions be converted from smaller functions");
|
||||
{
|
||||
JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
|
||||
|
||||
using SmallFn = FixedSizeFunction<20, void()>;
|
||||
using LargeFn = FixedSizeFunction<21, void()>;
|
||||
|
||||
bool smallCalled = false;
|
||||
bool largeCalled = false;
|
||||
|
||||
SmallFn small = [&smallCalled, a = std::array<char, 8>{}] { smallCalled = true; juce::ignoreUnused (a); };
|
||||
LargeFn large = [&largeCalled, a = std::array<char, 8>{}] { largeCalled = true; juce::ignoreUnused (a); };
|
||||
|
||||
large = std::move (small);
|
||||
|
||||
large();
|
||||
|
||||
expect (smallCalled);
|
||||
expect (! largeCalled);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FixedSizeFunctionTest fixedSizedFunctionTest;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
|
403
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister.h
vendored
Normal file
403
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister.h
vendored
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// This class is needed internally.
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A wrapper around the platform's native SIMD register type.
|
||||
|
||||
This class is only available on SIMD machines. Use JUCE_USE_SIMD to query
|
||||
if SIMD is available for your system.
|
||||
|
||||
SIMDRegister<Type> is a templated class representing the native
|
||||
vectorized version of FloatingType. SIMDRegister supports all numerical
|
||||
primitive types and std:complex<float> and std::complex<double> supports
|
||||
and most operations of the corresponding primitive
|
||||
type. Additionally, SIMDRegister can be accessed like an array to extract
|
||||
the individual elements.
|
||||
|
||||
If you are using SIMDRegister as a pointer, then you must ensure that the
|
||||
memory is sufficiently aligned for SIMD vector operations. Failing to do so
|
||||
will result in crashes or very slow code. Use SIMDRegister::isSIMDAligned
|
||||
to query if a pointer is sufficiently aligned for SIMD vector operations.
|
||||
|
||||
Note that using SIMDRegister without enabling optimizations will result
|
||||
in code with very poor performance.
|
||||
|
||||
@tags{DSP}
|
||||
*/
|
||||
template <typename Type>
|
||||
struct SIMDRegister
|
||||
{
|
||||
//==============================================================================
|
||||
/** The type that represents the individual constituents of the SIMD Register */
|
||||
using ElementType = Type;
|
||||
|
||||
/** STL compatible value_type definition (same as ElementType). */
|
||||
using value_type = ElementType;
|
||||
|
||||
/** The corresponding primitive integer type, for example, this will be int32_t
|
||||
if type is a float. */
|
||||
using MaskType = typename SIMDInternal::MaskTypeFor<ElementType>::type;
|
||||
|
||||
//==============================================================================
|
||||
// Here are some types which are needed internally
|
||||
|
||||
/** The native primitive type (used internally). */
|
||||
using PrimitiveType = typename SIMDInternal::PrimitiveType<ElementType>::type;
|
||||
|
||||
/** The native operations for this platform and type combination (used internally) */
|
||||
using NativeOps = SIMDNativeOps<PrimitiveType>;
|
||||
|
||||
/** The native type (used internally). */
|
||||
using vSIMDType = typename NativeOps::vSIMDType;
|
||||
|
||||
/** The corresponding integer SIMDRegister type (used internally). */
|
||||
using vMaskType = SIMDRegister<MaskType>;
|
||||
|
||||
/** The internal native type for the corresponding mask type (used internally). */
|
||||
using vMaskSIMDType = typename vMaskType::vSIMDType;
|
||||
|
||||
/** Wrapper for operations which need to be handled differently for complex
|
||||
and scalar types (used internally). */
|
||||
using CmplxOps = CmplxSIMDOps<ElementType>;
|
||||
|
||||
/** Type which is returned when using the subscript operator. The returned type
|
||||
should be used just like the type ElementType. */
|
||||
struct ElementAccess;
|
||||
|
||||
//==============================================================================
|
||||
/** The size in bytes of this register. */
|
||||
static constexpr size_t SIMDRegisterSize = sizeof (vSIMDType);
|
||||
|
||||
/** The number of elements that this vector can hold. */
|
||||
static constexpr size_t SIMDNumElements = SIMDRegisterSize / sizeof (ElementType);
|
||||
|
||||
vSIMDType value;
|
||||
|
||||
/** Default constructor. */
|
||||
inline SIMDRegister() noexcept = default;
|
||||
|
||||
/** Constructs an object from the native SIMD type. */
|
||||
inline SIMDRegister (vSIMDType a) noexcept : value (a) {}
|
||||
|
||||
/** Constructs an object from a scalar type by broadcasting it to all elements. */
|
||||
inline SIMDRegister (Type s) noexcept { *this = s; }
|
||||
|
||||
/** Destructor. */
|
||||
inline ~SIMDRegister() noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of elements in this vector. */
|
||||
static constexpr size_t size() noexcept { return SIMDNumElements; }
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a new SIMDRegister from the corresponding scalar primitive.
|
||||
The scalar is extended to all elements of the vector. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE expand (ElementType s) noexcept { return {CmplxOps::expand (s)}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the internal SIMD type (for example
|
||||
__mm128 for single-precision floating point on SSE architectures). */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromNative (vSIMDType a) noexcept { return {a}; }
|
||||
|
||||
/** Creates a new SIMDRegister from the first SIMDNumElements of a scalar array. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE fromRawArray (const ElementType* a) noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
return {CmplxOps::load (a)};
|
||||
}
|
||||
|
||||
/** Copies the elements of the SIMDRegister to a scalar array in memory. */
|
||||
inline void JUCE_VECTOR_CALLTYPE copyToRawArray (ElementType* a) const noexcept
|
||||
{
|
||||
jassert (isSIMDAligned (a));
|
||||
CmplxOps::store (value, a);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE get (size_t idx) const noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return CmplxOps::get (value, idx);
|
||||
}
|
||||
|
||||
/** Sets the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline void JUCE_VECTOR_CALLTYPE set (size_t idx, ElementType v) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
value = CmplxOps::set (value, idx, v);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementType JUCE_VECTOR_CALLTYPE operator[] (size_t idx) const noexcept
|
||||
{
|
||||
return get (idx);
|
||||
}
|
||||
|
||||
/** Returns the idx-th element of the receiver. Note that this does not check if idx
|
||||
is larger than the native register size. */
|
||||
inline ElementAccess JUCE_VECTOR_CALLTYPE operator[] (size_t idx) noexcept
|
||||
{
|
||||
jassert (idx < SIMDNumElements);
|
||||
return ElementAccess (*this, idx);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Adds another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (SIMDRegister v) noexcept { value = NativeOps::add (value, v.value); return *this; }
|
||||
|
||||
/** Subtracts another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (SIMDRegister v) noexcept { value = NativeOps::sub (value, v.value); return *this; }
|
||||
|
||||
/** Multiplies another SIMDRegister to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (SIMDRegister v) noexcept { value = CmplxOps::mul (value, v.value); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Broadcasts the scalar to all elements of the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator= (ElementType s) noexcept { value = CmplxOps::expand (s); return *this; }
|
||||
|
||||
/** Adds a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator+= (ElementType s) noexcept { value = NativeOps::add (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Subtracts a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator-= (ElementType s) noexcept { value = NativeOps::sub (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
/** Multiplies a scalar to the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator*= (ElementType s) noexcept { value = CmplxOps::mul (value, CmplxOps::expand (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (vMaskType v) noexcept { value = NativeOps::bit_and (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-or the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (vMaskType v) noexcept { value = NativeOps::bit_or (value, toVecType (v.value)); return *this; }
|
||||
|
||||
/** Bit-xor the receiver with SIMDRegister v and store the result in the receiver. */
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (vMaskType v) noexcept { value = NativeOps::bit_xor (value, toVecType (v.value)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Bit-and each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator&= (MaskType s) noexcept { value = NativeOps::bit_and (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-or each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator|= (MaskType s) noexcept { value = NativeOps::bit_or (value, toVecType (s)); return *this; }
|
||||
|
||||
/** Bit-xor each element of the receiver with the scalar s and store the result in the receiver.*/
|
||||
inline SIMDRegister& JUCE_VECTOR_CALLTYPE operator^= (MaskType s) noexcept { value = NativeOps::bit_xor (value, toVecType (s)); return *this; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the sum of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (SIMDRegister v) const noexcept { return { NativeOps::add (value, v.value) }; }
|
||||
|
||||
/** Returns the difference of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (SIMDRegister v) const noexcept { return { NativeOps::sub (value, v.value) }; }
|
||||
|
||||
/** Returns the product of the receiver and v.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (SIMDRegister v) const noexcept { return { CmplxOps::mul (value, v.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the sum of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator+ (ElementType s) const noexcept { return { NativeOps::add (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the difference of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator- (ElementType s) const noexcept { return { NativeOps::sub (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the product of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator* (ElementType s) const noexcept { return { CmplxOps::mul (value, CmplxOps::expand (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the bit-and of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (vMaskType v) const noexcept { return { NativeOps::bit_and (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-or of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (vMaskType v) const noexcept { return { NativeOps::bit_or (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns the bit-xor of the receiver and v. */
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (vMaskType v) const noexcept { return { NativeOps::bit_xor (value, toVecType (v.value)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-inverted value of the corresponding element in the receiver.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator~() const noexcept { return { NativeOps::bit_not (value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a vector where each element is the bit-and'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator& (MaskType s) const noexcept { return { NativeOps::bit_and (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-or'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator| (MaskType s) const noexcept { return { NativeOps::bit_or (value, toVecType (s)) }; }
|
||||
|
||||
/** Returns a vector where each element is the bit-xor'd value of the corresponding element in the receiver and the scalar s.*/
|
||||
inline SIMDRegister JUCE_VECTOR_CALLTYPE operator^ (MaskType s) const noexcept { return { NativeOps::bit_xor (value, toVecType (s)) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if all element-wise comparisons return true. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (SIMDRegister other) const noexcept { return NativeOps::allEqual (value, other.value); }
|
||||
|
||||
/** Returns true if any element-wise comparisons return false. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (SIMDRegister other) const noexcept { return ! (*this == other); }
|
||||
|
||||
/** Returns true if all elements are equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator== (Type s) const noexcept { return *this == SIMDRegister::expand (s); }
|
||||
|
||||
/** Returns true if any elements are not equal to the scalar. */
|
||||
inline bool JUCE_VECTOR_CALLTYPE operator!= (Type s) const noexcept { return ! (*this == s); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE equal (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::equal (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is not equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE notEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::notEqual (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is less than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE lessThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (b.value, a.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThan (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThan (a.value, b.value)); }
|
||||
|
||||
/** Returns a SIMDRegister of the corresponding integral type where each element has each bit set
|
||||
if the corresponding element of a is greater than or equal to the corresponding element of b, or zero otherwise.
|
||||
The result can then be used in bit operations defined above to avoid branches in vector SIMD code. */
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE greaterThanOrEqual (SIMDRegister a, SIMDRegister b) noexcept { return toMaskType (NativeOps::greaterThanOrEqual (a.value, b.value)); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a new vector where each element is the minimum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE min (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::min (a.value, b.value) }; }
|
||||
|
||||
/** Returns a new vector where each element is the maximum of the corresponding element of a and b. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE max (SIMDRegister a, SIMDRegister b) noexcept { return { NativeOps::max (a.value, b.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Multiplies b and c and adds the result to a. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE multiplyAdd (SIMDRegister a, const SIMDRegister b, SIMDRegister c) noexcept
|
||||
{
|
||||
return { CmplxOps::muladd (a.value, b.value, c.value) };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a scalar which is the sum of all elements of the receiver. */
|
||||
inline ElementType sum() const noexcept { return CmplxOps::sum (value); }
|
||||
|
||||
//==============================================================================
|
||||
/** Truncates each element to its integer part.
|
||||
Effectively discards the fractional part of each element. A.k.a. round to zero. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE truncate (SIMDRegister a) noexcept { return { NativeOps::truncate (a.value) }; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the absolute value of each element. */
|
||||
static SIMDRegister JUCE_VECTOR_CALLTYPE abs (SIMDRegister a) noexcept
|
||||
{
|
||||
return a - (a * (expand (ElementType (2)) & lessThan (a, expand (ElementType (0)))));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Checks if the given pointer is sufficiently aligned for using SIMD operations. */
|
||||
static bool isSIMDAligned (const ElementType* ptr) noexcept
|
||||
{
|
||||
uintptr_t bitmask = SIMDRegisterSize - 1;
|
||||
return (reinterpret_cast<uintptr_t> (ptr) & bitmask) == 0;
|
||||
}
|
||||
|
||||
/** Returns the next position in memory where isSIMDAligned returns true.
|
||||
|
||||
If the current position in memory is already aligned then this method
|
||||
will simply return the pointer.
|
||||
*/
|
||||
static ElementType* getNextSIMDAlignedPtr (ElementType* ptr) noexcept
|
||||
{
|
||||
return snapPointerToAlignment (ptr, SIMDRegisterSize);
|
||||
}
|
||||
|
||||
private:
|
||||
static vMaskType JUCE_VECTOR_CALLTYPE toMaskType (vSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vSIMDType in;
|
||||
vMaskSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return vMaskType::fromNative (u.out);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (vMaskSIMDType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = a;
|
||||
return u.out;
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE toVecType (MaskType a) noexcept
|
||||
{
|
||||
union
|
||||
{
|
||||
vMaskSIMDType in;
|
||||
vSIMDType out;
|
||||
} u;
|
||||
|
||||
u.in = CmplxSIMDOps<MaskType>::expand (a);
|
||||
return u.out;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#include "juce_SIMDRegister_Impl.h"
|
||||
#endif
|
177
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h
vendored
Normal file
177
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_Impl.h
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
struct SIMDRegister<Type>::ElementAccess
|
||||
{
|
||||
operator Type() const { return simd.get (idx); }
|
||||
ElementAccess& operator= (Type scalar) noexcept { simd.set (idx, scalar); return *this; }
|
||||
ElementAccess& operator= (ElementAccess& o) noexcept { return operator= ((Type) o); }
|
||||
|
||||
private:
|
||||
friend struct SIMDRegister;
|
||||
ElementAccess (SIMDRegister& owner, size_t index) noexcept : simd (owner), idx (index) {}
|
||||
SIMDRegister& simd;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
//==============================================================================
|
||||
/* This class is used internally by SIMDRegister to abstract away differences
|
||||
in operations which are different for complex and pure floating point types. */
|
||||
|
||||
// the pure floating-point version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const Scalar* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (a);
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, Scalar* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, dest);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::expand (s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::get (v, i);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, Scalar s) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::set (v, i, s);
|
||||
}
|
||||
|
||||
static Scalar JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::sum (a);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::mul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::multiplyAdd (a, b, c);
|
||||
}
|
||||
};
|
||||
|
||||
// The pure complex version
|
||||
template <typename Scalar>
|
||||
struct CmplxSIMDOps<std::complex<Scalar>>
|
||||
{
|
||||
using vSIMDType = typename SIMDNativeOps<Scalar>::vSIMDType;
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE load (const std::complex<Scalar>* a) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::load (reinterpret_cast<const Scalar*> (a));
|
||||
}
|
||||
|
||||
static void JUCE_VECTOR_CALLTYPE store (vSIMDType value, std::complex<Scalar>* dest) noexcept
|
||||
{
|
||||
SIMDNativeOps<Scalar>::store (value, reinterpret_cast<Scalar*> (dest));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE expand (std::complex<Scalar> s) noexcept
|
||||
{
|
||||
const int n = sizeof (vSIMDType) / sizeof (Scalar);
|
||||
|
||||
union
|
||||
{
|
||||
vSIMDType v;
|
||||
Scalar floats[(size_t) n];
|
||||
} u;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
u.floats[i] = (i & 1) == 0 ? s.real() : s.imag();
|
||||
|
||||
return u.v;
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE get (vSIMDType v, std::size_t i) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return std::complex<Scalar> (SIMDNativeOps<Scalar>::get (v, j), SIMDNativeOps<Scalar>::get (v, j + 1));
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE set (vSIMDType v, std::size_t i, std::complex<Scalar> s) noexcept
|
||||
{
|
||||
auto j = i << 1;
|
||||
return SIMDNativeOps<Scalar>::set (SIMDNativeOps<Scalar>::set (v, j, s.real()), j + 1, s.imag());
|
||||
}
|
||||
|
||||
static std::complex<Scalar> JUCE_VECTOR_CALLTYPE sum (vSIMDType a) noexcept
|
||||
{
|
||||
vSIMDType result = SIMDNativeOps<Scalar>::oddevensum (a);
|
||||
auto* ptr = reinterpret_cast<const Scalar*> (&result);
|
||||
return std::complex<Scalar> (ptr[0], ptr[1]);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE mul (vSIMDType a, vSIMDType b) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::cmplxmul (a, b);
|
||||
}
|
||||
|
||||
static vSIMDType JUCE_VECTOR_CALLTYPE muladd (vSIMDType a, vSIMDType b, vSIMDType c) noexcept
|
||||
{
|
||||
return SIMDNativeOps<Scalar>::add (a, SIMDNativeOps<Scalar>::cmplxmul (b, c));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace util
|
||||
{
|
||||
template <typename Type>
|
||||
inline void snapToZero (SIMDRegister<Type>&) noexcept {}
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
|
||||
// Extend some common used global functions to SIMDRegister types
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmin (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::min (a, b); }
|
||||
template <typename Type>
|
||||
inline dsp::SIMDRegister<Type> JUCE_VECTOR_CALLTYPE jmax (dsp::SIMDRegister<Type> a, dsp::SIMDRegister<Type> b) { return dsp::SIMDRegister<Type>::max (a, b); }
|
||||
|
||||
} // namespace juce
|
918
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp
vendored
Normal file
918
deps/juce/modules/juce_dsp/containers/juce_SIMDRegister_test.cpp
vendored
Normal file
@ -0,0 +1,918 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
namespace dsp
|
||||
{
|
||||
|
||||
namespace SIMDRegister_test_internal
|
||||
{
|
||||
template <typename type, typename = void> struct RandomPrimitive {};
|
||||
|
||||
template <typename type>
|
||||
struct RandomPrimitive<type, typename std::enable_if<std::is_floating_point<type>::value>::type>
|
||||
{
|
||||
static type next (Random& random)
|
||||
{
|
||||
return static_cast<type> (std::is_signed<type>::value ? (random.nextFloat() * 16.0) - 8.0
|
||||
: (random.nextFloat() * 8.0));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template <typename type>
|
||||
struct RandomPrimitive<type, typename std::enable_if<std::is_integral<type>::value>::type>
|
||||
{
|
||||
static type next (Random& random)
|
||||
{
|
||||
return static_cast<type> (random.nextInt64());
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template <typename type> struct RandomValue { static type next (Random& random) { return RandomPrimitive<type>::next (random); } };
|
||||
template <typename type>
|
||||
struct RandomValue<std::complex<type>>
|
||||
{
|
||||
static std::complex<type> next (Random& random)
|
||||
{
|
||||
return {RandomPrimitive<type>::next (random), RandomPrimitive<type>::next (random)};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename type>
|
||||
struct VecFiller
|
||||
{
|
||||
static void fill (type* dst, const int size, Random& random)
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
dst[i] = RandomValue<type>::next (random);
|
||||
}
|
||||
};
|
||||
|
||||
// We need to specialise for complex types: otherwise GCC 6 gives
|
||||
// us an ICE internal compiler error after which the compiler seg faults.
|
||||
template <typename type>
|
||||
struct VecFiller<std::complex<type>>
|
||||
{
|
||||
static void fill (std::complex<type>* dst, const int size, Random& random)
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
dst[i] = std::complex<type> (RandomValue<type>::next (random), RandomValue<type>::next (random));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename type>
|
||||
struct VecFiller<SIMDRegister<type>>
|
||||
{
|
||||
static SIMDRegister<type> fill (Random& random)
|
||||
{
|
||||
constexpr int size = (int) SIMDRegister<type>::SIMDNumElements;
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[size];
|
||||
#else
|
||||
type elements[(size_t) size] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
|
||||
VecFiller<type>::fill (elements, size, random);
|
||||
return SIMDRegister<type>::fromRawArray (elements);
|
||||
}
|
||||
};
|
||||
|
||||
// Avoid visual studio warning
|
||||
template <typename type>
|
||||
static type safeAbs (type a)
|
||||
{
|
||||
return static_cast<type> (std::abs (static_cast<double> (a)));
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static type safeAbs (std::complex<type> a)
|
||||
{
|
||||
return std::abs (a);
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static double difference (type a)
|
||||
{
|
||||
return static_cast<double> (safeAbs (a));
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static double difference (type a, type b)
|
||||
{
|
||||
return difference (a - b);
|
||||
}
|
||||
}
|
||||
|
||||
// These tests need to be strictly run on all platforms supported by JUCE as the
|
||||
// SIMD code is highly platform dependent.
|
||||
|
||||
class SIMDRegisterUnitTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
SIMDRegisterUnitTests()
|
||||
: UnitTest ("SIMDRegister UnitTests", UnitTestCategories::dsp)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
// Some helper classes
|
||||
template <typename type>
|
||||
static bool allValuesEqualTo (const SIMDRegister<type>& vec, const type scalar)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
|
||||
#else
|
||||
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
|
||||
vec.copyToRawArray (elements);
|
||||
|
||||
// as we do not want to rely on the access operator we cast this to a primitive pointer
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
if (elements[i] != scalar) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static bool vecEqualToArray (const SIMDRegister<type>& vec, const type* array)
|
||||
{
|
||||
HeapBlock<type> vecElementsStorage (SIMDRegister<type>::SIMDNumElements * 2);
|
||||
auto* ptr = SIMDRegister<type>::getNextSIMDAlignedPtr (vecElementsStorage.getData());
|
||||
vec.copyToRawArray (ptr);
|
||||
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
{
|
||||
double delta = SIMDRegister_test_internal::difference (ptr[i], array[i]);
|
||||
if (delta > 1e-4)
|
||||
{
|
||||
DBG ("a: " << SIMDRegister_test_internal::difference (ptr[i]) << " b: " << SIMDRegister_test_internal::difference (array[i]) << " difference: " << delta);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
static void copy (SIMDRegister<type>& vec, const type* ptr)
|
||||
{
|
||||
if (SIMDRegister<type>::isSIMDAligned (ptr))
|
||||
{
|
||||
vec = SIMDRegister<type>::fromRawArray (ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
vec[i] = ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Some useful operations to test
|
||||
struct Addition
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a += b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
|
||||
struct Subtraction
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a -= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
|
||||
struct Multiplication
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a *= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitAND
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a &= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a & b;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitOR
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a |= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a | b;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitXOR
|
||||
{
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static void inplace (typeOne& a, const typeTwo& b)
|
||||
{
|
||||
a ^= b;
|
||||
}
|
||||
|
||||
template <typename typeOne, typename typeTwo>
|
||||
static typeOne outofplace (const typeOne& a, const typeTwo& b)
|
||||
{
|
||||
return a ^ b;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// the individual tests
|
||||
struct InitializationTest
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
u.expect (allValuesEqualTo<type> (SIMDRegister<type>::expand (static_cast<type> (23)), 23));
|
||||
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
|
||||
#else
|
||||
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (elements, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister<type> a (SIMDRegister<type>::fromRawArray (elements));
|
||||
|
||||
u.expect (vecEqualToArray (a, elements));
|
||||
|
||||
SIMDRegister<type> b (a);
|
||||
a *= static_cast<type> (2);
|
||||
|
||||
u.expect (vecEqualToArray (b, elements));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AccessTest
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
// set-up
|
||||
SIMDRegister<type> a;
|
||||
type array [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
// Test non-const access operator
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
a[i] = array[i];
|
||||
|
||||
u.expect (vecEqualToArray (a, array));
|
||||
|
||||
// Test const access operator
|
||||
const SIMDRegister<type>& b = a;
|
||||
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
u.expect (b[i] == array[i]);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Operation>
|
||||
struct OperatorTests
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
for (int n = 0; n < 100; ++n)
|
||||
{
|
||||
// set-up
|
||||
SIMDRegister<type> a (static_cast<type> (0));
|
||||
SIMDRegister<type> b (static_cast<type> (0));
|
||||
SIMDRegister<type> c (static_cast<type> (0));
|
||||
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_c [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
copy (a, array_a); copy (b, array_b); copy (c, array_c);
|
||||
|
||||
// test in-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<type, type> (array_a[i], array_b[i]);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
copy (a, array_a); copy (b, array_b); copy (c, array_c);
|
||||
|
||||
// test in-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<type, type> (array_b[i], static_cast<type> (2));
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, type> (b, 2);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
// set-up again
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a, array_a); copy (b, array_b); copy (c, array_c);
|
||||
|
||||
// test out-of-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
array_c[i] = Operation::template outofplace<type, type> (array_a[i], array_b[i]);
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, SIMDRegister<type>> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, array_c));
|
||||
|
||||
// test out-of-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
array_c[i] = Operation::template outofplace<type, type> (array_b[i], static_cast<type> (2));
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, type> (b, 2);
|
||||
|
||||
u.expect (vecEqualToArray (a, array_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, array_c));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Operation>
|
||||
struct BitOperatorTests
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
typedef typename SIMDRegister<type>::vMaskType vMaskType;
|
||||
typedef typename SIMDRegister<type>::MaskType MaskType;
|
||||
|
||||
for (int n = 0; n < 100; ++n)
|
||||
{
|
||||
// Check flip sign bit and using as a union
|
||||
{
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
union ConversionUnion
|
||||
{
|
||||
inline ConversionUnion() : floatVersion (static_cast<type> (0)) {}
|
||||
inline ~ConversionUnion() {}
|
||||
SIMDRegister<type> floatVersion;
|
||||
vMaskType intVersion;
|
||||
} a, b;
|
||||
|
||||
vMaskType bitmask = vMaskType::expand (static_cast<MaskType> (1) << (sizeof (MaskType) - 1));
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a.floatVersion, array_a);
|
||||
copy (b.floatVersion, array_a);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, vMaskType> (a.floatVersion, bitmask);
|
||||
Operation::template inplace<vMaskType, vMaskType> (b.intVersion, bitmask);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
__declspec(align(sizeof (SIMDRegister<type>))) type elements[SIMDRegister<type>::SIMDNumElements];
|
||||
#else
|
||||
type elements[SIMDRegister<type>::SIMDNumElements] __attribute__((aligned(sizeof (SIMDRegister<type>))));
|
||||
#endif
|
||||
b.floatVersion.copyToRawArray (elements);
|
||||
|
||||
u.expect (vecEqualToArray (a.floatVersion, elements));
|
||||
}
|
||||
|
||||
// set-up
|
||||
SIMDRegister<type> a, c;
|
||||
vMaskType b;
|
||||
|
||||
MaskType array_a [SIMDRegister<MaskType>::SIMDNumElements];
|
||||
MaskType array_b [SIMDRegister<MaskType>::SIMDNumElements];
|
||||
MaskType array_c [SIMDRegister<MaskType>::SIMDNumElements];
|
||||
|
||||
type float_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type float_c [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<MaskType>::fill (array_b, SIMDRegister<MaskType>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
copy (a, float_a); copy (b, array_b); copy (c, float_c);
|
||||
|
||||
// test in-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<MaskType, MaskType> (array_a[i], array_b[i]);
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, vMaskType> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<MaskType>::fill (array_b, SIMDRegister<MaskType>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
copy (a, float_a); copy (b, array_b); copy (c, float_c);
|
||||
|
||||
// test in-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
Operation::template inplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
Operation::template inplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
|
||||
// set-up again
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<MaskType>::fill (array_b, SIMDRegister<MaskType>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (float_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
memcpy (array_a, float_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (array_c, float_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
copy (a, float_a); copy (b, array_b); copy (c, float_c);
|
||||
|
||||
// test out-of-place with both params being vectors
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
{
|
||||
array_c[i] =
|
||||
Operation::template outofplace<MaskType, MaskType> (array_a[i], array_b[i]);
|
||||
}
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, vMaskType> (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, float_c));
|
||||
|
||||
// test out-of-place with one param being scalar
|
||||
for (size_t i = 0; i < SIMDRegister<MaskType>::SIMDNumElements; ++i)
|
||||
array_c[i] = Operation::template outofplace<MaskType, MaskType> (array_a[i], static_cast<MaskType> (9));
|
||||
memcpy (float_a, array_a, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
memcpy (float_c, array_c, sizeof (type) * SIMDRegister<type>::SIMDNumElements);
|
||||
|
||||
c = Operation::template outofplace<SIMDRegister<type>, MaskType> (a, static_cast<MaskType> (9));
|
||||
|
||||
u.expect (vecEqualToArray (a, float_a));
|
||||
u.expect (vecEqualToArray (b, array_b));
|
||||
u.expect (vecEqualToArray (c, float_c));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckComparisonOps
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
typedef typename SIMDRegister<type>::vMaskType vMaskType;
|
||||
typedef typename SIMDRegister<type>::MaskType MaskType;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
// set-up
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_eq [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_neq [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_lt [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_le [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_gt [SIMDRegister<type>::SIMDNumElements];
|
||||
MaskType array_ge [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
// do check
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
array_eq [j] = (array_a[j] == array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_neq [j] = (array_a[j] != array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_lt [j] = (array_a[j] < array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_le [j] = (array_a[j] <= array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_gt [j] = (array_a[j] > array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
array_ge [j] = (array_a[j] >= array_b[j]) ? static_cast<MaskType> (-1) : 0;
|
||||
}
|
||||
|
||||
SIMDRegister<type> a (static_cast<type> (0));
|
||||
SIMDRegister<type> b (static_cast<type> (0));
|
||||
|
||||
vMaskType eq, neq, lt, le, gt, ge;
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
|
||||
eq = SIMDRegister<type>::equal (a, b);
|
||||
neq = SIMDRegister<type>::notEqual (a, b);
|
||||
lt = SIMDRegister<type>::lessThan (a, b);
|
||||
le = SIMDRegister<type>::lessThanOrEqual (a, b);
|
||||
gt = SIMDRegister<type>::greaterThan (a, b);
|
||||
ge = SIMDRegister<type>::greaterThanOrEqual (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (eq, array_eq ));
|
||||
u.expect (vecEqualToArray (neq, array_neq));
|
||||
u.expect (vecEqualToArray (lt, array_lt ));
|
||||
u.expect (vecEqualToArray (le, array_le ));
|
||||
u.expect (vecEqualToArray (gt, array_gt ));
|
||||
u.expect (vecEqualToArray (ge, array_ge ));
|
||||
|
||||
do
|
||||
{
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
} while (std::equal (array_a, array_a + SIMDRegister<type>::SIMDNumElements, array_b));
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
u.expect (a != b);
|
||||
u.expect (b != a);
|
||||
u.expect (! (a == b));
|
||||
u.expect (! (b == a));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a, array_a);
|
||||
copy (b, array_a);
|
||||
|
||||
u.expect (a == b);
|
||||
u.expect (b == a);
|
||||
u.expect (! (a != b));
|
||||
u.expect (! (b != a));
|
||||
|
||||
type scalar = a[0];
|
||||
a = SIMDRegister<type>::expand (scalar);
|
||||
|
||||
u.expect (a == scalar);
|
||||
u.expect (! (a != scalar));
|
||||
|
||||
scalar--;
|
||||
|
||||
u.expect (a != scalar);
|
||||
u.expect (! (a == scalar));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckMultiplyAdd
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
// set-up
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_c [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_d [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_a, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_b, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_c, SIMDRegister<type>::SIMDNumElements, random);
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array_d, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
// check
|
||||
for (size_t i = 0; i < SIMDRegister<type>::SIMDNumElements; ++i)
|
||||
array_d[i] = array_a[i] + (array_b[i] * array_c[i]);
|
||||
|
||||
SIMDRegister<type> a, b, c, d;
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
copy (c, array_c);
|
||||
|
||||
d = SIMDRegister<type>::multiplyAdd (a, b, c);
|
||||
|
||||
u.expect (vecEqualToArray (d, array_d));
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckMinMax
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
type array_a [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_b [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_min [SIMDRegister<type>::SIMDNumElements];
|
||||
type array_max [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
array_a[j] = static_cast<type> (random.nextInt (127));
|
||||
array_b[j] = static_cast<type> (random.nextInt (127));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
array_min[j] = (array_a[j] < array_b[j]) ? array_a[j] : array_b[j];
|
||||
array_max[j] = (array_a[j] > array_b[j]) ? array_a[j] : array_b[j];
|
||||
}
|
||||
|
||||
SIMDRegister<type> a (static_cast<type> (0));
|
||||
SIMDRegister<type> b (static_cast<type> (0));
|
||||
SIMDRegister<type> vMin (static_cast<type> (0));
|
||||
SIMDRegister<type> vMax (static_cast<type> (0));
|
||||
|
||||
copy (a, array_a);
|
||||
copy (b, array_b);
|
||||
|
||||
vMin = jmin (a, b);
|
||||
vMax = jmax (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (vMin, array_min));
|
||||
u.expect (vecEqualToArray (vMax, array_max));
|
||||
|
||||
copy (vMin, array_a);
|
||||
copy (vMax, array_a);
|
||||
|
||||
vMin = SIMDRegister<type>::min (a, b);
|
||||
vMax = SIMDRegister<type>::max (a, b);
|
||||
|
||||
u.expect (vecEqualToArray (vMin, array_min));
|
||||
u.expect (vecEqualToArray (vMax, array_max));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckSum
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
type array [SIMDRegister<type>::SIMDNumElements];
|
||||
type sumCheck = 0;
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
{
|
||||
sumCheck += array[j];
|
||||
}
|
||||
|
||||
SIMDRegister<type> a;
|
||||
copy (a, array);
|
||||
|
||||
u.expect (SIMDRegister_test_internal::difference (sumCheck, a.sum()) < 1e-4);
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckAbs
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
type inArray[SIMDRegister<type>::SIMDNumElements];
|
||||
type outArray[SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (inArray, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
SIMDRegister<type> a;
|
||||
copy (a, inArray);
|
||||
a = SIMDRegister<type>::abs (a);
|
||||
|
||||
auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : type (-x); };
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
outArray[j] = calcAbs (inArray[j]);
|
||||
|
||||
u.expect (vecEqualToArray (a, outArray));
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckTruncate
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
type inArray[SIMDRegister<type>::SIMDNumElements];
|
||||
type outArray[SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (inArray, SIMDRegister<type>::SIMDNumElements, random);
|
||||
|
||||
SIMDRegister<type> a;
|
||||
copy (a, inArray);
|
||||
a = SIMDRegister<type>::truncate (a);
|
||||
|
||||
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
|
||||
outArray[j] = (type) (int) inArray[j];
|
||||
|
||||
u.expect (vecEqualToArray (a, outArray));
|
||||
}
|
||||
};
|
||||
|
||||
struct CheckBoolEquals
|
||||
{
|
||||
template <typename type>
|
||||
static void run (UnitTest& u, Random& random)
|
||||
{
|
||||
bool is_signed = std::is_signed<type>::value;
|
||||
type array [SIMDRegister<type>::SIMDNumElements];
|
||||
|
||||
auto value = is_signed ? static_cast<type> ((random.nextFloat() * 16.0) - 8.0)
|
||||
: static_cast<type> (random.nextFloat() * 8.0);
|
||||
|
||||
std::fill (array, array + SIMDRegister<type>::SIMDNumElements, value);
|
||||
SIMDRegister<type> a, b;
|
||||
copy (a, array);
|
||||
|
||||
u.expect (a == value);
|
||||
u.expect (! (a != value));
|
||||
value += 1;
|
||||
|
||||
u.expect (a != value);
|
||||
u.expect (! (a == value));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (a, array);
|
||||
copy (b, array);
|
||||
|
||||
u.expect (a == b);
|
||||
u.expect (! (a != b));
|
||||
|
||||
SIMDRegister_test_internal::VecFiller<type>::fill (array, SIMDRegister<type>::SIMDNumElements, random);
|
||||
copy (b, array);
|
||||
|
||||
u.expect (a != b);
|
||||
u.expect (! (a == b));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <class TheTest>
|
||||
void runTestFloatingPoint (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <class TheTest>
|
||||
void runTestForAllTypes (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
TheTest::template run<int8_t> (*this, random);
|
||||
TheTest::template run<uint8_t> (*this, random);
|
||||
TheTest::template run<int16_t> (*this, random);
|
||||
TheTest::template run<uint16_t>(*this, random);
|
||||
TheTest::template run<int32_t> (*this, random);
|
||||
TheTest::template run<uint32_t>(*this, random);
|
||||
TheTest::template run<int64_t> (*this, random);
|
||||
TheTest::template run<uint64_t>(*this, random);
|
||||
TheTest::template run<std::complex<float>> (*this, random);
|
||||
TheTest::template run<std::complex<double>> (*this, random);
|
||||
}
|
||||
|
||||
template <class TheTest>
|
||||
void runTestNonComplex (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
TheTest::template run<int8_t> (*this, random);
|
||||
TheTest::template run<uint8_t> (*this, random);
|
||||
TheTest::template run<int16_t> (*this, random);
|
||||
TheTest::template run<uint16_t>(*this, random);
|
||||
TheTest::template run<int32_t> (*this, random);
|
||||
TheTest::template run<uint32_t>(*this, random);
|
||||
TheTest::template run<int64_t> (*this, random);
|
||||
TheTest::template run<uint64_t>(*this, random);
|
||||
}
|
||||
|
||||
template <class TheTest>
|
||||
void runTestSigned (const char* unitTestName)
|
||||
{
|
||||
beginTest (unitTestName);
|
||||
|
||||
Random random = getRandom();
|
||||
|
||||
TheTest::template run<float> (*this, random);
|
||||
TheTest::template run<double> (*this, random);
|
||||
TheTest::template run<int8_t> (*this, random);
|
||||
TheTest::template run<int16_t> (*this, random);
|
||||
TheTest::template run<int32_t> (*this, random);
|
||||
TheTest::template run<int64_t> (*this, random);
|
||||
}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
runTestForAllTypes<InitializationTest> ("InitializationTest");
|
||||
|
||||
runTestForAllTypes<AccessTest> ("AccessTest");
|
||||
|
||||
runTestForAllTypes<OperatorTests<Addition>> ("AdditionOperators");
|
||||
runTestForAllTypes<OperatorTests<Subtraction>> ("SubtractionOperators");
|
||||
runTestForAllTypes<OperatorTests<Multiplication>> ("MultiplicationOperators");
|
||||
|
||||
runTestForAllTypes<BitOperatorTests<BitAND>> ("BitANDOperators");
|
||||
runTestForAllTypes<BitOperatorTests<BitOR>> ("BitOROperators");
|
||||
runTestForAllTypes<BitOperatorTests<BitXOR>> ("BitXOROperators");
|
||||
|
||||
runTestNonComplex<CheckComparisonOps> ("CheckComparisons");
|
||||
runTestNonComplex<CheckBoolEquals> ("CheckBoolEquals");
|
||||
runTestNonComplex<CheckMinMax> ("CheckMinMax");
|
||||
|
||||
runTestForAllTypes<CheckMultiplyAdd> ("CheckMultiplyAdd");
|
||||
runTestForAllTypes<CheckSum> ("CheckSum");
|
||||
|
||||
runTestSigned<CheckAbs> ("CheckAbs");
|
||||
|
||||
runTestFloatingPoint<CheckTruncate> ("CheckTruncate");
|
||||
}
|
||||
};
|
||||
|
||||
static SIMDRegisterUnitTests SIMDRegisterUnitTests;
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
Reference in New Issue
Block a user