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:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View 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

View 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

View 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); }
}
}

View 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

View 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

View 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

View 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